From 818a7b226bb79fe6e7e82d25ad329419c931287c Mon Sep 17 00:00:00 2001 From: Kai Koehne <kai.koehne@nokia.com> Date: Thu, 7 Jan 2010 12:14:35 +0100 Subject: [PATCH] New QmlDesigner plugin & Design mode This adds a new "Design" mode that can be used to manipulate qml files in a visual way. It will only get build if you have the declarativeui module in Qt. This is a squashed import from the Bauhaus project. Share & enjoy :) --- .gitignore | 3 + share/qtcreator/qmldesigner/Button.qml | 86 + share/qtcreator/qmldesigner/gradient.png | Bin 0 -> 340205 bytes .../propertyeditor/Qt/AnchorBox.qml | 199 + .../Qt/BorderImageSpecifics.qml | 69 + .../propertyeditor/Qt/CheckBox.qml | 38 + .../propertyeditor/Qt/ColumnSpecifics.qml | 29 + .../propertyeditor/Qt/DoubleSpinBox.qml | 144 + .../propertyeditor/Qt/Extended.qml | 191 + .../propertyeditor/Qt/ExtendedPane.qml | 30 + .../propertyeditor/Qt/ExtendedSwitches.qml | 60 + .../propertyeditor/Qt/FlickableSpecifics.qml | 111 + .../propertyeditor/Qt/FlipableSpecifics.qml | 135 + .../propertyeditor/Qt/Geometry.qml | 173 + .../propertyeditor/Qt/GridSpecifics.qml | 49 + .../propertyeditor/Qt/GroupBox.qml | 57 + .../propertyeditor/Qt/GroupBoxOption.qml | 21 + .../propertyeditor/Qt/HorizontalLine.qml | 8 + .../propertyeditor/Qt/ImageSpecifics.qml | 26 + .../propertyeditor/Qt/IntEditor.qml | 51 + .../propertyeditor/Qt/ItemPane.qml | 104 + .../qmldesigner/propertyeditor/Qt/Layout.qml | 22 + .../propertyeditor/Qt/LayoutPane.qml | 29 + .../propertyeditor/Qt/ListView.qml | 85 + .../propertyeditor/Qt/ListViewSpecifics.qml | 106 + .../propertyeditor/Qt/Modifiers.qml | 218 + .../propertyeditor/Qt/PropertyFrame.qml | 10 + .../propertyeditor/Qt/RectangleSpecifics.qml | 134 + .../propertyeditor/Qt/ResetPane.qml | 41 + .../propertyeditor/Qt/RowSpecifics.qml | 29 + .../propertyeditor/Qt/ScrollArea.qml | 14 + .../qmldesigner/propertyeditor/Qt/SpinBox.qml | 72 + .../propertyeditor/Qt/Switches.qml | 85 + .../propertyeditor/Qt/TextEditSpecifics.qml | 273 + .../propertyeditor/Qt/TextInputSpecifics.qml | 176 + .../propertyeditor/Qt/TextSpecifics.qml | 172 + .../qmldesigner/propertyeditor/Qt/Type.qml | 94 + .../propertyeditor/Qt/WebViewSpecifics.qml | 96 + .../propertyeditor/Qt/anchorbox.css | 32 + .../propertyeditor/Qt/emptyPane.qml | 45 + .../propertyeditor/Qt/images/behaivour.png | Bin 0 -> 433 bytes .../Qt/images/blended-image-icon.png | Bin 0 -> 340 bytes .../propertyeditor/Qt/images/button.png | Bin 0 -> 687 bytes .../propertyeditor/Qt/images/default-icon.png | Bin 0 -> 341 bytes .../propertyeditor/Qt/images/downArrow.png | Bin 0 -> 855 bytes .../propertyeditor/Qt/images/expression.png | Bin 0 -> 575 bytes .../propertyeditor/Qt/images/extended.png | Bin 0 -> 293 bytes .../propertyeditor/Qt/images/grid-icon.png | Bin 0 -> 369 bytes .../propertyeditor/Qt/images/image-icon.png | Bin 0 -> 292 bytes .../propertyeditor/Qt/images/item-icon.png | Bin 0 -> 293 bytes .../propertyeditor/Qt/images/layout.png | Bin 0 -> 369 bytes .../propertyeditor/Qt/images/leftArrow.png | Bin 0 -> 885 bytes .../propertyeditor/Qt/images/list-icon.png | Bin 0 -> 424 bytes .../Qt/images/mouse-region-icon.png | Bin 0 -> 511 bytes .../propertyeditor/Qt/images/placeholder.png | Bin 0 -> 311 bytes .../propertyeditor/Qt/images/rect-icon.png | Bin 0 -> 237 bytes .../propertyeditor/Qt/images/reset-button.png | Bin 0 -> 741 bytes .../propertyeditor/Qt/images/rightArrow.png | Bin 0 -> 871 bytes .../propertyeditor/Qt/images/standard.png | Bin 0 -> 340 bytes .../propertyeditor/Qt/images/submenu.png | Bin 0 -> 447 bytes .../Qt/images/text-edit-icon.png | Bin 0 -> 368 bytes .../propertyeditor/Qt/images/text-icon.png | Bin 0 -> 582 bytes .../propertyeditor/Qt/images/upArrow.png | Bin 0 -> 849 bytes .../propertyeditor/Qt/layoutWidget.css | 35 + .../propertyeditor/Qt/propertyEditor.css | 397 + .../propertyeditor/Qt/specialCheckBox.css | 20 + .../qmldesigner/propertyeditor/Qt/switch.css | 32 + .../propertyeditor/Qt/typeLabel.css | 8 + share/qtcreator/qmldesigner/welcome-card.png | Bin 0 -> 59043 bytes share/qtcreator/qmldesigner/welcomescreen.qml | 147 + share/qtcreator/static.pro | 3 +- src/plugins/plugins.pro | 7 +- .../qmldesigner/QmlDesigner.pluginspec | 25 + src/plugins/qmldesigner/application.cpp | 46 + src/plugins/qmldesigner/application.h | 40 + .../formeditor/abstractformeditortool.cpp | 190 + .../formeditor/abstractformeditortool.h | 103 + .../components/formeditor/anchor.png | Bin 0 -> 415 bytes .../formeditor/anchorcontroller.cpp | 537 ++ .../components/formeditor/anchorcontroller.h | 117 + .../formeditor/anchorhandleitem.cpp | 212 + .../components/formeditor/anchorhandleitem.h | 91 + .../components/formeditor/anchorindicator.cpp | 116 + .../components/formeditor/anchorindicator.h | 64 + .../formeditor/anchorlinecontroller.cpp | 236 + .../formeditor/anchorlinecontroller.h | 93 + .../formeditor/anchorlinehandleitem.cpp | 148 + .../formeditor/anchorlinehandleitem.h | 86 + .../formeditor/anchorlineindicator.cpp | 131 + .../formeditor/anchorlineindicator.h | 69 + .../formeditor/anchormanipulator.cpp | 144 + .../components/formeditor/anchormanipulator.h | 71 + .../components/formeditor/anchortool.cpp | 226 + .../components/formeditor/anchortool.h | 88 + .../components/formeditor/controlelement.cpp | 66 + .../components/formeditor/controlelement.h | 57 + .../components/formeditor/dragtool.cpp | 315 + .../components/formeditor/dragtool.h | 105 + .../components/formeditor/formeditor.pri | 84 + .../components/formeditor/formeditor.qrc | 12 + .../formeditor/formeditorgraphicsview.cpp | 88 + .../formeditor/formeditorgraphicsview.h | 51 + .../components/formeditor/formeditoritem.cpp | 323 + .../components/formeditor/formeditoritem.h | 131 + .../formeditor/formeditormainview.cpp | 401 + .../formeditor/formeditormainview.h | 123 + .../formeditor/formeditormainwidget.cpp | 156 + .../formeditor/formeditormainwidget.h | 74 + .../formeditor/formeditornodeinstanceview.cpp | 48 + .../formeditor/formeditornodeinstanceview.h | 50 + .../components/formeditor/formeditorscene.cpp | 364 + .../components/formeditor/formeditorscene.h | 133 + .../formeditor/formeditorsubwindow.cpp | 38 + .../formeditor/formeditorsubwindow.h | 45 + .../components/formeditor/formeditorview.cpp | 531 ++ .../components/formeditor/formeditorview.h | 141 + .../formeditor/formeditorwidget.cpp | 221 + .../components/formeditor/formeditorwidget.h | 91 + .../components/formeditor/itemcreatortool.cpp | 191 + .../components/formeditor/itemcreatortool.h | 82 + .../formeditor/itemutilfunctions.cpp | 78 + .../components/formeditor/itemutilfunctions.h | 48 + .../components/formeditor/layeritem.cpp | 73 + .../components/formeditor/layeritem.h | 66 + .../formeditor/manipulatorlayeritem.h | 39 + .../components/formeditor/movemanipulator.cpp | 363 + .../components/formeditor/movemanipulator.h | 114 + .../components/formeditor/movetool.cpp | 345 + .../components/formeditor/movetool.h | 97 + .../formeditor/numberseriesaction.cpp | 88 + .../formeditor/numberseriesaction.h | 66 + .../formeditor/onedimensionalcluster.cpp | 146 + .../formeditor/onedimensionalcluster.h | 70 + .../components/formeditor/resize_handle.png | Bin 0 -> 160 bytes .../formeditor/resizecontroller.cpp | 273 + .../components/formeditor/resizecontroller.h | 104 + .../formeditor/resizehandleitem.cpp | 118 + .../components/formeditor/resizehandleitem.h | 84 + .../components/formeditor/resizeindicator.cpp | 91 + .../components/formeditor/resizeindicator.h | 81 + .../formeditor/resizemanipulator.cpp | 504 ++ .../components/formeditor/resizemanipulator.h | 91 + .../components/formeditor/resizetool.cpp | 177 + .../components/formeditor/resizetool.h | 76 + .../rubberbandselectionmanipulator.cpp | 153 + .../rubberbandselectionmanipulator.h | 83 + .../components/formeditor/scaleitem.cpp | 49 + .../components/formeditor/scaleitem.h | 53 + .../formeditor/scalemanipulator.cpp | 45 + .../components/formeditor/scalemanipulator.h | 63 + .../formeditor/selectionindicator.cpp | 118 + .../formeditor/selectionindicator.h | 65 + .../formeditor/selectionrectangle.cpp | 87 + .../formeditor/selectionrectangle.h | 63 + .../components/formeditor/selectiontool.cpp | 280 + .../components/formeditor/selectiontool.h | 93 + .../formeditor/singleselectionmanipulator.cpp | 132 + .../formeditor/singleselectionmanipulator.h | 72 + .../components/formeditor/snapper.cpp | 626 ++ .../components/formeditor/snapper.h | 112 + .../components/formeditor/snapping.png | Bin 0 -> 359 bytes .../formeditor/snappinglinecreator.cpp | 190 + .../formeditor/snappinglinecreator.h | 102 + .../components/formeditor/toolbox.cpp | 81 + .../components/formeditor/toolbox.h | 52 + .../components/formeditor/transform.png | Bin 0 -> 17296 bytes .../components/formeditor/zoomaction.cpp | 116 + .../components/formeditor/zoomaction.h | 68 + .../allpropertiesviewcontroller.cpp | 64 + .../integration/allpropertiesviewcontroller.h | 54 + .../integration/componentaction.cpp | 74 + .../components/integration/componentaction.h | 66 + .../components/integration/componentview.cpp | 179 + .../components/integration/componentview.h | 105 + .../integration/designdocumentcontroller.cpp | 815 +++ .../integration/designdocumentcontroller.h | 142 + .../designdocumentcontrollerview.cpp | 137 + .../designdocumentcontrollerview.h | 78 + .../components/integration/documentcloser.cpp | 139 + .../components/integration/documentcloser.h | 67 + .../components/integration/integration.pri | 34 + .../integration/integrationcore.cpp | 94 + .../components/integration/integrationcore.h | 61 + .../multipledocumentscontroller.cpp | 353 + .../integration/multipledocumentscontroller.h | 120 + .../integration/navigatorcontroller.cpp | 53 + .../integration/navigatorcontroller.h | 48 + .../stackedutilitypanelcontroller.cpp | 76 + .../stackedutilitypanelcontroller.h | 60 + .../integration/stateseditorcontroller.cpp | 50 + .../integration/stateseditorcontroller.h | 48 + .../integration/utilitypanelcontroller.cpp | 54 + .../integration/utilitypanelcontroller.h | 55 + .../integration/widgetboxcontroller.cpp | 51 + .../integration/widgetboxcontroller.h | 48 + .../components/integration/xuifiledialog.cpp | 91 + .../components/integration/xuifiledialog.h | 65 + .../itemlibrary/customdraganddrop.cpp | 301 + .../itemlibrary/customdraganddrop.h | 127 + .../components/itemlibrary/default-icon.png | Bin 0 -> 341 bytes .../components/itemlibrary/itemlibrary.cpp | 319 + .../components/itemlibrary/itemlibrary.h | 80 + .../components/itemlibrary/itemlibrary.pri | 12 + .../components/itemlibrary/itemlibrary.qrc | 5 + .../components/itemlibrary/itemlibrary.ui | 122 + .../itemlibrary/itemlibrarymodel.cpp | 297 + .../components/itemlibrary/itemlibrarymodel.h | 89 + .../qmldesigner/components/logger/logger.cpp | 171 + .../qmldesigner/components/logger/logger.h | 81 + .../qmldesigner/components/logger/logger.pri | 6 + .../components/navigator/navigator.pri | 9 + .../navigator/navigatortreemodel.cpp | 393 + .../components/navigator/navigatortreemodel.h | 115 + .../components/navigator/navigatorview.cpp | 227 + .../components/navigator/navigatorview.h | 98 + .../components/navigator/navigatorwidget.cpp | 88 + .../components/navigator/navigatorwidget.h | 56 + .../components/pluginmanager/iplugin.cpp | 36 + .../components/pluginmanager/iplugin.h | 56 + .../pluginmanager/pluginmanager.cpp | 174 + .../components/pluginmanager/pluginmanager.h | 75 + .../pluginmanager/pluginmanager.pri | 10 + .../components/pluginmanager/pluginpath.cpp | 193 + .../components/pluginmanager/pluginpath.h | 98 + .../propertyeditor/allpropertiesbox.cpp | 85 + .../propertyeditor/allpropertiesbox.h | 64 + .../propertyeditor/basiclayouts.cpp | 83 + .../components/propertyeditor/basiclayouts.h | 197 + .../propertyeditor/basicwidgets.cpp | 1142 +++ .../components/propertyeditor/basicwidgets.h | 132 + .../propertyeditor/behaviordialog.cpp | 174 + .../propertyeditor/behaviordialog.h | 98 + .../propertyeditor/behaviordialog.ui | 472 ++ .../components/propertyeditor/colorwidget.cpp | 259 + .../components/propertyeditor/colorwidget.h | 108 + .../components/propertyeditor/filewidget.cpp | 87 + .../components/propertyeditor/filewidget.h | 89 + .../components/propertyeditor/fontwidget.cpp | 172 + .../components/propertyeditor/fontwidget.h | 100 + .../genericpropertieswidget.cpp | 380 + .../propertyeditor/genericpropertieswidget.h | 96 + .../propertyeditor/gradientdialog.cpp | 353 + .../propertyeditor/gradientdialog.h | 89 + .../propertyeditor/gradientdialog.ui | 122 + .../propertyeditor/gradienteditor.cpp | 933 +++ .../propertyeditor/gradienteditor.h | 113 + .../propertyeditor/gradienteditor.ui | 1255 ++++ .../propertyeditor/images/button2_hovered.png | Bin 0 -> 898 bytes .../propertyeditor/images/button2_normal.png | Bin 0 -> 898 bytes .../propertyeditor/images/button2_pressed.png | Bin 0 -> 898 bytes .../propertyeditor/images/button_hovered.png | Bin 0 -> 390 bytes .../propertyeditor/images/button_normal.png | Bin 0 -> 379 bytes .../propertyeditor/images/button_pressed.png | Bin 0 -> 1328 bytes .../propertyeditor/images/frame.png | Bin 0 -> 4826 bytes .../propertyeditor/images/gradient.png | Bin 0 -> 604 bytes .../propertyeditor/layoutwidget.cpp | 106 + .../components/propertyeditor/layoutwidget.h | 137 + .../propertyeditor/propertyeditor.cpp | 583 ++ .../propertyeditor/propertyeditor.h | 126 + .../propertyeditor/propertyeditor.pri | 34 + .../propertyeditor/propertyeditor.qrc | 12 + .../propertyeditor/propertyeditorvalue.cpp | 343 + .../propertyeditor/propertyeditorvalue.h | 155 + .../propertyeditor/qlayoutobject.cpp | 57 + .../components/propertyeditor/qlayoutobject.h | 69 + .../propertyeditor/qmlanchorbindingproxy.cpp | 287 + .../propertyeditor/qmlanchorbindingproxy.h | 117 + .../propertyeditor/qproxylayoutitem.cpp | 176 + .../propertyeditor/qproxylayoutitem.h | 107 + .../qtgradienteditor/images/down.png | Bin 0 -> 594 bytes .../qtgradienteditor/images/edit.png | Bin 0 -> 503 bytes .../qtgradienteditor/images/editdelete.png | Bin 0 -> 831 bytes .../qtgradienteditor/images/minus.png | Bin 0 -> 250 bytes .../qtgradienteditor/images/plus.png | Bin 0 -> 462 bytes .../qtgradienteditor/images/spreadpad.png | Bin 0 -> 151 bytes .../qtgradienteditor/images/spreadreflect.png | Bin 0 -> 165 bytes .../qtgradienteditor/images/spreadrepeat.png | Bin 0 -> 156 bytes .../qtgradienteditor/images/typeconical.png | Bin 0 -> 937 bytes .../qtgradienteditor/images/typelinear.png | Bin 0 -> 145 bytes .../qtgradienteditor/images/typeradial.png | Bin 0 -> 583 bytes .../qtgradienteditor/images/up.png | Bin 0 -> 692 bytes .../qtgradienteditor/images/zoomin.png | Bin 0 -> 1208 bytes .../qtgradienteditor/images/zoomout.png | Bin 0 -> 1226 bytes .../qtgradienteditor/qtcolorbutton.cpp | 272 + .../qtgradienteditor/qtcolorbutton.h | 86 + .../qtgradienteditor/qtcolorbutton.pri | 4 + .../qtgradienteditor/qtcolorline.cpp | 1122 +++ .../qtgradienteditor/qtcolorline.h | 124 + .../qtgradienteditor/qtgradientdialog.cpp | 353 + .../qtgradienteditor/qtgradientdialog.h | 87 + .../qtgradienteditor/qtgradientdialog.ui | 121 + .../qtgradienteditor/qtgradienteditor.cpp | 952 +++ .../qtgradienteditor/qtgradienteditor.h | 111 + .../qtgradienteditor/qtgradienteditor.pri | 33 + .../qtgradienteditor/qtgradienteditor.qrc | 18 + .../qtgradienteditor/qtgradienteditor.ui | 1377 ++++ .../qtgradienteditor/qtgradientmanager.cpp | 135 + .../qtgradienteditor/qtgradientmanager.h | 92 + .../qtgradientstopscontroller.cpp | 724 ++ .../qtgradientstopscontroller.h | 106 + .../qtgradienteditor/qtgradientstopsmodel.cpp | 477 ++ .../qtgradienteditor/qtgradientstopsmodel.h | 121 + .../qtgradientstopswidget.cpp | 1154 +++ .../qtgradienteditor/qtgradientstopswidget.h | 115 + .../qtgradienteditor/qtgradientutils.cpp | 420 ++ .../qtgradienteditor/qtgradientutils.h | 66 + .../qtgradienteditor/qtgradientview.cpp | 292 + .../qtgradienteditor/qtgradientview.h | 99 + .../qtgradienteditor/qtgradientview.ui | 135 + .../qtgradienteditor/qtgradientviewdialog.cpp | 89 + .../qtgradienteditor/qtgradientviewdialog.h | 75 + .../qtgradienteditor/qtgradientviewdialog.ui | 121 + .../qtgradienteditor/qtgradientwidget.cpp | 815 +++ .../qtgradienteditor/qtgradientwidget.h | 120 + .../components/propertyeditor/resetwidget.cpp | 129 + .../components/propertyeditor/resetwidget.h | 102 + .../resources/images/checkbox_checked.png | Bin 0 -> 299 bytes .../images/checkbox_checked_hover.png | Bin 0 -> 302 bytes .../images/checkbox_checked_pressed.png | Bin 0 -> 474 bytes .../resources/images/checkbox_unchecked.png | Bin 0 -> 190 bytes .../images/checkbox_unchecked_hover.png | Bin 0 -> 190 bytes .../images/checkbox_unchecked_pressed.png | Bin 0 -> 345 bytes .../resources/images/down_arrow.png | Bin 0 -> 271 bytes .../resources/images/down_arrow_disabled.png | Bin 0 -> 258 bytes .../components/resources/images/frame.png | Bin 0 -> 544 bytes .../resources/images/pushbutton.png | Bin 0 -> 508 bytes .../resources/images/pushbutton_hover.png | Bin 0 -> 503 bytes .../resources/images/pushbutton_pressed.png | Bin 0 -> 485 bytes .../resources/images/radiobutton_checked.png | Bin 0 -> 355 bytes .../images/radiobutton_checked_hover.png | Bin 0 -> 532 bytes .../images/radiobutton_checked_pressed.png | Bin 0 -> 599 bytes .../images/radiobutton_unchecked.png | Bin 0 -> 240 bytes .../images/radiobutton_unchecked_hover.png | Bin 0 -> 492 bytes .../images/radiobutton_unchecked_pressed.png | Bin 0 -> 556 bytes .../components/resources/images/sizegrip.png | Bin 0 -> 129 bytes .../components/resources/images/spindown.png | Bin 0 -> 227 bytes .../resources/images/spindown_hover.png | Bin 0 -> 227 bytes .../resources/images/spindown_off.png | Bin 0 -> 249 bytes .../resources/images/spindown_pressed.png | Bin 0 -> 222 bytes .../components/resources/images/spinup.png | Bin 0 -> 236 bytes .../resources/images/spinup_hover.png | Bin 0 -> 236 bytes .../resources/images/spinup_off.png | Bin 0 -> 274 bytes .../resources/images/spinup_pressed.png | Bin 0 -> 232 bytes .../components/resources/images/up_arrow.png | Bin 0 -> 273 bytes .../resources/images/up_arrow_disabled.png | Bin 0 -> 266 bytes .../components/resources/resources.pri | 3 + .../components/resources/resources.qrc | 37 + .../components/resources/scrollbar.css | 42 + .../components/resources/stylesheet.css | 72 + .../resources/templates/Standard/Form.xml | 10 + .../components/resources/toolbutton.css | 23 + .../components/stateseditor/stateseditor.pri | 10 + .../components/stateseditor/stateseditor.qrc | 5 + .../stateseditor/stateseditormodel.cpp | 147 + .../stateseditor/stateseditormodel.h | 79 + .../stateseditor/stateseditorview.cpp | 438 ++ .../stateseditor/stateseditorview.h | 100 + .../stateseditor/stateseditorwidget.cpp | 218 + .../stateseditor/stateseditorwidget.h | 65 + .../components/stateseditor/stateslist.qml | 337 + .../themeloader/qts60stylethemeio.cpp | 357 + .../themeloader/qts60stylethemeio.h | 58 + .../themeloader/qts60stylethemeio.pri | 6 + src/plugins/qmldesigner/config.pri | 27 + src/plugins/qmldesigner/core/core.pri | 183 + src/plugins/qmldesigner/core/core.qrc | 6 + src/plugins/qmldesigner/core/depend_paths.pri | 1 + .../qmldesigner/core/exceptions/exception.cpp | 192 + .../exceptions/invalidargumentexception.cpp | 75 + .../core/exceptions/invalididexception.cpp | 45 + .../exceptions/invalidmetainfoexception.cpp | 64 + .../exceptions/invalidmodelnodeexception.cpp | 64 + .../exceptions/invalidmodelstateexception.cpp | 65 + .../invalidnodeinstanceexception.cpp | 65 + .../exceptions/invalidnodestateexception.cpp | 65 + .../exceptions/invalidpropertyexception.cpp | 74 + .../exceptions/invalidqmlsourceexception.cpp | 71 + .../invalidreparentingexception.cpp | 62 + .../exceptions/invalidslideindexexception.cpp | 64 + .../exceptions/modificationgroupexception.cpp | 63 + .../noanchoringpossibleexception.cpp | 46 + .../exceptions/notimplementedexception.cpp | 45 + .../exceptions/removebasestateexception.cpp | 64 + .../filemanager/addarraymembervisitor.cpp | 118 + .../core/filemanager/addarraymembervisitor.h | 72 + .../core/filemanager/addobjectvisitor.cpp | 93 + .../core/filemanager/addobjectvisitor.h | 62 + .../core/filemanager/addpropertyvisitor.cpp | 136 + .../core/filemanager/addpropertyvisitor.h | 68 + .../filemanager/astobjecttextextractor.cpp | 75 + .../core/filemanager/astobjecttextextractor.h | 59 + .../core/filemanager/changeimportsvisitor.cpp | 80 + .../core/filemanager/changeimportsvisitor.h | 61 + .../filemanager/changeobjecttypevisitor.cpp | 81 + .../filemanager/changeobjecttypevisitor.h | 60 + .../filemanager/changepropertyvisitor.cpp | 191 + .../core/filemanager/changepropertyvisitor.h | 70 + .../core/filemanager/changeset.cpp | 359 + .../qmldesigner/core/filemanager/changeset.h | 120 + .../core/filemanager/filemanager.pri | 41 + .../filemanager/firstdefinitionfinder.cpp | 98 + .../core/filemanager/firstdefinitionfinder.h | 63 + .../core/filemanager/idcollector.cpp | 93 + .../core/filemanager/idcollector.h | 69 + .../qmldesigner/core/filemanager/indenter.cpp | 90 + .../qmldesigner/core/filemanager/indenter.h | 43 + .../moveobjectbeforeobjectvisitor.cpp | 158 + .../moveobjectbeforeobjectvisitor.h | 79 + .../core/filemanager/moveobjectvisitor.cpp | 293 + .../core/filemanager/moveobjectvisitor.h | 72 + .../filemanager/objectlengthcalculator.cpp | 91 + .../core/filemanager/objectlengthcalculator.h | 60 + .../core/filemanager/parser/parser.pri | 22 + .../core/filemanager/parser/qmljs.g | 3029 ++++++++ .../core/filemanager/parser/qmljsast.cpp | 943 +++ .../core/filemanager/parser/qmljsast_p.h | 2665 +++++++ .../core/filemanager/parser/qmljsastfwd_p.h | 176 + .../filemanager/parser/qmljsastvisitor.cpp | 46 + .../filemanager/parser/qmljsastvisitor_p.h | 323 + .../core/filemanager/parser/qmljsengine_p.cpp | 198 + .../core/filemanager/parser/qmljsengine_p.h | 161 + .../core/filemanager/parser/qmljsglobal_p.h | 43 + .../core/filemanager/parser/qmljsgrammar.cpp | 960 +++ .../core/filemanager/parser/qmljsgrammar_p.h | 207 + .../core/filemanager/parser/qmljslexer.cpp | 1141 +++ .../core/filemanager/parser/qmljslexer_p.h | 237 + .../filemanager/parser/qmljsmemorypool_p.h | 120 + .../core/filemanager/parser/qmljsnodepool_p.h | 127 + .../core/filemanager/parser/qmljsparser.cpp | 1812 +++++ .../core/filemanager/parser/qmljsparser_p.h | 230 + .../core/filemanager/qmldocument.cpp | 165 + .../core/filemanager/qmldocument.h | 109 + .../core/filemanager/qmleditor_global.h | 41 + .../core/filemanager/qmlrefactoring.cpp | 160 + .../core/filemanager/qmlrefactoring.h | 80 + .../core/filemanager/qmlrewriter.cpp | 317 + .../core/filemanager/qmlrewriter.h | 97 + .../core/filemanager/qmlsymbol.cpp | 116 + .../qmldesigner/core/filemanager/qmlsymbol.h | 121 + .../filemanager/removepropertyvisitor.cpp | 108 + .../core/filemanager/removepropertyvisitor.h | 63 + .../removeuiobjectmembervisitor.cpp | 139 + .../filemanager/removeuiobjectmembervisitor.h | 75 + .../core/filemanager/utils_global.h | 37 + .../core/include/abstractproperty.h | 127 + .../qmldesigner/core/include/abstractview.h | 148 + .../qmldesigner/core/include/anchorline.h | 118 + .../core/include/bindingproperty.h | 64 + .../core/include/bytearraymodifier.h | 58 + .../core/include/componenttextmodifier.h | 69 + .../qmldesigner/core/include/corelib_global.h | 56 + .../core/include/customnotifications.h | 42 + .../core/include/enumeratormetainfo.h | 85 + .../qmldesigner/core/include/exception.h | 72 + .../qmldesigner/core/include/forwardview.h | 272 + src/plugins/qmldesigner/core/include/import.h | 76 + .../core/include/invalidargumentexception.h | 55 + .../core/include/invalididexception.h | 50 + .../core/include/invalidmetainfoexception.h | 50 + .../core/include/invalidmodelnodeexception.h | 50 + .../core/include/invalidmodelstateexception.h | 50 + .../include/invalidnodeinstanceexception.h | 48 + .../core/include/invalidnodestateexception.h | 50 + .../core/include/invalidpropertyexception.h | 54 + .../core/include/invalidqmlsourceexception.h | 54 + .../include/invalidreparentingexception.h | 49 + .../core/include/invalidslideindexexception.h | 48 + .../core/include/itemlibraryinfo.h | 100 + .../qmldesigner/core/include/iwidgetplugin.h | 52 + .../qmldesigner/core/include/mathutils.h | 46 + .../qmldesigner/core/include/metainfo.h | 123 + .../qmldesigner/core/include/metainfoparser.h | 77 + src/plugins/qmldesigner/core/include/model.h | 109 + .../qmldesigner/core/include/modelmerger.h | 59 + .../qmldesigner/core/include/modelnode.h | 186 + .../core/include/modelnodepositionstorage.h | 82 + .../qmldesigner/core/include/modelutilities.h | 51 + .../core/include/modificationgroupexception.h | 47 + .../core/include/modificationgrouptoken.h | 75 + .../include/noanchoringpossibleexception.h | 50 + .../core/include/nodeabstractproperty.h | 63 + .../qmldesigner/core/include/nodeanchors.h | 114 + .../qmldesigner/core/include/nodeinstance.h | 166 + .../core/include/nodeinstanceview.h | 153 + .../core/include/nodelistproperty.h | 71 + .../qmldesigner/core/include/nodemetainfo.h | 139 + .../qmldesigner/core/include/nodeproperty.h | 62 + .../core/include/notimplementedexception.h | 50 + .../core/include/objectpropertybinding.h | 55 + .../core/include/plaintexteditmodifier.h | 90 + .../core/include/propertybinding.h | 62 + .../core/include/propertycontainer.h | 82 + .../core/include/propertymetainfo.h | 123 + .../qmldesigner/core/include/propertynode.h | 48 + .../qmldesigner/core/include/qmlanchors.h | 113 + .../qmldesigner/core/include/qmlchangeset.h | 61 + .../qmldesigner/core/include/qmlitemnode.h | 120 + .../core/include/qmlmodelnodefacade.h | 61 + .../qmldesigner/core/include/qmlmodelview.h | 114 + .../qmldesigner/core/include/qmlobjectnode.h | 103 + .../qmldesigner/core/include/qmlstate.h | 82 + .../core/include/removebasestateexception.h | 49 + .../replaceallobjectdefinitionsvisitor.h | 59 + .../qmldesigner/core/include/rewriterview.h | 177 + .../qmldesigner/core/include/stylemanager.h | 68 + .../core/include/subcomponentmanager.h | 68 + .../qmldesigner/core/include/textmodifier.h | 91 + .../core/include/variantproperty.h | 70 + .../core/include/widgetplugin_helper.h | 37 + .../core/include/widgetqueryview.h | 56 + .../core/instances/componentnodeinstance.cpp | 100 + .../core/instances/componentnodeinstance.h | 60 + .../core/instances/dummynodeinstance.cpp | 121 + .../core/instances/dummynodeinstance.h | 73 + .../instances/graphicsobjectnodeinstance.cpp | 226 + .../instances/graphicsobjectnodeinstance.h | 90 + .../instances/graphicsscenenodeinstance.cpp | 158 + .../instances/graphicsscenenodeinstance.h | 75 + .../instances/graphicsviewnodeinstance.cpp | 111 + .../core/instances/graphicsviewnodeinstance.h | 67 + .../instances/graphicswidgetnodeinstance.cpp | 185 + .../instances/graphicswidgetnodeinstance.h | 80 + .../core/instances/nodeinstance.cpp | 589 ++ .../core/instances/nodeinstanceview.cpp | 610 ++ .../core/instances/objectnodeinstance.cpp | 690 ++ .../core/instances/objectnodeinstance.h | 185 + .../instances/proxywidgetnodeinstance.cpp | 82 + .../core/instances/proxywidgetnodeinstance.h | 59 + .../instances/qmlgraphicsitemnodeinstance.cpp | 562 ++ .../instances/qmlgraphicsitemnodeinstance.h | 79 + .../qmlpropertychangesnodeinstance.cpp | 205 + .../qmlpropertychangesnodeinstance.h | 103 + .../core/instances/qmlstatenodeinstance.cpp | 134 + .../core/instances/qmlstatenodeinstance.h | 66 + .../instances/qmltransitionnodeinstance.cpp | 87 + .../instances/qmltransitionnodeinstance.h | 60 + .../core/instances/qmlviewnodeinstance.cpp | 88 + .../core/instances/qmlviewnodeinstance.h | 61 + .../core/instances/widgetnodeinstance.cpp | 154 + .../core/instances/widgetnodeinstance.h | 75 + .../qmldesigner/core/iwidgetplugin.pri | 1 + .../core/metainfo/enumeratormetainfo.cpp | 153 + .../qmldesigner/core/metainfo/gui.metainfo | 184 + .../core/metainfo/itemlibraryinfo.cpp | 208 + .../qmldesigner/core/metainfo/metainfo.cpp | 678 ++ .../core/metainfo/metainfoparser.cpp | 386 + .../core/metainfo/nodemetainfo.cpp | 728 ++ .../core/metainfo/propertymetainfo.cpp | 441 ++ .../core/metainfo/subcomponentmanager.cpp | 442 ++ .../core/model/abstractproperty.cpp | 429 ++ .../qmldesigner/core/model/abstractview.cpp | 354 + .../qmldesigner/core/model/anchorline.cpp | 130 + .../core/model/bindingproperty.cpp | 200 + .../core/model/bytearraymodifier.cpp | 68 + .../core/model/componenttextmodifier.cpp | 127 + .../qmldesigner/core/model/copyhelper.cpp | 89 + .../qmldesigner/core/model/copyhelper.h | 63 + .../qmldesigner/core/model/copypasteutil.cpp | 61 + .../qmldesigner/core/model/copypasteutil.h | 63 + src/plugins/qmldesigner/core/model/import.cpp | 92 + .../core/model/internalbindingproperty.cpp | 77 + .../core/model/internalbindingproperty.h | 65 + .../qmldesigner/core/model/internalnode.cpp | 315 + .../qmldesigner/core/model/internalnode_p.h | 144 + .../model/internalnodeabstractproperty.cpp | 52 + .../core/model/internalnodeabstractproperty.h | 64 + .../core/model/internalnodelistproperty.cpp | 107 + .../core/model/internalnodelistproperty.h | 71 + .../core/model/internalnodeproperty.cpp | 109 + .../core/model/internalnodeproperty.h | 67 + .../core/model/internalproperty.cpp | 175 + .../qmldesigner/core/model/internalproperty.h | 109 + .../core/model/internalvariantproperty.cpp | 77 + .../core/model/internalvariantproperty.h | 64 + src/plugins/qmldesigner/core/model/model.cpp | 918 +++ src/plugins/qmldesigner/core/model/model_p.h | 195 + .../qmldesigner/core/model/modelmerger.cpp | 168 + .../qmldesigner/core/model/modelnode.cpp | 943 +++ .../model/modelnodepositionrecalculator.cpp | 100 + .../model/modelnodepositionrecalculator.h | 71 + .../core/model/modelnodepositionstorage.cpp | 67 + .../qmldesigner/core/model/modelrewriter.cpp | 510 ++ .../qmldesigner/core/model/modelrewriter.h | 125 + .../core/model/modeltotextmerger.cpp | 319 + .../core/model/modeltotextmerger.h | 95 + .../qmldesigner/core/model/modelutilities.cpp | 125 + .../core/model/modificationgrouptoken.cpp | 42 + .../core/model/nodeabstractproperty.cpp | 120 + .../qmldesigner/core/model/nodeanchors.cpp | 373 + .../core/model/nodelistproperty.cpp | 125 + .../qmldesigner/core/model/nodeproperty.cpp | 91 + .../core/model/objectpropertybinding.cpp | 55 + .../core/model/painteventfilter.cpp | 73 + .../core/model/painteventfilter_p.h | 65 + .../qmldesigner/core/model/parsedqml.cpp | 53 + .../qmldesigner/core/model/parsedqml.h | 64 + src/plugins/qmldesigner/core/model/paster.cpp | 307 + src/plugins/qmldesigner/core/model/paster.h | 76 + .../core/model/plaintexteditmodifier.cpp | 186 + .../core/model/propertybinding.cpp | 67 + .../core/model/propertycontainer.cpp | 143 + .../qmldesigner/core/model/propertynode.cpp | 38 + .../qmldesigner/core/model/propertyparser.cpp | 176 + .../qmldesigner/core/model/propertyparser.h | 49 + .../qmldesigner/core/model/qmlanchors.cpp | 411 ++ .../qmldesigner/core/model/qmlchangeset.cpp | 72 + .../qmldesigner/core/model/qmlitemnode.cpp | 330 + .../core/model/qmlmodelnodefacade.cpp | 59 + .../qmldesigner/core/model/qmlmodelview.cpp | 321 + .../qmldesigner/core/model/qmlobjectnode.cpp | 401 + .../qmldesigner/core/model/qmlstate.cpp | 288 + .../core/model/qmltextgenerator.cpp | 178 + .../qmldesigner/core/model/qmltextgenerator.h | 67 + .../qmldesigner/core/model/rewriteaction.cpp | 256 + .../qmldesigner/core/model/rewriteaction.h | 261 + .../core/model/rewriteactioncompressor.cpp | 305 + .../core/model/rewriteactioncompressor.h | 63 + .../qmldesigner/core/model/rewriterview.cpp | 459 ++ .../qmldesigner/core/model/textmodifier.cpp | 36 + .../core/model/texttomodelmerger.cpp | 509 ++ .../core/model/texttomodelmerger.h | 159 + .../qmldesigner/core/model/variantparser.cpp | 149 + .../qmldesigner/core/model/variantparser.h | 65 + .../core/model/variantproperty.cpp | 143 + .../qmldesigner/core/model/viewlogger.cpp | 200 + .../qmldesigner/core/model/viewlogger.h | 85 + .../pluginmanager/widgetpluginmanager.cpp | 103 + .../core/pluginmanager/widgetpluginmanager.h | 76 + .../core/pluginmanager/widgetpluginpath.cpp | 206 + .../core/pluginmanager/widgetpluginpath.h | 102 + .../qmldesigner/core/preview/stylemanager.cpp | 102 + .../qmldesigner/core/rewritertransaction.cpp | 91 + .../qmldesigner/core/rewritertransaction.h | 60 + src/plugins/qmldesigner/designmode.cpp | 287 + src/plugins/qmldesigner/designmode.h | 101 + src/plugins/qmldesigner/designmodewidget.cpp | 675 ++ src/plugins/qmldesigner/designmodewidget.h | 212 + src/plugins/qmldesigner/fxplugin/fx.metainfo | 143 + src/plugins/qmldesigner/fxplugin/fxplugin.cpp | 49 + src/plugins/qmldesigner/fxplugin/fxplugin.h | 58 + src/plugins/qmldesigner/fxplugin/fxplugin.pri | 17 + src/plugins/qmldesigner/fxplugin/fxplugin.pro | 4 + src/plugins/qmldesigner/fxplugin/fxplugin.qrc | 15 + .../fxplugin/images/blended-image-icon.png | Bin 0 -> 340 bytes .../fxplugin/images/default-icon.png | Bin 0 -> 341 bytes .../qmldesigner/fxplugin/images/grid-icon.png | Bin 0 -> 369 bytes .../fxplugin/images/image-icon.png | Bin 0 -> 292 bytes .../qmldesigner/fxplugin/images/item-icon.png | Bin 0 -> 293 bytes .../qmldesigner/fxplugin/images/list-icon.png | Bin 0 -> 424 bytes .../fxplugin/images/mouse-region-icon.png | Bin 0 -> 511 bytes .../qmldesigner/fxplugin/images/rect-icon.png | Bin 0 -> 237 bytes .../fxplugin/images/template_image.png | Bin 0 -> 4652 bytes .../fxplugin/images/text-edit-icon.png | Bin 0 -> 368 bytes .../qmldesigner/fxplugin/images/text-icon.png | Bin 0 -> 582 bytes .../qmldesigner/fxplugin/images/widget.png | Bin 0 -> 716 bytes .../qmldesigner/fxplugin/plugindestdir.pri | 5 + .../qmldesigner/images/mode_Design.png | Bin 0 -> 394 bytes src/plugins/qmldesigner/qmldesigner.pro | 3 + .../qmldesigner/qmldesigner_dependencies.pri | 3 + .../qmldesigner/qmldesignerconstants.h | 58 + src/plugins/qmldesigner/qmldesignerplugin.cpp | 100 + src/plugins/qmldesigner/qmldesignerplugin.h | 68 + src/plugins/qmldesigner/qmldesignerplugin.pro | 29 + src/plugins/qmldesigner/qmldesignerplugin.qrc | 5 + src/plugins/qmleditor/qmleditor.cpp | 1 - src/plugins/qmleditor/qmlhoverhandler.cpp | 10 +- src/plugins/qmleditor/qmllookupcontext.cpp | 2 +- .../qmlprojectmanager/qmlprojectmanager.cpp | 16 + .../qmlprojectmanager/qmltaskmanager.cpp | 2 + .../metatype/QtDeclarativeMetaTypeBackend.cpp | 6 + src/shared/qml/metatype/exception.cpp | 2 +- .../qml/metatype/invalidmetainfoexception.cpp | 2 +- src/shared/qml/metatype/metainfo.cpp | 6 +- src/shared/qml/metatype/nodemetainfo.cpp | 4 +- src/shared/qml/metatype/propertymetainfo.cpp | 4 +- src/shared/qml/qml.pri | 2 +- src/shared/qml/qmlsymbol.cpp | 15 +- src/shared/qml/qmlsymbol.h | 2 + src/tools/qmldesigner/qmldom/main.cpp | 173 + src/tools/qmldesigner/qmldom/qmldom.pro | 21 + .../standalone/128xBauhaus_Logo.png | Bin 0 -> 15545 bytes .../standalone/16xBauhaus_Logo.png | Bin 0 -> 537 bytes .../standalone/256xBauhaus_Logo.png | Bin 0 -> 54353 bytes .../standalone/32xBauhaus_Logo.png | Bin 0 -> 36752 bytes .../standalone/512xBauhaus_Logo.png | Bin 0 -> 154832 bytes .../standalone/64xBauhaus_Logo.png | Bin 0 -> 4512 bytes src/tools/qmldesigner/standalone/Info.plist | 189 + .../qmldesigner/standalone/aboutdialog.cpp | 95 + .../qmldesigner/standalone/aboutdialog.h | 53 + .../qmldesigner/standalone/application.cpp | 43 + .../qmldesigner/standalone/application.h | 40 + .../qmldesigner/standalone/bauhaus-logo.icns | Bin 0 -> 267319 bytes .../qmldesigner/standalone/bauhaus-macos.css | 0 .../standalone/bauhaus-windows.css | 3 + .../qmldesigner/standalone/bauhaus-x11.css | 3 + src/tools/qmldesigner/standalone/bauhaus.css | 28 + src/tools/qmldesigner/standalone/bauhaus.ico | Bin 0 -> 22382 bytes src/tools/qmldesigner/standalone/bauhaus.qrc | 19 + src/tools/qmldesigner/standalone/css.png | Bin 0 -> 4170 bytes src/tools/qmldesigner/standalone/edit.png | Bin 0 -> 3573 bytes src/tools/qmldesigner/standalone/main.cpp | 162 + .../qmldesigner/standalone/mainwindow.cpp | 565 ++ src/tools/qmldesigner/standalone/mainwindow.h | 120 + src/tools/qmldesigner/standalone/preview.png | Bin 0 -> 1681 bytes .../qmldesigner/standalone/standalone.pro | 44 + .../qmldesigner/standalone/templates/fx.qml | 7 + .../standalone/templates/qgraphicsview.qml | 10 + .../standalone/templates/qwidget.qml | 9 + .../standalone/toolbar-cssedit.png | Bin 0 -> 1808 bytes .../qmldesigner/standalone/toolbar-edit.png | Bin 0 -> 1142 bytes .../qmldesigner/standalone/welcomescreen.cpp | 150 + .../qmldesigner/standalone/welcomescreen.h | 77 + tests/auto/auto.pro | 3 +- tests/auto/qml/qml.pro | 3 + .../qmldesigner/bauhaustests/bauhaustests.pro | 18 + .../qmldesigner/bauhaustests/testbauhaus.cpp | 122 + .../qmldesigner/bauhaustests/testbauhaus.h | 51 + .../qml/qmldesigner/common/statichelpers.cpp | 174 + .../qml/qmldesigner/coretests/coretests.pro | 22 + .../qml/qmldesigner/coretests/testcore.cpp | 6485 +++++++++++++++++ .../auto/qml/qmldesigner/coretests/testcore.h | 233 + .../coretests/testrewriterview.cpp | 113 + .../qmldesigner/coretests/testrewriterview.h | 68 + .../qml/qmldesigner/data/fx/attributes.qml | 8 + .../auto/qml/qmldesigner/data/fx/bigtest.qml | 78 + .../qml/qmldesigner/data/fx/components.qml | 26 + tests/auto/qml/qmldesigner/data/fx/empty.qml | 4 + .../qml/qmldesigner/data/fx/helloworld.qml | 76 + tests/auto/qml/qmldesigner/data/fx/image.qml | 10 + .../qml/qmldesigner/data/fx/properties.qml | 5 + .../qml/qmldesigner/data/fx/selectiontest.qml | 39 + tests/auto/qml/qmldesigner/data/fx/states.qml | 59 + .../qmldesigner/data/fx/subitems/SubItem.qml | 14 + .../auto/qml/qmldesigner/data/fx/topitem.qml | 17 + tests/auto/qml/qmldesigner/data/qt-logo.png | Bin 0 -> 23519 bytes .../qml/qmldesigner/data/qwidget/test.qml | 27 + tests/auto/qml/qmldesigner/data/testfiles.qrc | 11 + .../propertyeditortests.pro | 24 + .../testpropertyeditor.cpp | 370 + .../propertyeditortests/testpropertyeditor.h | 57 + tests/auto/qml/qmldesigner/qmldesigner.pro | 5 + tests/auto/qml/qmldesigner/testview.cpp | 213 + tests/auto/qml/qmldesigner/testview.h | 93 + 743 files changed, 102919 insertions(+), 23 deletions(-) create mode 100644 share/qtcreator/qmldesigner/Button.qml create mode 100644 share/qtcreator/qmldesigner/gradient.png create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/AnchorBox.qml create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/BorderImageSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/CheckBox.qml create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/ColumnSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/DoubleSpinBox.qml create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/Extended.qml create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/ExtendedPane.qml create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/ExtendedSwitches.qml create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/FlickableSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/FlipableSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/Geometry.qml create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/GridSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/GroupBox.qml create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/GroupBoxOption.qml create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/HorizontalLine.qml create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/ImageSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/IntEditor.qml create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/ItemPane.qml create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/Layout.qml create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/LayoutPane.qml create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/ListView.qml create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/ListViewSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/Modifiers.qml create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/PropertyFrame.qml create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/RectangleSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/ResetPane.qml create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/RowSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/ScrollArea.qml create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/SpinBox.qml create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/Switches.qml create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/TextEditSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/TextInputSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/TextSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/Type.qml create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/WebViewSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/anchorbox.css create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/emptyPane.qml create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/images/behaivour.png create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/images/blended-image-icon.png create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/images/button.png create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/images/default-icon.png create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/images/downArrow.png create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/images/expression.png create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/images/extended.png create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/images/grid-icon.png create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/images/image-icon.png create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/images/item-icon.png create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/images/layout.png create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/images/leftArrow.png create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/images/list-icon.png create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/images/mouse-region-icon.png create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/images/placeholder.png create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/images/rect-icon.png create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/images/reset-button.png create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/images/rightArrow.png create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/images/standard.png create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/images/submenu.png create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/images/text-edit-icon.png create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/images/text-icon.png create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/images/upArrow.png create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/layoutWidget.css create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/propertyEditor.css create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/specialCheckBox.css create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/switch.css create mode 100644 share/qtcreator/qmldesigner/propertyeditor/Qt/typeLabel.css create mode 100644 share/qtcreator/qmldesigner/welcome-card.png create mode 100644 share/qtcreator/qmldesigner/welcomescreen.qml create mode 100644 src/plugins/qmldesigner/QmlDesigner.pluginspec create mode 100644 src/plugins/qmldesigner/application.cpp create mode 100644 src/plugins/qmldesigner/application.h create mode 100644 src/plugins/qmldesigner/components/formeditor/abstractformeditortool.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/abstractformeditortool.h create mode 100644 src/plugins/qmldesigner/components/formeditor/anchor.png create mode 100644 src/plugins/qmldesigner/components/formeditor/anchorcontroller.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/anchorcontroller.h create mode 100644 src/plugins/qmldesigner/components/formeditor/anchorhandleitem.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/anchorhandleitem.h create mode 100644 src/plugins/qmldesigner/components/formeditor/anchorindicator.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/anchorindicator.h create mode 100644 src/plugins/qmldesigner/components/formeditor/anchorlinecontroller.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/anchorlinecontroller.h create mode 100644 src/plugins/qmldesigner/components/formeditor/anchorlinehandleitem.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/anchorlinehandleitem.h create mode 100644 src/plugins/qmldesigner/components/formeditor/anchorlineindicator.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/anchorlineindicator.h create mode 100644 src/plugins/qmldesigner/components/formeditor/anchormanipulator.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/anchormanipulator.h create mode 100644 src/plugins/qmldesigner/components/formeditor/anchortool.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/anchortool.h create mode 100644 src/plugins/qmldesigner/components/formeditor/controlelement.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/controlelement.h create mode 100644 src/plugins/qmldesigner/components/formeditor/dragtool.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/dragtool.h create mode 100644 src/plugins/qmldesigner/components/formeditor/formeditor.pri create mode 100644 src/plugins/qmldesigner/components/formeditor/formeditor.qrc create mode 100644 src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.h create mode 100644 src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/formeditoritem.h create mode 100644 src/plugins/qmldesigner/components/formeditor/formeditormainview.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/formeditormainview.h create mode 100644 src/plugins/qmldesigner/components/formeditor/formeditormainwidget.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/formeditormainwidget.h create mode 100644 src/plugins/qmldesigner/components/formeditor/formeditornodeinstanceview.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/formeditornodeinstanceview.h create mode 100644 src/plugins/qmldesigner/components/formeditor/formeditorscene.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/formeditorscene.h create mode 100644 src/plugins/qmldesigner/components/formeditor/formeditorsubwindow.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/formeditorsubwindow.h create mode 100644 src/plugins/qmldesigner/components/formeditor/formeditorview.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/formeditorview.h create mode 100644 src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/formeditorwidget.h create mode 100644 src/plugins/qmldesigner/components/formeditor/itemcreatortool.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/itemcreatortool.h create mode 100644 src/plugins/qmldesigner/components/formeditor/itemutilfunctions.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/itemutilfunctions.h create mode 100644 src/plugins/qmldesigner/components/formeditor/layeritem.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/layeritem.h create mode 100644 src/plugins/qmldesigner/components/formeditor/manipulatorlayeritem.h create mode 100644 src/plugins/qmldesigner/components/formeditor/movemanipulator.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/movemanipulator.h create mode 100644 src/plugins/qmldesigner/components/formeditor/movetool.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/movetool.h create mode 100644 src/plugins/qmldesigner/components/formeditor/numberseriesaction.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/numberseriesaction.h create mode 100644 src/plugins/qmldesigner/components/formeditor/onedimensionalcluster.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/onedimensionalcluster.h create mode 100644 src/plugins/qmldesigner/components/formeditor/resize_handle.png create mode 100644 src/plugins/qmldesigner/components/formeditor/resizecontroller.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/resizecontroller.h create mode 100644 src/plugins/qmldesigner/components/formeditor/resizehandleitem.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/resizehandleitem.h create mode 100644 src/plugins/qmldesigner/components/formeditor/resizeindicator.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/resizeindicator.h create mode 100644 src/plugins/qmldesigner/components/formeditor/resizemanipulator.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/resizemanipulator.h create mode 100644 src/plugins/qmldesigner/components/formeditor/resizetool.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/resizetool.h create mode 100644 src/plugins/qmldesigner/components/formeditor/rubberbandselectionmanipulator.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/rubberbandselectionmanipulator.h create mode 100644 src/plugins/qmldesigner/components/formeditor/scaleitem.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/scaleitem.h create mode 100644 src/plugins/qmldesigner/components/formeditor/scalemanipulator.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/scalemanipulator.h create mode 100644 src/plugins/qmldesigner/components/formeditor/selectionindicator.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/selectionindicator.h create mode 100644 src/plugins/qmldesigner/components/formeditor/selectionrectangle.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/selectionrectangle.h create mode 100644 src/plugins/qmldesigner/components/formeditor/selectiontool.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/selectiontool.h create mode 100644 src/plugins/qmldesigner/components/formeditor/singleselectionmanipulator.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/singleselectionmanipulator.h create mode 100644 src/plugins/qmldesigner/components/formeditor/snapper.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/snapper.h create mode 100644 src/plugins/qmldesigner/components/formeditor/snapping.png create mode 100644 src/plugins/qmldesigner/components/formeditor/snappinglinecreator.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/snappinglinecreator.h create mode 100644 src/plugins/qmldesigner/components/formeditor/toolbox.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/toolbox.h create mode 100644 src/plugins/qmldesigner/components/formeditor/transform.png create mode 100644 src/plugins/qmldesigner/components/formeditor/zoomaction.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/zoomaction.h create mode 100644 src/plugins/qmldesigner/components/integration/allpropertiesviewcontroller.cpp create mode 100644 src/plugins/qmldesigner/components/integration/allpropertiesviewcontroller.h create mode 100644 src/plugins/qmldesigner/components/integration/componentaction.cpp create mode 100644 src/plugins/qmldesigner/components/integration/componentaction.h create mode 100644 src/plugins/qmldesigner/components/integration/componentview.cpp create mode 100644 src/plugins/qmldesigner/components/integration/componentview.h create mode 100644 src/plugins/qmldesigner/components/integration/designdocumentcontroller.cpp create mode 100644 src/plugins/qmldesigner/components/integration/designdocumentcontroller.h create mode 100644 src/plugins/qmldesigner/components/integration/designdocumentcontrollerview.cpp create mode 100644 src/plugins/qmldesigner/components/integration/designdocumentcontrollerview.h create mode 100644 src/plugins/qmldesigner/components/integration/documentcloser.cpp create mode 100644 src/plugins/qmldesigner/components/integration/documentcloser.h create mode 100644 src/plugins/qmldesigner/components/integration/integration.pri create mode 100644 src/plugins/qmldesigner/components/integration/integrationcore.cpp create mode 100644 src/plugins/qmldesigner/components/integration/integrationcore.h create mode 100644 src/plugins/qmldesigner/components/integration/multipledocumentscontroller.cpp create mode 100644 src/plugins/qmldesigner/components/integration/multipledocumentscontroller.h create mode 100644 src/plugins/qmldesigner/components/integration/navigatorcontroller.cpp create mode 100644 src/plugins/qmldesigner/components/integration/navigatorcontroller.h create mode 100644 src/plugins/qmldesigner/components/integration/stackedutilitypanelcontroller.cpp create mode 100644 src/plugins/qmldesigner/components/integration/stackedutilitypanelcontroller.h create mode 100644 src/plugins/qmldesigner/components/integration/stateseditorcontroller.cpp create mode 100644 src/plugins/qmldesigner/components/integration/stateseditorcontroller.h create mode 100644 src/plugins/qmldesigner/components/integration/utilitypanelcontroller.cpp create mode 100644 src/plugins/qmldesigner/components/integration/utilitypanelcontroller.h create mode 100644 src/plugins/qmldesigner/components/integration/widgetboxcontroller.cpp create mode 100644 src/plugins/qmldesigner/components/integration/widgetboxcontroller.h create mode 100644 src/plugins/qmldesigner/components/integration/xuifiledialog.cpp create mode 100644 src/plugins/qmldesigner/components/integration/xuifiledialog.h create mode 100644 src/plugins/qmldesigner/components/itemlibrary/customdraganddrop.cpp create mode 100644 src/plugins/qmldesigner/components/itemlibrary/customdraganddrop.h create mode 100644 src/plugins/qmldesigner/components/itemlibrary/default-icon.png create mode 100644 src/plugins/qmldesigner/components/itemlibrary/itemlibrary.cpp create mode 100644 src/plugins/qmldesigner/components/itemlibrary/itemlibrary.h create mode 100644 src/plugins/qmldesigner/components/itemlibrary/itemlibrary.pri create mode 100644 src/plugins/qmldesigner/components/itemlibrary/itemlibrary.qrc create mode 100644 src/plugins/qmldesigner/components/itemlibrary/itemlibrary.ui create mode 100644 src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp create mode 100644 src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h create mode 100644 src/plugins/qmldesigner/components/logger/logger.cpp create mode 100644 src/plugins/qmldesigner/components/logger/logger.h create mode 100644 src/plugins/qmldesigner/components/logger/logger.pri create mode 100644 src/plugins/qmldesigner/components/navigator/navigator.pri create mode 100644 src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp create mode 100644 src/plugins/qmldesigner/components/navigator/navigatortreemodel.h create mode 100644 src/plugins/qmldesigner/components/navigator/navigatorview.cpp create mode 100644 src/plugins/qmldesigner/components/navigator/navigatorview.h create mode 100644 src/plugins/qmldesigner/components/navigator/navigatorwidget.cpp create mode 100644 src/plugins/qmldesigner/components/navigator/navigatorwidget.h create mode 100644 src/plugins/qmldesigner/components/pluginmanager/iplugin.cpp create mode 100644 src/plugins/qmldesigner/components/pluginmanager/iplugin.h create mode 100644 src/plugins/qmldesigner/components/pluginmanager/pluginmanager.cpp create mode 100644 src/plugins/qmldesigner/components/pluginmanager/pluginmanager.h create mode 100644 src/plugins/qmldesigner/components/pluginmanager/pluginmanager.pri create mode 100644 src/plugins/qmldesigner/components/pluginmanager/pluginpath.cpp create mode 100644 src/plugins/qmldesigner/components/pluginmanager/pluginpath.h create mode 100644 src/plugins/qmldesigner/components/propertyeditor/allpropertiesbox.cpp create mode 100644 src/plugins/qmldesigner/components/propertyeditor/allpropertiesbox.h create mode 100644 src/plugins/qmldesigner/components/propertyeditor/basiclayouts.cpp create mode 100644 src/plugins/qmldesigner/components/propertyeditor/basiclayouts.h create mode 100644 src/plugins/qmldesigner/components/propertyeditor/basicwidgets.cpp create mode 100644 src/plugins/qmldesigner/components/propertyeditor/basicwidgets.h create mode 100644 src/plugins/qmldesigner/components/propertyeditor/behaviordialog.cpp create mode 100644 src/plugins/qmldesigner/components/propertyeditor/behaviordialog.h create mode 100644 src/plugins/qmldesigner/components/propertyeditor/behaviordialog.ui create mode 100644 src/plugins/qmldesigner/components/propertyeditor/colorwidget.cpp create mode 100644 src/plugins/qmldesigner/components/propertyeditor/colorwidget.h create mode 100644 src/plugins/qmldesigner/components/propertyeditor/filewidget.cpp create mode 100644 src/plugins/qmldesigner/components/propertyeditor/filewidget.h create mode 100644 src/plugins/qmldesigner/components/propertyeditor/fontwidget.cpp create mode 100644 src/plugins/qmldesigner/components/propertyeditor/fontwidget.h create mode 100644 src/plugins/qmldesigner/components/propertyeditor/genericpropertieswidget.cpp create mode 100644 src/plugins/qmldesigner/components/propertyeditor/genericpropertieswidget.h create mode 100644 src/plugins/qmldesigner/components/propertyeditor/gradientdialog.cpp create mode 100644 src/plugins/qmldesigner/components/propertyeditor/gradientdialog.h create mode 100644 src/plugins/qmldesigner/components/propertyeditor/gradientdialog.ui create mode 100644 src/plugins/qmldesigner/components/propertyeditor/gradienteditor.cpp create mode 100644 src/plugins/qmldesigner/components/propertyeditor/gradienteditor.h create mode 100644 src/plugins/qmldesigner/components/propertyeditor/gradienteditor.ui create mode 100644 src/plugins/qmldesigner/components/propertyeditor/images/button2_hovered.png create mode 100644 src/plugins/qmldesigner/components/propertyeditor/images/button2_normal.png create mode 100644 src/plugins/qmldesigner/components/propertyeditor/images/button2_pressed.png create mode 100644 src/plugins/qmldesigner/components/propertyeditor/images/button_hovered.png create mode 100644 src/plugins/qmldesigner/components/propertyeditor/images/button_normal.png create mode 100644 src/plugins/qmldesigner/components/propertyeditor/images/button_pressed.png create mode 100644 src/plugins/qmldesigner/components/propertyeditor/images/frame.png create mode 100644 src/plugins/qmldesigner/components/propertyeditor/images/gradient.png create mode 100644 src/plugins/qmldesigner/components/propertyeditor/layoutwidget.cpp create mode 100644 src/plugins/qmldesigner/components/propertyeditor/layoutwidget.h create mode 100644 src/plugins/qmldesigner/components/propertyeditor/propertyeditor.cpp create mode 100644 src/plugins/qmldesigner/components/propertyeditor/propertyeditor.h create mode 100644 src/plugins/qmldesigner/components/propertyeditor/propertyeditor.pri create mode 100644 src/plugins/qmldesigner/components/propertyeditor/propertyeditor.qrc create mode 100644 src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp create mode 100644 src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qlayoutobject.cpp create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qlayoutobject.h create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qmlanchorbindingproxy.cpp create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qmlanchorbindingproxy.h create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qproxylayoutitem.cpp create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qproxylayoutitem.h create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/images/down.png create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/images/edit.png create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/images/editdelete.png create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/images/minus.png create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/images/plus.png create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/images/spreadpad.png create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/images/spreadreflect.png create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/images/spreadrepeat.png create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/images/typeconical.png create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/images/typelinear.png create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/images/typeradial.png create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/images/up.png create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/images/zoomin.png create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/images/zoomout.png create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtcolorbutton.cpp create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtcolorbutton.h create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtcolorbutton.pri create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtcolorline.cpp create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtcolorline.h create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientdialog.cpp create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientdialog.h create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientdialog.ui create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradienteditor.cpp create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradienteditor.h create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradienteditor.pri create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradienteditor.qrc create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradienteditor.ui create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientmanager.cpp create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientmanager.h create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientstopscontroller.cpp create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientstopscontroller.h create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientstopsmodel.cpp create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientstopsmodel.h create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientstopswidget.cpp create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientstopswidget.h create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientutils.cpp create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientutils.h create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientview.cpp create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientview.h create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientview.ui create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientviewdialog.cpp create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientviewdialog.h create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientviewdialog.ui create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientwidget.cpp create mode 100644 src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientwidget.h create mode 100644 src/plugins/qmldesigner/components/propertyeditor/resetwidget.cpp create mode 100644 src/plugins/qmldesigner/components/propertyeditor/resetwidget.h create mode 100644 src/plugins/qmldesigner/components/resources/images/checkbox_checked.png create mode 100644 src/plugins/qmldesigner/components/resources/images/checkbox_checked_hover.png create mode 100644 src/plugins/qmldesigner/components/resources/images/checkbox_checked_pressed.png create mode 100644 src/plugins/qmldesigner/components/resources/images/checkbox_unchecked.png create mode 100644 src/plugins/qmldesigner/components/resources/images/checkbox_unchecked_hover.png create mode 100644 src/plugins/qmldesigner/components/resources/images/checkbox_unchecked_pressed.png create mode 100644 src/plugins/qmldesigner/components/resources/images/down_arrow.png create mode 100644 src/plugins/qmldesigner/components/resources/images/down_arrow_disabled.png create mode 100644 src/plugins/qmldesigner/components/resources/images/frame.png create mode 100644 src/plugins/qmldesigner/components/resources/images/pushbutton.png create mode 100644 src/plugins/qmldesigner/components/resources/images/pushbutton_hover.png create mode 100644 src/plugins/qmldesigner/components/resources/images/pushbutton_pressed.png create mode 100644 src/plugins/qmldesigner/components/resources/images/radiobutton_checked.png create mode 100644 src/plugins/qmldesigner/components/resources/images/radiobutton_checked_hover.png create mode 100644 src/plugins/qmldesigner/components/resources/images/radiobutton_checked_pressed.png create mode 100644 src/plugins/qmldesigner/components/resources/images/radiobutton_unchecked.png create mode 100644 src/plugins/qmldesigner/components/resources/images/radiobutton_unchecked_hover.png create mode 100644 src/plugins/qmldesigner/components/resources/images/radiobutton_unchecked_pressed.png create mode 100644 src/plugins/qmldesigner/components/resources/images/sizegrip.png create mode 100644 src/plugins/qmldesigner/components/resources/images/spindown.png create mode 100644 src/plugins/qmldesigner/components/resources/images/spindown_hover.png create mode 100644 src/plugins/qmldesigner/components/resources/images/spindown_off.png create mode 100644 src/plugins/qmldesigner/components/resources/images/spindown_pressed.png create mode 100644 src/plugins/qmldesigner/components/resources/images/spinup.png create mode 100644 src/plugins/qmldesigner/components/resources/images/spinup_hover.png create mode 100644 src/plugins/qmldesigner/components/resources/images/spinup_off.png create mode 100644 src/plugins/qmldesigner/components/resources/images/spinup_pressed.png create mode 100644 src/plugins/qmldesigner/components/resources/images/up_arrow.png create mode 100644 src/plugins/qmldesigner/components/resources/images/up_arrow_disabled.png create mode 100644 src/plugins/qmldesigner/components/resources/resources.pri create mode 100644 src/plugins/qmldesigner/components/resources/resources.qrc create mode 100644 src/plugins/qmldesigner/components/resources/scrollbar.css create mode 100644 src/plugins/qmldesigner/components/resources/stylesheet.css create mode 100644 src/plugins/qmldesigner/components/resources/templates/Standard/Form.xml create mode 100644 src/plugins/qmldesigner/components/resources/toolbutton.css create mode 100644 src/plugins/qmldesigner/components/stateseditor/stateseditor.pri create mode 100644 src/plugins/qmldesigner/components/stateseditor/stateseditor.qrc create mode 100644 src/plugins/qmldesigner/components/stateseditor/stateseditormodel.cpp create mode 100644 src/plugins/qmldesigner/components/stateseditor/stateseditormodel.h create mode 100644 src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp create mode 100644 src/plugins/qmldesigner/components/stateseditor/stateseditorview.h create mode 100644 src/plugins/qmldesigner/components/stateseditor/stateseditorwidget.cpp create mode 100644 src/plugins/qmldesigner/components/stateseditor/stateseditorwidget.h create mode 100644 src/plugins/qmldesigner/components/stateseditor/stateslist.qml create mode 100644 src/plugins/qmldesigner/components/themeloader/qts60stylethemeio.cpp create mode 100644 src/plugins/qmldesigner/components/themeloader/qts60stylethemeio.h create mode 100644 src/plugins/qmldesigner/components/themeloader/qts60stylethemeio.pri create mode 100644 src/plugins/qmldesigner/config.pri create mode 100644 src/plugins/qmldesigner/core/core.pri create mode 100644 src/plugins/qmldesigner/core/core.qrc create mode 100644 src/plugins/qmldesigner/core/depend_paths.pri create mode 100644 src/plugins/qmldesigner/core/exceptions/exception.cpp create mode 100644 src/plugins/qmldesigner/core/exceptions/invalidargumentexception.cpp create mode 100644 src/plugins/qmldesigner/core/exceptions/invalididexception.cpp create mode 100644 src/plugins/qmldesigner/core/exceptions/invalidmetainfoexception.cpp create mode 100644 src/plugins/qmldesigner/core/exceptions/invalidmodelnodeexception.cpp create mode 100644 src/plugins/qmldesigner/core/exceptions/invalidmodelstateexception.cpp create mode 100644 src/plugins/qmldesigner/core/exceptions/invalidnodeinstanceexception.cpp create mode 100644 src/plugins/qmldesigner/core/exceptions/invalidnodestateexception.cpp create mode 100644 src/plugins/qmldesigner/core/exceptions/invalidpropertyexception.cpp create mode 100644 src/plugins/qmldesigner/core/exceptions/invalidqmlsourceexception.cpp create mode 100644 src/plugins/qmldesigner/core/exceptions/invalidreparentingexception.cpp create mode 100644 src/plugins/qmldesigner/core/exceptions/invalidslideindexexception.cpp create mode 100644 src/plugins/qmldesigner/core/exceptions/modificationgroupexception.cpp create mode 100644 src/plugins/qmldesigner/core/exceptions/noanchoringpossibleexception.cpp create mode 100644 src/plugins/qmldesigner/core/exceptions/notimplementedexception.cpp create mode 100644 src/plugins/qmldesigner/core/exceptions/removebasestateexception.cpp create mode 100644 src/plugins/qmldesigner/core/filemanager/addarraymembervisitor.cpp create mode 100644 src/plugins/qmldesigner/core/filemanager/addarraymembervisitor.h create mode 100644 src/plugins/qmldesigner/core/filemanager/addobjectvisitor.cpp create mode 100644 src/plugins/qmldesigner/core/filemanager/addobjectvisitor.h create mode 100644 src/plugins/qmldesigner/core/filemanager/addpropertyvisitor.cpp create mode 100644 src/plugins/qmldesigner/core/filemanager/addpropertyvisitor.h create mode 100644 src/plugins/qmldesigner/core/filemanager/astobjecttextextractor.cpp create mode 100644 src/plugins/qmldesigner/core/filemanager/astobjecttextextractor.h create mode 100644 src/plugins/qmldesigner/core/filemanager/changeimportsvisitor.cpp create mode 100644 src/plugins/qmldesigner/core/filemanager/changeimportsvisitor.h create mode 100644 src/plugins/qmldesigner/core/filemanager/changeobjecttypevisitor.cpp create mode 100644 src/plugins/qmldesigner/core/filemanager/changeobjecttypevisitor.h create mode 100644 src/plugins/qmldesigner/core/filemanager/changepropertyvisitor.cpp create mode 100644 src/plugins/qmldesigner/core/filemanager/changepropertyvisitor.h create mode 100644 src/plugins/qmldesigner/core/filemanager/changeset.cpp create mode 100644 src/plugins/qmldesigner/core/filemanager/changeset.h create mode 100644 src/plugins/qmldesigner/core/filemanager/filemanager.pri create mode 100644 src/plugins/qmldesigner/core/filemanager/firstdefinitionfinder.cpp create mode 100644 src/plugins/qmldesigner/core/filemanager/firstdefinitionfinder.h create mode 100644 src/plugins/qmldesigner/core/filemanager/idcollector.cpp create mode 100644 src/plugins/qmldesigner/core/filemanager/idcollector.h create mode 100644 src/plugins/qmldesigner/core/filemanager/indenter.cpp create mode 100644 src/plugins/qmldesigner/core/filemanager/indenter.h create mode 100644 src/plugins/qmldesigner/core/filemanager/moveobjectbeforeobjectvisitor.cpp create mode 100644 src/plugins/qmldesigner/core/filemanager/moveobjectbeforeobjectvisitor.h create mode 100644 src/plugins/qmldesigner/core/filemanager/moveobjectvisitor.cpp create mode 100644 src/plugins/qmldesigner/core/filemanager/moveobjectvisitor.h create mode 100644 src/plugins/qmldesigner/core/filemanager/objectlengthcalculator.cpp create mode 100644 src/plugins/qmldesigner/core/filemanager/objectlengthcalculator.h create mode 100644 src/plugins/qmldesigner/core/filemanager/parser/parser.pri create mode 100644 src/plugins/qmldesigner/core/filemanager/parser/qmljs.g create mode 100644 src/plugins/qmldesigner/core/filemanager/parser/qmljsast.cpp create mode 100644 src/plugins/qmldesigner/core/filemanager/parser/qmljsast_p.h create mode 100644 src/plugins/qmldesigner/core/filemanager/parser/qmljsastfwd_p.h create mode 100644 src/plugins/qmldesigner/core/filemanager/parser/qmljsastvisitor.cpp create mode 100644 src/plugins/qmldesigner/core/filemanager/parser/qmljsastvisitor_p.h create mode 100644 src/plugins/qmldesigner/core/filemanager/parser/qmljsengine_p.cpp create mode 100644 src/plugins/qmldesigner/core/filemanager/parser/qmljsengine_p.h create mode 100644 src/plugins/qmldesigner/core/filemanager/parser/qmljsglobal_p.h create mode 100644 src/plugins/qmldesigner/core/filemanager/parser/qmljsgrammar.cpp create mode 100644 src/plugins/qmldesigner/core/filemanager/parser/qmljsgrammar_p.h create mode 100644 src/plugins/qmldesigner/core/filemanager/parser/qmljslexer.cpp create mode 100644 src/plugins/qmldesigner/core/filemanager/parser/qmljslexer_p.h create mode 100644 src/plugins/qmldesigner/core/filemanager/parser/qmljsmemorypool_p.h create mode 100644 src/plugins/qmldesigner/core/filemanager/parser/qmljsnodepool_p.h create mode 100644 src/plugins/qmldesigner/core/filemanager/parser/qmljsparser.cpp create mode 100644 src/plugins/qmldesigner/core/filemanager/parser/qmljsparser_p.h create mode 100644 src/plugins/qmldesigner/core/filemanager/qmldocument.cpp create mode 100644 src/plugins/qmldesigner/core/filemanager/qmldocument.h create mode 100644 src/plugins/qmldesigner/core/filemanager/qmleditor_global.h create mode 100644 src/plugins/qmldesigner/core/filemanager/qmlrefactoring.cpp create mode 100644 src/plugins/qmldesigner/core/filemanager/qmlrefactoring.h create mode 100644 src/plugins/qmldesigner/core/filemanager/qmlrewriter.cpp create mode 100644 src/plugins/qmldesigner/core/filemanager/qmlrewriter.h create mode 100644 src/plugins/qmldesigner/core/filemanager/qmlsymbol.cpp create mode 100644 src/plugins/qmldesigner/core/filemanager/qmlsymbol.h create mode 100644 src/plugins/qmldesigner/core/filemanager/removepropertyvisitor.cpp create mode 100644 src/plugins/qmldesigner/core/filemanager/removepropertyvisitor.h create mode 100644 src/plugins/qmldesigner/core/filemanager/removeuiobjectmembervisitor.cpp create mode 100644 src/plugins/qmldesigner/core/filemanager/removeuiobjectmembervisitor.h create mode 100644 src/plugins/qmldesigner/core/filemanager/utils_global.h create mode 100644 src/plugins/qmldesigner/core/include/abstractproperty.h create mode 100644 src/plugins/qmldesigner/core/include/abstractview.h create mode 100644 src/plugins/qmldesigner/core/include/anchorline.h create mode 100644 src/plugins/qmldesigner/core/include/bindingproperty.h create mode 100644 src/plugins/qmldesigner/core/include/bytearraymodifier.h create mode 100644 src/plugins/qmldesigner/core/include/componenttextmodifier.h create mode 100644 src/plugins/qmldesigner/core/include/corelib_global.h create mode 100644 src/plugins/qmldesigner/core/include/customnotifications.h create mode 100644 src/plugins/qmldesigner/core/include/enumeratormetainfo.h create mode 100644 src/plugins/qmldesigner/core/include/exception.h create mode 100644 src/plugins/qmldesigner/core/include/forwardview.h create mode 100644 src/plugins/qmldesigner/core/include/import.h create mode 100644 src/plugins/qmldesigner/core/include/invalidargumentexception.h create mode 100644 src/plugins/qmldesigner/core/include/invalididexception.h create mode 100644 src/plugins/qmldesigner/core/include/invalidmetainfoexception.h create mode 100644 src/plugins/qmldesigner/core/include/invalidmodelnodeexception.h create mode 100644 src/plugins/qmldesigner/core/include/invalidmodelstateexception.h create mode 100644 src/plugins/qmldesigner/core/include/invalidnodeinstanceexception.h create mode 100644 src/plugins/qmldesigner/core/include/invalidnodestateexception.h create mode 100644 src/plugins/qmldesigner/core/include/invalidpropertyexception.h create mode 100644 src/plugins/qmldesigner/core/include/invalidqmlsourceexception.h create mode 100644 src/plugins/qmldesigner/core/include/invalidreparentingexception.h create mode 100644 src/plugins/qmldesigner/core/include/invalidslideindexexception.h create mode 100644 src/plugins/qmldesigner/core/include/itemlibraryinfo.h create mode 100644 src/plugins/qmldesigner/core/include/iwidgetplugin.h create mode 100644 src/plugins/qmldesigner/core/include/mathutils.h create mode 100644 src/plugins/qmldesigner/core/include/metainfo.h create mode 100644 src/plugins/qmldesigner/core/include/metainfoparser.h create mode 100644 src/plugins/qmldesigner/core/include/model.h create mode 100644 src/plugins/qmldesigner/core/include/modelmerger.h create mode 100644 src/plugins/qmldesigner/core/include/modelnode.h create mode 100644 src/plugins/qmldesigner/core/include/modelnodepositionstorage.h create mode 100644 src/plugins/qmldesigner/core/include/modelutilities.h create mode 100644 src/plugins/qmldesigner/core/include/modificationgroupexception.h create mode 100644 src/plugins/qmldesigner/core/include/modificationgrouptoken.h create mode 100644 src/plugins/qmldesigner/core/include/noanchoringpossibleexception.h create mode 100644 src/plugins/qmldesigner/core/include/nodeabstractproperty.h create mode 100644 src/plugins/qmldesigner/core/include/nodeanchors.h create mode 100644 src/plugins/qmldesigner/core/include/nodeinstance.h create mode 100644 src/plugins/qmldesigner/core/include/nodeinstanceview.h create mode 100644 src/plugins/qmldesigner/core/include/nodelistproperty.h create mode 100644 src/plugins/qmldesigner/core/include/nodemetainfo.h create mode 100644 src/plugins/qmldesigner/core/include/nodeproperty.h create mode 100644 src/plugins/qmldesigner/core/include/notimplementedexception.h create mode 100644 src/plugins/qmldesigner/core/include/objectpropertybinding.h create mode 100644 src/plugins/qmldesigner/core/include/plaintexteditmodifier.h create mode 100644 src/plugins/qmldesigner/core/include/propertybinding.h create mode 100644 src/plugins/qmldesigner/core/include/propertycontainer.h create mode 100644 src/plugins/qmldesigner/core/include/propertymetainfo.h create mode 100644 src/plugins/qmldesigner/core/include/propertynode.h create mode 100644 src/plugins/qmldesigner/core/include/qmlanchors.h create mode 100644 src/plugins/qmldesigner/core/include/qmlchangeset.h create mode 100644 src/plugins/qmldesigner/core/include/qmlitemnode.h create mode 100644 src/plugins/qmldesigner/core/include/qmlmodelnodefacade.h create mode 100644 src/plugins/qmldesigner/core/include/qmlmodelview.h create mode 100644 src/plugins/qmldesigner/core/include/qmlobjectnode.h create mode 100644 src/plugins/qmldesigner/core/include/qmlstate.h create mode 100644 src/plugins/qmldesigner/core/include/removebasestateexception.h create mode 100644 src/plugins/qmldesigner/core/include/replaceallobjectdefinitionsvisitor.h create mode 100644 src/plugins/qmldesigner/core/include/rewriterview.h create mode 100644 src/plugins/qmldesigner/core/include/stylemanager.h create mode 100644 src/plugins/qmldesigner/core/include/subcomponentmanager.h create mode 100644 src/plugins/qmldesigner/core/include/textmodifier.h create mode 100644 src/plugins/qmldesigner/core/include/variantproperty.h create mode 100644 src/plugins/qmldesigner/core/include/widgetplugin_helper.h create mode 100644 src/plugins/qmldesigner/core/include/widgetqueryview.h create mode 100644 src/plugins/qmldesigner/core/instances/componentnodeinstance.cpp create mode 100644 src/plugins/qmldesigner/core/instances/componentnodeinstance.h create mode 100644 src/plugins/qmldesigner/core/instances/dummynodeinstance.cpp create mode 100644 src/plugins/qmldesigner/core/instances/dummynodeinstance.h create mode 100644 src/plugins/qmldesigner/core/instances/graphicsobjectnodeinstance.cpp create mode 100644 src/plugins/qmldesigner/core/instances/graphicsobjectnodeinstance.h create mode 100644 src/plugins/qmldesigner/core/instances/graphicsscenenodeinstance.cpp create mode 100644 src/plugins/qmldesigner/core/instances/graphicsscenenodeinstance.h create mode 100644 src/plugins/qmldesigner/core/instances/graphicsviewnodeinstance.cpp create mode 100644 src/plugins/qmldesigner/core/instances/graphicsviewnodeinstance.h create mode 100644 src/plugins/qmldesigner/core/instances/graphicswidgetnodeinstance.cpp create mode 100644 src/plugins/qmldesigner/core/instances/graphicswidgetnodeinstance.h create mode 100644 src/plugins/qmldesigner/core/instances/nodeinstance.cpp create mode 100644 src/plugins/qmldesigner/core/instances/nodeinstanceview.cpp create mode 100644 src/plugins/qmldesigner/core/instances/objectnodeinstance.cpp create mode 100644 src/plugins/qmldesigner/core/instances/objectnodeinstance.h create mode 100644 src/plugins/qmldesigner/core/instances/proxywidgetnodeinstance.cpp create mode 100644 src/plugins/qmldesigner/core/instances/proxywidgetnodeinstance.h create mode 100644 src/plugins/qmldesigner/core/instances/qmlgraphicsitemnodeinstance.cpp create mode 100644 src/plugins/qmldesigner/core/instances/qmlgraphicsitemnodeinstance.h create mode 100644 src/plugins/qmldesigner/core/instances/qmlpropertychangesnodeinstance.cpp create mode 100644 src/plugins/qmldesigner/core/instances/qmlpropertychangesnodeinstance.h create mode 100644 src/plugins/qmldesigner/core/instances/qmlstatenodeinstance.cpp create mode 100644 src/plugins/qmldesigner/core/instances/qmlstatenodeinstance.h create mode 100644 src/plugins/qmldesigner/core/instances/qmltransitionnodeinstance.cpp create mode 100644 src/plugins/qmldesigner/core/instances/qmltransitionnodeinstance.h create mode 100644 src/plugins/qmldesigner/core/instances/qmlviewnodeinstance.cpp create mode 100644 src/plugins/qmldesigner/core/instances/qmlviewnodeinstance.h create mode 100644 src/plugins/qmldesigner/core/instances/widgetnodeinstance.cpp create mode 100644 src/plugins/qmldesigner/core/instances/widgetnodeinstance.h create mode 100644 src/plugins/qmldesigner/core/iwidgetplugin.pri create mode 100644 src/plugins/qmldesigner/core/metainfo/enumeratormetainfo.cpp create mode 100644 src/plugins/qmldesigner/core/metainfo/gui.metainfo create mode 100644 src/plugins/qmldesigner/core/metainfo/itemlibraryinfo.cpp create mode 100644 src/plugins/qmldesigner/core/metainfo/metainfo.cpp create mode 100644 src/plugins/qmldesigner/core/metainfo/metainfoparser.cpp create mode 100644 src/plugins/qmldesigner/core/metainfo/nodemetainfo.cpp create mode 100644 src/plugins/qmldesigner/core/metainfo/propertymetainfo.cpp create mode 100644 src/plugins/qmldesigner/core/metainfo/subcomponentmanager.cpp create mode 100644 src/plugins/qmldesigner/core/model/abstractproperty.cpp create mode 100644 src/plugins/qmldesigner/core/model/abstractview.cpp create mode 100644 src/plugins/qmldesigner/core/model/anchorline.cpp create mode 100644 src/plugins/qmldesigner/core/model/bindingproperty.cpp create mode 100644 src/plugins/qmldesigner/core/model/bytearraymodifier.cpp create mode 100644 src/plugins/qmldesigner/core/model/componenttextmodifier.cpp create mode 100644 src/plugins/qmldesigner/core/model/copyhelper.cpp create mode 100644 src/plugins/qmldesigner/core/model/copyhelper.h create mode 100644 src/plugins/qmldesigner/core/model/copypasteutil.cpp create mode 100644 src/plugins/qmldesigner/core/model/copypasteutil.h create mode 100644 src/plugins/qmldesigner/core/model/import.cpp create mode 100644 src/plugins/qmldesigner/core/model/internalbindingproperty.cpp create mode 100644 src/plugins/qmldesigner/core/model/internalbindingproperty.h create mode 100644 src/plugins/qmldesigner/core/model/internalnode.cpp create mode 100644 src/plugins/qmldesigner/core/model/internalnode_p.h create mode 100644 src/plugins/qmldesigner/core/model/internalnodeabstractproperty.cpp create mode 100644 src/plugins/qmldesigner/core/model/internalnodeabstractproperty.h create mode 100644 src/plugins/qmldesigner/core/model/internalnodelistproperty.cpp create mode 100644 src/plugins/qmldesigner/core/model/internalnodelistproperty.h create mode 100644 src/plugins/qmldesigner/core/model/internalnodeproperty.cpp create mode 100644 src/plugins/qmldesigner/core/model/internalnodeproperty.h create mode 100644 src/plugins/qmldesigner/core/model/internalproperty.cpp create mode 100644 src/plugins/qmldesigner/core/model/internalproperty.h create mode 100644 src/plugins/qmldesigner/core/model/internalvariantproperty.cpp create mode 100644 src/plugins/qmldesigner/core/model/internalvariantproperty.h create mode 100644 src/plugins/qmldesigner/core/model/model.cpp create mode 100644 src/plugins/qmldesigner/core/model/model_p.h create mode 100644 src/plugins/qmldesigner/core/model/modelmerger.cpp create mode 100644 src/plugins/qmldesigner/core/model/modelnode.cpp create mode 100644 src/plugins/qmldesigner/core/model/modelnodepositionrecalculator.cpp create mode 100644 src/plugins/qmldesigner/core/model/modelnodepositionrecalculator.h create mode 100644 src/plugins/qmldesigner/core/model/modelnodepositionstorage.cpp create mode 100644 src/plugins/qmldesigner/core/model/modelrewriter.cpp create mode 100644 src/plugins/qmldesigner/core/model/modelrewriter.h create mode 100644 src/plugins/qmldesigner/core/model/modeltotextmerger.cpp create mode 100644 src/plugins/qmldesigner/core/model/modeltotextmerger.h create mode 100644 src/plugins/qmldesigner/core/model/modelutilities.cpp create mode 100644 src/plugins/qmldesigner/core/model/modificationgrouptoken.cpp create mode 100644 src/plugins/qmldesigner/core/model/nodeabstractproperty.cpp create mode 100644 src/plugins/qmldesigner/core/model/nodeanchors.cpp create mode 100644 src/plugins/qmldesigner/core/model/nodelistproperty.cpp create mode 100644 src/plugins/qmldesigner/core/model/nodeproperty.cpp create mode 100644 src/plugins/qmldesigner/core/model/objectpropertybinding.cpp create mode 100644 src/plugins/qmldesigner/core/model/painteventfilter.cpp create mode 100644 src/plugins/qmldesigner/core/model/painteventfilter_p.h create mode 100644 src/plugins/qmldesigner/core/model/parsedqml.cpp create mode 100644 src/plugins/qmldesigner/core/model/parsedqml.h create mode 100644 src/plugins/qmldesigner/core/model/paster.cpp create mode 100644 src/plugins/qmldesigner/core/model/paster.h create mode 100644 src/plugins/qmldesigner/core/model/plaintexteditmodifier.cpp create mode 100644 src/plugins/qmldesigner/core/model/propertybinding.cpp create mode 100644 src/plugins/qmldesigner/core/model/propertycontainer.cpp create mode 100644 src/plugins/qmldesigner/core/model/propertynode.cpp create mode 100644 src/plugins/qmldesigner/core/model/propertyparser.cpp create mode 100644 src/plugins/qmldesigner/core/model/propertyparser.h create mode 100644 src/plugins/qmldesigner/core/model/qmlanchors.cpp create mode 100644 src/plugins/qmldesigner/core/model/qmlchangeset.cpp create mode 100644 src/plugins/qmldesigner/core/model/qmlitemnode.cpp create mode 100644 src/plugins/qmldesigner/core/model/qmlmodelnodefacade.cpp create mode 100644 src/plugins/qmldesigner/core/model/qmlmodelview.cpp create mode 100644 src/plugins/qmldesigner/core/model/qmlobjectnode.cpp create mode 100644 src/plugins/qmldesigner/core/model/qmlstate.cpp create mode 100644 src/plugins/qmldesigner/core/model/qmltextgenerator.cpp create mode 100644 src/plugins/qmldesigner/core/model/qmltextgenerator.h create mode 100644 src/plugins/qmldesigner/core/model/rewriteaction.cpp create mode 100644 src/plugins/qmldesigner/core/model/rewriteaction.h create mode 100644 src/plugins/qmldesigner/core/model/rewriteactioncompressor.cpp create mode 100644 src/plugins/qmldesigner/core/model/rewriteactioncompressor.h create mode 100644 src/plugins/qmldesigner/core/model/rewriterview.cpp create mode 100644 src/plugins/qmldesigner/core/model/textmodifier.cpp create mode 100644 src/plugins/qmldesigner/core/model/texttomodelmerger.cpp create mode 100644 src/plugins/qmldesigner/core/model/texttomodelmerger.h create mode 100644 src/plugins/qmldesigner/core/model/variantparser.cpp create mode 100644 src/plugins/qmldesigner/core/model/variantparser.h create mode 100644 src/plugins/qmldesigner/core/model/variantproperty.cpp create mode 100644 src/plugins/qmldesigner/core/model/viewlogger.cpp create mode 100644 src/plugins/qmldesigner/core/model/viewlogger.h create mode 100644 src/plugins/qmldesigner/core/pluginmanager/widgetpluginmanager.cpp create mode 100644 src/plugins/qmldesigner/core/pluginmanager/widgetpluginmanager.h create mode 100644 src/plugins/qmldesigner/core/pluginmanager/widgetpluginpath.cpp create mode 100644 src/plugins/qmldesigner/core/pluginmanager/widgetpluginpath.h create mode 100644 src/plugins/qmldesigner/core/preview/stylemanager.cpp create mode 100644 src/plugins/qmldesigner/core/rewritertransaction.cpp create mode 100644 src/plugins/qmldesigner/core/rewritertransaction.h create mode 100644 src/plugins/qmldesigner/designmode.cpp create mode 100644 src/plugins/qmldesigner/designmode.h create mode 100644 src/plugins/qmldesigner/designmodewidget.cpp create mode 100644 src/plugins/qmldesigner/designmodewidget.h create mode 100644 src/plugins/qmldesigner/fxplugin/fx.metainfo create mode 100644 src/plugins/qmldesigner/fxplugin/fxplugin.cpp create mode 100644 src/plugins/qmldesigner/fxplugin/fxplugin.h create mode 100644 src/plugins/qmldesigner/fxplugin/fxplugin.pri create mode 100644 src/plugins/qmldesigner/fxplugin/fxplugin.pro create mode 100644 src/plugins/qmldesigner/fxplugin/fxplugin.qrc create mode 100644 src/plugins/qmldesigner/fxplugin/images/blended-image-icon.png create mode 100644 src/plugins/qmldesigner/fxplugin/images/default-icon.png create mode 100644 src/plugins/qmldesigner/fxplugin/images/grid-icon.png create mode 100644 src/plugins/qmldesigner/fxplugin/images/image-icon.png create mode 100644 src/plugins/qmldesigner/fxplugin/images/item-icon.png create mode 100644 src/plugins/qmldesigner/fxplugin/images/list-icon.png create mode 100644 src/plugins/qmldesigner/fxplugin/images/mouse-region-icon.png create mode 100644 src/plugins/qmldesigner/fxplugin/images/rect-icon.png create mode 100644 src/plugins/qmldesigner/fxplugin/images/template_image.png create mode 100644 src/plugins/qmldesigner/fxplugin/images/text-edit-icon.png create mode 100644 src/plugins/qmldesigner/fxplugin/images/text-icon.png create mode 100644 src/plugins/qmldesigner/fxplugin/images/widget.png create mode 100644 src/plugins/qmldesigner/fxplugin/plugindestdir.pri create mode 100644 src/plugins/qmldesigner/images/mode_Design.png create mode 100644 src/plugins/qmldesigner/qmldesigner.pro create mode 100644 src/plugins/qmldesigner/qmldesigner_dependencies.pri create mode 100644 src/plugins/qmldesigner/qmldesignerconstants.h create mode 100644 src/plugins/qmldesigner/qmldesignerplugin.cpp create mode 100644 src/plugins/qmldesigner/qmldesignerplugin.h create mode 100644 src/plugins/qmldesigner/qmldesignerplugin.pro create mode 100644 src/plugins/qmldesigner/qmldesignerplugin.qrc create mode 100644 src/tools/qmldesigner/qmldom/main.cpp create mode 100644 src/tools/qmldesigner/qmldom/qmldom.pro create mode 100644 src/tools/qmldesigner/standalone/128xBauhaus_Logo.png create mode 100644 src/tools/qmldesigner/standalone/16xBauhaus_Logo.png create mode 100644 src/tools/qmldesigner/standalone/256xBauhaus_Logo.png create mode 100644 src/tools/qmldesigner/standalone/32xBauhaus_Logo.png create mode 100644 src/tools/qmldesigner/standalone/512xBauhaus_Logo.png create mode 100644 src/tools/qmldesigner/standalone/64xBauhaus_Logo.png create mode 100644 src/tools/qmldesigner/standalone/Info.plist create mode 100644 src/tools/qmldesigner/standalone/aboutdialog.cpp create mode 100644 src/tools/qmldesigner/standalone/aboutdialog.h create mode 100644 src/tools/qmldesigner/standalone/application.cpp create mode 100644 src/tools/qmldesigner/standalone/application.h create mode 100644 src/tools/qmldesigner/standalone/bauhaus-logo.icns create mode 100644 src/tools/qmldesigner/standalone/bauhaus-macos.css create mode 100644 src/tools/qmldesigner/standalone/bauhaus-windows.css create mode 100644 src/tools/qmldesigner/standalone/bauhaus-x11.css create mode 100644 src/tools/qmldesigner/standalone/bauhaus.css create mode 100644 src/tools/qmldesigner/standalone/bauhaus.ico create mode 100644 src/tools/qmldesigner/standalone/bauhaus.qrc create mode 100644 src/tools/qmldesigner/standalone/css.png create mode 100644 src/tools/qmldesigner/standalone/edit.png create mode 100644 src/tools/qmldesigner/standalone/main.cpp create mode 100644 src/tools/qmldesigner/standalone/mainwindow.cpp create mode 100644 src/tools/qmldesigner/standalone/mainwindow.h create mode 100644 src/tools/qmldesigner/standalone/preview.png create mode 100644 src/tools/qmldesigner/standalone/standalone.pro create mode 100644 src/tools/qmldesigner/standalone/templates/fx.qml create mode 100644 src/tools/qmldesigner/standalone/templates/qgraphicsview.qml create mode 100644 src/tools/qmldesigner/standalone/templates/qwidget.qml create mode 100644 src/tools/qmldesigner/standalone/toolbar-cssedit.png create mode 100644 src/tools/qmldesigner/standalone/toolbar-edit.png create mode 100644 src/tools/qmldesigner/standalone/welcomescreen.cpp create mode 100644 src/tools/qmldesigner/standalone/welcomescreen.h create mode 100644 tests/auto/qml/qml.pro create mode 100644 tests/auto/qml/qmldesigner/bauhaustests/bauhaustests.pro create mode 100644 tests/auto/qml/qmldesigner/bauhaustests/testbauhaus.cpp create mode 100644 tests/auto/qml/qmldesigner/bauhaustests/testbauhaus.h create mode 100644 tests/auto/qml/qmldesigner/common/statichelpers.cpp create mode 100644 tests/auto/qml/qmldesigner/coretests/coretests.pro create mode 100644 tests/auto/qml/qmldesigner/coretests/testcore.cpp create mode 100644 tests/auto/qml/qmldesigner/coretests/testcore.h create mode 100644 tests/auto/qml/qmldesigner/coretests/testrewriterview.cpp create mode 100644 tests/auto/qml/qmldesigner/coretests/testrewriterview.h create mode 100644 tests/auto/qml/qmldesigner/data/fx/attributes.qml create mode 100644 tests/auto/qml/qmldesigner/data/fx/bigtest.qml create mode 100644 tests/auto/qml/qmldesigner/data/fx/components.qml create mode 100644 tests/auto/qml/qmldesigner/data/fx/empty.qml create mode 100644 tests/auto/qml/qmldesigner/data/fx/helloworld.qml create mode 100644 tests/auto/qml/qmldesigner/data/fx/image.qml create mode 100644 tests/auto/qml/qmldesigner/data/fx/properties.qml create mode 100644 tests/auto/qml/qmldesigner/data/fx/selectiontest.qml create mode 100644 tests/auto/qml/qmldesigner/data/fx/states.qml create mode 100644 tests/auto/qml/qmldesigner/data/fx/subitems/SubItem.qml create mode 100644 tests/auto/qml/qmldesigner/data/fx/topitem.qml create mode 100644 tests/auto/qml/qmldesigner/data/qt-logo.png create mode 100644 tests/auto/qml/qmldesigner/data/qwidget/test.qml create mode 100644 tests/auto/qml/qmldesigner/data/testfiles.qrc create mode 100644 tests/auto/qml/qmldesigner/propertyeditortests/propertyeditortests.pro create mode 100644 tests/auto/qml/qmldesigner/propertyeditortests/testpropertyeditor.cpp create mode 100644 tests/auto/qml/qmldesigner/propertyeditortests/testpropertyeditor.h create mode 100644 tests/auto/qml/qmldesigner/qmldesigner.pro create mode 100644 tests/auto/qml/qmldesigner/testview.cpp create mode 100644 tests/auto/qml/qmldesigner/testview.h diff --git a/.gitignore b/.gitignore index 85e9a5aca75..15a20a779f4 100644 --- a/.gitignore +++ b/.gitignore @@ -73,3 +73,6 @@ bin/qtcreator_process_stub* bin/qtcreator.exe share/doc/qtcreator/qtcreator.qch tests/manual/cplusplus/cplusplus0 +tests/auto/qml/qmldesigner/bauhaustests/tst_bauhaus +tests/auto/qml/qmldesigner/coretests/tst_qmldesigner_core +tests/auto/qml/qmldesigner/propertyeditortests/tst_propertyeditor diff --git a/share/qtcreator/qmldesigner/Button.qml b/share/qtcreator/qmldesigner/Button.qml new file mode 100644 index 00000000000..22b2a9c0634 --- /dev/null +++ b/share/qtcreator/qmldesigner/Button.qml @@ -0,0 +1,86 @@ +import Qt 4.6 + +Rectangle { + property var label: "Button" + signal clicked + + width: 75 + height: 20 + radius: 10 + color: "grey" + + Rectangle { + anchors.fill: parent; + anchors.leftMargin: 1; + anchors.rightMargin: 1; + anchors.topMargin: 1; + anchors.bottomMargin: 1; + + color: "#2c2c2c"; + radius: 9 + + Rectangle { + id: buttonGradientRectangle + anchors.fill: parent; + anchors.leftMargin: 1; + anchors.rightMargin: 1; + anchors.topMargin: 1; + anchors.bottomMargin: 1; + + color: "black"; + gradient: normalGradient + radius: 8; + + Gradient { + id: pressedGradient + GradientStop { position: 0.0; color: "#686868" } + GradientStop { position: 1.0; color: "#8a8a8a" } + } + + Gradient { + id: normalGradient + GradientStop { position: 0.0; color: "#8a8a8a" } + GradientStop { position: 1.0; color: "#686868" } + } + } + } + + Text { + color: "white" + text: parent.label + style: "Raised"; + anchors.left: parent.left + anchors.top: parent.top + anchors.right: parent.right + anchors.bottom: parent.bottom + horizontalAlignment: "AlignHCenter"; + verticalAlignment: "AlignVCenter"; + } + + MouseRegion { + id: mouseRegion + anchors.fill: parent + onReleased: { parent.clicked.emit(); } + } + + states: [ + State { + name: "released" + when: !mouseRegion.pressed + + PropertyChanges { + target: buttonGradientRectangle + gradient: normalGradient + } + }, + State { + name: "pressed" + when: mouseRegion.pressed + + PropertyChanges { + target: buttonGradientRectangle + gradient: pressedGradient + } + } + ] +} diff --git a/share/qtcreator/qmldesigner/gradient.png b/share/qtcreator/qmldesigner/gradient.png new file mode 100644 index 0000000000000000000000000000000000000000..dd71b4b691e844f7979230a6a8e650d95c363a83 GIT binary patch literal 340205 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%0AJ3SQzy#?6HU5?b=3 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%0DAEBzKhrxXe%Ry`MuHR4#Zu#7Y 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�BST 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Ξ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 be+3u-L(s9F+)T))00000NkvXXu0mjfgW$wk literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/AnchorBox.qml b/share/qtcreator/qmldesigner/propertyeditor/Qt/AnchorBox.qml new file mode 100644 index 00000000000..d76e657d672 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyeditor/Qt/AnchorBox.qml @@ -0,0 +1,199 @@ +import Qt 4.6 + +QWidget { + width:220;fixedWidth: width + height:220;fixedHeight: height + styleSheetFile: "anchorbox.css" + + Script { + function isBorderAnchored() { + return anchorBackend.leftAnchored || anchorBackend.topAnchored || anchorBackend.rightAnchored || anchorBackend.bottomAnchored; + } + + function fill() { + anchorBackend.fill(); + } + + function breakLayout() { + anchorBackend.resetLayout() + } + } + + QPushButton { + text: "fill"; + height:20;fixedHeight: height; + x: 0; + y: 0; + width:200;fixedWidth: width; + id: QPushButton1; + onReleased: fill(); + } + + QPushButton { + text: "break"; + y: 200; + height:20;fixedHeight: height; + width:200;fixedWidth: width; + x: 0; + id: QPushButton3; + onReleased: breakLayout(); + } + + QPushButton { + height:100;fixedHeight: height; + text: "left"; + font.bold: true; + x: 16 ; + y: 60; + //styleSheet: "border-radius:5px; background-color: #ffda82"; + width:30;fixedWidth: width; + id: QPushButton5; + checkable: true; + checked: anchorBackend.leftAnchored; + onReleased: { + if (checked) { + anchorBackend.horizontalCentered = false; + anchorBackend.leftAnchored = true; + } else { + anchorBackend.leftAnchored = false; + } + } + } + + QPushButton { + text: "top"; + font.bold: true; + //styleSheet: "border-radius:5px; background-color: #ffda82"; + height:72;fixedHeight: height; + width:100;fixedWidth: width; + x: 49; + y: 30; + id: QPushButton6; + checkable: true; + checked: anchorBackend.topAnchored; + onReleased: { + if (checked) { + anchorBackend.verticalCentered = false; + anchorBackend.topAnchored = true; + } else { + anchorBackend.topAnchored = false; + } + } + } + + QPushButton { + text: "right"; + font.bold: true; + x: 153; + y: 60; + //styleSheet: "border-radius:5px; background-color: #ffda82"; + width:30;fixedWidth: width; + height:100;fixedHeight: height; + id: QPushButton7; + checkable: true; + checked: anchorBackend.rightAnchored; + onReleased: { + if (checked) { + anchorBackend.horizontalCentered = false; + anchorBackend.rightAnchored = true; + } else { + anchorBackend.rightAnchored = false; + } + } + } + + QPushButton { + text: "bottom"; + font.bold: true; + //styleSheet: "border-radius:5px; background-color: #ffda82"; + width:100;fixedWidth: width; + x: 49; + y: 164; + height:27;fixedHeight: height; + id: QPushButton8; + checkable: true; + checked: anchorBackend.bottomAnchored; + onReleased: { + if (checked) { + anchorBackend.verticalCentered = false; + anchorBackend.bottomAnchored = true; + } else { + anchorBackend.bottomAnchored = false; + } + } + } + + QToolButton { + width:100;fixedWidth: width; + //styleSheet: "border-radius:50px;background-color: rgba(85, 170, 255, 255)"; + x: 49; + y: 60; + height:100;fixedHeight: height; + id: QPushButton9; + + QPushButton { + width:24;fixedWidth: width; + //styleSheet: "border-radius:5px; background-color: #bf3f00;"; + x: 38; + y: 2; + height:96;fixedHeight: height; + checkable: true; + id: horizontalCenterButton; + checked: anchorBackend.horizontalCentered; + onReleased: { + if (checked) { + anchorBackend.rightAnchored = false; + anchorBackend.leftAnchored = false; + anchorBackend.horizontalCentered = true; + } else { + anchorBackend.horizontalCentered = false; + } + } + } + + QPushButton { + height:24;fixedHeight: height; + x: 2; + y: 38; + width:96;fixedWidth: width; + id: verticalCenterButton; + checkable: true; + checked: anchorBackend.verticalCentered; + onReleased: { + if (checked) { + anchorBackend.topAnchored = false; + anchorBackend.bottomAnchored = false; + anchorBackend.verticalCentered = true; + } else { + anchorBackend.verticalCentered = false; + } + } + } + + QPushButton { + text: "center"; + font.bold: true; + //styleSheet: "border-radius:20px; background-color: #ff5500"; + width:40;fixedWidth: width; + height:40;fixedHeight: height; + x: 30; + y: 30; + id: centerButton; + checkable: true; + checked: anchorBackend.verticalCentered && anchorBackend.horizontalCentered; + onReleased: { + if (checked) { + anchorBackend.leftAnchored = false; + anchorBackend.topAnchored = false; + anchorBackend.rightAnchored = false; + anchorBackend.bottomAnchored = false; + anchorBackend.verticalCentered = true; + anchorBackend.horizontalCentered = true; + } else { + anchorBackend.verticalCentered = false; + anchorBackend.horizontalCentered = false; + } + } + } + } +} diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/BorderImageSpecifics.qml b/share/qtcreator/qmldesigner/propertyeditor/Qt/BorderImageSpecifics.qml new file mode 100644 index 00000000000..ed26dbc3741 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyeditor/Qt/BorderImageSpecifics.qml @@ -0,0 +1,69 @@ +import Qt 4.6 +import Bauhaus 1.0 + +GroupBox { + maximumHeight: 240; + + + finished: finishedNotify; + caption: "Image"; + + layout: QVBoxLayout { + topMargin: 12; + bottomMargin: 12; + leftMargin: 12; + rightMargin: 12; + + FileWidget { + enabled: isBaseState || backendValues.id.value != ""; + maximumWidth: 250; + text: "Source: "; + fileName: backendValues.source.value; + onFileNameChanged: { + backendValues.source.value = fileName; + } + } + + + IntEditor { + id: pixelSize; + backendValue: backendValues.border_left; + caption: "Border Left: " + baseStateFlag: isBaseState; + + step: 1; + minimumValue: 0; + maximumValue: 2000; + } + + IntEditor { + backendValue: backendValues.border_right; + caption: "Border Right: " + baseStateFlag: isBaseState; + + step: 1; + minimumValue: 0; + maximumValue: 2000; + } + + IntEditor { + backendValue: backendValues.border_top; + caption: "Border Top: " + baseStateFlag: isBaseState; + + step: 1; + minimumValue: 0; + maximumValue: 2000; + } + + IntEditor { + backendValue: backendValues.border_bottom; + caption: "Border Bottom:" + baseStateFlag: isBaseState; + + step: 1; + minimumValue: 0; + maximumValue: 2000; + } + } +} diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/CheckBox.qml b/share/qtcreator/qmldesigner/propertyeditor/Qt/CheckBox.qml new file mode 100644 index 00000000000..f5ad1b91acd --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyeditor/Qt/CheckBox.qml @@ -0,0 +1,38 @@ +import Qt 4.6 + +QCheckBox { //This is a special CheckBox that does color coding for states + id: CheckBox; + + property var backendValue; + property var baseStateFlag; + + checkable: true; + checked: backendValue.value === undefined ? false : backendValue.value; + onToggled: { + backendValue.value = checked; + } + + onBaseStateFlagChanged: { + evaluate(); + } + + onBackendValueChanged: { + evaluate(); + } + + Script { + function evaluate() { + if (baseStateFlag) { + if (backendValue.isInModel) + CheckBox.setStyleSheet("color: white;"); + else + CheckBox.setStyleSheet("color: gray;"); + } else { + if (backendValue.IsInSubState) + CheckBox.setStyleSheet("color: blue;"); + else + CheckBox.setStyleSheet("color: gray;"); + } + } + } +} diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/ColumnSpecifics.qml b/share/qtcreator/qmldesigner/propertyeditor/Qt/ColumnSpecifics.qml new file mode 100644 index 00000000000..f95f56a101d --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyeditor/Qt/ColumnSpecifics.qml @@ -0,0 +1,29 @@ +import Qt 4.6 +import Bauhaus 1.0 + +GroupBox { + maximumHeight: 200; + + finished: finishedNotify; + caption: "Grid"; + id: GridSpecifics; + + layout: QVBoxLayout { + + topMargin: 18; + bottomMargin: 2; + leftMargin: 8; + rightMargin: 8; + + + IntEditor { + id: spacing; + backendValue: backendValues.spacing; + caption: "Spacing: " + baseStateFlag: isBaseState; + step: 1; + minimumValue: 0; + maximumValue: 2000; + } + } +} diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/DoubleSpinBox.qml b/share/qtcreator/qmldesigner/propertyeditor/Qt/DoubleSpinBox.qml new file mode 100644 index 00000000000..d7926804799 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyeditor/Qt/DoubleSpinBox.qml @@ -0,0 +1,144 @@ +import Qt 4.6 +import Bauhaus 1.0 + +QWidget { //This is a special DoubleSpinBox that does color coding for states + id: DoubleSpinBox; + + property var backendValue; + property var baseStateFlag; + property alias singleStep: box.singleStep + property alias minimum: box.minimum + property alias maximum: box.maximum + + minimumHeight: 22; + + onBaseStateFlagChanged: { + evaluate(); + setIcon(); + } + + onBackendValueChanged: { + evaluate(); + setIcon(); + } + + Script { + function evaluate() { + if (baseStateFlag) { + if (backendValue != null && backendValue.isInModel) + DoubleSpinBox.setStyleSheet("color: white;"); + else + DoubleSpinBox.setStyleSheet("color: gray;"); + } else { + if (backendValue != null && backendValue.isInSubState) + DoubleSpinBox.setStyleSheet("color: blue;"); + else + DoubleSpinBox.setStyleSheet("color: gray;"); + } + } + } + + layout: QHBoxLayout { + topMargin: 0; + bottomMargin: 0; + leftMargin: 0; + rightMargin: 2; + spacing: 0; + + QDoubleSpinBox { + id: box; + decimals: 2; + keyboardTracking: false; + enabled: (backendValue == null || backendValue.complexNode == null) ? false : !backendValue.isBound && !backendValue.complexNode.exists + value: DoubleSpinBox.backendValue == null ? .0 : DoubleSpinBox.backendValue.value; + onValueChanged: { + if (DoubleSpinBox.backendValue != null) + DoubleSpinBox.backendValue.value = value; + } + onFocusChanged: { + //extendedSwitches.active = focus; + //extendedSwitches.backendValue = backendValue; + } + + onMouseOverChanged: { + //extendedButton.active = mouseOver; + } + } + } + + QToolButton { + property bool active: false; + id: extendedButton; + iconFromFile: "images/expression.png"; + visible: false; + width: 14; + height: 14; + y: box.y + 2; + x: box.x + 2; + focusPolicy: "Qt::NoFocus"; + opacity: 0; + + opacity: Behavior { + NumberAnimation { + easing: "easeInSine" + duration: 200 + } + } + + onPressed: { + + } + + Script { + function setIcon() { + if (backendValue == null) + extendedButton.iconFromFile = "images/placeholder.png" + else if (backendValue.isBound) { + extendedButton.iconFromFile = "images/expression.png" + } else { + if (backendValue.complexNode != null && backendValue.complexNode.exists) { + extendedButton.iconFromFile = "images/behaivour.png" + } else { + extendedButton.iconFromFile = "images/placeholder.png" + } + } + } + } + + onMouseOverChanged: { + if (mouseOver) { + iconFromFile = "images/submenu.png"; + opacity = 1; + } else { + setIcon(); + opacity = 0; + } + } + + onActiveChanged: { + if (active) { + setIcon(); + opacity = 1; + } else { + opacity = 0; + } + } + + menu: QMenu { + actions: [ + QAction { + text: "Reset"; + }, + QAction { + text: "Set Expression"; + }, + QAction { + text: "Add Behaivour"; + } + ] + } + toolButtonStyle: "Qt::ToolButtonIconOnly"; + popupMode: "QToolButton::InstantPopup"; + arrowType: "Qt::NoArrow"; + } +} diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/Extended.qml b/share/qtcreator/qmldesigner/propertyeditor/Qt/Extended.qml new file mode 100644 index 00000000000..ad568ffd0ae --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyeditor/Qt/Extended.qml @@ -0,0 +1,191 @@ +import Qt 4.6 +import Bauhaus 1.0 + +GroupBox { +caption: "Effects" +id: Extended; +maximumHeight: 260; + +layout: QVBoxLayout { + topMargin: 12; + leftMargin: 10; + rightMargin: 10; + + property var effect: backendValues == null || backendValues.effect === undefined ? null : backendValues.effect + property var complexNode : effect === null ? null : effect.complexNode + + QWidget { + maximumHeight: 40; + layout: QHBoxLayout { + QLabel { + text: "Effects: "; + font.bold: true; + } + QComboBox { + enabled: isBaseState; + property var type: complexNode == null ? null : complexNode.type + property var dirty; + id: effectComboBox; + items : { [ + "None", + "Blur", + "Opacity", + "Colorize", + "DropShadow" + ] } + + onCurrentTextChanged: { + if (dirty) //avoid recursion; + return; + if (complexNode != null && complexNode.exists) + complexNode.remove(); + if (currentText == "None") { + ; + } else if (complexNode != null) { + complexNode.add("Qt/" + currentText); + } + } + + onTypeChanged: { + dirty = true; + if (complexNode != null && complexNode.type != "") + currentText = complexNode.type; + else + currentText = "None"; + dirty = false; + } + } + } + }// QWidget + + property var properties: complexNode == null ? null : complexNode.properties + + QWidget { + minimumHeight: 20; + layout: QVBoxLayout { + + QWidget { + visible: effectComboBox.currentText == "Blur"; + layout: QVBoxLayout { + topMargin: 12; + IntEditor { + id: blurRadius; + backendValue: properties == null ? null : properties.blurRadius; + caption: "Blur Radius:" + baseStateFlag: isBaseState; + + step: 1; + minimumValue: 0; + maximumValue: 20; + } + } + } + + QWidget { + visible: effectComboBox.currentText == "Opacity"; + layout: QVBoxLayout { + DoubleSpinBox { + id: OpcacityEffectSpinBox; + objectName: "OpcacityEffectSpinBox"; + backendValue: properties == null ? null : properties.opacity; + minimum: 0; + maximum: 1; + singleStep: 0.1; + baseStateFlag: isBaseState; + } + } + } + + QWidget { + visible: effectComboBox.currentText == "Colorize"; + layout: QVBoxLayout { + + property var colorProp: properties == null ? null : properties.color + + ColorWidget { + id: ColorizeColor; + text: "Color:"; + minimumHeight: 20; + minimumWidth: 20; + color: (colorProp == null) ? "black" : colorProp.value; + onColorChanged: { + colorProp.value = strColor; + } + } + + } + } + + QWidget { + visible: effectComboBox.currentText == "Pixelize"; + layout: QVBoxLayout { + topMargin: 12; + IntEditor { + id: pixelSize; + backendValue: properties == null ? null : properties.pixelSize; + caption: "Pixel Size:" + baseStateFlag: isBaseState; + + step: 1; + minimumValue: 0; + maximumValue: 20; + } + } + } + + QWidget { + visible: effectComboBox.currentText == "DropShadow"; + layout: QVBoxLayout { + + topMargin: 12; + IntEditor { + id: blurRadiusShadow; + backendValue: properties == null ? null : properties.blurRadius; + caption: "Blur Radius:" + baseStateFlag: isBaseState; + + step: 1; + minimumValue: 0; + maximumValue: 20; + } + + ColorWidget { + id: DropShadowColor; + maximumHeight: 40; + text: "Color:"; + minimumHeight: 20; + minimumWidth: 20; + color: properties == null || properties.color == null ? "black" : properties.color.value + onColorChanged: { + if (properties != null && properties.color != null) + properties.color.value = strColor; + } + } + + IntEditor { + id: xOffset; + backendValue: properties == null ? 0 : properties.xOffset; + caption: "x Offset: " + baseStateFlag: isBaseState; + + step: 1; + minimumValue: 0; + maximumValue: 20; + } + + IntEditor { + id: yOffset; + backendValue: properties == null ? 0 : properties.yOffset; + caption: "y Offset: " + baseStateFlag: isBaseState; + + step: 1; + minimumValue: 0; + maximumValue: 20; + } + } + } + } + } + } //QVBoxLayout +} //GroupBox diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/ExtendedPane.qml b/share/qtcreator/qmldesigner/propertyeditor/Qt/ExtendedPane.qml new file mode 100644 index 00000000000..8283e6ecdb0 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyeditor/Qt/ExtendedPane.qml @@ -0,0 +1,30 @@ +import Qt 4.6 +import Bauhaus 1.0 + +QScrollArea { + widgetResizable: true; + styleSheetFile: ":/qmldesigner/scrollbar.css"; + horizontalScrollBarPolicy: "Qt::ScrollBarAlwaysOff"; + id: ExtendedPane; + visible: false; + visible: false; + content: ProperyEditorExtended; + QFrame { + minimumHeight: 440; + id: ProperyEditorExtended + layout: QVBoxLayout { + topMargin: 2; + bottomMargin: 2; + leftMargin: 2; + rightMargin: 2; + Type { + } + Extended { + id: extendedBox; + } + + QScrollArea { + } + } + } +} diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/ExtendedSwitches.qml b/share/qtcreator/qmldesigner/propertyeditor/Qt/ExtendedSwitches.qml new file mode 100644 index 00000000000..508711314be --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyeditor/Qt/ExtendedSwitches.qml @@ -0,0 +1,60 @@ +import Qt 4.6 +import Bauhaus 1.0 + +QFrame { + visible: false; + focusPolicy: "Qt::NoFocus" + id: extendedSwitches; + property bool active: false; + property var backendValue; + styleSheetFile: "switch.css"; + property var specialModeIcon; + specialModeIcon: "images/standard.png"; + + opacity: 0; + + opacity: Behavior { + NumberAnimation { + easing: "easeInSine" + duration: 200 + } + } + + onActiveChanged: { + if (!nameLineEdit.focus) + if (active) { + opacity = 1; + } else { + opacity = 0; + } + } + + layout: QHBoxLayout { + topMargin: 6; + bottomMargin: 4; + leftMargin: 10; + rightMargin: 8; + spacing: 4; + + QPushButton { + focusPolicy: "Qt::NoFocus" + onReleased: backendValue.resetValue(); + iconFromFile: "images/reset-button.png"; + toolTip: "reset property"; + } + + QLineEdit { + id: nameLineEdit; + readOnly: false; + //focusPolicy: "Qt::NoFocus" + text: backendValue == null ? "" : backendValue.expression; + onEditingFinished: { + if (backendValue != null) + backendValue.expression = text; + } + onFocusChanged: { + extendedSwitches.active = focus; + } + } + } +} diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/FlickableSpecifics.qml b/share/qtcreator/qmldesigner/propertyeditor/Qt/FlickableSpecifics.qml new file mode 100644 index 00000000000..758fc1a3cab --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyeditor/Qt/FlickableSpecifics.qml @@ -0,0 +1,111 @@ +import Qt 4.6 +import Bauhaus 1.0 + +GroupBox { + finished: finishedNotify; + caption: "Flickable"; + + layout: QVBoxLayout { + topMargin: 15; + bottomMargin: 6; + leftMargin: 0; + rightMargin: 0; + + QWidget { + id: contentWidget; + maximumHeight: 220; + + layout: QHBoxLayout { + topMargin: 0; + bottomMargin: 0; + leftMargin: 10; + rightMargin: 10; + + QWidget { + layout: QVBoxLayout { + topMargin: 0; + bottomMargin: 0; + leftMargin: 0; + rightMargin: 0; + QLabel { + minimumHeight: 20; + text: "Horizontal Velocity:" + font.bold: true; + } + + QLabel { + minimumHeight: 20; + text: "Vertical Velocity:" + font.bold: true; + } + + QLabel { + minimumHeight: 20; + text: "Maximum Flick Velocity:" + font.bold: true; + } + + QLabel { + minimumHeight: 20; + text: "Over Shoot:" + font.bold: true; + } + } + } + + + QWidget { + layout: QVBoxLayout { + topMargin: 0; + bottomMargin: 0; + leftMargin: 0; + rightMargin: 0; + + + DoubleSpinBox { + id: HorizontalVelocitySpinBox; + objectName: "HorizontalVelocitySpinBox"; + backendValue: backendValues.horizontalVelocity; + minimumWidth: 30; + minimum: 0.1 + maximum: 10 + singleStep: 0.1 + baseStateFlag: isBaseState; + } + + DoubleSpinBox { + id: VerticalVelocitySpinBox; + objectName: "VerticalVelocitySpinBox"; + backendValue: backendValues.verticalVelocity; + minimumWidth: 30; + minimum: 0.1 + maximum: 10 + singleStep: 0.1 + baseStateFlag: isBaseState; + } + + DoubleSpinBox { + id: MaximumVelocitySpinBox; + objectName: "MaximumVelocitySpinBox"; + backendValue: backendValues.maximumFlickVelocity; + minimumWidth: 30; + minimum: 0.1 + maximum: 10 + singleStep: 0.1 + baseStateFlag: isBaseState; + } + + CheckBox { + id: OvershootCheckBox; + text: "overshoot"; + backendValue: backendValues.overShoot; + baseStateFlag: isBaseState; + checkable: true; + } + } + } + + } + } + } +} diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/FlipableSpecifics.qml b/share/qtcreator/qmldesigner/propertyeditor/Qt/FlipableSpecifics.qml new file mode 100644 index 00000000000..a5efb0d3190 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyeditor/Qt/FlipableSpecifics.qml @@ -0,0 +1,135 @@ +import Qt 4.6 +import Bauhaus 1.0 + +GroupBox { + maximumHeight: 200; + + finished: finishedNotify; + caption: "Text"; + + layout: QVBoxLayout { + id: textSpecifics; + topMargin: 4; + bottomMargin: 2; + leftMargin: 4; + rightMargin: 4; + QWidget { + layout: QHBoxLayout { + topMargin: 8; + bottomMargin: 2; + leftMargin: 10; + rightMargin: 10; + QLabel { + text: "Text:" + font.bold: true; + } + QLineEdit { + text: backendValues.text.value; + onTextChanged: { + backendValues.text.value = text; + } + } + } + } + QWidget { + layout: QHBoxLayout { + topMargin: 6; + bottomMargin: 2; + leftMargin: 10; + rightMargin: 10; + QLabel { + text: "wrap:" + font.bold: true; + } + QCheckBox { + + text: "isWrapping"; + checkable: true; + checked: backendValues.wrap.value; + onToggled: { + backendValues.wrap.value = checked; + } + } + } + } + + FontWidget { + text: "Font:"; + dataFont: backendValues.font.value; + onDataFontChanged: { + backendValues.font.value = dataFont; + } + } + + QWidget { + layout: QHBoxLayout { + topMargin: 4; + bottomMargin: 2; + leftMargin: 10; + rightMargin: 6; + + } + } + + QWidget { + layout: QHBoxLayout { + topMargin: 4; + bottomMargin: 2; + leftMargin: 10; + rightMargin: 6; + QLabel { + text: "Alignment:" + font.bold: true; + } + QLabel { + styleSheet: "QLabel {font: normal }"; + text: backendValues.horizontalAlignment.value; + } + + QComboBox { + items : { ["AlignLeft", "AlignRight", "AlignHCenter"] } + currentText: backendValues.hAlign.value; + onItemsChanged: { + currentText = backendValues.horizontalAlignment.value; + print(currentText); + } + + onCurrentTextChanged: { + if (count == 3) + print("set"); + print(currentText); + backendValues.horizontalAlignment.value = currentText; + print(currentText); + print("set"); + } + + } + } + } + HorizontalLine { + } + QWidget { + layout: QHBoxLayout { + topMargin: 2; + bottomMargin: 2; + leftMargin: 10; + rightMargin: 10; + + ColorWidget { + text: "Color:"; + color: backendValues.color.value; + onColorChanged: { + backendValues.color.value = strColor; + } + } + ColorWidget { + text: "Style color:"; + color: backendValues.styleColor.value; + onColorChanged: { + backendValues.styleColor.value = strColor; + } + } + } + } + } +} diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/Geometry.qml b/share/qtcreator/qmldesigner/propertyeditor/Qt/Geometry.qml new file mode 100644 index 00000000000..d23246c4a37 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyeditor/Qt/Geometry.qml @@ -0,0 +1,173 @@ +import Qt 4.6 + +GroupBox { + finished: finishedNotify; + id: Geometry; + caption: "Geometry"; + + maximumHeight: 240; + + layout: QHBoxLayout { + topMargin: 14; + bottomMargin: 4; + leftMargin: 4; + rightMargin: 4; + + QWidget { + + layout: QVBoxLayout { + topMargin: 0; + bottomMargin: 60; + leftMargin: 10; + rightMargin: 10; + spacing: 25; + + QLabel { + text: "Position:"; + font.bold: true; + } + QLabel { + text: "Size:" + font.bold: true + } + } + } + + QWidget { + layout: QVBoxLayout { + topMargin: 0; + bottomMargin: 0; + QWidget { + layout: QHBoxLayout { + topMargin: 0; + bottomMargin: 0; + + //--- + + QWidget { // 1 + layout: QHBoxLayout { //2 + topMargin: 0; + bottomMargin: 0; + QWidget { //3 + layout: QVBoxLayout { //4 + topMargin: 0; + bottomMargin: 0; + DoubleSpinBox { + id: XSpinBox; + singleStep: 1; + objectName: "XSpinBox"; + enabled: anchorBackend.hasParent; + backendValue: backendValues.x == undefined ? 0 : backendValues.x + minimum: -2000; + maximum: 2000; + baseStateFlag: isBaseState; + } + QLabel { //6 + text: "X"; + alignment: "Qt::AlignHCenter" + font.bold: true; + } // 6 + } //QHBoxLayout //4 + } //QWidget //3 + QWidget { // 7 + layout: QVBoxLayout { //8 + topMargin: 0; + bottomMargin: 0; + + DoubleSpinBox { + id: YSpinBox; + singleStep: 1; + objectName: "YSpinBox"; + backendValue: backendValues.y == undefined ? 0 : backendValues.y + enabled: anchorBackend.hasParent; + minimum: -2000; + maximum: 2000; + baseStateFlag: isBaseState; + } + QLabel { //10 + text: "Y"; + alignment: "Qt::AlignHCenter"; + font.bold: true; + } //10 + } //QVBoxLayout //8 + } // QWidget //3 + + } //QHBoxLayout //7 + } //QWidget //1 + //--- + } //QHBoxLayout + } //GroupBox + QWidget { + layout: QHBoxLayout { + topMargin: 0; + bottomMargin: 10; + //--- + QWidget { + layout: QHBoxLayout { + topMargin: 0; + bottomMargin: 0; + QWidget { + layout: QVBoxLayout { + topMargin: 0; + bottomMargin: 0; + DoubleSpinBox { + id: WidthSpinBox; + singleStep: 1; + objectName: "WidthSpinBox"; + backendValue: backendValues.width == undefined ? 0 : backendValues.width + minimum: -2000; + maximum: 2000; + baseStateFlag: isBaseState; + } + QLabel { + text: "Width"; + alignment: "Qt::AlignHCenter"; + font.bold: true; + } + } //QVBoxLayout + } //QWidget + QWidget { + layout: QVBoxLayout { + topMargin: 0; + bottomMargin: 0; + DoubleSpinBox { + id: HeightSpinBox; + singleStep: 1; + objectName: "HeightSpinBox"; + backendValue: backendValues.height == undefined ? 0 : backendValues.height + minimum: -2000; + maximum: 2000; + baseStateFlag: isBaseState; + } + QLabel { + id: HeightLabel; + text: "Height"; + alignment: "Qt::AlignHCenter"; + font.bold: true; + } + } //QVBoxLayout + } //QWidget + } //QHBoxLayout + } //QWidget + //--- + + } // QHBoxLayout + } //Widget + + IntEditor { + id: borderWidth; + backendValue: backendValues.z == undefined ? 0 : backendValues.z + caption: "z:" + baseStateFlag: isBaseState; + step: 1; + minimumValue: 0; + maximumValue: 100; + } + } //QVBoxLayout + + } //QWidget + + + } //QHBoxLayout + +} //QGroupBox diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/GridSpecifics.qml b/share/qtcreator/qmldesigner/propertyeditor/Qt/GridSpecifics.qml new file mode 100644 index 00000000000..f2215d7632f --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyeditor/Qt/GridSpecifics.qml @@ -0,0 +1,49 @@ +import Qt 4.6 +import Bauhaus 1.0 + +GroupBox { + maximumHeight: 200; + + finished: finishedNotify; + caption: "Grid"; + id: GridSpecifics; + + layout: QVBoxLayout { + + topMargin: 18; + bottomMargin: 2; + leftMargin: 8; + rightMargin: 8; + + + IntEditor { + id: spacing; + backendValue: backendValues.spacing; + caption: "Spacing: " + baseStateFlag: isBaseState; + step: 1; + minimumValue: 0; + maximumValue: 2000; + } + + IntEditor { + id: rows; + backendValue: backendValues.rows; + caption: "Rows: " + baseStateFlag: isBaseState; + step: 1; + minimumValue: 0; + maximumValue: 2000; + } + + IntEditor { + id: columns; + backendValue: backendValues.columns; + caption: "Columns: " + baseStateFlag: isBaseState; + step: 1; + minimumValue: 0; + maximumValue: 2000; + } + } +} diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/GroupBox.qml b/share/qtcreator/qmldesigner/propertyeditor/Qt/GroupBox.qml new file mode 100644 index 00000000000..d1106cc1cf0 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyeditor/Qt/GroupBox.qml @@ -0,0 +1,57 @@ +import Qt 4.6 +import Bauhaus 1.0 + +QExtGroupBox { + id: GroupBox; + + property var finished; + + property var caption; + + property var oldMaximumHeight; + + onFinishedChanged: { + CheckBox.raise(); + maximumHeight = height; + oldMaximumHeight = maximumHeight; + visible = false; + visible = true; + x = 6; + } + + QToolButton { + //QCheckBox { + id: CheckBox; + text: ""; + focusPolicy: "Qt::NoFocus"; + styleSheetFile: "specialCheckBox.css"; + y: 4; + x: 6; + height: 12; + width: 12; + arrowType: "Qt::DownArrow"; + toolButtonStyle: "Qt::ToolButtonTextBesideIcon"; + checkable: true; + checked: true; + font.bold: true; + onClicked: { + if (checked) { + //GroupBox.maximumHeight = oldMaximumHeight; + collapsed = false; + text = ""; + width = 12; + x = 6; + arrowType = "Qt::DownArrow"; + } else { + //GroupBox.maximumHeight = 20; + + collapsed = true; + text = GroupBox.caption; + visible = true; + width = 120; + x = 2; + arrowType = "Qt::RightArrow"; + } + } + } +} diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/GroupBoxOption.qml b/share/qtcreator/qmldesigner/propertyeditor/Qt/GroupBoxOption.qml new file mode 100644 index 00000000000..0e62cd036cd --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyeditor/Qt/GroupBoxOption.qml @@ -0,0 +1,21 @@ +import Qt 4.6 +import Bauhaus 1.0 + +QExtGroupBox { + id: GroupBoxOption; + + property var finished; + + property var caption; + + property var oldMaximumHeight; + + onFinishedChanged: { + CheckBox.raise(); + maximumHeight = height; + oldMaximumHeight = maximumHeight; + minimumHeight = height; + x = 6; + visible = false; + } +} diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/HorizontalLine.qml b/share/qtcreator/qmldesigner/propertyeditor/Qt/HorizontalLine.qml new file mode 100644 index 00000000000..b1f64185674 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyeditor/Qt/HorizontalLine.qml @@ -0,0 +1,8 @@ +import Qt 4.6 + + QLineEdit { + text: ""; + readOnly: true; + maximumHeight: 2; + styleSheet: "QLineEdit {border: 2px solid #3F3F3F;}"; + } \ No newline at end of file diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/ImageSpecifics.qml b/share/qtcreator/qmldesigner/propertyeditor/Qt/ImageSpecifics.qml new file mode 100644 index 00000000000..28d475853f2 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyeditor/Qt/ImageSpecifics.qml @@ -0,0 +1,26 @@ +import Qt 4.6 +import Bauhaus 1.0 + +GroupBox { + maximumHeight: 240; + + finished: finishedNotify; + caption: "Image"; + + layout: QVBoxLayout { + topMargin: 12; + bottomMargin: 12; + leftMargin: 2; + rightMargin: 2; + + FileWidget { + enabled: isBaseState || backendValues.id.value != ""; + maximumWidth: 250; + text: "Source: "; + fileName: backendValues.source.value; + onFileNameChanged: { + backendValues.source.value = fileName; + } + } + } +} diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/IntEditor.qml b/share/qtcreator/qmldesigner/propertyeditor/Qt/IntEditor.qml new file mode 100644 index 00000000000..69a34bb40b2 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyeditor/Qt/IntEditor.qml @@ -0,0 +1,51 @@ +import Qt 4.6 +import Bauhaus 1.0 + +QWidget { + id: IntEditor; + + property var backendValue; + property var baseStateFlag; + + property var caption; + + property var maximumValue: 99 + property var minimumValue: 0 + property var step: 1 + + layout: QHBoxLayout { + topMargin: 2; + bottomMargin: 2; + leftMargin: 0; + rightMargin: 0; + QLabel { + text: caption + font.bold: true; + } + + SpinBox { + backendValue: IntEditor.backendValue; + minimum: minimumValue; + maximum: maximumValue; + baseStateFlag: IntEditor.baseStateFlag; + } + + + QSlider { + property alias backendValue: IntEditor.backendValue + orientation: "Qt::Horizontal"; + minimum: minimumValue + maximum: maximumValue + singleStep: step + + value: (backendValue == undefined + || backendValue == null + || backendValue.value == undefined + || backendValue.value == null) ? 0 : backendValue.value + onValueChanged: { + if (backendValue != undefined && backendValue != null) + backendValue.value = value; + } + } + } +} diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/ItemPane.qml b/share/qtcreator/qmldesigner/propertyeditor/Qt/ItemPane.qml new file mode 100644 index 00000000000..94842b81cb4 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyeditor/Qt/ItemPane.qml @@ -0,0 +1,104 @@ +import Qt 4.6 +import Bauhaus 1.0 + +PropertyFrame { + layout: QVBoxLayout { + topMargin: 0; + bottomMargin: 0; + leftMargin: 0; + rightMargin: 0; + spacing: 0; + + Switches { + } + + ExtendedSwitches { + id: extendedSwitches; + } + + ScrollArea { + styleSheetFile: ":/qmldesigner/scrollbar.css"; + widgetResizable: true; + + finished: finishedNotify; + + horizontalScrollBarPolicy: "Qt::ScrollBarAlwaysOff"; + id: standardPane; + + + content: ProperyEditorStandard; + QFrame { + minimumHeight: 1100; + id: ProperyEditorStandard + layout: QVBoxLayout { + topMargin: 2; + bottomMargin: 2; + leftMargin: 2; + rightMargin: 2; + Type { + } + HorizontalLine { + } + + Geometry { + } + + Modifiers { + + } + + WidgetLoader { + id: specificsOne; + source: specificsUrl; + } + + + QScrollArea { + } + + } // layout + } //QWidget + } //QScrollArea + + + ScrollArea { + widgetResizable: true; + styleSheetFile: ":/qmldesigner/scrollbar.css"; + finished: finishedNotify; + horizontalScrollBarPolicy: "Qt::ScrollBarAlwaysOff"; + id: specialPane; + visible: false; + visible: false; + content: ProperyEditorSpecial; + QFrame { + minimumHeight: 600; + id: ProperyEditorSpecial + layout: QVBoxLayout { + topMargin: 2; + bottomMargin: 2; + leftMargin: 2; + rightMargin: 2; + Type { + } + + WidgetLoader { + id: specificsOne2; + source: specificsUrl; + } + + QScrollArea { + } + } + } + } + + ExtendedPane { + id: extendedPane; + } + + LayoutPane { + id: layoutPane; + } + + } +} diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/Layout.qml b/share/qtcreator/qmldesigner/propertyeditor/Qt/Layout.qml new file mode 100644 index 00000000000..e1460cbc808 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyeditor/Qt/Layout.qml @@ -0,0 +1,22 @@ +import Qt 4.6 + +GroupBox { + finished: finishedNotify; + caption: "Layout"; +id: Layout; +maximumHeight: 340; + enabled: anchorBackend.hasParent; + + layout: QVBoxLayout { + topMargin: 20; + bottomMargin: 10; + leftMargin: 40; + rightMargin: 20; + spacing: 20 + QLabel { + text: "Layout" + } + AnchorBox {} + } + +} diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/LayoutPane.qml b/share/qtcreator/qmldesigner/propertyeditor/Qt/LayoutPane.qml new file mode 100644 index 00000000000..d2698c629ae --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyeditor/Qt/LayoutPane.qml @@ -0,0 +1,29 @@ +import Qt 4.6 +import Bauhaus 1.0 + +QScrollArea { + widgetResizable: true; + styleSheetFile: ":/qmldesigner/scrollbar.css"; + horizontalScrollBarPolicy: "Qt::ScrollBarAlwaysOff"; + id: LayoutPane; + visible: false; + content: ProperyEditorLayout; + QFrame { + enabled: isBaseState; + id: ProperyEditorLayout; + minimumHeight: 460; + layout: QVBoxLayout { + topMargin: 2; + bottomMargin: 2; + leftMargin: 2; + rightMargin: 2; + Type { + } + Layout { + id: layoutBox; + } + QScrollArea { + } + } + } +} \ No newline at end of file diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/ListView.qml b/share/qtcreator/qmldesigner/propertyeditor/Qt/ListView.qml new file mode 100644 index 00000000000..b5a44435319 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyeditor/Qt/ListView.qml @@ -0,0 +1,85 @@ +import Qt 4.6 +import Bauhaus 1.0 + +PropertyFrame { + layout: QVBoxLayout { + topMargin: 0; + bottomMargin: 0; + leftMargin: 0; + rightMargin: 0; + spacing: 0; + + Switches { + } + + QScrollArea { + horizontalScrollBarPolicy: "Qt::ScrollBarAlwaysOff"; + id: standardPane; + content: ProperyEditorStandard; + QFrame { + minimumHeight: 400; + id: ProperyEditorStandard + layout: QVBoxLayout { + topMargin: 2; + bottomMargin: 2; + leftMargin: 2; + rightMargin: 2; + Type { + } + HorizontalLine { + } + + Geometry { + } + + Modifiers { + + } + + QScrollArea { + } + + } // layout + } //QWidget + } //QScrollArea + + + QScrollArea { + horizontalScrollBarPolicy: "Qt::ScrollBarAlwaysOff"; + id: specialPane; + visible: false; + visible: false; + content: ProperyEditorSpecial; + QFrame { + minimumHeight: 200; + id: ProperyEditorSpecial + layout: QVBoxLayout { + topMargin: 2; + bottomMargin: 2; + leftMargin: 2; + rightMargin: 2; + Type { + } + + QScrollArea { + } + } + } + } + + ExtendedPane { + id: extendedPane; + } + + LayoutPane { + id: layoutPane; + } + + + ResetPane { + id: resetPane; + } + + + } +} diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/ListViewSpecifics.qml b/share/qtcreator/qmldesigner/propertyeditor/Qt/ListViewSpecifics.qml new file mode 100644 index 00000000000..28f7c4c8702 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyeditor/Qt/ListViewSpecifics.qml @@ -0,0 +1,106 @@ +import Qt 4.6 +import Bauhaus 1.0 + +GroupBox { + finished: finishedNotify; + caption: "List View"; + + layout: QVBoxLayout { + topMargin: 15; + bottomMargin: 6; + leftMargin: 0; + rightMargin: 0; + + QWidget { + id: contentWidget; + maximumHeight: 260; + + layout: QHBoxLayout { + topMargin: 0; + bottomMargin: 0; + leftMargin: 10; + rightMargin: 10; + + QWidget { + layout: QVBoxLayout { + topMargin: 0; + bottomMargin: 0; + leftMargin: 0; + rightMargin: 0; + QLabel { + minimumHeight: 22; + text: "Highlight Follows:" + font.bold: true; + } + + QLabel { + minimumHeight: 22; + text: "Key Navigation Wraps:" + font.bold: true; + } + + QLabel { + minimumHeight: 22; + text: "Snap Position:" + font.bold: true; + } + + QLabel { + minimumHeight: 22; + text: "Spacing:" + font.bold: true; + } + } + } + + QWidget { + layout: QVBoxLayout { + topMargin: 0; + bottomMargin: 0; + leftMargin: 0; + rightMargin: 0; + + CheckBox { + id: HighlightFollowsCurrentItemCheckBox; + text: ""; + backendValue: backendValues.highlightFollowsCurrentItem; + baseStateFlag: isBaseState; + checkable: true; + } + + CheckBox { + id: WrapCheckBox; + text: ""; + backendValue: backendValues.wrap; + baseStateFlag: isBaseState; + checkable: true; + } + + SpinBox { + id: SnapPositionSpinBox; + objectName: "SnapPositionSpinBox"; + backendValue: backendValues.snapPosition; + minimumWidth: 30; + minimum: 0; + maximum: 1000; + singleStep: 1; + baseStateFlag: isBaseState; + } + + SpinBox { + id: SpacingSpinBox; + objectName: "SpacingSpinBox"; + backendValue: backendValues.spacing; + minimumWidth: 30; + minimum: 0; + maximum: 1000; + singleStep: 1; + baseStateFlag: isBaseState; + } + + } + } + } + } + } +} diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/Modifiers.qml b/share/qtcreator/qmldesigner/propertyeditor/Qt/Modifiers.qml new file mode 100644 index 00000000000..6d4a764514e --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyeditor/Qt/Modifiers.qml @@ -0,0 +1,218 @@ +import Qt 4.6 + +GroupBox { + + + + finished: finishedNotify; + caption: "Modifiers" + minimumHeight: 30; + minimumHeight: 180; + id: Mofifiers; + + layout: QVBoxLayout { + topMargin: 2; + bottomMargin: 6; + leftMargin: 0; + rightMargin: 0; + + QWidget { + id: contentWidget; + maximumHeight: 240; + + layout: QHBoxLayout { + topMargin: 0; + bottomMargin: 0; + leftMargin: 10; + rightMargin: 10; + QWidget { + layout: QVBoxLayout { + topMargin: 0; + bottomMargin: 30; + leftMargin: 0; + rightMargin: 0; + + QLabel { + minimumHeight: 50; + text: "Rotation:" + font.bold: true; + } + + QLabel { + text: "Scale:" + font.bold: true; + } + + QLabel { + text: "Scale:" + text: "" + font.bold: true; + } + + QLabel { + text: "Opacity:" + font.bold: true; + } + QLabel { + text: "Visibility:" + font.bold: true; + } + } + } + QWidget { + layout: QVBoxLayout { + + topMargin: 02; + bottomMargin: 2; + leftMargin: 5; + rightMargin: 0; + + QWidget { + layout: QHBoxLayout { + topMargin: 3; + bottomMargin: 0; + leftMargin: 0; + rightMargin: 0; + SpinBox { + id: RotationSpinBox; + backendValue: backendValues.rotation; + onBackendValueChanged: { + if (backendValue.value > 180) + RotationDial.value = (backendValue.value - 360); + else + RotationDial.value = backendValue.value; + } + minimum: 0; + maximum: 360; + baseStateFlag: isBaseState; + } + QDial { + id: RotationDial; + wrapping: true; + focusPolicy: "Qt::ClickFocus"; + minimumHeight: 20; + maximumHeight: 50; + minimum: -180; + maximum: 180; + singleStep: 45; + onValueChanged : { + if (value < 0) + RotationSpinBox.backendValue.value = 360 + value; + else + RotationSpinBox.backendValue.value = value; + } + } + } + } + + + + QWidget { + layout: QHBoxLayout { + topMargin: 10; + bottomMargin: 0; + leftMargin: 0; + rightMargin: 0; + DoubleSpinBox { + id: ScaleSpinBox; + objectName: "ScaleSpinBox"; + backendValue: backendValues.scale; + minimumWidth: 60; + minimum: 0.01 + maximum: 10 + singleStep: 0.1 + baseStateFlag: isBaseState; + onBackendValueChanged: { + ScaleSlider.value = backendValue.value * 10; + } + } + QSlider { + id: ScaleSlider; + orientation: "Qt::Horizontal"; + minimum: 1; + maximum: 100; + singleStep: 1; + onValueChanged: { + backendValues.scale.value = value / 10; + } + } + } + } + + QWidget { + layout: QHBoxLayout { + topMargin: 5; + bottomMargin: 5; + leftMargin: 10; + rightMargin: 0; + + QLabel { + text: "Origin: "; + } + QComboBox { + items : { [ + "TopLeft", "Top", "TopRight", "Left", "Center", "Right", "BottomLeft", "Bottom", + "BottomRight" + ] } + + currentText: backendValues.transformOrigin.value; + onItemsChanged: { + currentText = backendValues.transformOrigin.value; + } + onCurrentTextChanged: { + backendValues.transformOrigin.value = currentText; + } + } + + } + } + + QWidget { + layout: QHBoxLayout { + topMargin: 5; + bottomMargin: 10; + leftMargin: 0; + rightMargin: 0; + + DoubleSpinBox { + id: OpacitySpinBox; + backendValue: backendValues.opacity === undefined ? null : backendValues.opacity + minimumWidth: 60; + minimum: 0; + maximum: 1; + singleStep: 0.1 + baseStateFlag: isBaseState; + } + QSlider { + orientation: "Qt::Horizontal"; + minimum: 0 + maximum: 100 + singleStep: 5; + value: backendValues.opacity === undefined ? 0 : (backendValues.opacity.value * 100) + onValueChanged: { + if (backendValues.opacity !== undefined) + backendValues.opacity.value = value / 100; + } + } + } + } + CheckBox { + id: VisibleCheckBox; + text: "item visibilty"; + backendValue: backendValues.visible === undefined ? false : backendValues.visible; + baseStateFlag: isBaseState; + checkable: true; + } + CheckBox { + id: ClipCheckBox; + text: "clipping item"; + backendValue: backendValues.clip === undefined ? false : backendValues.clip; + baseStateFlag: isBaseState; + checkable: true; + } + } + } + } + } + } +} diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/PropertyFrame.qml b/share/qtcreator/qmldesigner/propertyeditor/Qt/PropertyFrame.qml new file mode 100644 index 00000000000..84e9082fe4b --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyeditor/Qt/PropertyFrame.qml @@ -0,0 +1,10 @@ +import Qt 4.6 + +WidgetFrame { + id: PropertyFrame; + + minimumWidth: 300; + //maximumWidth: 300; + styleSheetFile: "propertyEditor.css"; + //visible: isBaseState; +} diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/RectangleSpecifics.qml b/share/qtcreator/qmldesigner/propertyeditor/Qt/RectangleSpecifics.qml new file mode 100644 index 00000000000..a33264ef489 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyeditor/Qt/RectangleSpecifics.qml @@ -0,0 +1,134 @@ +import Qt 4.6 +import Bauhaus 1.0 + +GroupBox { + + finished: finishedNotify; + caption: "Rect" + + minimumHeight: 340; + maximumHeight: 340; + + layout: QVBoxLayout { + topMargin: 12; + bottomMargin: 20; + leftMargin: 20; + rightMargin: 20; + + QWidget { + layout: QVBoxLayout { + topMargin: 2; + bottomMargin: 20; + leftMargin: 0; + rightMargin: 0; + + QWidget { + layout: QHBoxLayout { + topMargin: 8; + bottomMargin: 20; + leftMargin: 10; + rightMargin: 10; + QLabel { + text: "Radius:" + font.bold: true; + } + SpinBox { + id: RadiusSpinBox; + backendValue: backendValues.radius === undefined ? null : backendValues.radius + minimum: 0; + maximum: 100; + baseStateFlag: isBaseState; + } + QSlider { + orientation: "Qt::Horizontal"; + minimum: 0; + maximum: 100; + singleStep: 1; + value: backendValues.radius === undefined ? null : backendValues.radius.value + onValueChanged: { + backendValues.radius.value = value; + } + } + } + } + QWidget { + layout: QHBoxLayout { + topMargin: 2; + bottomMargin: 2; + leftMargin: 10; + rightMargin: 10; + spacing: 20; + + ColorWidget { + text: "Color:"; + color: backendValues.color === undefined ? null : backendValues.color.value; + onColorChanged: { + backendValues.color.value = strColor; + } + //modelNode: backendValues.color.modelNode; + complexGradientNode: backendValues.color === undefined ? null : backendValues.color.complexNode + + showGradientButton: true; + } + ColorWidget { + text: "Tint color:"; + color: backendValues.tintColor === undefined ? "black" : backendValues.tintColor.value + onColorChanged: { + backendValues.color.value = strColor; + } + } + } + } + HorizontalLine { + } + } + } + QWidget { + minimumHeight: 80; + maximumHeight: 120; + + layout: QHBoxLayout { + topMargin: 2; + topMargin: 0; + bottomMargin: 0; + leftMargin: 0; + rightMargin: 0; + + QWidget { + id: PenGroupBox; + + maximumHeight: 80; + + layout: QVBoxLayout { + topMargin: 10; + bottomMargin: 10; + leftMargin: 20; + rightMargin: 20; + IntEditor { + + id: borderWidth; + backendValue: backendValues.border_width === undefined ? 0 : backendValues.border_width + + caption: "Pen Width:" + baseStateFlag: isBaseState; + + step: 1; + minimumValue: 0; + maximumValue: 100; + } + ColorWidget { + id: PenColor; + text: "Pen Color:"; + minimumHeight: 20; + minimumWidth: 20; + color: backendValues.border_color.value; + onColorChanged: { + backendValues.border_color.value = strColor; + } + } + } + } + } + } + } +} diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/ResetPane.qml b/share/qtcreator/qmldesigner/propertyeditor/Qt/ResetPane.qml new file mode 100644 index 00000000000..aa164ddda42 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyeditor/Qt/ResetPane.qml @@ -0,0 +1,41 @@ +import Qt 4.6 +import Bauhaus 1.0 + +QScrollArea { + horizontalScrollBarPolicy: "Qt::ScrollBarAlwaysOff"; + id: ResetPane; + visible: false; + content: ProperyEditorReset; + QFrame { + //minimumHeight: 1800; + id: ProperyEditorReset + layout: QVBoxLayout { + topMargin: 2; + bottomMargin: 2; + leftMargin: 2; + rightMargin: 2; + + Type { + } + ResetWidget { + id: resetWidget; + //minimumHeight: 2000; + + + height: 500; + width: 200; + backendObject: backendValues; + + QLineEdit { + visible: false; + text: backendValues.id; + onTextChanged: { + resetWidget.resetView(); + } + } + } + + + } + } +} \ No newline at end of file diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/RowSpecifics.qml b/share/qtcreator/qmldesigner/propertyeditor/Qt/RowSpecifics.qml new file mode 100644 index 00000000000..f95f56a101d --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyeditor/Qt/RowSpecifics.qml @@ -0,0 +1,29 @@ +import Qt 4.6 +import Bauhaus 1.0 + +GroupBox { + maximumHeight: 200; + + finished: finishedNotify; + caption: "Grid"; + id: GridSpecifics; + + layout: QVBoxLayout { + + topMargin: 18; + bottomMargin: 2; + leftMargin: 8; + rightMargin: 8; + + + IntEditor { + id: spacing; + backendValue: backendValues.spacing; + caption: "Spacing: " + baseStateFlag: isBaseState; + step: 1; + minimumValue: 0; + maximumValue: 2000; + } + } +} diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/ScrollArea.qml b/share/qtcreator/qmldesigner/propertyeditor/Qt/ScrollArea.qml new file mode 100644 index 00000000000..19acd3361ad --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyeditor/Qt/ScrollArea.qml @@ -0,0 +1,14 @@ +import Qt 4.6 +import Bauhaus 1.0 + + +QScrollArea { + + property var finished; + + onFinishedChanged: { + setupProperWheelBehaviour(); + } + + +} \ No newline at end of file diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/SpinBox.qml b/share/qtcreator/qmldesigner/propertyeditor/Qt/SpinBox.qml new file mode 100644 index 00000000000..868236afc13 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyeditor/Qt/SpinBox.qml @@ -0,0 +1,72 @@ +import Qt 4.6 +import Bauhaus 1.0 + +QWidget { //This is a special SpinBox that does color coding for states + id: SpinBox; + + property var backendValue; + property var baseStateFlag; + property alias singleStep: box.singleStep; + property alias minimum: box.minimum + property alias maximum: box.maximum + + minimumHeight: 22; + + onBaseStateFlagChanged: { + evaluate(); + } + + onBackendValueChanged: { + evaluate(); + } + + Script { + function evaluate() { + if (baseStateFlag) { + if (backendValue != null && backendValue.isInModel) + SpinBox.setStyleSheet("color: white;"); + else + SpinBox.setStyleSheet("color: gray;"); + } else { + if (backendValue != null && backendValue.isInSubState) + SpinBox.setStyleSheet("color: blue;"); + else + SpinBox.setStyleSheet("color: gray;"); + } + } + } + + layout: QHBoxLayout { + topMargin: 0; + bottomMargin: 0; + leftMargin: 0; + rightMargin: 10; + spacing: 0; + + QSpinBox { + property alias backendValue: SpinBox.backendValue + + keyboardTracking: false; + id: box; + enabled: backendValue === undefined || backendValue.isBound === undefined || backendValue.isBound === null ? false : !backendValue.isBound + value: backendValue == undefined || backendValue.value == undefined || backendValue.value === null ? 0 : backendValue.value; + onValueChanged: { + if (backendValue != undefined && backendValue != null) + backendValue.value = value; + } + onFocusChanged: { + //extendedSwitches.active = focus; + //extendedSwitches.backendValue = backendValue; + } + } + } + + QToolButton { + visible: false; + width: 10; + height: 10; + y: box.y + box.height - 11; + x: box.width - 1; + focusPolicy: "Qt::NoFocus"; + } +} diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/Switches.qml b/share/qtcreator/qmldesigner/propertyeditor/Qt/Switches.qml new file mode 100644 index 00000000000..84db9bbc251 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyeditor/Qt/Switches.qml @@ -0,0 +1,85 @@ +import Qt 4.6 + +QFrame { + styleSheetFile: "switch.css"; + property var specialModeIcon; + specialModeIcon: "images/standard.png"; + maximumWidth: 286; + minimumWidth: 286; + layout: QHBoxLayout { + topMargin: 4; + bottomMargin: 0; + leftMargin: 4; + rightMargin: 80; + spacing: 0; + + QPushButton { + checkable: true; + checked: true; + id: standardMode; + toolTip: "general item properties"; + iconFromFile: "images/rect-icon.png"; + onClicked: { + extendedMode.checked = false; + layoutMode.checked = false; + specialMode.checked = false; + checked = true; + standardPane.visible = true; + extendedPane.visible = false; + layoutPane.visible = false; + specialPane.visible = false; + } + } + QPushButton { + checkable: true; + checked: false; + id: specialMode; + toolTip: "type specific properties"; + iconFromFile: specialModeIcon; + onClicked: { + extendedMode.checked = false; + standardMode.checked = false; + layoutMode.checked = false; + checked = true; + specialPane.visible = true; + standardPane.visible = false; + extendedPane.visible = false; + layoutPane.visible = false; + } + } + QPushButton { + id: extendedMode; + toolTip: "extended properties"; + checkable: true; + checked: false; + iconFromFile: "images/extended.png"; + onClicked: { + standardMode.checked = false; + layoutMode.checked = false; + specialMode.checked = false; + checked = true; + standardPane.visible = false; + extendedPane.visible = true; + layoutPane.visible = false; + specialPane.visible = false; + } + } + QPushButton { + id: layoutMode; + checkable: true; + checked: false; + toolTip: "layout properties"; + iconFromFile: "images/layout.png"; + onClicked: { + extendedMode.checked = false; + standardMode.checked = false; + specialMode.checked = false; + checked = true; + standardPane.visible = false; + extendedPane.visible = false; + layoutPane.visible = true; + specialPane.visible = false; + } + } + } +} diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/TextEditSpecifics.qml b/share/qtcreator/qmldesigner/propertyeditor/Qt/TextEditSpecifics.qml new file mode 100644 index 00000000000..34fd8168b63 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyeditor/Qt/TextEditSpecifics.qml @@ -0,0 +1,273 @@ +import Qt 4.6 +import Bauhaus 1.0 + +GroupBox { + maximumHeight: 600; + + finished: finishedNotify; + caption: "Text Edit"; + + layout: QVBoxLayout { + id: textSpecifics; + topMargin: 20; + bottomMargin: 2; + leftMargin: 4; + rightMargin: 4; + QWidget { + layout: QHBoxLayout { + topMargin: 8; + bottomMargin: 2; + leftMargin: 10; + rightMargin: 0; + spacing: 20; + QLabel { + alignment: "AlignTop"; + text: "Text:" + font.bold: true; + } + QTextEdit { + minimumHeight: 80; + property var localText: backendValues.text.value; + onLocalTextChanged: { + if (localText != plainText) + plainText = localText; + } + + onTextChanged: { + backendValues.text.value = plainText; + } + + } + } + } + QWidget { + layout: QHBoxLayout { + topMargin: 2; + bottomMargin: 2; + leftMargin: 10; + rightMargin: 10; + QLabel { + text: "wrap:" + font.bold: true; + } + CheckBox { + text: "isWrapping"; + baseStateFlag: isBaseState; + checkable: true; + backendValue: backendValues.wrap; + } + } + } + + FontWidget { + text: "Font:"; + + bold: backendValues.font_bold.value; + italic: backendValues.font_italic.value; + family: backendValues.font_family.value; + fontSize: backendValues.font_pointSize.value; + + onDataFontChanged: { + if (bold) + backendValues.font_bold.value = bold; + else + backendValues.font_bold.resetValue(); + + if (italic) + backendValues.font_italic.value = bold; + else + backendValues.font_italic.resetValue(); + + backendValues.font_family.value = family; + backendValues.font_pointSize.value = fontSize; + } + } + + HorizontalLine { + } + + QWidget { + layout: QHBoxLayout { + topMargin: 4; + bottomMargin: 2; + leftMargin: 10; + rightMargin: 6; + + } + } + + + QWidget { + layout: QHBoxLayout { + topMargin: 4; + bottomMargin: 8; + leftMargin: 6; + rightMargin: 6; + QLabel { + text: "Text Format:" + font.bold: true; + } + + QComboBox { + minimumHeight: 22; + items : { ["PlainText", "RichText", "AutoText"] } + currentText: backendValues.textFormat.value; + onItemsChanged: { + currentText = backendValues.textFormat.value; + } + + onCurrentTextChanged: { + if (count == 3) + backendValues.textFormat.value = currentText; + } + } + } + } + + + HorizontalLine { + } + + + QWidget { + layout: QHBoxLayout { + topMargin: 4; + bottomMargin: 2; + leftMargin: 6; + rightMargin: 6; + QLabel { + text: "Horizontal Alignment:" + font.bold: true; + } + + QComboBox { + items : { ["AlignLeft", "AlignRight", "AlignHCenter"] } + currentText: backendValues.horizontalAlignment.value; + onItemsChanged: { + currentText = backendValues.horizontalAlignment.value; + } + + onCurrentTextChanged: { + if (count == 3) + backendValues.horizontalAlignment.value = currentText; + } + } + } + } + + QWidget { + layout: QHBoxLayout { + topMargin: 4; + bottomMargin: 2; + leftMargin: 6; + rightMargin: 6; + QLabel { + text: "Vertical Alignment:" + font.bold: true; + } + + QComboBox { + items : { ["AlignTop", "AlignBottom", "AlignVCenter"] } + currentText: backendValues.verticalAlignment.value; + onItemsChanged: { + currentText = backendValues.verticalAlignment.value; + } + + onCurrentTextChanged: { + if (count == 3) + backendValues.verticalAlignment.value = currentText; + } + } + } + } + + HorizontalLine { + } + + ColorWidget { + minimumHeight: 20; + maximumHeight: 20; + text: "Color:"; + color: backendValues.color.value; + onColorChanged: { + backendValues.color.value = strColor; + } + } + + ColorWidget { + minimumHeight: 20; + maximumHeight: 20; + text: "Selection Color:"; + color: backendValues.selectionColor.value; + onColorChanged: { + backendValues.selectionColor.value = strColor; + } + } + + ColorWidget { + minimumHeight: 20; + maximumHeight: 20; + text: "Selected Text Color:"; + color: backendValues.selectedTextColor.value; + onColorChanged: { + backendValues.selectedTextColor.value = strColor; + } + } + + HorizontalLine { + } + + + QWidget { + layout: QHBoxLayout { + topMargin: 2; + bottomMargin: 2; + leftMargin: 10; + rightMargin: 10; + + CheckBox { + + text: "Read Only"; + baseStateFlag: isBaseState; + checkable: true; + backendValue: backendValues.readOnly; + } + + CheckBox { + text: "Cursor Visible"; + baseStateFlag: isBaseState; + checkable: true; + backendValue: backendValues.cursorVisible; + } + } + } + + HorizontalLine { + } + + QWidget { + layout: QHBoxLayout { + topMargin: 2; + bottomMargin: 2; + leftMargin: 10; + rightMargin: 10; + + CheckBox { + text: "Focus On Press"; + baseStateFlag: isBaseState; + checkable: true; + backendValue: backendValues. focusOnPress.value; + } + + CheckBox { + text: "Persistent Selection"; + baseStateFlag: isBaseState; + checkable: true; + backendValue: backendValues.persistentSelection.value; + } + + } + } + + } +} diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/TextInputSpecifics.qml b/share/qtcreator/qmldesigner/propertyeditor/Qt/TextInputSpecifics.qml new file mode 100644 index 00000000000..82a1cd21fc7 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyeditor/Qt/TextInputSpecifics.qml @@ -0,0 +1,176 @@ +import Qt 4.6 +import Bauhaus 1.0 + +GroupBox { + maximumHeight: 420; + minimumHeight: 420; + + finished: finishedNotify; + caption: "Text Input"; + + layout: QVBoxLayout { + id: textSpecifics; + topMargin: 12; + bottomMargin: 2; + leftMargin: 10; + rightMargin: 10; + QWidget { + layout: QHBoxLayout { + topMargin: 8; + bottomMargin: 2; + leftMargin: 10; + rightMargin: 10; + QLabel { + text: "Text:" + font.bold: true; + } + QLineEdit { + minimumHeight: 24; + text: backendValues.text.value; + onTextChanged: { + backendValues.text.value = text; + } + } + } + } + + FontWidget { + text: "Font:"; + + bold: backendValues.font_bold.value; + italic: backendValues.font_italic.value; + family: backendValues.font_family.value; + fontSize: backendValues.font_pointSize.value; + + onDataFontChanged: { + + if (bold) + backendValues.font_bold.value = bold; + else + backendValues.font_bold.resetValue(); + + if (italic) + backendValues.font_italic.value = bold; + else + backendValues.font_italic.resetValue(); + + backendValues.font_family.value = family; + backendValues.font_pointSize.value = fontSize; + } + } + + HorizontalLine { + } + + + QWidget { + layout: QHBoxLayout { + topMargin: 4; + bottomMargin: 2; + leftMargin: 6; + rightMargin: 6; + QLabel { + text: "Horizontal Alignment:" + font.bold: true; + } + + QComboBox { + items : { ["AlignLeft", "AlignRight", "AlignHCenter"] } + currentText: backendValues.horizontalAlignment.value; + onItemsChanged: { + currentText = backendValues.horizontalAlignment.value; + } + + onCurrentTextChanged: { + if (count == 3) + backendValues.horizontalAlignment.value = currentText; + } + + } + } + } + + HorizontalLine { + } + + ColorWidget { + minimumHeight: 20; + maximumHeight: 20; + text: "Color:"; + color: backendValues.color.value; + onColorChanged: { + backendValues.color.value = strColor; + } + } + + ColorWidget { + minimumHeight: 20; + maximumHeight: 20; + text: "Selection Color:"; + color: backendValues.selectionColor.value; + onColorChanged: { + backendValues.selectionColor.value = strColor; + } + } + + ColorWidget { + minimumHeight: 20; + maximumHeight: 20; + text: "Selected Text Color:"; + color: backendValues.selectedTextColor.value; + onColorChanged: { + backendValues.selectedTextColor.value = strColor; + } + } + + HorizontalLine { + } + + + QWidget { + layout: QHBoxLayout { + topMargin: 2; + bottomMargin: 2; + leftMargin: 10; + rightMargin: 10; + + CheckBox { + + text: "Read Only"; + baseStateFlag: isBaseState; + checkable: true; + backendValue: backendValues.readOnly; + } + + CheckBox { + + text: "Cursor Visible"; + baseStateFlag: isBaseState; + checkable: true; + backendValue: backendValues.cursorVisible; + + } + } + } + + HorizontalLine { + } + + QWidget { + layout: QHBoxLayout { + topMargin: 2; + bottomMargin: 2; + leftMargin: 10; + rightMargin: 10; + + CheckBox { + text: "Focus On Press"; + baseStateFlag: isBaseState; + checkable: true; + backendValue: backendValues. focusOnPress; + } + + } + } + } +} diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/TextSpecifics.qml b/share/qtcreator/qmldesigner/propertyeditor/Qt/TextSpecifics.qml new file mode 100644 index 00000000000..0b0c0cd2edf --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyeditor/Qt/TextSpecifics.qml @@ -0,0 +1,172 @@ +import Qt 4.6 +import Bauhaus 1.0 + +GroupBox { + maximumHeight: 280; + + finished: finishedNotify; + caption: "Text"; + + layout: QVBoxLayout { + id: textSpecifics; + topMargin: 12; + bottomMargin: 2; + leftMargin: 4; + rightMargin: 4; + QWidget { + layout: QHBoxLayout { + topMargin: 8; + bottomMargin: 2; + leftMargin: 10; + rightMargin: 10; + QLabel { + text: "Text:" + font.bold: true; + } + QLineEdit { + minimumHeight: 24; + text: backendValues.text.value; + onTextChanged: { + backendValues.text.value = text; + } + } + } + } + QWidget { + layout: QHBoxLayout { + topMargin: 6; + bottomMargin: 2; + leftMargin: 10; + rightMargin: 10; + QLabel { + text: "wrap:" + font.bold: true; + } + CheckBox { + baseStateFlag: isBaseState; + text: "isWrapping"; + checkable: true; + backendValue: backendValues.wrap; + } + } + } + + FontWidget { + text: "Font:"; + + bold: backendValues.font_bold.value; + italic: backendValues.font_italic.value; + family: backendValues.font_family.value; + fontSize: backendValues.font_pointSize.value; + + onDataFontChanged: { + + if (bold) + backendValues.font_bold.value = bold; + else + backendValues.font_bold.resetValue(); + + if (italic) + backendValues.font_italic.value = bold; + else + backendValues.font_italic.resetValue(); + + backendValues.font_family.value = family; + backendValues.font_pointSize.value = fontSize; + } + } + + HorizontalLine { + } + + QWidget { + layout: QHBoxLayout { + topMargin: 4; + bottomMargin: 2; + leftMargin: 10; + rightMargin: 6; + + } + } + + QWidget { + layout: QHBoxLayout { + topMargin: 4; + bottomMargin: 2; + leftMargin: 10; + rightMargin: 6; + QLabel { + text: "Horizontal Alignment:" + font.bold: true; + } + + QComboBox { + items : { ["AlignLeft", "AlignRight", "AlignHCenter"] } + currentText: backendValues.horizontalAlignment.value; + onItemsChanged: { + currentText = backendValues.horizontalAlignment.value; + } + + onCurrentTextChanged: { + if (count == 3); + backendValues.horizontalAlignment.value = currentText; + } + + } + } + } + + QWidget { + layout: QHBoxLayout { + topMargin: 4; + bottomMargin: 2; + leftMargin: 10; + rightMargin: 6; + QLabel { + text: "Vertical Alignment:" + font.bold: true; + } + + QComboBox { + items : { ["AlignTop", "AlignBottom", "AlignVCenter"] } + currentText: backendValues.verticalAlignment.value; + onItemsChanged: { + currentText = backendValues.verticalAlignment.value; + } + + onCurrentTextChanged: { + if (count == 3) + backendValues.verticalAlignment.value = currentText; + } + + } + } + } + + HorizontalLine { + } + QWidget { + layout: QHBoxLayout { + topMargin: 2; + bottomMargin: 2; + leftMargin: 10; + rightMargin: 10; + + ColorWidget { + text: "Color:"; + color: backendValues.color.value; + onColorChanged: { + backendValues.color.value = strColor; + } + } + ColorWidget { + text: "Style color:"; + color: backendValues.styleColor.value; + onColorChanged: { + backendValues.styleColor.value = strColor; + } + } + } + } + } +} diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/Type.qml b/share/qtcreator/qmldesigner/propertyeditor/Qt/Type.qml new file mode 100644 index 00000000000..aa9b72291d1 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyeditor/Qt/Type.qml @@ -0,0 +1,94 @@ +import Qt 4.6 + +GroupBox { + id: Type; + finished: finishedNotify; + caption: "Type"; + + maximumHeight: 100; + minimumWidth: 280; + layout: QHBoxLayout { + + topMargin: 2; + bottomMargin: 0; + leftMargin: 10; + rightMargin: 10; + + + + QWidget { + layout: QVBoxLayout { + topMargin: 6; + bottomMargin: 0; + leftMargin: 10; + bottomMargin: 10; + + QLabel { + text: "Type:"; + windowTextColor: isBaseState ? "#000000" : "#FF0000"; + } + + QLabel { + font.bold: true; + text: "Id:"; + } + + QLabel { + text: "state:"; + font.bold: true; + } + + } //QVBoxLayout + } //QWidget + QWidget { + + layout: QVBoxLayout { + topMargin: 6; + bottomMargin: 0; + leftMargin: 10; + bottomMargin: 10; + + QLineEdit { + styleSheetFile: "typeLabel.css" + text: backendValues.className === undefined ? "" : backendValues.className.value; + readOnly :true; + } + + QLineEdit { + id: IdLineEdit; + objectName: "IdLineEdit"; + readOnly: isBaseState != true; + text: backendValues.id === undefined ? "" : backendValues.id.value; + onEditingFinished: { + backendValues.id.value = text; + } + } //LineEdit + + QLineEdit { + visible: isBaseState != true; + textColor: "#ff0000"; + readOnly :true; + text: stateName; + styleSheetFile: "typeLabel.css" + onTextChanged: { + if (!visible) { + IdLineEdit.textColor = "#000000"; + } else { + IdLineEdit.textColor = "#777777"; + } + } + } //LineEdit + + QLineEdit { + visible: isBaseState; + readOnly :true; + text: ""; + styleSheetFile: "typeLabel.css" + } //LineEdit + + } //QVBoxLayout + } //QWidget + + + } //QHBoxLayout +} diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/WebViewSpecifics.qml b/share/qtcreator/qmldesigner/propertyeditor/Qt/WebViewSpecifics.qml new file mode 100644 index 00000000000..925c2bd4f3b --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyeditor/Qt/WebViewSpecifics.qml @@ -0,0 +1,96 @@ +import Qt 4.6 +import Bauhaus 1.0 + +GroupBox { + maximumHeight: 200; + + finished: finishedNotify; + caption: "WebView"; + id: webViewSpecifics; + + layout: QVBoxLayout { + + topMargin: 18; + bottomMargin: 2; + leftMargin: 8; + rightMargin: 8; + QWidget { + layout: QHBoxLayout { + leftMargin: 0; + rightMargin: 0; + QLabel { + minimumHeight: 20; + text: "Url:" + font.bold: true; + } + QLineEdit { + text: backendValues.url.value; + onEditingFinished: { + backendValues.url.value = text; + } + } + } + } + + + + IntEditor { + id: preferredWidth; + backendValue: backendValues.preferredWidth; + caption: "Prefered Width: " + baseStateFlag: isBaseState; + step: 1; + minimumValue: 0; + maximumValue: 2000; + } + + IntEditor { + id: webPageWidth; + backendValue: backendValues.preferredHeight; + caption: "Web Page Height:" + baseStateFlag: isBaseState; + step: 1; + minimumValue: 0; + maximumValue: 2000; + } + + QWidget { + layout: QHBoxLayout { + topMargin: 10; + bottomMargin: 0; + leftMargin: 0; + rightMargin: 0; + spacing: 20; + + QLabel { + minimumHeight: 20; + text: "ZommFactor:" + font.bold: true; + } + DoubleSpinBox { + id: ZoomSpinBox; + objectName: "ZommSpinBox"; + backendValue: backendValues.zoomFactor; + minimumWidth: 60; + minimum: 0.01 + maximum: 10 + singleStep: 0.1 + baseStateFlag: isBaseState; + onBackendValueChanged: { + ZoomSlider.value = backendValue.value * 10; + } + } + QSlider { + id: ZoomSlider; + orientation: "Qt::Horizontal"; + minimum: 1; + maximum: 100; + singleStep: 1; + onValueChanged: { + backendValues.zoomFactor.value = value / 10; + } + } + } + } + } +} diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/anchorbox.css b/share/qtcreator/qmldesigner/propertyeditor/Qt/anchorbox.css new file mode 100644 index 00000000000..d93895b1c8a --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyeditor/Qt/anchorbox.css @@ -0,0 +1,32 @@ +QPushButton::checked +{ + border: 3px solid #2222aa; + border-radius: 4px; + border-image: none; + background-color: #909090; +} + +QPushButton:pressed +{ + border: 2px solid #000000; +} + +QPushButton:flat { + border: 1px solid #000000; +} + +QPushButton:default { + border-color: 1px solid black; +} + +QPushButton { + border: 2px solid #333333; + color: white; +} + +QFrame{ + border: 2px solid black; + border-radius: 4px; + padding: 2px; + background: #aaabae; + } \ No newline at end of file diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/emptyPane.qml b/share/qtcreator/qmldesigner/propertyeditor/Qt/emptyPane.qml new file mode 100644 index 00000000000..cb4d9bccb81 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyeditor/Qt/emptyPane.qml @@ -0,0 +1,45 @@ +import Qt 4.6 +import Bauhaus 1.0 + +PropertyFrame { + layout: QVBoxLayout { + topMargin: 0; + bottomMargin: 0; + leftMargin: 0; + rightMargin: 0; + spacing: 0; + + Switches { + } + + ScrollArea { + styleSheetFile: ":/qmldesigner/scrollbar.css"; + widgetResizable: true; + content: QFrame { + maximumHeight: 38; + layout: QVBoxLayout { + topMargin: 6; + bottomMargin: 2; + leftMargin: 2; + rightMargin: 2; + QExtGroupBox { + font.bold: true; + maximumHeight: 100; + minimumWidth: 280; + minimumHeight: 32; + layout: QHBoxLayout { + topMargin: 6; + bottomMargin: 2; + QLabel { + minimumHeight: 20; + text: "No Item Selected"; + alignment: "AlignHCenter"; + } + } + } + } + } + } + + } +} diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/images/behaivour.png b/share/qtcreator/qmldesigner/propertyeditor/Qt/images/behaivour.png new file mode 100644 index 0000000000000000000000000000000000000000..15113bd4d13e85292e1d44c79f1246a54a11d265 GIT binary patch literal 433 zcmeAS@N?(olHy`uVBq!ia0vp^d?3uh1SBVD?P>#3+9j?LCC){ui6xo&c?uz!xv2~x zrKt+03SNnM3PuKo1`38oRz?O^#s&)7K%PNc+R8mZZR{nUzOL-I7+JYhxH6WnHUbK9 z7I;J!Gca%qfiUBxyLEqnf-LEdzK#qG8~eHcB(gFvFmigjIEGZ*>J73z)$Abh=UaDu zL`26@_X|!Nzj7qKHa&94IHF^2`i3nZxuQ6m<POA13%UfD=-*5go+R+8>*?EbXOi!2 z@VNeU|NU40-Q`3+*93|jEsZ^Fut4(o#>`nZ=NBH-Sj4t~v&liACH>>Bb%iq9bG=t^ zC`t&sPSog{_dGCeT55sbX`!}<20TYrKejlqm?tKJ?R~?ftqH4ABjd#mX+(NYQsD~V zzro-tX;V5cJ#t#S#DYe-#r-j_ceQ=CIp5D_;I&N5HSxCD`Ooj!6!aZDBY291GiE*e z{jYkR=cJO@?eBlPhK82td#qcf)%W;-3CFTbt<y%nm-oGAwC!V_xs3Bw?(Kc`4AWBA Z|GHNY)@pF&J}{6NJYD@<);T3K0RXqVrF{SZ literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/images/blended-image-icon.png b/share/qtcreator/qmldesigner/propertyeditor/Qt/images/blended-image-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..7c0911d63df28d558f10438cf19f1d21d3638df2 GIT binary patch literal 340 zcmV-a0jvIrP)<h;3K|Lk000e1NJLTq000&M000&U0ssI2OV;~r0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUy_DMuRRCwC7R7(nkFbtiAcku**xbYnB zJX>++)(Z$Cf+z4Qe&@+6H58^(>r56kt$EMnnI@{L>h;W`ec$6a4#rQ@6m8oEmT??C zF-elhE#N1`aU57ZDG+Q^tm}#gR_xO>b%2LqAVwYncv7GcQozC5_q~It>pJ6SS$0t@ z%VH?#`xw8i3n7e2($DkU0dCut@h1fU@aB0o6w?1>CI;{yQJ^FqQ48t2DJY`WA~=gb zXi?X-Y`YLGtbgvSh0MZge%~x)Ye`c|eeF&yCmXydid$g2!L71+<!;}RFbZ7=M~-|^ mmZksr(Nn8wnkVD;Ex-U*=!rfoJ#hyB0000<MNUMnLSTY0S&k_H literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/images/button.png b/share/qtcreator/qmldesigner/propertyeditor/Qt/images/button.png new file mode 100644 index 0000000000000000000000000000000000000000..cc16dbbec310288bd863d2f00067b415911063b2 GIT binary patch literal 687 zcmV;g0#N;lP)<h;3K|Lk000e1NJLTq000mG000gM1^@s6CNG?d00004b3#c}2nYxW zd<bNS0000PbVXQnQ*UN;cVTj60C#tHE@^ISb7Ns}WiD@WXPfRk8UO$SK}keGR47wp z`2YVu12VSq*5M9La_@=B3OJr!k$5wwGU;|~R^Z9dWX~x;IRSL_C>)P)YmbDy&|4=j zANYIl(#HR*_fPx3Waq^Hn~p5}zvtxo|7j&rcYR|V!mt|P9BgLPGNJm_`P+y7Z#}u_ z|LOxE%@h7F+}`_t!S-%2UVCu%|K3?m@4TYy+)xYvI*k+P<QrG-pZdS^^s@hJ56%3) zZ1+SI%`m*-@cjR2r7^dGdidc6xP@7U9zM1E@7{B(|F1tX`~UJilm0K--UrmsgJA#& zZ$Gl;pI@wFG2DR248KK}?wt6)>G<6LEB8(Lzj#MK&~{ACvp2Sb@y?U0{zql_ZGjsQ zn;mrQ*wyV={SCr%HnsonUsCgb;<CE`)7LitU$t-M|CFMrlgKVudHl*Qtp1*|vf+Q* ztkVD8^DE&7>^QOVe^jRbPPhSHk+$)>j;{X)(F}3=yse%8J7$;tub-Irzja0lssTsW z{0~fUtwD|jf#ibl`=I!qx25y{gr&9rt9!HmS9WJ&7_fNTr2j?r84rMZq)^fU&=nrt z)0^H^v}OO#t%>_z+>(M}z|1vm|L3oo_Srw)IRPUf0RfY5j8k4tMZ)75OZxuT_U6G2 z=$>2sf6cD>|I3?mp9aLa)C1KrqZj}Z0Afxc7M4>JGYX7%+m)CXcC)-C_f=I}{;R~i z@Y}(O9tRaPC9HvJ#DJI=W(cwi7(uQ8VpbsL0Ae0V1z|}kB@r2@22Q9P$TCKlJ^<V{ V5{@J1B3}Ri002ovPDHLkV1huGTsi;% literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/images/default-icon.png b/share/qtcreator/qmldesigner/propertyeditor/Qt/images/default-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a90779f02a090f925796852ade5478ed33f22cda GIT binary patch literal 341 zcmV-b0jmCqP)<h;3K|Lk000e1NJLTq000&M000&U0ssI2OV;~r0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUy_en%SRCwB?lsgiHAPk0gXS|EOm1AQ~ z<q2#(lf7O*8%xH<(%zf+LXFD|8$nqXQaI7@e<mUDF3a-eX5O}KzVBP>hhgv-WBa~a zBS3y4AQ30Tx~>Az_r0Mq2m<ba{DJ`A5P>BZ#Ih`w2&jj@&-0AeMNuUDuIr>0d=c<x zft;oZucMT5CywJV4BNI<vjDjcAqj9)Mu@D&aa8q$5LnPO&21qk0=H8|BuN6%s;YGD z)Mvp%Pv4CSc{+rT2(_JQn!>XviXRBgj;MQavm>~j(rAI@dH&yYnh*G8I&l_Rmbo4? n3p)|)W3IPWUDqzx?;*ed!{Rpa=`bfP00000NkvXXu0mjfBNC9Q literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/images/downArrow.png b/share/qtcreator/qmldesigner/propertyeditor/Qt/images/downArrow.png new file mode 100644 index 0000000000000000000000000000000000000000..b92cb987b96dff53705b445144ade2e01770b53e GIT binary patch literal 855 zcmV-d1E~CoP)<h;3K|Lk000e1NJLTq000mG001Be1^@s68;SVL00009a7bBm000id z000id0mpBsWB>pF1am@3R0s$N2z&@+hyVZp8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H10@O)FK~zXft(9#^Q&AYlk2;;rIa{0CMAM~_FPWvH&do|AQfZ+n zQxr7|G|@h2&1qRdAle6s8od}vU!szTASzO`Ec;egMwppIL|RFqDdNnxf9D=`Hk}Y$ zA2^qD?>+bT|3BwE=N<>pAd5ng;XuYG2yqh$iN*4VI64zDcx%b9*{?Xj;_x&OxTg@P zvV`zRC6Lnl<-+C-p##+i#E2|cL0=b$%=$3wtoBDp$r`9Kg^<_*tsFp+D#ZGO5)?E> z!}u^BrU$!FdS?d;uIrF>c@vUO`@<*K719)eO*U2sya3!>1{8{6Y>7eT6D`V{<Iq3c z%alG2yg|;DaHN}qkyN9GGQ$NVY$0>j;|=iL?}n_a!Kiu`gQIO4m|ArB`ul^`L0$uO zaE>~tRU^tMhb(nr9+(5(fRGX?Dj&zuriJNIEL{a1z6`x*4tjpPMt1#X>^&2R#A-FR zRw?1d&BI&`G6%c?TMjE=Y>LOpRz2$4^HFwp8$SJd#~gHjdj*3z6h#e*(ANe-TOo%Z ztq0eD*_B{dUH~zzZ_P0uRJUg1=V%|sC&zH1y&T=&JD5UO-%A{C+=o%i2zvV8BKgz? z1QohN6h7yGH-J+KDwg6<V*+{yI+^P9^b|%cL##hMHi*fo3D$q!)r9yeZ>V!u!A17Z z9PkGC<-5UfJ_Om9BGB>qDN|b<_g~yVT!n(If>*lGwhnj$lm-DbCMDf%en_tIM|0<G z8w1zc%!n<QLu-;FqEw32iB7f-cmue)R%SXQmgc9c^kjM7Y`=(^nHiYxonXqW(dUXZ zXArTS{{b%mm$1j~Sr<`9WL6h>XSXv|u0<PVP#J`@0v)aSTld0>05}MDpvck$Seqln z#v%zK%VY>G_JA+Fm+f(az?;~p+c;Pe!2IG%Gz6jqdZg){AfkPjbxW1!$J#ygcmwQi hhGiZuf9(Dz<u9vpVNa|PvRD8B002ovPDHLkV1lF=cGCa= literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/images/expression.png b/share/qtcreator/qmldesigner/propertyeditor/Qt/images/expression.png new file mode 100644 index 0000000000000000000000000000000000000000..680bd408cc547e9b7527facd3c1fafa4420f33a6 GIT binary patch literal 575 zcmV-F0>J%=P)<h;3K|Lk000e1NJLTq000gE000gM0ssI2kSe-{0000hbVXQnLvm$d zbZKvHAXI5>WdKcYZXh%uN?~pwGB7bPATc&NG&DLjG$1Q5F)%P6w7o$9000McNliru z+5rR*3IHp*VDtb0010qNS#tmY3labT3lag+-G2N4000DMK}|sb0I`n?{9y$E00Eat zL_t(|+HH|bt3gp1hV8vi?w1m_Ff($A3`Hq^fRbNez&}umi83@ZWMpP!U|?XRq$H$} z+kldCQsgdj-p%P;);VA8)%sTNe%JGUi?ZA8yx*l(t93eEx7$sp)3sWyP$)1AgY!4u zp9+V=7K^3d?`ySMgTWw?NNAcq9*+!qLMoTbZ8qEcvm6eG-|ttcRE<W1bj@TkN~Ll* z943=Vw24H*Xf$>@oqqtP)2T=#x?ZmoMZHcoo6UGU#)br_*Xs&}!sT)S*W>Y+&1R`o z3fyM1`Ry7E2BC<>Vs5wld_ME}d|X{D7Gb$qEFhrq;Xkohtk>&VmW@WEGMQ{Pn`N`v z{eHh(F5B()YPI@g4TVBZrxTmOV1N~rtyXJ3pGP8*$K&yJEypSp3awTvbg5LTTCIZ5 z>-GA4zEY|5GY0268jZHwE#~}6eY@Q-@X2IC42P2j0s;0}@pv4q$z&>*%lUkska7SB z(d+f_Hpsr)?F0gWMx&`zD);-HbVb1RdW|_84u@PWcRHOwa=Bax*AG|bsXM2L>?{BP N002ovPDHLkV1g}r_7MO8 literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/images/extended.png b/share/qtcreator/qmldesigner/propertyeditor/Qt/images/extended.png new file mode 100644 index 0000000000000000000000000000000000000000..82587ae0ecc4d29e2fa6213874d4b32698ccb63f GIT binary patch literal 293 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H1SGw${mubWk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xj(fT|hE&|T6LOpLh=RZ#?r%w~O=mg6o=sO>!f}x6QrE{q zW$8Wf<{!1WXN8uv%{=<%diDQGu|?OkmhS1b?{l6KbfC$(XCiBuS0a1O;jFc-0$j!n zi~^k}EFU+^PKkQ$$$4Nh3xhRlj{vLXtj@sy%Pr1Nb~SvZ&-G%Vwu(;IX%4;<L0&pt zf&4#|xFQy<{u$Tv%g=G|j<t_M_eLy8I(Rwi^MT{}&*mie@-XG49Fdqjlkr4qiyNQD p+O0aAEw7Z>v+sXbfBaqj0K?*wmyWp|GX{By!PC{xWt~$(698>GbOZnZ literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/images/grid-icon.png b/share/qtcreator/qmldesigner/propertyeditor/Qt/images/grid-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..113e14c00a05aa7e0e13e65ce2b01226dacb6406 GIT binary patch literal 369 zcmV-%0gnEOP)<h;3K|Lk000e1NJLTq000&M000&U0ssI2OV;~r00009a7bBm000XU z000XU0RWnu7ytkPBS}O-R5%f}R67cTKom@Zcku>_ja|UbF4zg)O0cvLynv0h=ncGz z-^^x~hk>x!B;*y3o#@P)H!eAsWqJCooNe1&*Zo>#97kte*L~lIVfaGD^E}X%1dO2j zzW<R*-}fXs%d+6a-gTX*1VLbiLcGX{Lhyo;AI*>y?P8v1V0IX>hZv?L5Ck~vZQF7e z8;Ez51P4yj1Q%f#N)~S+DWE?|G)*HCQ52b>NL;uAFkGQuA7u%-td5hC>@Z>vbrzh@ zUe~q62_ZAoE{==DaV%Nju^D2(3yG?#L?THNGZYCGC%&I7e|wZA<Zb-J?JSBy;)Fpn zL>Bsj!w*jAX_`tFZy_m95_z5r-}1oO>MsYCWtk=I_!_zA<HtWfwm;tiNpHy#4g1N~ P00000NkvXXu0mjfX?T|? literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/images/image-icon.png b/share/qtcreator/qmldesigner/propertyeditor/Qt/images/image-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..4dfcae7d472d50acfc50098e28788edec8adee0d GIT binary patch literal 292 zcmV+<0o(qGP)<h;3K|Lk000e1NJLTq000&M000&U0ssI2OV;~r0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUy#z{m$RCwB~l<f(_AP|OI$Sy7*IFF+V zj^Gl43pk43Jvm8jX{aCZ$Au&JKJnh@J*AY7y>;ZA2_egRYweLz0?IN1<P}j$fy5Xy zC8m@%M2rz0n9P13BFHNO8wnvmWQ_e4Ypo%o>IY)Pm%g$ga7*}Nq@BL9A|S`aIX8&t zD?$E7c<%=hePu&nSI8dmqOYuo(~Gr6?)G8}vFpXx9}z-K9q<o3@xa`0N5<FX9a%)+ qmN@5&jhrKAYicQ_cE;~ZfB^u@$4`MW06mES0000<MNUMnLSTZgpLJjW literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/images/item-icon.png b/share/qtcreator/qmldesigner/propertyeditor/Qt/images/item-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..82587ae0ecc4d29e2fa6213874d4b32698ccb63f GIT binary patch literal 293 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H1SGw${mubWk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xj(fT|hE&|T6LOpLh=RZ#?r%w~O=mg6o=sO>!f}x6QrE{q zW$8Wf<{!1WXN8uv%{=<%diDQGu|?OkmhS1b?{l6KbfC$(XCiBuS0a1O;jFc-0$j!n zi~^k}EFU+^PKkQ$$$4Nh3xhRlj{vLXtj@sy%Pr1Nb~SvZ&-G%Vwu(;IX%4;<L0&pt zf&4#|xFQy<{u$Tv%g=G|j<t_M_eLy8I(Rwi^MT{}&*mie@-XG49Fdqjlkr4qiyNQD p+O0aAEw7Z>v+sXbfBaqj0K?*wmyWp|GX{By!PC{xWt~$(698>GbOZnZ literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/images/layout.png b/share/qtcreator/qmldesigner/propertyeditor/Qt/images/layout.png new file mode 100644 index 0000000000000000000000000000000000000000..113e14c00a05aa7e0e13e65ce2b01226dacb6406 GIT binary patch literal 369 zcmV-%0gnEOP)<h;3K|Lk000e1NJLTq000&M000&U0ssI2OV;~r00009a7bBm000XU z000XU0RWnu7ytkPBS}O-R5%f}R67cTKom@Zcku>_ja|UbF4zg)O0cvLynv0h=ncGz z-^^x~hk>x!B;*y3o#@P)H!eAsWqJCooNe1&*Zo>#97kte*L~lIVfaGD^E}X%1dO2j zzW<R*-}fXs%d+6a-gTX*1VLbiLcGX{Lhyo;AI*>y?P8v1V0IX>hZv?L5Ck~vZQF7e z8;Ez51P4yj1Q%f#N)~S+DWE?|G)*HCQ52b>NL;uAFkGQuA7u%-td5hC>@Z>vbrzh@ zUe~q62_ZAoE{==DaV%Nju^D2(3yG?#L?THNGZYCGC%&I7e|wZA<Zb-J?JSBy;)Fpn zL>Bsj!w*jAX_`tFZy_m95_z5r-}1oO>MsYCWtk=I_!_zA<HtWfwm;tiNpHy#4g1N~ P00000NkvXXu0mjfX?T|? literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/images/leftArrow.png b/share/qtcreator/qmldesigner/propertyeditor/Qt/images/leftArrow.png new file mode 100644 index 0000000000000000000000000000000000000000..45187cf0474680b59419c26f1bd308c52f316820 GIT binary patch literal 885 zcmV-*1B(2KP)<h;3K|Lk000e1NJLTq001BW000mO1^@s6cL04^00009a7bBm000id z000id0mpBsWB>pF1am@3R0s$N2z&@+hyVZp8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H10`f^jK~zXfrPX^#Q&AiT@auCnO<U7V(R6CyE3GBEIStG*2U+=G z4n-p$S!hA(e3U)@@e%Z(EXph*vOgsMFzB@;&4@(kkAkun6%sxCBUB3Arqk0o-+3*z z#teV(;f#Ckz2|$*z4y$C(Bc+Rk~}AzWt_I0>?stWH5g22o!u<Q$8Ij_c7H}>0sd8- zh9iuu#0%nU{UP&Y1ZdmiU~^L#L|3i^f2{z#>9vfbvN=PNB_Pb|J>IapD-m?}Q=#Tg z8dUdehLX-i$iEZ|>f>P$ROA7o9j;(^qI?Hk6Sj6Fyxa?Py$WdPS3&KA6u9@c1HMjt zfU52-P<UAmxouIPZjpgB&mFipdVk8g7LEiLdlDx9Y7{g+R=~kWO6VSL1-|Cy<_L3c zpaynckwflT8h}!_(g0L~&KJa4u3$Gnbft(sLlJ4{OPNP!-lrnhUev+toEiAK_3|QN z%C0M*pnWZ5pN@cxCK+sKltKVq1%_Ygg_Y-jtgDs~MyIFGnz}Hw52npV;LA8;Ak4Fo zhcNW97oNV?!;?36Vc^wm*xnoqA@qG<Ji<A!37|X_M3^J`Y?zw<1xu}hQ!2tlmM;gP zoCBKxkrg7sly)Y7e)tAhmubdGm~Uev@O}I<jE;YSA3sN7VsZ?$ClnwnS_bZ74r~I1 z?D8ax=6v+L7}~~={?|RgmwDDqm}6aKkajo-lItaqRO<_>x_R}qr2=vVVD}x6YFr7U ztdoMeCB&jR6b9e*0$)?6Nx~GL*#!K+)&bTZ5D{iYh6~sYz;{TsZiGpqshQ*kKS*s1 zgy)|I!C;z#(zbM1n6cH&B{^<{;k@WRK-v8a`3|409AWq%2{mHE@Ilg#h7x9fM>eqK z{ad0=Oc;L+-I6Jx+!?HAZu1UUC-bBGI3v!X8wj<kPyjJ|y&=9z3~MTUAei<s;%zSA z8pwgYfgJ|04&<$*e=d}8qlGBb1%&Jv<sNF`Xh4ULV5#5z|6czaV45(~v{^}300000 LNkvXXu0mjfO_Pdd literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/images/list-icon.png b/share/qtcreator/qmldesigner/propertyeditor/Qt/images/list-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..9c96743f0e4f3fd22a160864382f2dbf05c6de0d GIT binary patch literal 424 zcmV;Z0ayNsP)<h;3K|Lk000e1NJLTq000&M000&U0ssI2OV;~r00009a7bBm000XU z000XU0RWnu7ytkPT1iAfR5%f(k~^w{U=W3Gz`bM#O0ZS169ns^yRr&`UBKGHRwVAg zt(d4ea>6kA)tmbWDDyhs9L5}qqWJpX&zh#`cs%|Y()YbJj^jMfM08#ELzZpZoP>Nq zz=-6I<9wvtZZ|El9*>9QjN7&a<#amju?)!u0uvCE+cb>^xvq=!JH-8dhm!k=FubtH zF`|K6T;Lqc(UVv&H)1`uAT)9q2JETp8XC{@45g$TV+#2O0uyM{b5&KML=Xf4-Wv!p zj~-&*_c6yJW1+CfF%}mW`n(!1vUQ-xUbiq@lw}E76h&(>E2ay5UiR?DD7n|`RfFO< zM*11S{X`gEwr{cisCw;Y3>W-f=$hvwF3)rH&gb){o-#o!=forD<X!N_9E+FBMT3$g zAwncCghnFfeijWcEJCIwZ~qJQvMkeU_u@AMEQzZ$P4_fLT_gw&VHg_i-=<G@KFMKo S#%xLe0000<MNUMnLSTZ5i?|p7 literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/images/mouse-region-icon.png b/share/qtcreator/qmldesigner/propertyeditor/Qt/images/mouse-region-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..8c23b6c0fce201d1621cf30a0c259021ca4f5812 GIT binary patch literal 511 zcmV<b0RaAqP)<h;3K|Lk000e1NJLTq000&M000&U0ssI2OV;~r0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzp-DtRRCwB?luOHkP!NW{PmQRgD1!(J zC8#J`w1_|w`m_FomhKGd7YMp>BhdwlF3{b2<G`W3be)BcI`cU5&N=5_Rn@=WjB{PL zTCIMaZ<;1|KA%DP8iDj1;&Qp*VLTqk<MCuN`7z>jI^l+g-EQYNP9~En6bdZMzKKBk z4M7?K2>x_BozLgRViBy55pa&jBOF{U%SxqEk|af=(YFw!5o8_?2l{O`n~`$z`TPq6 zGAUxe-}`4a8jZ^3aw3rkL?G)00*05Q9Q4I7OfZ6Ifi5tVCrb$Ps@LltnQsWH#d8Fi zx~}V@DAsB<j^hHf2t%aPX-tRs>-BmvnXFc;Mx(*=yjP2R1g3zX!C<i2Y!-_}yWRFr zmnz5*Enq}Wu)E!Er_;foTdfwj%jL4)@4F%PP(>^jlVusL(m$YuQmJGZhHcwW5p@L* zmC8xBJn^YDn@!}Skl}C$N-mf4FD-^F@GwJ=q9}+#N1CQ>w_8uZ!yXAUK@iX&oNP85 zi9|fX4}0W^RVtPHDR>d|$oc8i>h*fR9KTlq1^_E;urd8Sacuwq002ovPDHLkV1h8m B<2L{R literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/images/placeholder.png b/share/qtcreator/qmldesigner/propertyeditor/Qt/images/placeholder.png new file mode 100644 index 0000000000000000000000000000000000000000..18010a5ee51be87bde66e468f815c9358d667111 GIT binary patch literal 311 zcmeAS@N?(olHy`uVBq!ia0vp^d?3uh1SBVD?P>#3+9j?LCC){ui6xo&c?uz!xv2~x zrKt+03SNnM3PuKo1`39TRwl+)CPoU{K%T+Uk~JXR>?NMQuI#rMS-EuhFJ9Z<1Qg;d z@Q5sCVBi)4Va7{$>;3=*S<)SS9T^xl_H+M9WCim3JzX3_DsJtaXv@W{DA1a`DMItc z%B2}Qzwd8R-*C}Shjr`53qGQES`VnT>`9V%_WUt#^S9dlv6ufolF4x9_&e{{TE?D@ zL0yK^iun}3+`AvN_1ap~Y+aY+GdCHcTsS;6&srw;ObIHv(ioNfbdGbV7w6B;Uk$Ia z*H2MuVUWJ?;a<OC*sE>t4$7X}`G4O7=2yZEl?J=k9r?Kz=rjgTS3j3^P6<r_9q4l$ literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/images/rect-icon.png b/share/qtcreator/qmldesigner/propertyeditor/Qt/images/rect-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..4555d886fbc35cf63ca9408cd0aa25a08a16dc20 GIT binary patch literal 237 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H1SGw${mubWk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5X`aNA7Ln?0FnR1$!L6L*CT)288)9Vea-J7MgH;C^4%@g+F z!4|m-r+pd}^3VOeD7*dIr+b%c)?PhpnW(u`poW9-N8p>if}E`lc|DEX69YIZL^N80 zIVwc=mQQ8+S<n^Dk+3H!dk)i%iA<`6T$zvFvd%razAAA->%okhJUb4WU#=^QwA56s jdb-EZ?dh+-ED{Xcx@D98J(G(9I*q~8)z4*}Q$iB}?8jRa literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/images/reset-button.png b/share/qtcreator/qmldesigner/propertyeditor/Qt/images/reset-button.png new file mode 100644 index 0000000000000000000000000000000000000000..bf53422b9e8db442bfc50c249ca1b0e5fd874871 GIT binary patch literal 741 zcmV<B0vi2^P)<h;3K|Lk000e1NJLTq000mG000gM1^@s6CNG?d00007bV*G`2iXh` z4J8CSjQZFB000SaNLh0L03N~s03N~tZqRi;00004XF*Lt006O%3;baP0007XNkl<Z zc-m!>OK1~O6o&tqNtiTgGU;Q|NR1UzR|U~paN$C_(H5=fPWNs^LANcXv@5&Njo{Xe zAXq3=1d&oS7{r35(jv4H4c2MXN7`g2P3Aol?+xk1*aK&C@BP1X{(BB12m)kT#!-yM zPlwuiPEGN_;H6-Dw4GrXwp=I`OkMk0%x1?Q@BH>2N25_N{{p7^E)2DI$DiG~{~#&+ z*=5yV5==*fUyQ-*>crgKY<54lJvz3sICBgz(c9O5e)#J9YnQKx)z$AXl?n{YK-IRu z+D@$i7wf>1aadgZuyA{PWoe!Q*m~P79f`!Np^;mnwf+-UMT2cwO=23(-T?+8oo%s1 z_j95V=vfUAhzi$lJbZM9U0DaCC@?ewhGT(o8)6S4<uE*QGZ~4;?|OhpmpBsRd6voV z!_rN-RR=!DC4gqJPR?nDRf`D-kwJRqt5JOZUf*^ehGD`YmEqW^k|YzT2~F39tZQId z1_90odv6bXYm4Z0;M$gC^Zq8^Y+YP53`klDIvVY8q6o86kv%}Upu9WK^{Q<fo^O{_ z&6W>|xC{qOlNB|{L$By*51{7rZ{E(l-teF72C<I3R>3zpj|07Y>=Ris%7t`lLy<OL zcmTRxh1}M?&!wGWT2ruGEI=hz{tqtL3Q3HVx7XGti6-X(XgrlN^UJB!q^#?kiHqmJ z2SbgjEdfOO&tlOo<vz~NJ(<q_czJYJ+e8X?94EkWTwuT->KRNXr^7<Dr}IRtjb;4U z-Pux9Mag~2WX8T&YKFYmsB^j&-YS!|q(4QItEaDz4~03n+^Ru*0d_s<I9#Hs(pCNo Xbw4;3r8NWD00000NkvXXu0mjfMIlcL literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/images/rightArrow.png b/share/qtcreator/qmldesigner/propertyeditor/Qt/images/rightArrow.png new file mode 100644 index 0000000000000000000000000000000000000000..118436bbcc96aba355c3f4f581e41d01a3fb54af GIT binary patch literal 871 zcmV-t1DO1YP)<h;3K|Lk000e1NJLTq001BW000mO1^@s6cL04^00009a7bBm000id z000id0mpBsWB>pF1am@3R0s$N2z&@+hyVZp8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H10^~_VK~zXfrPXUlQ(+hf@S{#=(`IYBP4cp|@K%zEI;V-1<RB$c zrWlGPXrX;boogyV!LTGN3O{uBgAfs-9||nZNrBOaD5K927MdW<E_lh5xA*zav*4^T z!v}txao%(JKkqr`ogd>RtfMsLemH7r*>!M;LXPd?;xBCd16})_57%|Mzjven|0=;O zFru#wh4fY(6m=&+#f?-bu*X4i<5tjB$U(`^CBDkz2u+EcksT+LQ1>7cEJOLw^7sHW z4;DaeUnW%E+zqCSaS&A<45}kC@Y<+2z~_YKj>Xr9f@L@hYz`x|KFtNY!wg^NXW;J3 zb5M0V4a%-3fvHmu+VUU}Qu*~cX}R~r91mfn@?H{jJkNr*F#}kKbD;lyH;A>e`kRqU zPa2`@8V|skF7N<ch9w--B{J|DAh}V+pP`D`MswHj&PR>RI+6{Rp&V#>lm{<941!oo z%L|NH@0+2}u7`acT8QMcK*X7L&%F3!PqT)RtK)U>?#mcVOgrH1l*^mXWAJ*?0jsMk zAlCN<CnGgI`CvQ~4zc`wAi?nhcmy!CM=^5il?^sp)6Pjo^4cRIv3@fs(gg4bkl3JN z<npsq@N;Pn=H_Q%_Qw>=eES4b&X2IdyC>4>9<VYZ&Vasp6C*)t0Xzc49t&Z_+>^W} zg>O^bZ*|F)1V)Oxc0$k4S&-J9;VwpuRzCG)?GZ$l$r<tb4rt9XMhsRhm^x$Oklp3r z`DkeGs{v`}{o{8T$!-k?F~Hu_Dn_;x27uQ9;()dykdbVjoaERdK<vcYTg8YNU}R!| z5qHOu&NZchj0mB89pJovhT=fjVSy1bNJfjA5or)h?@>nb+H@cW%(SQ((V6*@%qi0R zLE5>;J0MLK!Pjx3F69dd7hNTXl#@zGZ&JgqhA@cXV?<pX0J3cYc)PLB0MbB81OId3 x6#HFNMFF6oeO%CX7hgB@`3N@p{r~Uze*k!nGNlS?lPUlJ002ovPDHLkV1oDji#PxP literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/images/standard.png b/share/qtcreator/qmldesigner/propertyeditor/Qt/images/standard.png new file mode 100644 index 0000000000000000000000000000000000000000..7c0911d63df28d558f10438cf19f1d21d3638df2 GIT binary patch literal 340 zcmV-a0jvIrP)<h;3K|Lk000e1NJLTq000&M000&U0ssI2OV;~r0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUy_DMuRRCwC7R7(nkFbtiAcku**xbYnB zJX>++)(Z$Cf+z4Qe&@+6H58^(>r56kt$EMnnI@{L>h;W`ec$6a4#rQ@6m8oEmT??C zF-elhE#N1`aU57ZDG+Q^tm}#gR_xO>b%2LqAVwYncv7GcQozC5_q~It>pJ6SS$0t@ z%VH?#`xw8i3n7e2($DkU0dCut@h1fU@aB0o6w?1>CI;{yQJ^FqQ48t2DJY`WA~=gb zXi?X-Y`YLGtbgvSh0MZge%~x)Ye`c|eeF&yCmXydid$g2!L71+<!;}RFbZ7=M~-|^ mmZksr(Nn8wnkVD;Ex-U*=!rfoJ#hyB0000<MNUMnLSTY0S&k_H literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/images/submenu.png b/share/qtcreator/qmldesigner/propertyeditor/Qt/images/submenu.png new file mode 100644 index 0000000000000000000000000000000000000000..99c01e556395e9a031de449154f837ad4af3da98 GIT binary patch literal 447 zcmV;w0YLtVP)<h;3K|Lk000e1NJLTq000gE000gM0ssI2kSe-{0000hbVXQnLvm$d zbZKvHAXI5>WdKxlWgs;mN?~pwGB7bPATcpIGc-CdIUp-AF)%Rb-uU<c000McNliru z+5rU$COZm^qrCtC010qNS#tmY3labT3lag+-G2N4000DMK}|sb0I`n?{9y$E009?C zL_t(|+Ko~@i-a%~Jac{^YO9s3)@q@(mMY>ewh?4&8^Lm6(bh^t1x0Tp;_ku0?Snif zGn4niOBly-_I>~QFBpa)pZE~4v4os%S(ZJ|yWMU8!!R^WQ<fzF^&N$6+eUF+*W)<m zIPSgDvMgDawQWnS1WQqrD2jNVXMP75@KEW=!|J-OAP9e65j<3iSuE2uk?;3=UDxkE z%d&zXfLavASydE8WZ(A<!?=9FU@rBv&hvbXjgT-5HBAFX1_lER^<{lNpXY5@Hc1jp z0~wf-mwH&Ms#@2T?$Ity)2k_Xs4r{Z_n6=1z5)z*sC4_Ss_M3F<2XJZ4>Xk|3FDlm p3EMo+yRO4SoZkSi^~oPV`~Wyic`D?00DS-e002ovPDHLkV1oWAu$BM- literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/images/text-edit-icon.png b/share/qtcreator/qmldesigner/propertyeditor/Qt/images/text-edit-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..6a064ab07c1fb301161797f48b611106f371fc9c GIT binary patch literal 368 zcmV-$0gwKPP)<h;3K|Lk000e1NJLTq000&M000&U0ssI2OV;~r0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz5=lfsRCwB?lsyteKoEs@%j~*_Od?fs zmk@~sBm^W95`mIPgaVG>GS1>}+m59QI~pB~!1Sm4_0!L?EFbrR^nEXi;^q9hu1VK* zK)H;7^bs)(gX1_H#}Pta-+2Fm=lS`hK`+2fMNuY1+qUqC<M_h&;e_-N0gZT`x2?jm ztV7U(h@xow8l%yKfRcRQ-&SE51`5$M4IqLb_}48vAbmoFVThcFEXz9x=ouP84q_$H zh@e`4oa`!8RXxzcbzO`LSXv}Wa>|{8Agr~pZTplpXGNAO(llLFp=lb1wa96jCbD^5 z*NdXqF?g0`hgwLIw4LjMz<8Nv=6Sw@z!;hJkI<EKcWYHub!Yv41Q-Bn0{hY{$1xKC O0000<MNUMnLSTXi!Jl3L literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/images/text-icon.png b/share/qtcreator/qmldesigner/propertyeditor/Qt/images/text-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..9ce62037ba3c7700e9f06dfdb48fcd61eda8f7f8 GIT binary patch literal 582 zcmV-M0=fN(P)<h;3K|Lk000e1NJLTq000&M000&U0ssI2OV;~r0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz=t)FDRCwB~lufItQ546o$MOD_w<HMz zDHA5XfHLp}d_n^sz`#HmiHVdlz(7$%DX)3w{@itXIOo)L<L>fUd#}Cz>%abM@2$z_ z^S|C3AD7D|7K^<c-|2Kd&gU~IFGCQ1gScL=Z$X?+C;ncBAp8dLw2*GMdpI1(q}6K8 zX0zApwb^Vcgdz}sAIs&EV*P%<+wI=%c1XBbEX4K6;%OmJFdB_crxTm}tyXJ19z*A| zkmK<v5Wo)xgUx1xVX;_3p^(Gj5QzPLFNOS^60QgYW4&HOk5s{6kWGz7V?LjYYrEaD zg2A`f>oIk?T;d}9#zN#IXEK?TJSLN=UayZvqi8hBbo*y86>mHqXR}1zScn#nW3^gI z%yc?cS!gsG;KJdsUav=s$z&p~FIr$Ab-&-oP-_+!5U3ak1hDy*N~LDAnNFuA>JCw> z)rP}iDwXp2d{(QqP$;0Q3ZdGHqEIFsQ4`;_;QY|`NF*ZN5edchXwwTsft0Gnodv7- zU`Vx8^w>G<*=*Ksw^^Z5#DeUdg?h9ynM}D{u2!pdyFHOepdW1(7v~CyN~K~j7(5;i zJ5fYjU+#!br^{xu>WP#`L|nOC?%R1)uhqX^v*$PU-OK(To*aI~V)32l_qPB80NSV? U4A;*g`~Uy|07*qoM6N<$f~({b82|tP literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/images/upArrow.png b/share/qtcreator/qmldesigner/propertyeditor/Qt/images/upArrow.png new file mode 100644 index 0000000000000000000000000000000000000000..e689de743eb571040e0bc07bbfe83071796e78a7 GIT binary patch literal 849 zcmV-X1FrmuP)<h;3K|Lk000e1NJLTq000mG001Be1^@s68;SVL00009a7bBm000id z000id0mpBsWB>pF1am@3R0s$N2z&@+hyVZp8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H10?tW9K~zXft(9#^Q&AYlk2;;rIa||hlDegVZz-9ma~jyILWV<` zLsBUvEiEXWb0uYxRv#=-3XC!cDWn&Y3bdjZKUhSWW?&{%ND6(b2y!~Lf9Ia*?CJ#J z`oOvOT<&vz&;NPubMA2iHL}2zxo%{_PKb|0Ol-DE;_gYvk6Vk}9IH6MLJKew_+}BP z^ThBo5XkBCQpS5jsH3(>iufuuG8@BTYKg`6`cOobuYuYmhRg+M<p4^980$-A*x#NA zb7vYVA8y5=JE<sYOGMt~7-XCaMNok^<XIwKHckbC09-A@N~AD9PJ;EB9#xN0(cE(o z-+m3_<O37(uf!pzIT9IXv{2=GfyB;d&SruEA^UugcP$b%-AOq1T!+@)3QSIovs`~q z{6uZX9^^Dp1!uKLF#AJcn6Cp<AQ%u;E{FALDjj;<{7{SOnJHeSZT3Gnb+-uV^;&GM zQ6Z44!)y;S1%d$^E0i#IrJ-Z62{V6ZTz0K}+K$t=%b~CKhlcipcbMZ#u&W?|l=ipo zco5EampY@r8F_-??|m%7$FW{?^|!ORZE_r04Kau)@r5LAR)JsuM~*0yqqr>rmtI(K z|9vY8uf}0;>@ABrF!CBZ>NIHSY{22m*{lZIf>rQR{8t0P0L@+>7%xWA5zTtua3LH+ zV~!g1e|?3NY9*4Z{GqqdK@|k%i1~dW7@#tWptGpxZqs0Ay$0Lrf-yAO&lJ4-+{2>J zDcE#Gj@9WNd<B95Twkk9o=B$orC0-4o}Wesn1a49FCDqh=C9251d+P<9|!_)k^FW! zR3ua?n2HlOiqUw_0_*icrYO;@fZ8ag9q8`N-?<l71i(SK14WT7LU6tq(FbIRKdQin zGCzdSXT@$0h}IHbbe@700kE=Dlj#T~Tj-IN=>Z9yd#zg>dCu46p(hwHM_<l;>0@^} btEtaF<Gx*e*xMfi00000NkvXXu0mjfN^f|e literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/layoutWidget.css b/share/qtcreator/qmldesigner/propertyeditor/Qt/layoutWidget.css new file mode 100644 index 00000000000..ce857900b87 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyeditor/Qt/layoutWidget.css @@ -0,0 +1,35 @@ +QPushButton::checked + { + border-radius: 4px; + border: 4px solid #4f4f41; + border-image: none; + background-color: #9a9b9e; + } + + QPushButton:pressed + { + border: 1px solid #5f5f51; + background-color: #dadbde; + background: none; + color: white; + } + + QPushButton:flat { + border: 1px solid #5f5f51; + background-color: #eadbde; + color: white; + } + + QPushButton:default { + border-color: 1px solid black; + background-color: #eadbde; + color: white; + } + + +QFrame{ + border: 2px solid black; + border-radius: 4px; + padding: 2px; + background: #aaabae; + } \ No newline at end of file diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/propertyEditor.css b/share/qtcreator/qmldesigner/propertyeditor/Qt/propertyEditor.css new file mode 100644 index 00000000000..31af443f4c6 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyeditor/Qt/propertyEditor.css @@ -0,0 +1,397 @@ +QFrame, QSpinBox { + background-color: #707070; +} + +QFrame +{ + border-radius: 0px; + font-size: 11px +} + +WidgetFrame { + border: 0px; + background-color: #707070; +} + + QComboBox QAbstractItemView { + border: 2px solid darkgray; + background-color: White; + } + +QLabel { + border: none; + border-image: none; + padding: 0; + background: none; + color: #dedede; + font: bold +} + +QScrollArea { + border: 0px; + border-radius: 0px; + background-color: #707070; +} + +QGroupBox +{ +background-color: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, + stop: 0 #4a4a4a, stop: 1 #4f4f4f); + border: 1px solid #4F4F4F; + border-radius: 5px; + color: white; + font: bold +} + +QLineEdit +{ + color: white; + border: 2px solid #0F0F0F; + border-radius: 6px; + border-width: 1; + background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #2c2c2c, stop: 1 #333333); +} + +QTextEdit +{ + border: 2px solid #8F8F8F; + border-radius: 10px; + padding: 0 8px; + background-color: white; + selection-background-color: darkgray; +} + + QSpinBox + { + font-size: 11px; + color: white; + padding-right: 20px; /* make room for the arrows */ + padding-top: 2px; /* make room for the arrows */ + padding-bottom: 2px; /* make room for the arrows */ + border: 2px solid #0F0F0F; + border-radius: 6px; + border-width: 1; + background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #2c2c2c, stop: 1 #333333); + + min-height: 14px; + max-height: 14px; + } + + QDoubleSpinBox + { + font-size: 11px; + color: white; + padding-right: 20px; /* make room for the arrows */ + padding-top: 2px; /* make room for the arrows */ + padding-bottom: 2px; /* make room for the arrows */ + border: 2px solid #0F0F0F; + border-radius: 6px; + border-width: 1; + background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #2c2c2c, stop: 1 #333333); + max-height: 14px; + min-height: 14px; + } + + QSpinBox::down-button { + subcontrol-origin: border; + subcontrol-position: bottom right; /* position at bottom right corner */ + border-image: url(:/qmldesigner/images/spindown.png) 1; + + width: 16px; + border-width: 1px; + border-top-width: 0; + } + +QSpinBox::down-button:hover { + border-image: url(:/qmldesigner/images/spindown_hover.png) 1; +} + +QSpinBox::down-button:pressed { + border-image: url(:/qmldesigner/images/spindown_pressed.png) 1; +} + + + QSpinBox::up-button { + subcontrol-origin: border; + subcontrol-position: top right; /* position at bottom right corner */ + border-image: url(:/qmldesigner/images/spinup.png) 1; + + width: 16px; + border-width: 1px; + } + +QSpinBox::up-button:hover { + border-image: url(:/qmldesigner/images/spinup_hover.png) 1; +} + +QSpinBox::up-button:pressed { + border-image: url(:/qmldesigner/images/spinup_pressed.png) 1; +} + + + QDoubleSpinBox::down-button { + subcontrol-origin: border; + subcontrol-position: bottom right; /* position at bottom right corner */ + border-image: url(:/qmldesigner/images/spindown.png) 1; + + width: 16px; + border-width: 1px; + } + +QDoubleSpinBox::down-button:hover { + border-image: url(:/qmldesigner/images/spindown_hover.png) 1; +} + +QDoubleSpinBox::down-button:pressed { + border-image: url(:/qmldesigner/images/spindown_pressed.png) 1; +} + +QDoubleSpinBox::up-button { + subcontrol-origin: border; + subcontrol-position: top right; /* position at bottom right corner */ + border-image: url(:/qmldesigner/images/spinup.png) 1; + + width: 16px; + border-width: 1px; +} + +QDoubleSpinBox::up-button:hover { + border-image: url(:/qmldesigner/images/spinup_hover.png) 1; +} + +QDoubleSpinBox::up-button:pressed { + border-image: url(:/qmldesigner/images/spinup_pressed.png) 1; +} + +QToolButton +{ + background: none; + border: none; + background-color: none; +} + +QToolButton::pressed +{ + background: none; + border: none; + background-color: none; +} + +QToolButton::checked +{ + background: none; + border: none; +} + +QPushButton { + padding: 0px; + font-size: 11px +} + +QPushButton, QComboBox[editable="false"], +QComboBox[editable="true"]::drop-down { + border-image: url(:/qmldesigner/images/pushbutton.png) 3; + border-width: 3; +} + +QPushButton:hover, QComboBox[editable="false"]:hover, +QComboBox[editable="true"]::drop-down:hover, QMenuBar::item:hover { + border-image: url(:/qmldesigner/images/pushbutton_hover.png) 3; + border-width: 3; +} + +QPushButton:pressed, QComboBox[editable="false"]:on, +QComboBox[editable="true"]::drop-down:on, QMenuBar::item:on { + border-image: url(:/qmldesigner/images/pushbutton_pressed.png) 3; + border-width: 3; +} + +/* Customize read-only comboboxes. */ + +QComboBox +{ + font-size: 11px; + color: white; +} + +QComboBox[editable="false"] { + padding-left: 3px; + padding-right: 20px; /* space for the arrow */ +} + +QComboBox[editable="false"]::drop-down { + subcontrol-origin: padding; + subcontrol-position: top right; + width: 15px; + border-left-style: solid; + border-left-color: black; + border-left-width: 1px; +} + +QComboBox[editable="false"]::down-arrow { + subcontrol-origin: content; + subcontrol-position: center; + position: relative; + left: 1px; /* 1 pixel dropdown border */ +} + +/* The combobox arrow is on when the popup is open. */ +QComboBox[editable="false"]::down-arrow:on { + position: relative; + top: 1px; + left: 2px; +} + +/* Customize editable comboboxes. */ + +QComboBox[editable="true"] { + padding-right: 16px; +} + +QComboBox[editable="true"]::drop-down { + subcontrol-origin: border; + subcontrol-position: top right; + width: 13px; + position: absolute; + top: 2px; + bottom: 2px; + right: 2px; +} + +QComboBox[editable="true"]::drop-down, +QComboBox[editable="true"]::drop-down:hover, +QComboBox[editable="true"]::drop-down:on { + border-width: 0px; + border-left-width: 3px; /* we need only left and center part */ +} + +/* Shift the arrow when it's open. */ +QComboBox[editable="true"]::down-arrow:on { + position: relative; + top: 1px; + left: 1px; +} + + QRadioButton { + spacing: 5px; +} + +QRadioButton::indicator { + width: 13px; + height: 13px; +} + +QRadioButton::indicator::unchecked { + image: url(:/qmldesigner/images/radiobutton_unchecked.png); +} + +QRadioButton::indicator:unchecked:hover { + image: url(:/qmldesigner/images/radiobutton_unchecked_hover.png); +} + +QRadioButton::indicator:unchecked:pressed { + image: url(:/qmldesigner/images/radiobutton_unchecked_pressed.png); +} + +QRadioButton::indicator::checked { + image: url(:/qmldesigner/images/radiobutton_checked.png); +} + +QRadioButton::indicator:checked:hover { + image: url(:/qmldesigner/images/radiobutton_checked_hover.png); +} + +QRadioButton::indicator:checked:pressed { + image: url(:/qmldesigner/images/radiobutton_checked_pressed.png); +} + +/* Customize arrows. */ + +*::down-arrow, *::menu-indicator { + image: url(:/qmldesigner/images/down_arrow.png); + width: 7px; + height: 7px; +} + +*::down-arrow:disabled, *::down-arrow:off { + image: url(:/qmldesigner/images/down_arrow_disabled.png); +} + +*::up-arrow { + image: url(:/qmldesigner/images/up_arrow.png); + width: 7px; + height: 7px; +} + +*::up-arrow:disabled, *::up-arrow:off { + image: url(:/qmldesigner/images/up_arrow_disabled.png); +} + +/* Customize push buttons and comboboxes. Our read-only combobox + is very similar to a push button, so they share the same border image. */ + + +/* Customize check boxes. */ +QCheckBox { + spacing: 8px; + font-size: 11px +} + +QCheckBox::indicator { + width: 20px; + height: 20px; +} + +QCheckBox::indicator:unchecked { + image: url(:/qmldesigner/images/checkbox_unchecked.png); +} + +QCheckBox::indicator:unchecked:hover { + image: url(:/qmldesigner/images/checkbox_unchecked_hover.png); +} + +QCheckBox::indicator:unchecked:pressed { + image: url(:/qmldesigner/images/checkbox_unchecked_pressed.png); +} + +QCheckBox::indicator:checked { + image: url(:/qmldesigner/images/checkbox_checked.png); +} + +QCheckBox::indicator:checked:hover { + image: url(:/qmldesigner/images/checkbox_checked_hover.png); +} + +QCheckBox::indicator:checked:pressed { + image: url(:/qmldesigner/images/checkbox_checked_pressed.png); +} + +QMenu { + background-color: #494949; /* sets background of the menu */ + border: 1px solid black; + margin: 0px; /* some spacing around the menu */ + color: #cacaca; + } + + QMenu::item { + /* sets background of menu item. set this to something non-transparent + if you want menu color and menu item color to be different */ + background-color: transparent; + padding: 2px 25px 2px 20px; + } + + QMenu::item:selected { /* when user selects item using mouse or keyboard */ + background-color: #d2d2d2; + padding: 2px; + color: #404040; + border-radius: 4px; + } +QMenu::separator { + height: 1px; + background: #404040; + margin-left: 5px; + margin-right: 5px; + } diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/specialCheckBox.css b/share/qtcreator/qmldesigner/propertyeditor/Qt/specialCheckBox.css new file mode 100644 index 00000000000..73aa535f7ff --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyeditor/Qt/specialCheckBox.css @@ -0,0 +1,20 @@ +QCheckBox { + spacing: 2px; + border-width: 2px; + border-radius: 2px; + } + + QCheckBox::indicator { + width: 13px; + height: 13px; + } + QLabel { + border : 2px solid gray; + border-radius: 10px; + padding: 0 8px; + background: yellow; + selection-background-color: darkgray; + } + + + \ No newline at end of file diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/switch.css b/share/qtcreator/qmldesigner/propertyeditor/Qt/switch.css new file mode 100644 index 00000000000..b4fc0a3080e --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyeditor/Qt/switch.css @@ -0,0 +1,32 @@ +QPushButton { + border: 0px solid #6f6f61; + border-image: url(:/images/button2_normal.png) 2 2 2 2; + border-radius: 1px; + min-width: 20px; + min-height: 20px; + margin: 0px 0 2px 0; + } + + QPushButton::checked + { + border-image: url(:/images/button2_pressed.png) 2 2 2 2; + border: 1px solid #4f4f41; + } + + QPushButton:pressed { + border-image: url(:/images/button2_pressed.png) 2 2 2 2; + + } + + QPushButton:flat { + border: none; /* no border for a flat push button */ + } + + QPushButton:default { + border-image: url(:/images/button2_normal.png) 2 2 2 2; + } + + QPushButton:hover { + border-image: url(:/images/button2_hovered.png) 2 2 2 2; + } + \ No newline at end of file diff --git a/share/qtcreator/qmldesigner/propertyeditor/Qt/typeLabel.css b/share/qtcreator/qmldesigner/propertyeditor/Qt/typeLabel.css new file mode 100644 index 00000000000..8df9e606dbf --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyeditor/Qt/typeLabel.css @@ -0,0 +1,8 @@ +QLineEdit { + color: lightGray; + border: 2px solid #0F0F0F; + border-radius: 6px; + border-width: 2; + background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #2c2c2c, stop: 1 #333333); +} \ No newline at end of file diff --git a/share/qtcreator/qmldesigner/welcome-card.png b/share/qtcreator/qmldesigner/welcome-card.png new file mode 100644 index 0000000000000000000000000000000000000000..36633ddb87efac8a02c70aefc45df7195492a094 GIT binary patch literal 59043 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 literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/welcomescreen.qml b/share/qtcreator/qmldesigner/welcomescreen.qml new file mode 100644 index 00000000000..81a4cc43564 --- /dev/null +++ b/share/qtcreator/qmldesigner/welcomescreen.qml @@ -0,0 +1,147 @@ +import Qt 4.6 + +Image { + id: screen + property var selectedFile + signal openFile + source: "gradient.png" + + width: 1045 + height: 680 + + Image { + source: "welcome-card.png" + + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + transformOrigin: "Center" + smooth: true + scale: 0 + + //Animation + scale: SequentialAnimation { + running: true + NumberAnimation { + to: 1 + duration: 400 + easing: "easeOutCirc" + } + } + Text { + text: "Recent files" + style: "Sunken" + color: "white" + font.pointSize: 14 + x: 70 + y: 160 + } + + Text { + text: "New file" + style: "Sunken" + color: "white" + font.pointSize: 14 + x: 380 + y: 160 + opacity: 0.8 + } + + Item { + id: leftSide + width: 300 + anchors.top: parent.top + anchors.bottom: parent.bottom + + ListView { + id: recentFilesListView + width: 280 + height: 320 + x: 60 + y: 200 + overShoot: false + + model: recentFiles + delegate: fileDelegate + } + } + + Item { + id: rightSide + x: 300 + width: 300 + anchors.top: parent.top + anchors.bottom: parent.bottom + + + + ListView { + id: templatesListView + width: 280 + height: 320 + x: 80 + y: 200 + overShoot: false + + model: templatesList + delegate: fileDelegate + } + } + Button { + id: chooseButton + label: " Choose" + + anchors.right: parent.right + anchors.bottom: parent.bottom + anchors.bottomMargin: 13 + anchors.rightMargin: 40 + } + } + + Component { + id: fileDelegate + Item { + width: parent.width + height: fileDelegateText.height + + Text { + id: fileDelegateText + anchors.left: parent.left + color: "white" + text: name + } + + MouseRegion { + anchors.top: fileDelegateText.top + anchors.left: fileDelegateText.left + anchors.right: fileDelegateText.right + anchors.bottom: fileDelegateText.bottom + onClicked: { + screen.selectedFile = fileName; + screen.openFile(); + } + } + } + } +/* + ListModel { + id: recentFiles + ListElement { + fileName: "file1.qml" + } + ListElement { + fileName: "file2.qml" + } + ListElement { + fileName: "file3.qml" + } + } +*/ + + ListModel { + id: templatesList + ListElement { + fileName: ":/qmldesigner/templates/General/Empty Fx" + name: "Fx Rectangle (640x480)" + } + } +} diff --git a/share/qtcreator/static.pro b/share/qtcreator/static.pro index 0256c8ad73d..46db15fa930 100644 --- a/share/qtcreator/static.pro +++ b/share/qtcreator/static.pro @@ -28,7 +28,8 @@ DATA_DIRS = \ designer \ schemes \ styles \ - gdbmacros + gdbmacros \ + qmldesigner !isEmpty(copydata) { diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro index 11060df7620..d889f91d218 100644 --- a/src/plugins/plugins.pro +++ b/src/plugins/plugins.pro @@ -38,7 +38,8 @@ SUBDIRS = plugin_coreplugin \ debugger/dumper.pro contains(QT_CONFIG, declarative) { - SUBDIRS += plugin_qmlinspector + SUBDIRS += plugin_qmlinspector \ + plugin_qmldesigner } plugin_coreplugin.subdir = coreplugin @@ -184,6 +185,10 @@ plugin_qmlprojectmanager.depends += plugin_projectexplorer plugin_qmlprojectmanager.depends += plugin_help plugin_qmlprojectmanager.depends += plugin_qmleditor +plugin_qmldesigner.subdir = qmldesigner +plugin_qmldesigner.depends = plugin_coreplugin +plugin_qmldesigner.depends += plugin_texteditor + plugin_qmlinspector.subdir = qmlinspector plugin_qmlinspector.depends += plugin_projectexplorer plugin_qmlinspector.depends += plugin_coreplugin diff --git a/src/plugins/qmldesigner/QmlDesigner.pluginspec b/src/plugins/qmldesigner/QmlDesigner.pluginspec new file mode 100644 index 00000000000..a7894e63538 --- /dev/null +++ b/src/plugins/qmldesigner/QmlDesigner.pluginspec @@ -0,0 +1,25 @@ +<plugin name="QmlDesigner" version="1.3.80" compatVersion="1.3.80"> + <vendor>Nokia Corporation</vendor> + <copyright>(C) 2008-2009 Nokia Corporation</copyright> + <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 Nokia. + +GNU Lesser General Public License Usage + +Alternatively, this plugin may be used under the terms of the GNU Lesser +General Public License version 2.1 as published by the Free Software +Foundation. Please review the following information to +ensure the GNU Lesser General Public License version 2.1 requirements +will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.</license> + <description>Editor for QML.</description> + <url>http://qt.nokia.com</url> + <dependencyList> + <dependency name="Core" version="1.3.80"/> + <dependency name="TextEditor" version="1.3.80"/> + </dependencyList> +</plugin> diff --git a/src/plugins/qmldesigner/application.cpp b/src/plugins/qmldesigner/application.cpp new file mode 100644 index 00000000000..223f185c67d --- /dev/null +++ b/src/plugins/qmldesigner/application.cpp @@ -0,0 +1,46 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "application.h" +#include <QtCore/QCoreApplication> +#include <QtCore/QFileInfo> + + +#ifdef Q_OS_MAC +# define SHARE_PATH "/../Resources/qmldesigner" +#else +# define SHARE_PATH "/../share/qtcreator/qmldesigner" +#endif + +QString Application::sharedDirPath() +{ + QString appPath = QCoreApplication::applicationDirPath(); + + return QFileInfo(appPath + SHARE_PATH).absoluteFilePath(); +} diff --git a/src/plugins/qmldesigner/application.h b/src/plugins/qmldesigner/application.h new file mode 100644 index 00000000000..adf7f81001d --- /dev/null +++ b/src/plugins/qmldesigner/application.h @@ -0,0 +1,40 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef APPLICATION_H +#define APPLICATION_H + +#include <QtCore/QString> + +class Application { +public: + static QString sharedDirPath(); +}; + +#endif // APPLICATION_H diff --git a/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.cpp b/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.cpp new file mode 100644 index 00000000000..884ba4c351f --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.cpp @@ -0,0 +1,190 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "abstractformeditortool.h" +#include "formeditorview.h" +#include "formeditorview.h" + +#include <QtDebug> +#include <QGraphicsSceneDragDropEvent> +#include <nodemetainfo.h> + +namespace QmlDesigner { + +AbstractFormEditorTool::AbstractFormEditorTool(FormEditorView *editorView) : m_view(editorView) +{ +} + + +AbstractFormEditorTool::~AbstractFormEditorTool() +{ + +} + +FormEditorView* AbstractFormEditorTool::view() const +{ + return m_view; +} + +FormEditorScene* AbstractFormEditorTool::scene() const +{ + return view()->scene(); +} + +void AbstractFormEditorTool::setItems(const QList<FormEditorItem*> &itemList) +{ + m_itemList = itemList; + selectedItemsChanged(m_itemList); +} + +QList<FormEditorItem*> AbstractFormEditorTool::items() const +{ + return m_itemList; +} + +bool AbstractFormEditorTool::topItemIsMovable(const QList<QGraphicsItem*> & itemList) +{ + QGraphicsItem *firstSelectableItem = topMovableGraphicsItem(itemList); + if (firstSelectableItem == 0) + return false; + + FormEditorItem *formEditorItem = FormEditorItem::fromQGraphicsItem(firstSelectableItem); + QList<QmlItemNode> selectedNodes = view()->selectedQmlItemNodes(); + + if (formEditorItem != 0 + && selectedNodes.contains(formEditorItem->qmlItemNode())) + return true; + + return false; + +} + +bool AbstractFormEditorTool::topSelectedItemIsMovable(const QList<QGraphicsItem*> &itemList) +{ + QList<QmlItemNode> selectedNodes = view()->selectedQmlItemNodes(); + + foreach (QGraphicsItem *item, itemList) { + FormEditorItem *formEditorItem = FormEditorItem::fromQGraphicsItem(item); + if (formEditorItem + && selectedNodes.contains(formEditorItem->qmlItemNode()) + && !formEditorItem->qmlItemNode().isRootNode() + && (formEditorItem->qmlItemNode().hasShowContent())) + return true; + } + + foreach (QGraphicsItem *item, itemList) { + FormEditorItem *formEditorItem = FormEditorItem::fromQGraphicsItem(item); + if (formEditorItem + && !formEditorItem->qmlItemNode().isRootNode() + && selectedNodes.contains(formEditorItem->qmlItemNode())) + return true; + } + + return false; + +} + + +bool AbstractFormEditorTool::topItemIsResizeHandle(const QList<QGraphicsItem*> &/*itemList*/) +{ + return false; +} + +QGraphicsItem *AbstractFormEditorTool::topMovableGraphicsItem(const QList<QGraphicsItem*> &itemList) +{ + foreach (QGraphicsItem *item, itemList) { + if (item->flags().testFlag(QGraphicsItem::ItemIsMovable)) + return item; + } + + return 0; +} +FormEditorItem *AbstractFormEditorTool::topMovableFormEditorItem(const QList<QGraphicsItem*> &itemList) +{ + foreach (QGraphicsItem *item, itemList) { + FormEditorItem *formEditorItem = FormEditorItem::fromQGraphicsItem(item); + if (formEditorItem + && (formEditorItem->qmlItemNode().hasShowContent())) + return formEditorItem; + } + + return 0; +} + +FormEditorItem* AbstractFormEditorTool::topFormEditorItem(const QList<QGraphicsItem*> & itemList) +{ + foreach (QGraphicsItem *item, itemList) { + FormEditorItem *formEditorItem = FormEditorItem::fromQGraphicsItem(item); + if (formEditorItem && !formEditorItem->qmlItemNode().isRootNode()) + return formEditorItem; + } + + return 0; +} + +FormEditorItem* AbstractFormEditorTool::topFormEditorItemWithRootItem(const QList<QGraphicsItem*> & itemList) +{ + foreach (QGraphicsItem *item, itemList) { + FormEditorItem *formEditorItem = FormEditorItem::fromQGraphicsItem(item); + if (formEditorItem) + return formEditorItem; + } + + return 0; +} + +void AbstractFormEditorTool::dropEvent(QGraphicsSceneDragDropEvent * /* event */) +{ + Q_ASSERT(false); +} + +void AbstractFormEditorTool::dragEnterEvent(QGraphicsSceneDragDropEvent * event) +{ + if (event->mimeData()->hasFormat("application/vnd.bauhaus.itemlibraryinfo") || + event->mimeData()->hasFormat("application/vnd.bauhaus.libraryresource")) { + event->accept(); + view()->changeToDragTool(); + view()->currentTool()->dragEnterEvent(event); + } else { + event->ignore(); + } +} + +void AbstractFormEditorTool::dragLeaveEvent(QGraphicsSceneDragDropEvent * /* event */) +{ + Q_ASSERT(false); +} + +void AbstractFormEditorTool::dragMoveEvent(QGraphicsSceneDragDropEvent * /* event */) +{ + Q_ASSERT(false); +} + + +} diff --git a/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.h b/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.h new file mode 100644 index 00000000000..c2e387c696f --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.h @@ -0,0 +1,103 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef ABSTRACTFORMEDITORTOOL_H +#define ABSTRACTFORMEDITORTOOL_H + +#include "formeditoritem.h" + +class QGraphicsItem; + +namespace QmlDesigner { + +class FormEditorView; + +class AbstractFormEditorTool +{ + +public: + AbstractFormEditorTool(FormEditorView* view); + + virtual ~AbstractFormEditorTool(); + + virtual void mousePressEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event) = 0; + virtual void mouseMoveEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event) = 0; + virtual void mouseReleaseEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event) = 0; + virtual void mouseDoubleClickEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event) = 0; + + virtual void hoverMoveEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event) = 0; + + virtual void dropEvent(QGraphicsSceneDragDropEvent * event); + virtual void dragEnterEvent(QGraphicsSceneDragDropEvent * event); + virtual void dragLeaveEvent(QGraphicsSceneDragDropEvent * event); + virtual void dragMoveEvent(QGraphicsSceneDragDropEvent * event); + + virtual void keyPressEvent(QKeyEvent *event) = 0; + virtual void keyReleaseEvent(QKeyEvent *keyEvent) = 0; + + virtual void itemsAboutToRemoved(const QList<FormEditorItem*> &itemList) = 0; + // virtual QVariant itemChange(QList<QGraphicsItem*> itemList, +// QGraphicsItem::GraphicsItemChange change, +// const QVariant &value ) = 0; +// virtual void update() = 0; + virtual void clear() = 0; + + virtual void formEditorItemsChanged(const QList<FormEditorItem*> &itemList) = 0; + + void setItems(const QList<FormEditorItem*> &itemList); + QList<FormEditorItem*> items() const; + + static QGraphicsItem* topMovableGraphicsItem(const QList<QGraphicsItem*> &itemList); + static FormEditorItem* topMovableFormEditorItem(const QList<QGraphicsItem*> &itemList); + bool topItemIsMovable(const QList<QGraphicsItem*> &itemList); + bool topSelectedItemIsMovable(const QList<QGraphicsItem*> &itemList); + bool topItemIsResizeHandle(const QList<QGraphicsItem*> &itemList); + + static FormEditorItem* topFormEditorItem(const QList<QGraphicsItem*> &itemList); + static FormEditorItem* topFormEditorItemWithRootItem(const QList<QGraphicsItem*> &itemList); +protected: + + virtual void selectedItemsChanged(const QList<FormEditorItem*> &itemList) = 0; + + + FormEditorView *view() const; + FormEditorScene* scene() const; +private: + FormEditorView *m_view; + QList<FormEditorItem*> m_itemList; +}; + +} + +#endif // ABSTRACTFORMEDITORTOOL_H diff --git a/src/plugins/qmldesigner/components/formeditor/anchor.png b/src/plugins/qmldesigner/components/formeditor/anchor.png new file mode 100644 index 0000000000000000000000000000000000000000..fa59038318e7747072188234147d4d122061a731 GIT binary patch literal 415 zcmeAS@N?(olHy`uVBq!ia0vp^d?3uh1|;P@bT0xa#^NA%Cx&(BWL^R}Y)RhkE<hRx zemd{22U45`9+AZi4BWyX%*Zfnjs#GUy~NYkmHj3=KfA2yezU2&fu_iNx;TbtoZs7L z?|nEx<Y@ls)7+Yz9MgOn{rF{KABh)i%P+YUJLfOYQT+!(;qC_;;*~i3Z#iUqUgprw z=YOj8&&3uMJuX)dUXFj;iqBi#VZLy%Xy=~j_1F7j?Oqxkd;C%JP2!Kw3QNBT1lDA` zh<Hx2`SYuA^Ua<qL6Z#2co+m&8c%Fm)38rtX_(i_(Dy5AfBskMT=asI!7b>N@wuPJ z_icFpdy3(=oLSEp4S5c4(|LWNa85PDjvL`6vESQ*tUND&DcupFv+k44@l87Z0vBIv zDK2u?+<PZ)`^g%+`Mmqo3Xa{_ntOZl-GbZOa$~QnD!Q;UXmmaD<tRLD`JLVR_uZEz zRU&N<cP`7^wJftLH1vIoz~0PRdopJ!^*xq+{)hG7A@$-nbx(q~FaHb*T2EI$mvv4F FO#pL0tGoaJ literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/formeditor/anchorcontroller.cpp b/src/plugins/qmldesigner/components/formeditor/anchorcontroller.cpp new file mode 100644 index 00000000000..e59712216c0 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/anchorcontroller.cpp @@ -0,0 +1,537 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "anchorcontroller.h" + +#include "formeditoritem.h" +#include "layeritem.h" +#include "formeditorscene.h" +#include "anchorhandleitem.h" +#include <QtDebug> +#include <cmath> +namespace QmlDesigner { + +AnchorControllerData::AnchorControllerData(LayerItem *layerItem, FormEditorItem *formEditorItem) + : layerItem(layerItem), + formEditorItem(formEditorItem), + topItem(0), + leftItem(0), + rightItem(0), + bottomItem(0) +{ +} + +AnchorControllerData::AnchorControllerData(const AnchorControllerData &other) + : layerItem(other.layerItem), + formEditorItem(other.formEditorItem), + topItem(other.topItem), + leftItem(other.leftItem), + rightItem(other.rightItem), + bottomItem(other.bottomItem) +{ +} + +AnchorControllerData::~AnchorControllerData() +{ + if (layerItem) { + layerItem->scene()->removeItem(topItem); + layerItem->scene()->removeItem(leftItem); + layerItem->scene()->removeItem(rightItem); + layerItem->scene()->removeItem(bottomItem); + } +} + + +AnchorController::AnchorController() + : m_data(new AnchorControllerData(0, 0)) +{ + +} + +AnchorController::AnchorController(const QSharedPointer<AnchorControllerData> &data) + : m_data(data) +{ + +} + +AnchorController::AnchorController(LayerItem *layerItem, FormEditorItem *formEditorItem) + : m_data(new AnchorControllerData(layerItem, formEditorItem)) +{ + m_data->topItem = new AnchorHandleItem(layerItem, *this); + m_data->topItem->setZValue(400); + m_data->topItem->setToolTip(m_data->topItem->toolTipString()); + + m_data->leftItem = new AnchorHandleItem(layerItem, *this); + m_data->leftItem->setZValue(400); + m_data->leftItem->setToolTip(m_data->leftItem->toolTipString()); + + m_data->rightItem = new AnchorHandleItem(layerItem, *this); + m_data->rightItem->setZValue(400); + m_data->rightItem->setToolTip(m_data->rightItem->toolTipString()); + + m_data->bottomItem = new AnchorHandleItem(layerItem, *this); + m_data->bottomItem->setZValue(400); + m_data->bottomItem->setToolTip(m_data->bottomItem->toolTipString()); + + m_data->sceneTransform = formEditorItem->sceneTransform(); + + updatePosition(); +} + + +bool AnchorController::isValid() const +{ + return m_data->formEditorItem != 0; +} + +void AnchorController::show() +{ + m_data->topItem->show(); + m_data->leftItem->show(); + m_data->rightItem->show(); + m_data->bottomItem->show(); +} + + + +void AnchorController::hide() +{ + m_data->topItem->hide(); + m_data->leftItem->hide(); + m_data->rightItem->hide(); + m_data->bottomItem->hide(); +} + + +static QPointF topCenter(const QRectF &rect) +{ + return QPointF(rect.center().x(), rect.top()); +} + +static QPointF leftCenter(const QRectF &rect) +{ + return QPointF(rect.left(), rect.center().y()); +} + +static QPointF rightCenter(const QRectF &rect) +{ + return QPointF(rect.right(), rect.center().y()); +} + +static QPointF bottomCenter(const QRectF &rect) +{ + return QPointF(rect.center().x(), rect.bottom()); +} + +static QPainterPath curveToPath(const QPointF &firstPoint, + const QPointF &secondPoint, + const QPointF &thirdPoint, + const QPointF &fourthPoint) +{ + QPainterPath path; + path.moveTo(firstPoint); + path.cubicTo(secondPoint, thirdPoint, fourthPoint); + + return path; +} + +static QPointF anchorPoint(const QRectF &boundingRect, AnchorLine::Type anchorLine, double baseOffset, double innerOffset = 0.0) +{ + switch(anchorLine) { + case AnchorLine::Top : return topCenter(boundingRect) + QPointF(baseOffset, innerOffset); + case AnchorLine::Bottom : return bottomCenter(boundingRect) - QPointF(baseOffset, innerOffset); + case AnchorLine::Left : return leftCenter(boundingRect) + QPointF(innerOffset, baseOffset); + case AnchorLine::Right : return rightCenter(boundingRect) - QPointF(innerOffset, baseOffset); + default: return QPointF(); + } + + return QPointF(); +} + + +static QPainterPath createArrowPath(QPointF arrowCenter, double arrowDegrees) +{ + QRectF arrowRect(0.0, 0.0, 16., 16.); + arrowRect.moveCenter(arrowCenter); + QPainterPath arrowPath; + + + arrowPath.moveTo(arrowCenter); + + arrowPath.arcTo(arrowRect, arrowDegrees + 180 - 20, 40.); + + return arrowPath; +} + +AnchorHandlePathData AnchorController::createPainterPathForAnchor(const QRectF &boundingRect, + AnchorLine::Type anchorLine, + const QPointF &targetPoint) const +{ + + + QPointF firstPointInLayerSpace(m_data->sceneTransform.map(anchorPoint(boundingRect, anchorLine, 0.0, 5.0))); + + QPointF topLeftBoundingBoxInLayerSpace(m_data->sceneTransform.map(boundingRect.topLeft())); + QPointF bottomLeftBoundingBoxInLayerSpace(m_data->sceneTransform.map(boundingRect.bottomLeft())); + QPointF topRightBoundingBoxInLayerSpace(m_data->sceneTransform.map(boundingRect.topRight())); + QPointF bottomRightBoundingBoxInLayerSpace(m_data->sceneTransform.map(boundingRect.bottomRight())); + + AnchorLine::Type secondAnchorLine(AnchorLine::Invalid); + + QPointF secondPointInLayerSpace(targetPoint); + if (targetPoint.isNull()) { + AnchorLine targetAnchorLine(m_data->formEditorItem->qmlItemNode().anchors().instanceAnchor(anchorLine)); + secondAnchorLine = targetAnchorLine.type(); + FormEditorItem *targetItem = m_data->formEditorItem->scene()->itemForQmlItemNode(targetAnchorLine.qmlItemNode());; + bool secondItemIsParent = m_data->formEditorItem->parentItem() == targetItem; + + if (secondItemIsParent) + secondPointInLayerSpace = (targetItem->mapToItem(m_data->layerItem.data(), + anchorPoint(targetItem->qmlItemNode().instanceBoundingRect(), targetAnchorLine.type(), 0.0))); + else + + secondPointInLayerSpace = (targetItem->mapToItem(m_data->layerItem.data(), + anchorPoint(targetItem->qmlItemNode().instanceBoundingRect(), targetAnchorLine.type(), 0.0))); + } + + QPointF firstControlPointInLayerSpace = (3. * firstPointInLayerSpace + 3 * secondPointInLayerSpace) / 6.; + QPointF secondControlPointInLayerSpace = (3 * firstPointInLayerSpace + 3. * secondPointInLayerSpace) / 6.; + + bool showAnchorLine(true); + switch (anchorLine) { + case AnchorLine::Top : + case AnchorLine::Bottom : + firstControlPointInLayerSpace.rx() = firstPointInLayerSpace.x(); + if (qAbs(secondPointInLayerSpace.y() - firstPointInLayerSpace.y()) < 18.0) + showAnchorLine = false; + if (qAbs(secondControlPointInLayerSpace.y() - secondPointInLayerSpace.y()) < 20.0 && + qAbs(secondControlPointInLayerSpace.x() - secondPointInLayerSpace.x()) > 20.0) { + firstControlPointInLayerSpace.ry() = firstPointInLayerSpace.y() + ((firstControlPointInLayerSpace.y() - firstPointInLayerSpace.y() > 0) ? 20 : -20); + } + break; + case AnchorLine::Left : + case AnchorLine::Right : + firstControlPointInLayerSpace.ry() = firstPointInLayerSpace.y(); + if (qAbs(secondPointInLayerSpace.x() - firstPointInLayerSpace.x()) < 18.0) + showAnchorLine = false; + if (qAbs(secondControlPointInLayerSpace.x() - secondPointInLayerSpace.x()) < 20.0 && + qAbs(secondControlPointInLayerSpace.y() - secondPointInLayerSpace.y()) > 20.0) { + firstControlPointInLayerSpace.rx() = firstPointInLayerSpace.x() + ((firstControlPointInLayerSpace.x() - firstPointInLayerSpace.x() > 0) ? 20 : -20); + } + break; + default: break; + } + + switch(secondAnchorLine) { + case AnchorLine::Top : + case AnchorLine::Bottom : + secondControlPointInLayerSpace.rx() = secondPointInLayerSpace.x(); + if (qAbs(secondControlPointInLayerSpace.y() - secondPointInLayerSpace.y()) < 20.0 && + qAbs(secondControlPointInLayerSpace.x() - secondPointInLayerSpace.x()) > 20.0) { + secondControlPointInLayerSpace.ry() = secondPointInLayerSpace.y() + ((secondControlPointInLayerSpace.y() - secondPointInLayerSpace.y() < 0) ? 20 : -20); + } + break; + case AnchorLine::Left : + case AnchorLine::Right : + secondControlPointInLayerSpace.ry() = secondPointInLayerSpace.y(); + if (qAbs(secondControlPointInLayerSpace.x() - secondPointInLayerSpace.x()) < 20.0 && + qAbs(secondControlPointInLayerSpace.y() - secondPointInLayerSpace.y()) > 20.0) { + secondControlPointInLayerSpace.rx() = secondPointInLayerSpace.x() + ((secondControlPointInLayerSpace.x() - secondPointInLayerSpace.x() < 0) ? 20 : -20); + } + break; + default: break; + } + + QPainterPath anchorLinePath; + anchorLinePath.setFillRule(Qt::WindingFill); + + QRectF baseRect(0.0, 0.0, 5., 5.); + baseRect.moveCenter(firstPointInLayerSpace); + QPainterPath basePath; + basePath.addRoundedRect(baseRect, 6., 6.); + anchorLinePath = anchorLinePath.united(basePath); + + QRectF baseLineRect; + switch (anchorLine) { + case AnchorLine::Left : { + baseLineRect = QRectF(topLeftBoundingBoxInLayerSpace, bottomLeftBoundingBoxInLayerSpace); + baseLineRect.setWidth(3); + } + break; + case AnchorLine::Top : { + baseLineRect = QRectF(topLeftBoundingBoxInLayerSpace, topRightBoundingBoxInLayerSpace); + baseLineRect.setHeight(3); + } + break; + case AnchorLine::Right : { + baseLineRect = QRectF(topRightBoundingBoxInLayerSpace, bottomRightBoundingBoxInLayerSpace); + baseLineRect.adjust(-3, 0, 0, 0); + } + break; + case AnchorLine::Bottom : { + baseLineRect = QRectF(bottomLeftBoundingBoxInLayerSpace, bottomRightBoundingBoxInLayerSpace); + baseLineRect.adjust(0, -3, 0, 0); + } + break; + default: break; + } + + if (!baseLineRect.isEmpty()) { + + QPainterPath baseLinePath; + baseLinePath.addRoundedRect(baseLineRect, 1., 1.); + anchorLinePath = anchorLinePath.united(baseLinePath); + } + + QPainterPath arrowPath; + arrowPath.setFillRule(Qt::WindingFill); + + + + if (showAnchorLine) { + QPainterPath curvePath(curveToPath(firstPointInLayerSpace, + firstControlPointInLayerSpace, + secondControlPointInLayerSpace, + secondPointInLayerSpace)); + + double arrowDegrees = curvePath.angleAtPercent(curvePath.percentAtLength(curvePath.length() - 2.5)); + + + + QPainterPathStroker arrowPathStroker; + arrowPathStroker.setWidth(2.0); + arrowPathStroker.setCapStyle(Qt::RoundCap); + + arrowPath = arrowPath.united(arrowPathStroker.createStroke(curvePath)); + + + + + + QRectF arrowCutRect(0.0, 0.0, 8., 8.); + arrowCutRect.moveCenter(secondPointInLayerSpace); + QPainterPath arrowCutPath; + arrowCutPath.addRect(arrowCutRect); + arrowPath = arrowPath.subtracted(arrowCutPath); + + arrowPath = arrowPath.united(createArrowPath(secondPointInLayerSpace, arrowDegrees)); + } + + AnchorHandlePathData pathData; + pathData.arrowPath = arrowPath; + pathData.sourceAnchorLinePath = anchorLinePath; + pathData.beginArrowPoint = firstPointInLayerSpace; + pathData.endArrowPoint = secondPointInLayerSpace; + + pathData.targetAnchorLinePath = createTargetAnchorLinePath(anchorLine); + pathData.targetNamePath = createTargetNamePathPath(anchorLine); + + return pathData; +} + +QPainterPath AnchorController::createTargetNamePathPath(AnchorLine::Type anchorLine) const +{ + QPainterPath path; + QmlAnchors anchors(formEditorItem()->qmlItemNode().anchors()); + if (anchors.instanceHasAnchor(anchorLine)) { + AnchorLine targetAnchorLine(anchors.instanceAnchor(anchorLine)); + + FormEditorItem *targetItem = formEditorItem()->scene()->itemForQmlItemNode(targetAnchorLine.qmlItemNode()); + QRectF boundingRect(targetItem->qmlItemNode().instanceBoundingRect()); + + QTransform sceneTransform(targetItem->qmlItemNode().instanceSceneTransform()); + + QPointF centerBoundingBoxInLayerSpace(sceneTransform.map(boundingRect.center())); + + QFont font; + font.setPixelSize(24); + QString nameString(QString("%1 (%2)").arg(targetAnchorLine.qmlItemNode().simplfiedTypeName()).arg(targetAnchorLine.qmlItemNode().id())); + path.addText(0., -4., font, nameString); + //path.translate(centerBoundingBoxInLayerSpace - path.qmlItemNode().instanceBoundingRect().center()); + + } + + return path; +} + +QPainterPath AnchorController::createTargetAnchorLinePath(AnchorLine::Type anchorLine) const +{ + QPainterPath path; + QmlAnchors anchors(formEditorItem()->qmlItemNode().anchors()); + if (anchors.instanceHasAnchor(anchorLine)) { + AnchorLine targetAnchorLine(anchors.instanceAnchor(anchorLine)); + + FormEditorItem *targetItem = formEditorItem()->scene()->itemForQmlItemNode(targetAnchorLine.qmlItemNode()); + QRectF boundingRect(targetItem->qmlItemNode().instanceBoundingRect()); + + QTransform sceneTransform(targetItem->qmlItemNode().instanceSceneTransform()); + + QPointF topLeftBoundingBoxInLayerSpace(sceneTransform.map(boundingRect.topLeft())); + QPointF bottomLeftBoundingBoxInLayerSpace(sceneTransform.map(boundingRect.bottomLeft())); + QPointF topRightBoundingBoxInLayerSpace(sceneTransform.map(boundingRect.topRight())); + QPointF bottomRightBoundingBoxInLayerSpace(sceneTransform.map(boundingRect.bottomRight())); + + + switch(targetAnchorLine.type()) { + case AnchorLine::Top : { + path.moveTo(topLeftBoundingBoxInLayerSpace); + path.lineTo(topRightBoundingBoxInLayerSpace); + } + break; + case AnchorLine::Bottom : { + path.moveTo(bottomLeftBoundingBoxInLayerSpace); + path.lineTo(bottomRightBoundingBoxInLayerSpace); + } + break; + case AnchorLine::Left : { + path.moveTo(topLeftBoundingBoxInLayerSpace); + path.lineTo(bottomLeftBoundingBoxInLayerSpace); + } + break; + case AnchorLine::Right : { + path.moveTo(topRightBoundingBoxInLayerSpace); + path.lineTo(bottomRightBoundingBoxInLayerSpace); + } + break; + default: break; + } + + QPainterPathStroker pathStroker; + pathStroker.setWidth(20.0); + pathStroker.setCapStyle(Qt::RoundCap); + path = pathStroker.createStroke(path); + + + } + + return path; +} + +void AnchorController::updatePosition() +{ + QRectF boundingRect = m_data->formEditorItem->qmlItemNode().instanceBoundingRect(); + QPointF beginPoint; + QPointF endPoint; + QmlAnchors anchors(m_data->formEditorItem->qmlItemNode().anchors()); + m_data->sceneTransform = m_data->formEditorItem->sceneTransform(); + + if (anchors.instanceHasAnchor(AnchorLine::Top)) + m_data->topItem->setHandlePath(createPainterPathForAnchor(boundingRect, AnchorLine::Top)); + else + m_data->topItem->setHandlePath(AnchorHandlePathData()); + + if (anchors.instanceHasAnchor(AnchorLine::Bottom)) + m_data->bottomItem->setHandlePath(createPainterPathForAnchor(boundingRect, AnchorLine::Bottom)); + else + m_data->bottomItem->setHandlePath(AnchorHandlePathData()); + + if (anchors.instanceHasAnchor(AnchorLine::Right)) + m_data->rightItem->setHandlePath(createPainterPathForAnchor(boundingRect, AnchorLine::Right)); + else + m_data->rightItem->setHandlePath(AnchorHandlePathData()); + + if (anchors.instanceHasAnchor(AnchorLine::Left)) + m_data->leftItem->setHandlePath(createPainterPathForAnchor(boundingRect, AnchorLine::Left)); + else + m_data->leftItem->setHandlePath(AnchorHandlePathData()); +} + + +FormEditorItem* AnchorController::formEditorItem() const +{ + return m_data->formEditorItem; +} + +QWeakPointer<AnchorControllerData> AnchorController::weakPointer() const +{ + return m_data; +} + + +bool AnchorController::isTopHandle(const AnchorHandleItem *handle) const +{ + return handle == m_data->topItem; +} + +bool AnchorController::isLeftHandle(const AnchorHandleItem *handle) const +{ + return handle == m_data->leftItem; +} + +bool AnchorController::isRightHandle(const AnchorHandleItem *handle) const +{ + return handle == m_data->rightItem; +} + +bool AnchorController::isBottomHandle(const AnchorHandleItem *handle) const +{ + return handle == m_data->bottomItem; +} + +void AnchorController::updateTargetPoint(AnchorLine::Type anchorLine, const QPointF &targetPoint) +{ + QRectF boundingRect = m_data->formEditorItem->qmlItemNode().instanceBoundingRect(); + + switch(anchorLine) { + case AnchorLine::Top : + m_data->topItem->setHandlePath(createPainterPathForAnchor(boundingRect, anchorLine, targetPoint)); break; + case AnchorLine::Bottom : + m_data->bottomItem->setHandlePath(createPainterPathForAnchor(boundingRect, anchorLine, targetPoint)); break; + case AnchorLine::Left : + m_data->leftItem->setHandlePath(createPainterPathForAnchor(boundingRect, anchorLine, targetPoint)); break; + case AnchorLine::Right : + m_data->rightItem->setHandlePath(createPainterPathForAnchor(boundingRect, anchorLine, targetPoint)); break; + default: break; + } +} + +void AnchorController::highlight(AnchorLine::Type anchorLine) +{ + switch(anchorLine) { + case AnchorLine::Top : + m_data->topItem->setHighlighted(true); break; + case AnchorLine::Bottom : + m_data->bottomItem->setHighlighted(true); break; + case AnchorLine::Left : + m_data->leftItem->setHighlighted(true); break; + case AnchorLine::Right : + m_data->rightItem->setHighlighted(true); break; + default: break; + } +} + +void AnchorController::clearHighlight() +{ + m_data->topItem->setHighlighted(false); + m_data->leftItem->setHighlighted(false); + m_data->rightItem->setHighlighted(false); + m_data->bottomItem->setHighlighted(false); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/formeditor/anchorcontroller.h b/src/plugins/qmldesigner/components/formeditor/anchorcontroller.h new file mode 100644 index 00000000000..4d693dd4b03 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/anchorcontroller.h @@ -0,0 +1,117 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef ANCHORCONTROLLER_H +#define ANCHORCONTROLLER_H + +#include <QSharedPointer> +#include <QPainterPath> +#include <QPair> +#include <QTransform> +#include <qmlanchors.h> + +namespace QmlDesigner { + +struct AnchorHandlePathData +{ + QPainterPath sourceAnchorLinePath; + QPainterPath targetAnchorLinePath; + QPainterPath arrowPath; + QPainterPath targetNamePath; + QPointF beginArrowPoint; + QPointF endArrowPoint; +}; + +class FormEditorItem; +class LayerItem; +class AnchorHandleItem; + +class AnchorControllerData +{ +public: + AnchorControllerData(LayerItem *layerItem, + FormEditorItem *formEditorItem); + AnchorControllerData(const AnchorControllerData &other); + ~AnchorControllerData(); + + QWeakPointer<LayerItem> layerItem; + FormEditorItem *formEditorItem; + + AnchorHandleItem *topItem; + AnchorHandleItem *leftItem; + AnchorHandleItem *rightItem; + AnchorHandleItem *bottomItem; + + QTransform sceneTransform; +}; + + +class AnchorController +{ + public: + AnchorController(); + AnchorController(LayerItem *layerItem, FormEditorItem *formEditorItem); + AnchorController(const QSharedPointer<AnchorControllerData> &data); + + + void show(); + void hide(); + + void updatePosition(); + + bool isValid() const; + + QWeakPointer<AnchorControllerData> weakPointer() const; + + + FormEditorItem *formEditorItem() const; + + bool isTopHandle(const AnchorHandleItem *handle) const; + bool isLeftHandle(const AnchorHandleItem *handle) const; + bool isRightHandle(const AnchorHandleItem *handle) const; + bool isBottomHandle(const AnchorHandleItem *handle) const; + + void updateTargetPoint(AnchorLine::Type anchorLine, const QPointF &targetPoint); + + void clearHighlight(); + void highlight(AnchorLine::Type anchorLine); + +private: //functions + AnchorHandlePathData createPainterPathForAnchor(const QRectF &boundingRect, + AnchorLine::Type anchorLine, + const QPointF &targetPoint = QPointF()) const; + QPainterPath createTargetAnchorLinePath(AnchorLine::Type anchorLine) const; + QPainterPath createTargetNamePathPath(AnchorLine::Type anchorLine) const; +private: + QSharedPointer<AnchorControllerData> m_data; +}; + +} // namespace QmlDesigner + +#endif // ANCHORCONTROLLER_H diff --git a/src/plugins/qmldesigner/components/formeditor/anchorhandleitem.cpp b/src/plugins/qmldesigner/components/formeditor/anchorhandleitem.cpp new file mode 100644 index 00000000000..95939c6e0f7 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/anchorhandleitem.cpp @@ -0,0 +1,212 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "anchorhandleitem.h" + +#include <formeditoritem.h> +#include <QPen> +#include <QPainterPathStroker> +#include <cmath> +#include <QtDebug> + +namespace QmlDesigner { + +AnchorHandleItem::AnchorHandleItem(QGraphicsItem *parent, const AnchorController &anchorController) + : QGraphicsItemGroup(parent), + m_anchorControllerData(anchorController.weakPointer()), + m_sourceAnchorLinePathItem(new QGraphicsPathItem(this)), + m_arrowPathItem(new QGraphicsPathItem(this)), + m_targetAnchorLinePathItem(new QGraphicsPathItem(this)), + m_targetNamePathItem(new QGraphicsPathItem(this)) +{ + addToGroup(m_sourceAnchorLinePathItem); + addToGroup(m_arrowPathItem); + addToGroup(m_targetAnchorLinePathItem); + addToGroup(m_targetNamePathItem); + + setFlag(QGraphicsItem::ItemIsMovable, true); +} + +AnchorLine::Type AnchorHandleItem::sourceAnchorLine() const +{ + if (isTopHandle()) + return AnchorLine::Top; + if (isBottomHandle()) + return AnchorLine::Bottom; + if (isLeftHandle()) + return AnchorLine::Left; + if (isRightHandle()) + return AnchorLine::Right; + + return AnchorLine::Invalid; +} + +AnchorLine AnchorHandleItem::targetAnchorLine() const +{ + QmlAnchors anchors(anchorController().formEditorItem()->qmlItemNode().anchors()); + + if (isTopHandle()) + return anchors.instanceAnchor(AnchorLine::Top); + if (isBottomHandle()) + return anchors.instanceAnchor(AnchorLine::Bottom); + if (isLeftHandle()) + return anchors.instanceAnchor(AnchorLine::Left); + if (isRightHandle()) + return anchors.instanceAnchor(AnchorLine::Right); + + return AnchorLine(); +} + +static QString anchorLineToString(AnchorLine::Type anchorLineType) +{ + switch(anchorLineType) { + case AnchorLine::Top: return "Top"; + case AnchorLine::Bottom: return "Bottom"; + case AnchorLine::Left: return "Left"; + case AnchorLine::Right: return "Right"; + default: break; + } + + return QString(); + +} + +QString AnchorHandleItem::toolTipString() const +{ + QString templateString("<p>Anchor Handle</p><p>%1</p><p>%2</p>"); + QmlItemNode fromNode(anchorController().formEditorItem()->qmlItemNode()); + QString fromString(QString("%3: %1(%2)").arg(fromNode.simplfiedTypeName(), fromNode.id(), anchorLineToString(sourceAnchorLine()))); + + AnchorLine toAnchorLine(targetAnchorLine()); + QmlItemNode toNode(toAnchorLine.qmlItemNode()); + QString toString; + if (toNode.isValid()) + toString = QString("%3: %1(%2)").arg(toNode.simplfiedTypeName(), toNode.id(), anchorLineToString(toAnchorLine.type())); + + return templateString.arg(fromString).arg(toString); +} + +void AnchorHandleItem::setHandlePath(const AnchorHandlePathData &pathData) +{ + m_beginArrowPoint = pathData.beginArrowPoint; + m_endArrowPoint = pathData.endArrowPoint; + m_arrowPathItem->setPath(pathData.arrowPath); + m_sourceAnchorLinePathItem->setPath(pathData.sourceAnchorLinePath); + m_targetAnchorLinePathItem->setPath(pathData.targetAnchorLinePath); + m_targetNamePathItem->setPath(pathData.targetNamePath); + + setHighlighted(false); +} + +AnchorController AnchorHandleItem::anchorController() const +{ + Q_ASSERT(!m_anchorControllerData.isNull()); + return AnchorController(m_anchorControllerData.toStrongRef()); +} + +AnchorHandleItem* AnchorHandleItem::fromGraphicsItem(QGraphicsItem *item) +{ + return qgraphicsitem_cast<AnchorHandleItem*>(item); +} + +bool AnchorHandleItem::isTopHandle() const +{ + return anchorController().isTopHandle(this); +} + +bool AnchorHandleItem::isLeftHandle() const +{ + return anchorController().isLeftHandle(this); +} + +bool AnchorHandleItem::isRightHandle() const +{ + return anchorController().isRightHandle(this); +} + +bool AnchorHandleItem::isBottomHandle() const +{ + return anchorController().isBottomHandle(this); +} + +AnchorLine::Type AnchorHandleItem::anchorType() const +{ + if (isTopHandle()) + return AnchorLine::Top; + + if (isBottomHandle()) + return AnchorLine::Bottom; + + if (isLeftHandle()) + return AnchorLine::Left; + + if (isRightHandle()) + return AnchorLine::Right; + + + return AnchorLine::Invalid; +} + +void AnchorHandleItem::setHighlighted(bool highlight) +{ + QLinearGradient gradient(m_beginArrowPoint, m_endArrowPoint); + gradient.setCoordinateMode(QGradient::LogicalMode); + m_arrowPathItem->setPen(QPen(QBrush(Qt::gray), 1.0, Qt::SolidLine, Qt::RoundCap, Qt::MiterJoin)); + m_targetAnchorLinePathItem->setPen(QColor(70, 0, 0, 90)); + m_targetAnchorLinePathItem->setBrush(QColor(255, 0, 0, 50)); + m_arrowPathItem->setPen(QPen(QBrush(Qt::gray), 1.0, Qt::SolidLine, Qt::RoundCap, Qt::MiterJoin)); + m_targetNamePathItem->setPen(QColor(0, 0, 255, 90)); + m_targetNamePathItem->setBrush(QColor(0, 0, 255, 50)); + + if (highlight) { + gradient.setColorAt(0.0, QColor(0, 0, 120, 255)); + gradient.setColorAt(1.0, QColor(120, 0, 0, 255)); + m_arrowPathItem->setBrush(gradient); + m_sourceAnchorLinePathItem->setPen(QColor(0, 0, 70, 255)); + m_sourceAnchorLinePathItem->setBrush(QColor(0, 0, 70, 255)); + m_targetAnchorLinePathItem->show(); + m_targetNamePathItem->show(); + + } else { + gradient.setColorAt(0.0, QColor(0, 0, 255, 255)); + gradient.setColorAt(1.0, QColor(255, 0, 0, 255)); + m_arrowPathItem->setBrush(gradient); + m_sourceAnchorLinePathItem->setPen(QColor(0, 0, 100, 255)); + m_sourceAnchorLinePathItem->setBrush(QColor(0, 0, 100, 255)); + m_targetAnchorLinePathItem->hide(); + m_targetNamePathItem->hide(); + } +} + +QPointF AnchorHandleItem::itemSpacePosition() const +{ + return parentItem()->mapToItem(anchorController().formEditorItem(), pos()); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/formeditor/anchorhandleitem.h b/src/plugins/qmldesigner/components/formeditor/anchorhandleitem.h new file mode 100644 index 00000000000..6d6bd057f7d --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/anchorhandleitem.h @@ -0,0 +1,91 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef ANCHORHANDLEITEM_H +#define ANCHORHANDLEITEM_H + +#include <QGraphicsItemGroup> + +#include "anchorcontroller.h" + +namespace QmlDesigner { + +class AnchorHandleItem : public QGraphicsItemGroup +{ +public: + enum + { + Type = 0xEAEC + }; + + + AnchorHandleItem(QGraphicsItem *parent, const AnchorController &anchorController); + + void setHandlePath(const AnchorHandlePathData &pathData); + + int type() const; + + AnchorController anchorController() const; + + static AnchorHandleItem* fromGraphicsItem(QGraphicsItem *item); + + + bool isTopHandle() const; + bool isLeftHandle() const; + bool isRightHandle() const; + bool isBottomHandle() const; + + QPointF itemSpacePosition() const; + + AnchorLine::Type anchorType() const; + + QString toolTipString() const; + AnchorLine targetAnchorLine() const; + AnchorLine::Type sourceAnchorLine() const; + + void setHighlighted(bool highlight); + +private: + QWeakPointer<AnchorControllerData> m_anchorControllerData; + QGraphicsPathItem *m_sourceAnchorLinePathItem; + QGraphicsPathItem *m_arrowPathItem; + QGraphicsPathItem *m_targetAnchorLinePathItem; + QGraphicsPathItem *m_targetNamePathItem; + QPointF m_beginArrowPoint; + QPointF m_endArrowPoint; +}; + +inline int AnchorHandleItem::type() const +{ + return Type; +} + +} // namespace QmlDesigner + +#endif // ANCHORHANDLEITEM_H diff --git a/src/plugins/qmldesigner/components/formeditor/anchorindicator.cpp b/src/plugins/qmldesigner/components/formeditor/anchorindicator.cpp new file mode 100644 index 00000000000..78762c23f7f --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/anchorindicator.cpp @@ -0,0 +1,116 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "anchorindicator.h" + +#include <QSet> + +namespace QmlDesigner { + +AnchorIndicator::AnchorIndicator(LayerItem *layerItem) + : m_layerItem(layerItem) +{ + Q_ASSERT(layerItem); +} + +AnchorIndicator::~AnchorIndicator() +{ + m_itemControllerHash.clear(); +} + +void AnchorIndicator::show() +{ + QHashIterator<FormEditorItem*, AnchorController> itemControllerIterator(m_itemControllerHash); + while (itemControllerIterator.hasNext()) { + AnchorController controller = itemControllerIterator.next().value(); + controller.show(); + } +} + + +void AnchorIndicator::hide() +{ + QHashIterator<FormEditorItem*, AnchorController> itemControllerIterator(m_itemControllerHash); + while (itemControllerIterator.hasNext()) { + AnchorController controller = itemControllerIterator.next().value(); + controller.hide(); + } +} + +void AnchorIndicator::clear() +{ + m_itemControllerHash.clear(); +} + +void AnchorIndicator::setItems(const QList<FormEditorItem*> &itemList) +{ + clear(); + + foreach (FormEditorItem *item, itemList) { + AnchorController controller(m_layerItem, item); + m_itemControllerHash.insert(item, controller); + } + + updateItems(itemList); +} + +void AnchorIndicator::updateItems(const QList<FormEditorItem*> &itemList) +{ + foreach (FormEditorItem *item, itemList) { + if (m_itemControllerHash.contains(item)) { + AnchorController controller(m_itemControllerHash.value(item)); + controller.updatePosition(); + } + } +} + +void AnchorIndicator::updateTargetPoint(FormEditorItem *item, AnchorLine::Type anchorLine, const QPointF &targetPoint) +{ + AnchorController controller(m_itemControllerHash.value(item)); + controller.updateTargetPoint(anchorLine, targetPoint); +} + +void AnchorIndicator::clearHighlight() +{ + QHashIterator<FormEditorItem*, AnchorController> itemControllerIterator(m_itemControllerHash); + while (itemControllerIterator.hasNext()) { + AnchorController controller = itemControllerIterator.next().value(); + controller.clearHighlight(); + } +} + +void AnchorIndicator::highlight(FormEditorItem *item, AnchorLine::Type anchorLine) +{ + if (m_itemControllerHash.contains(item)) { + AnchorController controller(m_itemControllerHash.value(item)); + controller.highlight(anchorLine); + } +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/formeditor/anchorindicator.h b/src/plugins/qmldesigner/components/formeditor/anchorindicator.h new file mode 100644 index 00000000000..c36ed04be80 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/anchorindicator.h @@ -0,0 +1,64 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef ANCHORINDICATOR_H +#define ANCHORINDICATOR_H + +#include "anchorcontroller.h" +#include <QList> +#include <QHash> + +namespace QmlDesigner { + +class AnchorIndicator +{ +public: + AnchorIndicator(LayerItem *layerItem); + ~AnchorIndicator(); + + void show(); + void hide(); + void clear(); + + void setItems(const QList<FormEditorItem*> &itemList); + + void updateItems(const QList<FormEditorItem*> &itemList); + void updateTargetPoint(FormEditorItem *item, AnchorLine::Type anchorLine, const QPointF &targetPoint); + + void clearHighlight(); + void highlight(FormEditorItem *item, AnchorLine::Type anchorLine); + +private: + QHash<FormEditorItem*, AnchorController> m_itemControllerHash; + LayerItem *m_layerItem; +}; + +} // namespace QmlDesigner + +#endif // ANCHORINDICATOR_H diff --git a/src/plugins/qmldesigner/components/formeditor/anchorlinecontroller.cpp b/src/plugins/qmldesigner/components/formeditor/anchorlinecontroller.cpp new file mode 100644 index 00000000000..6877ad1b0f9 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/anchorlinecontroller.cpp @@ -0,0 +1,236 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "anchorlinecontroller.h" + +#include "formeditoritem.h" +#include "layeritem.h" +#include <QGraphicsScene> + +#include "anchorlinehandleitem.h" + +namespace QmlDesigner { + + +AnchorLineControllerData::AnchorLineControllerData(LayerItem *layerItem, FormEditorItem *formEditorItem) + : layerItem(layerItem), + formEditorItem(formEditorItem), + topItem(0), + leftItem(0), + rightItem(0), + bottomItem(0) +{ +} + +AnchorLineControllerData::AnchorLineControllerData(const AnchorLineControllerData &other) + : layerItem(other.layerItem), + formEditorItem(other.formEditorItem), + topItem(other.topItem), + leftItem(other.leftItem), + rightItem(other.rightItem), + bottomItem(other.bottomItem) +{} + +AnchorLineControllerData::~AnchorLineControllerData() +{ + if (layerItem) { + layerItem->scene()->removeItem(topItem); + layerItem->scene()->removeItem(leftItem); + layerItem->scene()->removeItem(rightItem); + layerItem->scene()->removeItem(bottomItem); + } +} + + +AnchorLineController::AnchorLineController() + : m_data(new AnchorLineControllerData(0, 0)) +{ + +} + +AnchorLineController::AnchorLineController(const QSharedPointer<AnchorLineControllerData> &data) + : m_data(data) +{ + +} + +AnchorLineController::AnchorLineController(LayerItem *layerItem, FormEditorItem *formEditorItem) + : m_data(new AnchorLineControllerData(layerItem, formEditorItem)) +{ + m_data->topItem = new AnchorLineHandleItem(layerItem, *this); + m_data->topItem->setZValue(300); + + m_data->leftItem = new AnchorLineHandleItem(layerItem, *this); + m_data->leftItem->setZValue(300); + + m_data->rightItem = new AnchorLineHandleItem(layerItem, *this); + m_data->rightItem->setZValue(300); + + m_data->bottomItem = new AnchorLineHandleItem(layerItem, *this); + m_data->bottomItem->setZValue(300); + + updatePosition(); +} + + +bool AnchorLineController::isValid() const +{ + return m_data->formEditorItem != 0; +} + +void AnchorLineController::show(AnchorLine::Type anchorLineMask) +{ + if (anchorLineMask & AnchorLine::Top) + m_data->topItem->show(); + else + m_data->topItem->hide(); + + if (anchorLineMask & AnchorLine::Left) + m_data->leftItem->show(); + else + m_data->leftItem->hide(); + + if (anchorLineMask & AnchorLine::Right) + m_data->rightItem->show(); + else + m_data->rightItem->hide(); + + if (anchorLineMask & AnchorLine::Bottom) + m_data->bottomItem->show(); + else + m_data->bottomItem->hide(); +} + +void AnchorLineController::hide() +{ + m_data->topItem->hide(); + m_data->leftItem->hide(); + m_data->rightItem->hide(); + m_data->bottomItem->hide(); +} + +static QPainterPath rectToPath(const QRectF &rect) +{ + QPainterPath path; + path.addRoundedRect(rect, 4, 4); + + return path; +} + +void AnchorLineController::updatePosition() +{ + QRectF boundingRect = m_data->formEditorItem->qmlItemNode().instanceBoundingRect(); + + QRectF topBoundingRect(boundingRect); + QRectF leftBoundingRect(boundingRect); + QRectF bottomBoundingRect(boundingRect); + QRectF rightBoundingRect(boundingRect); + + + if (formEditorItem()->isContainer()) { + topBoundingRect.setBottom(boundingRect.top() + 6); + topBoundingRect.adjust(7, -5, -7, 0); + + leftBoundingRect.setRight(boundingRect.left() + 6); + leftBoundingRect.adjust(-5, 7, 0, -7); + + bottomBoundingRect.setTop(boundingRect.bottom() - 6); + bottomBoundingRect.adjust(7, 0, -7, 5); + + rightBoundingRect.setLeft(boundingRect.right() - 6); + rightBoundingRect.adjust(0, 7, 5, -7); + + } else { + double height = qMin(boundingRect.height() / 4., 10.); + double width = qMin(boundingRect.width() / 4., 10.); + + topBoundingRect.setHeight(height); + topBoundingRect.adjust(width, -4, -width, -1); + + leftBoundingRect.setWidth(width); + leftBoundingRect.adjust(-4, height, -1, -height); + + bottomBoundingRect.setTop(boundingRect.bottom() - height); + bottomBoundingRect.adjust(width, 1, -width, 4); + + rightBoundingRect.setLeft(boundingRect.right() - width); + rightBoundingRect.adjust(1, height, 4, -height); + } + + m_data->topItem->setHandlePath(m_data->formEditorItem->mapToItem(m_data->layerItem.data(), + rectToPath(topBoundingRect))); + m_data->leftItem->setHandlePath(m_data->formEditorItem->mapToItem(m_data->layerItem.data(), + rectToPath(leftBoundingRect))); + m_data->bottomItem->setHandlePath(m_data->formEditorItem->mapToItem(m_data->layerItem.data(), + rectToPath(bottomBoundingRect))); + m_data->rightItem->setHandlePath(m_data->formEditorItem->mapToItem(m_data->layerItem.data(), + rectToPath(rightBoundingRect))); +} + + +FormEditorItem* AnchorLineController::formEditorItem() const +{ + return m_data->formEditorItem; +} + +QWeakPointer<AnchorLineControllerData> AnchorLineController::weakPointer() const +{ + return m_data; +} + + +bool AnchorLineController::isTopHandle(const AnchorLineHandleItem *handle) const +{ + return handle == m_data->topItem; +} + +bool AnchorLineController::isLeftHandle(const AnchorLineHandleItem *handle) const +{ + return handle == m_data->leftItem; +} + +bool AnchorLineController::isRightHandle(const AnchorLineHandleItem *handle) const +{ + return handle == m_data->rightItem; +} + +bool AnchorLineController::isBottomHandle(const AnchorLineHandleItem *handle) const +{ + return handle == m_data->bottomItem; +} + +void AnchorLineController::clearHighlight() +{ + m_data->topItem->setHiglighted(false); + m_data->leftItem->setHiglighted(false); + m_data->rightItem->setHiglighted(false); + m_data->bottomItem->setHiglighted(false); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/formeditor/anchorlinecontroller.h b/src/plugins/qmldesigner/components/formeditor/anchorlinecontroller.h new file mode 100644 index 00000000000..98701705e00 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/anchorlinecontroller.h @@ -0,0 +1,93 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef ANCHORLINECONTROLLER_H +#define ANCHORLINECONTROLLER_H + +#include <QWeakPointer> +#include <QSharedPointer> +#include <qmlanchors.h> + +namespace QmlDesigner { + +class FormEditorItem; +class LayerItem; +class AnchorLineHandleItem; + +class AnchorLineControllerData +{ +public: + AnchorLineControllerData(LayerItem *layerItem, + FormEditorItem *formEditorItem); + AnchorLineControllerData(const AnchorLineControllerData &other); + ~AnchorLineControllerData(); + + QWeakPointer<LayerItem> layerItem; + FormEditorItem *formEditorItem; + + AnchorLineHandleItem *topItem; + AnchorLineHandleItem *leftItem; + AnchorLineHandleItem *rightItem; + AnchorLineHandleItem *bottomItem; +}; + + +class AnchorLineController +{ + public: + AnchorLineController(); + AnchorLineController(LayerItem *layerItem, FormEditorItem *formEditorItem); + AnchorLineController(const QSharedPointer<AnchorLineControllerData> &data); + + void show(AnchorLine::Type anchorLineMask); + void hide(); + + void updatePosition(); + + bool isValid() const; + + QWeakPointer<AnchorLineControllerData> weakPointer() const; + + + FormEditorItem *formEditorItem() const; + + bool isTopHandle(const AnchorLineHandleItem *handle) const; + bool isLeftHandle(const AnchorLineHandleItem *handle) const; + bool isRightHandle(const AnchorLineHandleItem *handle) const; + bool isBottomHandle(const AnchorLineHandleItem *handle) const; + + void clearHighlight(); + +private: + QSharedPointer<AnchorLineControllerData> m_data; +}; + +} // namespace QmlDesigner + +#endif // ANCHORLINECONTROLLER_H diff --git a/src/plugins/qmldesigner/components/formeditor/anchorlinehandleitem.cpp b/src/plugins/qmldesigner/components/formeditor/anchorlinehandleitem.cpp new file mode 100644 index 00000000000..e2aa10b67f4 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/anchorlinehandleitem.cpp @@ -0,0 +1,148 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "anchorlinehandleitem.h" + +#include <formeditoritem.h> +#include <QPen> +#include <cmath> + +namespace QmlDesigner { + +AnchorLineHandleItem::AnchorLineHandleItem(QGraphicsItem *parent, const AnchorLineController &anchorLineController) + : QGraphicsPathItem(parent), + m_anchorLineControllerData(anchorLineController.weakPointer()) +{ + setFlag(QGraphicsItem::ItemIsMovable, true); + setHiglighted(false); +} + +void AnchorLineHandleItem::setHandlePath(const QPainterPath & path) +{ + setPath(path); + update(); +} + +QRectF AnchorLineHandleItem::boundingRect() const +{ + return QGraphicsPathItem::boundingRect(); +} + +QPainterPath AnchorLineHandleItem::shape() const +{ + return QGraphicsPathItem::shape(); +} + +AnchorLineController AnchorLineHandleItem::anchorLineController() const +{ + Q_ASSERT(!m_anchorLineControllerData.isNull()); + return AnchorLineController(m_anchorLineControllerData.toStrongRef()); +} + +AnchorLine::Type AnchorLineHandleItem::anchorLine() const +{ + if (isTopHandle()) + return AnchorLine::Top; + + if (isLeftHandle()) + return AnchorLine::Left; + + if (isRightHandle()) + return AnchorLine::Right; + + if (isBottomHandle()) + return AnchorLine::Bottom; + + return AnchorLine::Invalid; +} + +AnchorLineHandleItem* AnchorLineHandleItem::fromGraphicsItem(QGraphicsItem *item) +{ + return qgraphicsitem_cast<AnchorLineHandleItem*>(item); +} + +bool AnchorLineHandleItem::isTopHandle() const +{ + return anchorLineController().isTopHandle(this); +} + +bool AnchorLineHandleItem::isLeftHandle() const +{ + return anchorLineController().isLeftHandle(this); +} + +bool AnchorLineHandleItem::isRightHandle() const +{ + return anchorLineController().isRightHandle(this); +} + +bool AnchorLineHandleItem::isBottomHandle() const +{ + return anchorLineController().isBottomHandle(this); +} + +AnchorLine::Type AnchorLineHandleItem::anchorLineType() const +{ + if (isTopHandle()) + return AnchorLine::Top; + + if (isBottomHandle()) + return AnchorLine::Bottom; + + if (isLeftHandle()) + return AnchorLine::Left; + + if (isRightHandle()) + return AnchorLine::Right; + + + return AnchorLine::Invalid; +} + +QPointF AnchorLineHandleItem::itemSpacePosition() const +{ + return parentItem()->mapToItem(anchorLineController().formEditorItem(), pos()); +} + +void AnchorLineHandleItem::setHiglighted(bool highlight) +{ + if (highlight) { + QPen pen; + pen.setColor(QColor(108, 141, 221)); + setPen(pen); + setBrush(QColor(108, 141, 221, 140)); + } else { + QPen pen; + pen.setColor(QColor(108, 141, 221)); + setPen(pen); + setBrush(QColor(108, 141, 221, 60)); + } +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/formeditor/anchorlinehandleitem.h b/src/plugins/qmldesigner/components/formeditor/anchorlinehandleitem.h new file mode 100644 index 00000000000..b35ec346ac2 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/anchorlinehandleitem.h @@ -0,0 +1,86 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef ANCHORLINEHANDLEITEM_H +#define ANCHORLINEHANDLEITEM_H + +#include <QGraphicsPathItem> + +#include "anchorlinecontroller.h" + +namespace QmlDesigner { + +class AnchorLineHandleItem : public QGraphicsPathItem +{ +public: + enum + { + Type = 0xEAEB + }; + + + AnchorLineHandleItem(QGraphicsItem *parent, const AnchorLineController &AnchorLineController); + + void setHandlePath(const QPainterPath & path); + + int type() const; + QRectF boundingRect() const; + QPainterPath shape() const; + + AnchorLineController anchorLineController() const; + AnchorLine::Type anchorLine() const; + + + static AnchorLineHandleItem* fromGraphicsItem(QGraphicsItem *item); + + + bool isTopHandle() const; + bool isLeftHandle() const; + bool isRightHandle() const; + bool isBottomHandle() const; + + QPointF itemSpacePosition() const; + + AnchorLine::Type anchorLineType() const; + + void setHiglighted(bool highlight); + + +private: + QWeakPointer<AnchorLineControllerData> m_anchorLineControllerData; +}; + +inline int AnchorLineHandleItem::type() const +{ + return Type; +} + +} // namespace QmlDesigner + +#endif // ANCHORLINEHANDLEITEM_H diff --git a/src/plugins/qmldesigner/components/formeditor/anchorlineindicator.cpp b/src/plugins/qmldesigner/components/formeditor/anchorlineindicator.cpp new file mode 100644 index 00000000000..a521faf216c --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/anchorlineindicator.cpp @@ -0,0 +1,131 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "anchorlineindicator.h" + +#include <QSet> + +namespace QmlDesigner { + +AnchorLineIndicator::AnchorLineIndicator(LayerItem *layerItem) + : m_layerItem(layerItem) +{ + Q_ASSERT(layerItem); +} + +AnchorLineIndicator::~AnchorLineIndicator() +{ + m_itemControllerHash.clear(); +} + +void AnchorLineIndicator::show(AnchorLine::Type anchorLineMask) +{ + QHashIterator<FormEditorItem*, AnchorLineController> itemControllerIterator(m_itemControllerHash); + while (itemControllerIterator.hasNext()) { + AnchorLineController controller = itemControllerIterator.next().value(); + controller.show(anchorLineMask); + } +} + +void AnchorLineIndicator::hide() +{ + QHashIterator<FormEditorItem*, AnchorLineController> itemControllerIterator(m_itemControllerHash); + while (itemControllerIterator.hasNext()) { + AnchorLineController controller = itemControllerIterator.next().value(); + controller.hide(); + } +} + +void AnchorLineIndicator::clear() +{ + m_itemControllerHash.clear(); +} + +void AnchorLineIndicator::setItem(FormEditorItem* item) +{ + if (!item) + return; + + QList<FormEditorItem*> itemList; + itemList.append(item); + + setItems(itemList); +} + +static bool equalLists(const QList<FormEditorItem*> &firstList, const QList<FormEditorItem*> &secondList) +{ + return firstList.toSet() == secondList.toSet(); +} + +void AnchorLineIndicator::setItems(const QList<FormEditorItem*> &itemList) +{ + if (equalLists(itemList, m_itemControllerHash.keys())) + return; + + clear(); + + foreach (FormEditorItem *item, itemList) { + AnchorLineController controller(m_layerItem, item); + m_itemControllerHash.insert(item, controller); + } + + show(AnchorLine::AllMask); +} + +void AnchorLineIndicator::updateItems(const QList<FormEditorItem*> &itemList) +{ + foreach (FormEditorItem *item, itemList) { + if (m_itemControllerHash.contains(item)) { + AnchorLineController controller(m_itemControllerHash.value(item)); + controller.updatePosition(); + } + } +} + +void AnchorLineIndicator::update() +{ + foreach (FormEditorItem *item, m_itemControllerHash.keys()) { + if (m_itemControllerHash.contains(item)) { + AnchorLineController controller(m_itemControllerHash.value(item)); + controller.updatePosition(); + } + } +} + +void AnchorLineIndicator::clearHighlight() +{ + foreach (FormEditorItem *item, m_itemControllerHash.keys()) { + if (m_itemControllerHash.contains(item)) { + AnchorLineController controller(m_itemControllerHash.value(item)); + controller.clearHighlight(); + } + } +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/formeditor/anchorlineindicator.h b/src/plugins/qmldesigner/components/formeditor/anchorlineindicator.h new file mode 100644 index 00000000000..2a4c9966fce --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/anchorlineindicator.h @@ -0,0 +1,69 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef ANCHORLINEINDICATOR_H +#define ANCHORLINEINDICATOR_H + +#include "layeritem.h" +#include "anchorlinecontroller.h" +#include <QHash> + +namespace QmlDesigner { + +class FormEditorItem; + +class AnchorLineIndicator +{ +public: + AnchorLineIndicator(LayerItem *layerItem); + ~AnchorLineIndicator(); + + void show(AnchorLine::Type anchorLineMask); + + void hide(); + + void clear(); + + void update(); + + void setItems(const QList<FormEditorItem*> &itemList); + void setItem(FormEditorItem* item); + void updateItems(const QList<FormEditorItem*> &itemList); + + void clearHighlight(); + +private: + QHash<FormEditorItem*, AnchorLineController> m_itemControllerHash; + + LayerItem *m_layerItem; +}; + +} // namespace QmlDesigner + +#endif // ANCHORLINEINDICATOR_H diff --git a/src/plugins/qmldesigner/components/formeditor/anchormanipulator.cpp b/src/plugins/qmldesigner/components/formeditor/anchormanipulator.cpp new file mode 100644 index 00000000000..1468ae1e5fd --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/anchormanipulator.cpp @@ -0,0 +1,144 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "anchormanipulator.h" + +#include "formeditoritem.h" +#include "formeditorscene.h" +#include "formeditorview.h" +#include <model.h> +#include <rewritertransaction.h> + +namespace QmlDesigner { + +AnchorManipulator::AnchorManipulator(FormEditorView *view) + : m_beginFormEditorItem(0), + m_beginAnchorLine(AnchorLine::Invalid), + m_view(view) +{ +} + +AnchorManipulator::~AnchorManipulator() +{ + clear(); +} + +void AnchorManipulator::begin(FormEditorItem *beginItem, AnchorLine::Type anchorLine) +{ + m_beginFormEditorItem = beginItem; + m_beginAnchorLine = anchorLine; +} + +static double offset(const QPointF &topLeft, const QPointF &bottomRight, AnchorLine::Type anchorLine) +{ + switch(anchorLine) { + case AnchorLine::Top : return topLeft.y(); + case AnchorLine::Left : return topLeft.x(); + case AnchorLine::Bottom : return bottomRight.y(); + case AnchorLine::Right : return bottomRight.x(); + default: break; + } + + return 0.0; +} + +void AnchorManipulator::setMargin(FormEditorItem *endItem, AnchorLine::Type endAnchorLine) +{ + QPointF beginItemTopLeft(m_beginFormEditorItem->mapToParent(m_beginFormEditorItem->qmlItemNode().instanceBoundingRect().topLeft())); + QPointF endItemTopLeft(m_beginFormEditorItem->parentItem()->mapFromItem(endItem, endItem->qmlItemNode().instanceBoundingRect().topLeft())); + + QPointF beginItemBottomRight(m_beginFormEditorItem->mapToParent(m_beginFormEditorItem->qmlItemNode().instanceBoundingRect().bottomRight())); + QPointF endItemBottomRight(m_beginFormEditorItem->parentItem()->mapFromItem(endItem, endItem->qmlItemNode().instanceBoundingRect().bottomRight())); + + QPointF topLeftAnchorOffset = beginItemTopLeft - endItemTopLeft; + QPointF bottomRightAnchorOffset = endItemBottomRight - beginItemBottomRight; + + + double anchorOffset = 0.0; + if (m_beginAnchorLine & (AnchorLine::Bottom | AnchorLine::Right)) { + anchorOffset = offset(endItemTopLeft, endItemBottomRight, endAnchorLine) - + offset(beginItemTopLeft, beginItemBottomRight, m_beginAnchorLine); + } else { + anchorOffset = offset(beginItemTopLeft, beginItemBottomRight, m_beginAnchorLine) - + offset(endItemTopLeft, endItemBottomRight, endAnchorLine); + } + + m_beginFormEditorItem->qmlItemNode().anchors().setMargin(m_beginAnchorLine, anchorOffset); +} +void AnchorManipulator::addAnchor(FormEditorItem *endItem, AnchorLine::Type endAnchorLine) +{ + RewriterTransaction m_rewriterTransaction = m_view->beginRewriterTransaction(); + setMargin(endItem, endAnchorLine); + + m_beginFormEditorItem->qmlItemNode().anchors().setAnchor(m_beginAnchorLine, + endItem->qmlItemNode(), + endAnchorLine); +} + +void AnchorManipulator::removeAnchor() +{ + RewriterTransaction transaction = m_view->beginRewriterTransaction(); + QmlAnchors anchors(m_beginFormEditorItem->qmlItemNode().anchors()); + if (anchors.instanceHasAnchor(m_beginAnchorLine)) { + anchors.removeAnchor(m_beginAnchorLine); + anchors.removeMargin(m_beginAnchorLine); + } +} + +void AnchorManipulator::clear() +{ + m_beginFormEditorItem = 0; + m_beginAnchorLine = AnchorLine::Invalid; +} + +bool AnchorManipulator::isActive() const +{ + return m_beginFormEditorItem && m_beginAnchorLine != AnchorLine::Invalid; +} + +AnchorLine::Type AnchorManipulator::beginAnchorLine() const +{ + return m_beginAnchorLine; +} + +bool AnchorManipulator::beginAnchorLineIsHorizontal() const +{ + return beginAnchorLine() & AnchorLine::HorizontalMask; +} +bool AnchorManipulator::beginAnchorLineIsVertical() const +{ + return beginAnchorLine() & AnchorLine::HorizontalMask; +} + +FormEditorItem *AnchorManipulator::beginFormEditorItem() const +{ + return m_beginFormEditorItem; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/formeditor/anchormanipulator.h b/src/plugins/qmldesigner/components/formeditor/anchormanipulator.h new file mode 100644 index 00000000000..b44238a4e10 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/anchormanipulator.h @@ -0,0 +1,71 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef ANCHORMANIPULATOR_H +#define ANCHORMANIPULATOR_H + +#include <qmlanchors.h> + +namespace QmlDesigner { + +class FormEditorItem; +class FormEditorView; + +class AnchorManipulator +{ +public: + AnchorManipulator(FormEditorView *view); + ~AnchorManipulator(); + void begin(FormEditorItem *beginItem, AnchorLine::Type anchorLine); + void addAnchor(FormEditorItem *endItem, AnchorLine::Type anchorLine); + void removeAnchor(); + + void clear(); + + bool isActive() const; + + bool beginAnchorLineIsHorizontal() const; + bool beginAnchorLineIsVertical() const; + + AnchorLine::Type beginAnchorLine() const; + + FormEditorItem *beginFormEditorItem() const; + +private: // fucntions + void setMargin(FormEditorItem *endItem, AnchorLine::Type endAnchorLine); + +private: // variables + FormEditorItem *m_beginFormEditorItem; + AnchorLine::Type m_beginAnchorLine; + QWeakPointer<FormEditorView> m_view; +}; + +} // namespace QmlDesigner + +#endif // ANCHORMANIPULATOR_H diff --git a/src/plugins/qmldesigner/components/formeditor/anchortool.cpp b/src/plugins/qmldesigner/components/formeditor/anchortool.cpp new file mode 100644 index 00000000000..524352819d4 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/anchortool.cpp @@ -0,0 +1,226 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "anchortool.h" + +#include "formeditorscene.h" +#include "formeditorview.h" +#include <qmlitemnode.h> +#include <qmlanchors.h> +#include "anchorlinehandleitem.h" + +#include <QGraphicsSceneMouseEvent> + +#include <QtDebug> + +namespace QmlDesigner { + +AnchorTool::AnchorTool(FormEditorView* editorView) + : AbstractFormEditorTool(editorView), + m_anchorLineIndicator(editorView->scene()->manipulatorLayerItem()), + m_anchorIndicator(editorView->scene()->manipulatorLayerItem()), + m_anchorManipulator(editorView), + m_lastAnchorLineHandleItem(0) +{ + m_hoverTimeLine.setDuration(200); + connect(&m_hoverTimeLine, SIGNAL(finished()), SLOT(checkIfStillHovering())); +} + +AnchorTool::~AnchorTool() +{ + +} + +void AnchorTool::mousePressEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *) +{ + AnchorLineHandleItem *anchorLineHandleItem = topAnchorLineHandleItem(itemList); + if (anchorLineHandleItem) { + m_anchorManipulator.begin(anchorLineHandleItem->anchorLineController().formEditorItem(), + anchorLineHandleItem->anchorLineType()); + } + + m_anchorLineIndicator.clear(); + +} + +bool areAchorable(FormEditorItem *firstItem, FormEditorItem *secondItem) +{ + return firstItem->qmlItemNode().anchors().canAnchor(secondItem->qmlItemNode()); +} + +void AnchorTool::mouseMoveEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event) +{ + if (m_anchorManipulator.isActive()) { + FormEditorItem *targetItem = 0; + AnchorLineHandleItem *anchorLineHandleItem = topAnchorLineHandleItem(itemList); + if (anchorLineHandleItem && areAchorable(m_anchorManipulator.beginFormEditorItem(), anchorLineHandleItem->anchorLineController().formEditorItem())) { + targetItem = anchorLineHandleItem->anchorLineController().formEditorItem(); + } else { + FormEditorItem *topFormEditItem = topFormEditorItemWithRootItem(itemList); + if (topFormEditItem && areAchorable(m_anchorManipulator.beginFormEditorItem(), topFormEditItem)) { + targetItem = topFormEditItem; + } else { + m_anchorLineIndicator.hide(); + m_anchorIndicator.updateTargetPoint(m_anchorManipulator.beginFormEditorItem(), m_anchorManipulator.beginAnchorLine(), event->scenePos()); + } + } + + if (targetItem) { + targetItem->qmlItemNode().selectNode(); + m_anchorLineIndicator.setItem(targetItem); + m_anchorLineIndicator.show(m_anchorManipulator.beginFormEditorItem()->qmlItemNode().anchors().possibleAnchorLines(m_anchorManipulator.beginAnchorLine(), targetItem->qmlItemNode())); + m_anchorIndicator.updateTargetPoint(m_anchorManipulator.beginFormEditorItem(), m_anchorManipulator.beginAnchorLine(), event->scenePos()); + targetItem->qmlItemNode().selectNode(); + } + } + +} + +void AnchorTool::mouseReleaseEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *) +{ + if (m_anchorManipulator.isActive()) { + AnchorLineHandleItem *anchorLineHandleItem = topAnchorLineHandleItem(itemList); + if (anchorLineHandleItem) { + m_anchorManipulator.addAnchor(anchorLineHandleItem->anchorLineController().formEditorItem(), + anchorLineHandleItem->anchorLineType()); + } else { + m_anchorManipulator.removeAnchor(); + } + + + } + + FormEditorItem *topFormEditItem = topFormEditorItem(itemList); + if (topFormEditItem) + topFormEditItem->qmlItemNode().selectNode(); + + m_anchorManipulator.clear(); + m_anchorLineIndicator.clear(); +} + +void AnchorTool::mouseDoubleClickEvent(const QList<QGraphicsItem*> & /*itemList*/, + QGraphicsSceneMouseEvent * /*event*/) +{ +} + +void AnchorTool::hoverMoveEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event) +{ + m_anchorLineIndicator.clearHighlight(); + m_anchorIndicator.clearHighlight(); + m_lastMousePosition = event->scenePos(); + FormEditorItem *topFormEditItem = 0; + AnchorLineHandleItem *anchorLineHandleItem = topAnchorLineHandleItem(itemList); + + if (anchorLineHandleItem) { + anchorLineHandleItem->setHiglighted(true); + m_anchorIndicator.highlight(anchorLineHandleItem->anchorLineController().formEditorItem(), + anchorLineHandleItem->anchorLineType()); + topFormEditItem = anchorLineHandleItem->anchorLineController().formEditorItem(); + if (m_hoverTimeLine.state() == QTimeLine::NotRunning) { + m_lastAnchorLineHandleItem = anchorLineHandleItem; + m_hoverTimeLine.start(); + } + } else { + topFormEditItem = topFormEditorItem(itemList); + } + + if (topFormEditItem) { + m_anchorLineIndicator.setItem(topFormEditItem); + m_anchorLineIndicator.show(AnchorLine::AllMask); + topFormEditItem->qmlItemNode().selectNode(); + } else { + + m_anchorLineIndicator.clear(); + } +} + +void AnchorTool::checkIfStillHovering() +{ + AnchorLineHandleItem *anchorLineHandleItem = topAnchorLineHandleItem(scene()->items(m_lastMousePosition)); + + if (anchorLineHandleItem && anchorLineHandleItem == m_lastAnchorLineHandleItem) { + FormEditorItem *sourceFormEditItem = anchorLineHandleItem->anchorLineController().formEditorItem(); + QmlAnchors anchors(sourceFormEditItem->qmlItemNode().anchors()); + if (anchors.instanceHasAnchor(anchorLineHandleItem->anchorLine())) { + QmlItemNode targetNode(anchors.instanceAnchor(anchorLineHandleItem->anchorLine()).qmlItemNode()); + FormEditorItem *targetFormEditorItem = scene()->itemForQmlItemNode(targetNode); + targetFormEditorItem->showAttention(); + } + } +} + +void AnchorTool::keyPressEvent(QKeyEvent *) +{ +} + +void AnchorTool::keyReleaseEvent(QKeyEvent *) +{ +} + +void AnchorTool::itemsAboutToRemoved(const QList<FormEditorItem*> &removedItems) +{ + QList<FormEditorItem*> newItemList = items().toSet().subtract(removedItems.toSet()).toList(); + setItems(newItemList); + m_anchorIndicator.setItems(newItemList); + m_anchorLineIndicator.clear(); +} + +void AnchorTool::selectedItemsChanged(const QList<FormEditorItem*> &/*itemList*/) +{ + m_anchorIndicator.setItems(view()->scene()->allFormEditorItems()); + m_anchorIndicator.show(); +} + +void AnchorTool::clear() +{ + m_anchorLineIndicator.clear(); + m_anchorIndicator.clear(); +} + +void AnchorTool::formEditorItemsChanged(const QList<FormEditorItem*> &) +{ + m_anchorLineIndicator.updateItems(view()->scene()->allFormEditorItems()); + m_anchorIndicator.updateItems(view()->scene()->allFormEditorItems()); +} + +AnchorLineHandleItem* AnchorTool::topAnchorLineHandleItem(const QList<QGraphicsItem*> & itemList) +{ + foreach (QGraphicsItem *item, itemList) { + AnchorLineHandleItem *anchorLineItem = AnchorLineHandleItem::fromGraphicsItem(item); + if (anchorLineItem) + return anchorLineItem; + } + + return 0; +} +} diff --git a/src/plugins/qmldesigner/components/formeditor/anchortool.h b/src/plugins/qmldesigner/components/formeditor/anchortool.h new file mode 100644 index 00000000000..d11dd1818e1 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/anchortool.h @@ -0,0 +1,88 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef ANCHORTOOL_H +#define ANCHORTOOL_H + +#include <QTimeLine> + +#include "abstractformeditortool.h" + +#include "anchorlineindicator.h" +#include "anchorindicator.h" +#include "anchormanipulator.h" + +namespace QmlDesigner { + +class AnchorLineHandleItem; + +class AnchorTool : public QObject, public AbstractFormEditorTool +{ + Q_OBJECT +public: + AnchorTool(FormEditorView* editorView); + ~AnchorTool(); + + void mousePressEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event); + void mouseMoveEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event); + void mouseReleaseEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event); + void mouseDoubleClickEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event); + void hoverMoveEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event); + void keyPressEvent(QKeyEvent *event); + void keyReleaseEvent(QKeyEvent *keyEvent); + + void itemsAboutToRemoved(const QList<FormEditorItem*> &itemList); + + void selectedItemsChanged(const QList<FormEditorItem*> &itemList); + + void clear(); + + void formEditorItemsChanged(const QList<FormEditorItem*> &itemList); + + static AnchorLineHandleItem* topAnchorLineHandleItem(const QList<QGraphicsItem*> & itemList); + +private slots: + void checkIfStillHovering(); + +private: //variables + AnchorLineIndicator m_anchorLineIndicator; + AnchorIndicator m_anchorIndicator; + AnchorManipulator m_anchorManipulator; + QTimeLine m_hoverTimeLine; + QPointF m_lastMousePosition; + AnchorLineHandleItem *m_lastAnchorLineHandleItem; +}; + +} +#endif // ANCHORTOOL_H diff --git a/src/plugins/qmldesigner/components/formeditor/controlelement.cpp b/src/plugins/qmldesigner/components/formeditor/controlelement.cpp new file mode 100644 index 00000000000..5894fe5c45e --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/controlelement.cpp @@ -0,0 +1,66 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "controlelement.h" + +#include <QGraphicsScene> +#include <QGraphicsRectItem> +#include <QBrush> +#include "layeritem.h" +#include <QtDebug> + + +namespace QmlDesigner { + +ControlElement::ControlElement(LayerItem *layerItem) + : m_controlShape(new QGraphicsRectItem(layerItem)) +{ + QPen pen; + pen.setStyle(Qt::DashLine); + pen.setColor(Qt::blue); + m_controlShape->setPen(pen); +} + +ControlElement::~ControlElement() +{ + delete m_controlShape; +} + +void ControlElement::hide() +{ + m_controlShape->hide(); +} + +void ControlElement::setBoundingRect(const QRectF &rect) +{ + m_controlShape->show(); + m_controlShape->setRect(m_controlShape->mapFromScene(rect).boundingRect()); +} + +} diff --git a/src/plugins/qmldesigner/components/formeditor/controlelement.h b/src/plugins/qmldesigner/components/formeditor/controlelement.h new file mode 100644 index 00000000000..1ec1f3a8649 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/controlelement.h @@ -0,0 +1,57 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef CONTROLELEMENT_H +#define CONTROLELEMENT_H + + +#include <QGraphicsRectItem> + + +namespace QmlDesigner { + +class LayerItem; + +class ControlElement +{ +public: + ControlElement(LayerItem *layerItem); + ~ControlElement(); + + void hide(); + + void setBoundingRect(const QRectF &rect); + +private: + QGraphicsRectItem *m_controlShape; +}; + +} + +#endif // CONTROLELEMENT_H diff --git a/src/plugins/qmldesigner/components/formeditor/dragtool.cpp b/src/plugins/qmldesigner/components/formeditor/dragtool.cpp new file mode 100644 index 00000000000..7878cf86a3d --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/dragtool.cpp @@ -0,0 +1,315 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "dragtool.h" + +#include "formeditorscene.h" +#include "formeditorview.h" +#include "modelutilities.h" +#include "itemutilfunctions.h" +#include <customdraganddrop.h> +#include <metainfo.h> + +#include "resizehandleitem.h" + +#include <QApplication> +#include <QGraphicsSceneMouseEvent> +#include <QtDebug> + +namespace QmlDesigner { + +DragTool::DragTool(FormEditorView *editorView) + : AbstractFormEditorTool(editorView), + m_moveManipulator(editorView->scene()->manipulatorLayerItem(), editorView), + m_selectionIndicator(editorView->scene()->manipulatorLayerItem()) +{ +// view()->setCursor(Qt::SizeAllCursor); +} + + +DragTool::~DragTool() +{ + +} + +void DragTool::clear() +{ + m_moveManipulator.clear(); + m_selectionIndicator.clear(); + m_movingItem.clear(); +} + +void DragTool::mousePressEvent(const QList<QGraphicsItem*> &, + QGraphicsSceneMouseEvent *) +{ + +} + +void DragTool::mouseMoveEvent(const QList<QGraphicsItem*> &, + QGraphicsSceneMouseEvent *) +{ + +} + +void DragTool::hoverMoveEvent(const QList<QGraphicsItem*> &, + QGraphicsSceneMouseEvent * /*event*/) +{ + +} + +void DragTool::keyPressEvent(QKeyEvent *) +{ + +} + +void DragTool::keyReleaseEvent(QKeyEvent *) +{ + +} + + +void DragTool::mouseReleaseEvent(const QList<QGraphicsItem*> &/*itemList*/, + QGraphicsSceneMouseEvent *) +{ + +} + +void DragTool::mouseDoubleClickEvent(const QList<QGraphicsItem*> & /*itemList*/, + QGraphicsSceneMouseEvent * /*event*/) +{ + +} + +void DragTool::itemsAboutToRemoved(const QList<FormEditorItem*> & /* removedItemList */) +{ + +} + +void DragTool::selectedItemsChanged(const QList<FormEditorItem*> &) +{ + +} + + + +void DragTool::updateMoveManipulator() +{ + if (m_moveManipulator.isActive()) + return; +} + +void DragTool::beginWithPoint(const QPointF &beginPoint) +{ + m_movingItem = scene()->itemForQmlItemNode(m_dragNode); + + m_moveManipulator.setItem(m_movingItem.data()); + m_moveManipulator.begin(beginPoint); + +} + +void DragTool::createQmlItemNode(const ItemLibraryInfo &itemLibraryRepresentation, QmlItemNode parentNode, QPointF scenePos) +{ + QmlDesignerItemLibraryDragAndDrop::CustomDragAndDrop::hide(); + + MetaInfo metaInfo = MetaInfo::global(); + + FormEditorItem *parentItem = scene()->itemForQmlItemNode(parentNode); + QPointF pos = parentItem->mapFromScene(scenePos); + + m_dragNode = view()->createQmlItemNode(itemLibraryRepresentation, pos, parentNode); + + Q_ASSERT(m_dragNode.modelNode().isValid()); + Q_ASSERT(m_dragNode.isValid()); + + QList<QmlItemNode> nodeList; + nodeList.append(m_dragNode); + view()->setSelectedQmlItemNodes(nodeList); + m_selectionIndicator.setItems(scene()->itemsForQmlItemNodes(nodeList)); +} + +void DragTool::createQmlItemNodeFromImage(const QString &imageName, QmlItemNode parentNode, QPointF scenePos) +{ + QmlDesignerItemLibraryDragAndDrop::CustomDragAndDrop::hide(); + + MetaInfo metaInfo = MetaInfo::global(); + + FormEditorItem *parentItem = scene()->itemForQmlItemNode(parentNode); + QPointF pos = parentItem->mapFromScene(scenePos); + + m_dragNode = view()->createQmlItemNodeFromImage(imageName, pos, parentNode); + + QList<QmlItemNode> nodeList; + nodeList.append(m_dragNode); + view()->setSelectedQmlItemNodes(nodeList); + m_selectionIndicator.setItems(scene()->itemsForQmlItemNodes(nodeList)); +} + +FormEditorItem* DragTool::calculateContainer(const QPointF &point, FormEditorItem * currentItem) +{ + QList<QGraphicsItem *> list = scene()->items(point); + foreach (QGraphicsItem *item, list) { + FormEditorItem *formEditorItem = FormEditorItem::fromQGraphicsItem(item); + if (formEditorItem && formEditorItem != currentItem && formEditorItem->isContainer()) + return formEditorItem; + } + return 0; +} + + +void DragTool::formEditorItemsChanged(const QList<FormEditorItem*> & itemList) +{ + if (m_movingItem && itemList.contains(m_movingItem.data())) { + QList<FormEditorItem*> updateItemList; + updateItemList.append(m_movingItem.data()); + m_selectionIndicator.updateItems(updateItemList); + } +} + + +void DragTool::dropEvent(QGraphicsSceneDragDropEvent * event) +{ + if (event->mimeData()->hasFormat("application/vnd.bauhaus.itemlibraryinfo") || + event->mimeData()->hasFormat("application/vnd.bauhaus.libraryresource")) { + event->accept(); + end(event->scenePos()); + //Q_ASSERT(m_token.isValid()); + m_rewriterTransaction.commit(); + m_dragNode = ModelNode(); + view()->changeToSelectionTool(); + } +} + +void DragTool::dragEnterEvent(QGraphicsSceneDragDropEvent * event) +{ + if (event->mimeData()->hasFormat("application/vnd.bauhaus.itemlibraryinfo") || + event->mimeData()->hasFormat("application/vnd.bauhaus.libraryresource")) { + if (!m_rewriterTransaction.isValid()) { + m_rewriterTransaction = view()->beginRewriterTransaction(); + } + } +} + +void DragTool::dragLeaveEvent(QGraphicsSceneDragDropEvent * event) +{ + if (event->mimeData()->hasFormat("application/vnd.bauhaus.itemlibraryinfo") || + event->mimeData()->hasFormat("application/vnd.bauhaus.libraryresource")) { + event->accept(); + if (m_dragNode.isValid()) + m_dragNode.destroy(); + end(event->scenePos()); + m_rewriterTransaction.commit(); + QmlDesignerItemLibraryDragAndDrop::CustomDragAndDrop::show(); + QList<QmlItemNode> nodeList; + view()->setSelectedQmlItemNodes(nodeList); + view()->changeToSelectionTool(); + } +} + +static ItemLibraryInfo ItemLibraryInfoFromData(const QByteArray &data) +{ + QDataStream stream(data); + + ItemLibraryInfo itemLibraryInfo; + stream >> itemLibraryInfo; + + return itemLibraryInfo; +} + +void DragTool::dragMoveEvent(QGraphicsSceneDragDropEvent * event) +{ + if (event->mimeData()->hasFormat("application/vnd.bauhaus.itemlibraryinfo") || + event->mimeData()->hasFormat("application/vnd.bauhaus.libraryresource")) { + event->accept(); + QPointF scenePos = event->scenePos(); + if (m_dragNode.isValid()) { + + FormEditorItem *parentItem = calculateContainer(event->scenePos() - QPoint(2, 2)); + if (!parentItem) { //if there is no parent any more - the use left the scene + end(event->scenePos()); + m_dragNode.destroy(); //delete the node then + QmlDesignerItemLibraryDragAndDrop::CustomDragAndDrop::show(); + return; + } + //move + move(event->scenePos()); + } else { + //create new node if container + + FormEditorItem *parentItem = calculateContainer(event->scenePos()); + if (!parentItem) + return; + QmlItemNode parentNode; //get possible container parentNode + if (parentItem) + parentNode = parentItem->qmlItemNode(); + + if (event->mimeData()->hasFormat("application/vnd.bauhaus.itemlibraryinfo")) { + Q_ASSERT(!event->mimeData()->data("application/vnd.bauhaus.itemlibraryinfo").isEmpty()); + ItemLibraryInfo ItemLibraryInfo = ItemLibraryInfoFromData(event->mimeData()->data("application/vnd.bauhaus.itemlibraryinfo")); + createQmlItemNode(ItemLibraryInfo, parentNode, event->scenePos()); + } else if (event->mimeData()->hasFormat("application/vnd.bauhaus.libraryresource")) { + Q_ASSERT(!event->mimeData()->data("application/vnd.bauhaus.libraryresource").isEmpty()); + QString imageName = QString::fromLatin1((event->mimeData()->data("application/vnd.bauhaus.libraryresource"))); + createQmlItemNodeFromImage(imageName, parentNode, event->scenePos()); + } else Q_ASSERT(false); + beginWithPoint(event->scenePos()); + } + } + if (event->mimeData()->hasFormat("application/vnd.bauhaus.libraryresource")) { + } +} + +void DragTool::end(QPointF scenePos) +{ + m_moveManipulator.end(scenePos); + clear(); +} + +void DragTool::move(QPointF scenePos) +{ + if (!m_movingItem) + return; + + FormEditorItem *containerItem = calculateContainer(scenePos - QPoint(2, 2), m_movingItem.data()); + if (containerItem && + containerItem != m_movingItem->parentItem() && + view()->currentState().isBaseState()) { + + m_moveManipulator.reparentTo(containerItem); + } + + //MoveManipulator::Snapping useSnapping = MoveManipulator::NoSnapping; + MoveManipulator::Snapping useSnapping = MoveManipulator::UseSnapping; + /* if (event->modifiers().testFlag(Qt::ControlModifier) != view()->isSnapButtonChecked()) + useSnapping = MoveManipulator::UseSnapping;*/ + + m_moveManipulator.update(scenePos, useSnapping); +} + + +} diff --git a/src/plugins/qmldesigner/components/formeditor/dragtool.h b/src/plugins/qmldesigner/components/formeditor/dragtool.h new file mode 100644 index 00000000000..97f676482b3 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/dragtool.h @@ -0,0 +1,105 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef DRAGTOOL_H +#define DRAGTOOL_H + +#include "abstractformeditortool.h" +#include "movemanipulator.h" +#include "selectionindicator.h" +#include "resizeindicator.h" + +#include <QHash> + + +namespace QmlDesigner { + + +class DragTool : public AbstractFormEditorTool +{ +public: + DragTool(FormEditorView* editorView); + ~DragTool(); + + void mousePressEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event); + void mouseMoveEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event); + void mouseReleaseEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event); + void mouseDoubleClickEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event); + void hoverMoveEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event); + + void keyPressEvent(QKeyEvent *event); + void keyReleaseEvent(QKeyEvent *keyEvent); + + void itemsAboutToRemoved(const QList<FormEditorItem*> &itemList); + + void selectedItemsChanged(const QList<FormEditorItem*> &itemList); + + void updateMoveManipulator(); + + void beginWithPoint(const QPointF &beginPoint); + + + virtual void dropEvent(QGraphicsSceneDragDropEvent * event); + virtual void dragEnterEvent(QGraphicsSceneDragDropEvent * event); + virtual void dragLeaveEvent(QGraphicsSceneDragDropEvent * event); + virtual void dragMoveEvent(QGraphicsSceneDragDropEvent * event); + + //void beginWithPoint(const QPointF &beginPoint); + + void clear(); + + void formEditorItemsChanged(const QList<FormEditorItem*> &itemList); + +protected: + + +private: + + void createQmlItemNode(const ItemLibraryInfo &ItemLibraryRepresentation, QmlItemNode parentNode, QPointF scenePos); + void createQmlItemNodeFromImage(const QString &imageName, QmlItemNode parentNode, QPointF scenePos); + FormEditorItem* calculateContainer(const QPointF &point, FormEditorItem * currentItem = 0); + + void begin(QPointF scenePos); + void end(QPointF scenePos); + void move(QPointF scenePos); + + MoveManipulator m_moveManipulator; + SelectionIndicator m_selectionIndicator; + QWeakPointer<FormEditorItem> m_movingItem; + RewriterTransaction m_rewriterTransaction; + QmlItemNode m_dragNode; +}; + +} +#endif // DRAGTOOL_H diff --git a/src/plugins/qmldesigner/components/formeditor/formeditor.pri b/src/plugins/qmldesigner/components/formeditor/formeditor.pri new file mode 100644 index 00000000000..1c3bd78988b --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/formeditor.pri @@ -0,0 +1,84 @@ +VPATH += $$PWD +QT += opengl +INCLUDEPATH += $$PWD +INCLUDEPATH += $$PWD/../navigator +DEPENDPATH += $$PWD +SOURCES += formeditoritem.cpp \ + formeditorview.cpp \ + formeditorscene.cpp \ + formeditorwidget.cpp \ + selectiontool.cpp \ + abstractformeditortool.cpp \ + controlelement.cpp \ + resizemanipulator.cpp \ + movemanipulator.cpp \ + layeritem.cpp \ + itemutilfunctions.cpp \ + selectionrectangle.cpp \ + rubberbandselectionmanipulator.cpp \ + movetool.cpp \ + selectionindicator.cpp \ + snappinglinecreator.cpp \ + snapper.cpp \ + onedimensionalcluster.cpp \ + singleselectionmanipulator.cpp \ + scalemanipulator.cpp \ + resizetool.cpp \ + resizeindicator.cpp \ + scaleitem.cpp \ + resizecontroller.cpp \ + resizehandleitem.cpp \ + anchortool.cpp \ + anchorlineindicator.cpp \ + anchorlinecontroller.cpp \ + anchorlinehandleitem.cpp \ + anchormanipulator.cpp \ + anchorindicator.cpp \ + anchorcontroller.cpp \ + anchorhandleitem.cpp \ + dragtool.cpp \ + itemcreatortool.cpp \ + toolbox.cpp \ + zoomaction.cpp \ + formeditorgraphicsview.cpp \ + numberseriesaction.cpp +HEADERS += formeditorscene.h \ + formeditorwidget.h \ + formeditoritem.h \ + formeditorview.h \ + selectiontool.h \ + abstractformeditortool.h \ + controlelement.h \ + resizemanipulator.h \ + movemanipulator.h \ + layeritem.h \ + itemutilfunctions.h \ + selectionrectangle.h \ + rubberbandselectionmanipulator.h \ + movetool.h \ + selectionindicator.h \ + snappinglinecreator.h \ + snapper.h \ + onedimensionalcluster.h \ + singleselectionmanipulator.h \ + scalemanipulator.h \ + resizetool.h \ + resizeindicator.h \ + scaleitem.h \ + resizecontroller.h \ + resizehandleitem.h \ + anchortool.h \ + anchorlineindicator.h \ + anchorlinecontroller.h \ + anchorlinehandleitem.h \ + anchormanipulator.h \ + anchorindicator.h \ + anchorcontroller.h \ + anchorhandleitem.h \ + dragtool.h \ + itemcreatortool.cpp \ + toolbox.h \ + zoomaction.h \ + formeditorgraphicsview.h \ + numberseriesaction.h +RESOURCES += formeditor.qrc diff --git a/src/plugins/qmldesigner/components/formeditor/formeditor.qrc b/src/plugins/qmldesigner/components/formeditor/formeditor.qrc new file mode 100644 index 00000000000..14e0dbd0fbd --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/formeditor.qrc @@ -0,0 +1,12 @@ +<RCC> + <qresource prefix="/icon/handle"> + <file>resize_handle.png</file> + </qresource> + <qresource prefix="/icon/tool"> + <file>anchor.png</file> + <file>transform.png</file> + </qresource> + <qresource prefix="/icon/layout"> + <file>snapping.png</file> + </qresource> +</RCC> diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.cpp new file mode 100644 index 00000000000..fb2e905eda4 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.cpp @@ -0,0 +1,88 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "formeditorgraphicsview.h" + +#include <QWheelEvent> + +namespace QmlDesigner { + +FormEditorGraphicsView::FormEditorGraphicsView(QWidget *parent) : + QGraphicsView(parent) +{ + setTransformationAnchor(QGraphicsView::AnchorUnderMouse); + setResizeAnchor(QGraphicsView::AnchorViewCenter); +// setCacheMode(QGraphicsView::CacheNone); + setCacheMode(QGraphicsView::CacheBackground); + setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate); +// setViewportUpdateMode(QGraphicsView::NoViewportUpdate); + setRenderHint(QPainter::Antialiasing, false); + + setFrameShape(QFrame::NoFrame); + + setAutoFillBackground(true); + setBackgroundRole(QPalette::Window); + + const int checkerbordSize= 20; + QPixmap tilePixmap(checkerbordSize * 2, checkerbordSize * 2); + tilePixmap.fill(Qt::white); + QPainter tilePainter(&tilePixmap); + QColor color(220, 220, 220); + tilePainter.fillRect(0, 0, checkerbordSize, checkerbordSize, color); + tilePainter.fillRect(checkerbordSize, checkerbordSize, checkerbordSize, checkerbordSize, color); + tilePainter.end(); + + setBackgroundBrush(tilePixmap); + + viewport()->setMouseTracking(true); +} + +void FormEditorGraphicsView::wheelEvent(QWheelEvent *event) +{ + if (event->modifiers().testFlag(Qt::ControlModifier)) { + event->ignore(); + } else { + QGraphicsView::wheelEvent(event); + } + +} + +void FormEditorGraphicsView::drawBackground(QPainter *painter, const QRectF &rect) +{ + painter->save(); + painter->setBrushOrigin(0, 0); + painter->fillRect(rect.intersected(sceneRect()), backgroundBrush()); + // paint rect around editable area + painter->setPen(Qt::darkGray); + QRectF frameRect = sceneRect().adjusted(-1, -1, 0, 0); + painter->drawRect(frameRect); + painter->restore(); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.h b/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.h new file mode 100644 index 00000000000..2e79f656a47 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.h @@ -0,0 +1,51 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef FORMEDITORGRAPHICSVIEW_H +#define FORMEDITORGRAPHICSVIEW_H + +#include <QGraphicsView> + +namespace QmlDesigner { + +class FormEditorGraphicsView : public QGraphicsView +{ +Q_OBJECT +public: + explicit FormEditorGraphicsView(QWidget *parent = 0); + + +protected: + void drawBackground(QPainter *painter, const QRectF &rect); + void wheelEvent(QWheelEvent *event); +}; + +} // namespace QmlDesigner + +#endif // FORMEDITORGRAPHICSVIEW_H diff --git a/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp b/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp new file mode 100644 index 00000000000..91652fc37cb --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp @@ -0,0 +1,323 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "formeditoritem.h" +#include "formeditorscene.h" +#include "formeditornodeinstanceview.h" +#include "selectiontool.h" + +#include <modelnode.h> +#include <nodemetainfo.h> +#include <widgetqueryview.h> + +#include <QGraphicsSceneMouseEvent> +#include <QDebug> +#include <QPainter> +#include <QStyleOptionGraphicsItem> +#include <QGraphicsView> +#include <QTimeLine> + +#include <cmath> + +#include <invalidmodelnodeexception.h> +#include <invalidnodestateexception.h> + +namespace QmlDesigner { + + + +FormEditorScene *FormEditorItem::scene() const { + return qobject_cast<FormEditorScene*>(QGraphicsItem::scene()); +} + +FormEditorItem::FormEditorItem(const QmlItemNode &qmlItemNode, FormEditorScene* scene) + : QGraphicsObject(scene->formLayerItem()), + m_snappingLineCreator(this), + m_qmlItemNode(qmlItemNode), + m_borderWidth(1.0), + m_opacity(0.6) +{ + setCacheMode(QGraphicsItem::ItemCoordinateCache); + setup(); +} + +void FormEditorItem::setup() +{ + if (qmlItemNode().hasInstanceParent()) + setParentItem(scene()->itemForQmlItemNode(qmlItemNode().instanceParent().toQmlItemNode())); + + if (QGraphicsItem::parentItem() == scene()->formLayerItem()) + m_borderWidth = 0.0; + + setFlag(QGraphicsItem::ItemIsMovable, true); + updateGeometry(); + updateVisibilty(); +} + +QRectF FormEditorItem::boundingRect() const +{ + return m_boundingRect; +} + +void FormEditorItem::updateGeometry() +{ + prepareGeometryChange(); + m_boundingRect = qmlItemNode().instanceBoundingRect(); + setTransform(qmlItemNode().instanceTransform()); + setTransform(m_attentionTransform, true); + //the property for zValue is called z in QGraphicsObject + Q_ASSERT(qmlItemNode().instanceValue("z").isValid()); + setZValue(qmlItemNode().instanceValue("z").toDouble()); +} + +void FormEditorItem::updateVisibilty() +{ +// setVisible(nodeInstance().isVisible()); +// setOpacity(nodeInstance().opacity()); +} + +void FormEditorItem::showAttention() +{ + if (m_attentionTimeLine.isNull()) { + m_attentionTimeLine = new QTimeLine(500, this); + m_attentionTimeLine->setCurveShape(QTimeLine::SineCurve); + connect(m_attentionTimeLine.data(), SIGNAL(valueChanged(qreal)), SLOT(changeAttention(qreal))); + connect(m_attentionTimeLine.data(), SIGNAL(finished()), m_attentionTimeLine.data(), SLOT(deleteLater())); + + m_attentionTimeLine->start(); + } +} + +void FormEditorItem::changeAttention(qreal value) +{ + if (QGraphicsItem::parentItem() == scene()->formLayerItem()) { + setAttentionHighlight(value); + } else { + setAttentionHighlight(value); + setAttentionScale(value); + } +} + +FormEditorView *FormEditorItem::formEditorView() const +{ + return scene()->editorView(); +} + +void FormEditorItem::setAttentionScale(double sinusScale) +{ + if (!qFuzzyIsNull(sinusScale)) { + double scale = std::sqrt(sinusScale); + m_attentionTransform.reset(); + QPointF centerPoint(qmlItemNode().instanceBoundingRect().center()); + m_attentionTransform.translate(centerPoint.x(), centerPoint.y()); + m_attentionTransform.scale(scale * 0.15 + 1.0, scale * 0.15 + 1.0); + m_attentionTransform.translate(-centerPoint.x(), -centerPoint.y()); + m_inverseAttentionTransform = m_attentionTransform.inverted(); + prepareGeometryChange(); + setTransform(qmlItemNode().instanceTransform()); + setTransform(m_attentionTransform, true); + } else { + m_attentionTransform.reset(); + prepareGeometryChange(); + setTransform(qmlItemNode().instanceTransform()); + } +} + +void FormEditorItem::setAttentionHighlight(double value) +{ + m_opacity = 0.6 + value; + if (QGraphicsItem::parentItem() == scene()->formLayerItem()) + m_borderWidth = value * 4; + else + m_borderWidth = 1. + value * 3; + + update(); +} + +FormEditorItem::~FormEditorItem() +{ + scene()->removeItemFromHash(this); +} + +/* \brief returns the parent item skipping all proxyItem*/ +FormEditorItem *FormEditorItem::parentItem() const +{ + return qgraphicsitem_cast<FormEditorItem*> (QGraphicsItem::parentItem()); +} + +FormEditorItem* FormEditorItem::fromQGraphicsItem(QGraphicsItem *graphicsItem) +{ + return qgraphicsitem_cast<FormEditorItem*>(graphicsItem); +} + +static QRectF alignedRect(const QRectF &rect) +{ + QRectF alignedRect(rect); + alignedRect.setTop(std::floor(rect.top()) + 0.5); + alignedRect.setBottom(std::floor(rect.bottom()) + 0.5); + alignedRect.setLeft(std::floor(rect.left()) + 0.5); + alignedRect.setRight(std::floor(rect.right()) + 0.5); + + return alignedRect; +} + +void FormEditorItem::paintBoundingRect(QPainter *painter) const +{ + if (QGraphicsItem::parentItem() == scene()->formLayerItem() && qFuzzyIsNull(m_borderWidth)) + return; + + QPen pen; + pen.setJoinStyle(Qt::MiterJoin); + + switch(scene()->paintMode()) { + case FormEditorScene::AnchorMode: { + pen.setColor(Qt::black); + pen.setWidth(m_borderWidth); + } + break; + case FormEditorScene::NormalMode: { + pen.setColor(Qt::gray); + } + break; + } + + painter->setPen(pen); + int offset = m_borderWidth / 2; + painter->drawRect(alignedRect(boundingRect().adjusted(offset, offset, -offset, -offset))); +} + +void FormEditorItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) +{ + painter->save(); + painter->setRenderHint(QPainter::Antialiasing, true); + switch(scene()->paintMode()) { + case FormEditorScene::AnchorMode: + painter->setOpacity(m_opacity); + break; + case FormEditorScene::NormalMode: + painter->setOpacity(qmlItemNode().instanceValue("opacity").toDouble()); + break; + } + + qmlItemNode().paintInstance(painter); + + painter->setRenderHint(QPainter::Antialiasing, false); + paintBoundingRect(painter); + + painter->restore(); +} + +AbstractFormEditorTool* FormEditorItem::tool() const +{ + return static_cast<FormEditorScene*>(scene())->currentTool(); +} + +SnapLineMap FormEditorItem::topSnappingLines() const +{ + return m_snappingLineCreator.topLines(); +} + +SnapLineMap FormEditorItem::bottomSnappingLines() const +{ + return m_snappingLineCreator.bottomLines(); +} + +SnapLineMap FormEditorItem::leftSnappingLines() const +{ + return m_snappingLineCreator.leftLines(); +} + +SnapLineMap FormEditorItem::rightSnappingLines() const +{ + return m_snappingLineCreator.rightLines(); +} + +SnapLineMap FormEditorItem::horizontalCenterSnappingLines() const +{ + return m_snappingLineCreator.horizontalCenterLines(); +} + +SnapLineMap FormEditorItem::verticalCenterSnappingLines() const +{ + return m_snappingLineCreator.verticalCenterLines(); +} + +SnapLineMap FormEditorItem::topSnappingOffsets() const +{ + return m_snappingLineCreator.topOffsets(); +} + +SnapLineMap FormEditorItem::bottomSnappingOffsets() const +{ + return m_snappingLineCreator.bottomOffsets(); +} + +SnapLineMap FormEditorItem::leftSnappingOffsets() const +{ + return m_snappingLineCreator.leftOffsets(); +} + +SnapLineMap FormEditorItem::rightSnappingOffsets() const +{ + return m_snappingLineCreator.rightOffsets(); +} + + +void FormEditorItem::updateSnappingLines(const QList<FormEditorItem*> &exceptionList, + FormEditorItem *transformationSpaceItem) +{ + m_snappingLineCreator.update(exceptionList, transformationSpaceItem); +} + + +QList<FormEditorItem*> FormEditorItem::childFormEditorItems() const +{ + QList<FormEditorItem*> formEditorItemList; + + foreach (QGraphicsItem *item, childItems()) { + FormEditorItem *formEditorItem = fromQGraphicsItem(item); + if (formEditorItem) + formEditorItemList.append(formEditorItem); + } + + return formEditorItemList; +} + +bool FormEditorItem::isContainer() const +{ + return qmlItemNode().modelNode().metaInfo().isContainer(); +} + +QmlItemNode FormEditorItem::qmlItemNode() const +{ + return m_qmlItemNode; +} + +} + diff --git a/src/plugins/qmldesigner/components/formeditor/formeditoritem.h b/src/plugins/qmldesigner/components/formeditor/formeditoritem.h new file mode 100644 index 00000000000..b5ffc769923 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/formeditoritem.h @@ -0,0 +1,131 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef FORMEDITORITEM_H +#define FORMEDITORITEM_H + +#include <QWeakPointer> +#include <QGraphicsWidget> +#include <qmlitemnode.h> +#include "snappinglinecreator.h" + +class QTimeLine; + +namespace QmlDesigner { + +class FormEditorScene; +class FormEditorView; +class AbstractFormEditorTool; + +namespace Internal { + class GraphicItemResizer; + class MoveController; +} + +class FormEditorItem : public QGraphicsObject +{ + Q_OBJECT + + friend class QmlDesigner::FormEditorScene; +public: + ~FormEditorItem(); + + void paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = 0 ); + + bool isContainer() const; + QmlItemNode qmlItemNode() const; + + + enum { Type = UserType + 0xfffa }; + + int type() const; + + static FormEditorItem* fromQGraphicsItem(QGraphicsItem *graphicsItem); + + void updateSnappingLines(const QList<FormEditorItem*> &exceptionList, + FormEditorItem *transformationSpaceItem); + + SnapLineMap topSnappingLines() const; + SnapLineMap bottomSnappingLines() const; + SnapLineMap leftSnappingLines() const; + SnapLineMap rightSnappingLines() const; + SnapLineMap horizontalCenterSnappingLines() const; + SnapLineMap verticalCenterSnappingLines() const; + + SnapLineMap topSnappingOffsets() const; + SnapLineMap bottomSnappingOffsets() const; + SnapLineMap leftSnappingOffsets() const; + SnapLineMap rightSnappingOffsets() const; + + QList<FormEditorItem*> childFormEditorItems() const; + FormEditorScene *scene() const; + FormEditorItem *parentItem() const; + + QRectF boundingRect() const; + + void updateGeometry(); + void updateVisibilty(); + + void showAttention(); + + FormEditorView *formEditorView() const; + +protected: + AbstractFormEditorTool* tool() const; + void paintBoundingRect(QPainter *painter) const; + +private slots: + void changeAttention(qreal value); + +private: // functions + FormEditorItem(const QmlItemNode &qmlItemNode, FormEditorScene* scene); + void setup(); + void setAttentionScale(double scale); + void setAttentionHighlight(double value); + +private: // variables + SnappingLineCreator m_snappingLineCreator; + QmlItemNode m_qmlItemNode; + QWeakPointer<QTimeLine> m_attentionTimeLine; + QTransform m_attentionTransform; // make item larger in anchor mode + QTransform m_inverseAttentionTransform; + QRectF m_boundingRect; + double m_borderWidth; + double m_opacity; +}; + + +inline int FormEditorItem::type() const +{ + return UserType + 0xfffa; +} + +} + +#endif // FORMEDITORITEM_H diff --git a/src/plugins/qmldesigner/components/formeditor/formeditormainview.cpp b/src/plugins/qmldesigner/components/formeditor/formeditormainview.cpp new file mode 100644 index 00000000000..d1da63fafcf --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/formeditormainview.cpp @@ -0,0 +1,401 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "formeditormainview.h" + +#include "formeditorview.h" +#include "formeditorwidget.h" +#include <QPixmapCache> +#include <QtDebug> + +#include "zoomaction.h" + +namespace QmlDesigner { + + +FormEditorMainView::FormEditorMainView() + : m_formMainEditorWidget(new FormEditorMainWidget(this)) +{ + QPixmapCache::setCacheLimit(1024 * 100); +} + +FormEditorMainView::~FormEditorMainView() +{ + resetViews(); + + if (m_formMainEditorWidget) + m_formMainEditorWidget->deleteLater(); +} + +void FormEditorMainView::modelAttached(Model *model) +{ + AbstractView::modelAttached(model); + + setupSubViews(); + + foreach(const QWeakPointer<FormEditorView> &view, m_formEditorViewList) { + if (view) + view->modelAttached(model); + } + + m_formMainEditorWidget->setModel(model); +} + +void FormEditorMainView::createSubView(const QmlModelState &state) +{ + FormEditorView *subView = new FormEditorView(this); + subView->setCurrentState(state); + m_formEditorViewList.append(subView); + + m_formMainEditorWidget->addWidget(subView->widget()); + m_subWindowMap.insert(state, subView->widget()); + + subView->setZoomLevel(zoomAction()->zoomLevel()); + connect(zoomAction(), SIGNAL(zoomLevelChanged(double)), subView, SLOT(setZoomLevel(double))); +} + +void FormEditorMainView::removeSubView(const ModelState &state) +{ + QWidget *subWindow = m_subWindowMap.take(state).data(); + Q_ASSERT(subWindow); + if (subWindow == m_formMainEditorWidget->currentWidget()) + setCurrentState(); + delete subWindow; + + FormEditorView *editorView = 0; + foreach(const QWeakPointer<FormEditorView> &view, m_formEditorViewList) { + if (view->modelState() == state) { + editorView = view.data(); + break; + } + } + Q_ASSERT(editorView); + m_formEditorViewList.removeOne(editorView); + delete editorView; +} + +static bool modelStateLessThan(const ModelState &firstState, const ModelState &secondState) + { + if (firstState.isBaseState()) + return false; + + return firstState.name().toLower() > secondState.name().toLower(); + } + +void FormEditorMainView::setupSubViews() +{ + foreach(const QWeakPointer<FormEditorView> &view, m_formEditorViewList) + delete view.data(); + m_formEditorViewList.clear(); + + foreach(const QWeakPointer<QWidget> &view, m_subWindowMap.values()) + delete view.data(); + m_subWindowMap.clear(); + + QList<ModelState> invertedStates(model()->modelStates()); + qSort(invertedStates.begin(), invertedStates.end(), modelStateLessThan); + foreach(const ModelState &state, invertedStates) + createSubView(state); +} + +void FormEditorMainView::resetViews() +{ + m_subWindowMap.clear(); + foreach(const QWeakPointer<FormEditorView> &view, m_formEditorViewList) + delete view.data(); + m_formEditorViewList.clear(); +} + +void FormEditorMainView::nodeCreated(const ModelNode &createdNode) +{ + foreach(const QWeakPointer<FormEditorView> &view, m_formEditorViewList) { + if (view) + view->nodeCreated(createdNode); + } +} + +void FormEditorMainView::modelAboutToBeDetached(Model *model) +{ + foreach(const QWeakPointer<FormEditorView> &view, m_formEditorViewList) { + if (view) + view->modelAboutToBeDetached(model); + } + + resetViews(); + + m_formMainEditorWidget->setModel(0); + + AbstractView::modelAboutToBeDetached(model); +} + +void FormEditorMainView::nodeAboutToBeRemoved(const ModelNode &removedNode) +{ + foreach(const QWeakPointer<FormEditorView> &view, m_formEditorViewList) { + if (view) + view->nodeAboutToBeRemoved(removedNode); + } +} + +void FormEditorMainView::propertiesAdded(const NodeState &state, const QList<NodeProperty>& propertyList) +{ + foreach(const QWeakPointer<FormEditorView> &view, m_formEditorViewList) { + if (view) + view->propertiesAdded(state, propertyList); + } +} + +void FormEditorMainView::propertiesAboutToBeRemoved(const NodeState &state, const QList<NodeProperty>& propertyList) +{ + foreach(const QWeakPointer<FormEditorView> &view, m_formEditorViewList) { + if (view) + view->propertiesAboutToBeRemoved(state, propertyList); + } +} + +void FormEditorMainView::propertyValuesChanged(const NodeState &state, const QList<NodeProperty>& propertyList) +{ + foreach(const QWeakPointer<FormEditorView> &view, m_formEditorViewList) { + if (view) + view->propertyValuesChanged(state, propertyList); + } +} + +void FormEditorMainView::nodeIdChanged(const ModelNode& node, const QString& newId, const QString& oldId) +{ + foreach(const QWeakPointer<FormEditorView> &view, m_formEditorViewList) { + if (view) + view->nodeIdChanged(node, newId, oldId); + } +} + +void FormEditorMainView::nodeReparented(const ModelNode &node, const ModelNode &oldParent, const ModelNode &newParent) +{ + foreach(const QWeakPointer<FormEditorView> &view, m_formEditorViewList) { + if (view) + view->nodeReparented(node, oldParent, newParent); + } +} + +FormEditorMainWidget *FormEditorMainView::widget() const +{ + return m_formMainEditorWidget.data(); +} + +NodeInstanceView *FormEditorMainView::nodeInstanceView(const ModelState &modelState) const +{ + foreach (const QWeakPointer<FormEditorView> &view, m_formEditorViewList) { + if (view->modelState() == modelState) + return view->nodeInstanceView(); + } + + return 0; +} + +void FormEditorMainView::selectedNodesChanged(const QList<ModelNode> &selectedNodeList, + const QList<ModelNode> &lastSelectedNodeList) +{ + foreach(const QWeakPointer<FormEditorView> &view, m_formEditorViewList) { + if (view) + view->selectedNodesChanged(selectedNodeList, lastSelectedNodeList); + } +} + +void FormEditorMainView::modelStateAboutToBeRemoved(const ModelState &modelState) +{ + foreach(const QWeakPointer<FormEditorView> &view, m_formEditorViewList) { + if (view) + view->modelStateAboutToBeRemoved(modelState); + } + + removeSubView(modelState); +} + +void FormEditorMainView::modelStateAdded(const ModelState &modelState) +{ + createSubView(modelState); + m_formEditorViewList.last()->modelAttached(model()); + + foreach(const QWeakPointer<FormEditorView> &view, m_formEditorViewList) { + if (view) + view->modelStateAdded(modelState); + } +} + +void FormEditorMainView::nodeStatesAboutToBeRemoved(const QList<NodeState> &nodeStateList) +{ + foreach(const QWeakPointer<FormEditorView> &view, m_formEditorViewList) { + if (view) + view->nodeStatesAboutToBeRemoved(nodeStateList); + } +} + +void FormEditorMainView::nodeStatesAdded(const QList<NodeState> &nodeStateList) +{ + foreach(const QWeakPointer<FormEditorView> &view, m_formEditorViewList) { + if (view) + view->nodeStatesAdded(nodeStateList); + } +} + +void FormEditorMainView::setCurrentState(const QmlModelState &state) +{ + Q_ASSERT(m_subWindowMap.contains(state)); + m_formMainEditorWidget->setCurrentWidget(m_subWindowMap.value(state).data()); + emit stateChanged(state); +} + +ModelState FormEditorMainView::currentState() const +{ + QWidget *currentWidget = m_formMainEditorWidget->currentWidget(); + QMapIterator<ModelState, QWeakPointer<QWidget> > iter(m_subWindowMap); + while (iter.hasNext()) { + iter.next(); + if (iter.value().data() == currentWidget) { + return iter.key(); + } + } + Q_ASSERT_X(0, Q_FUNC_INFO, "cannot find current state"); + return ModelState(); +} + +FormEditorMainView::EditorTool FormEditorMainView::currentTool() const +{ + return m_currentTool; +} + +void FormEditorMainView::setCurrentTool(FormEditorMainView::EditorTool tool) +{ + if (m_currentTool == tool) + return; + m_currentTool = tool; + switch (tool) { + case MoveTool: { + foreach(const QWeakPointer<FormEditorView> &view, m_formEditorViewList) + view->changeToMoveTool(); + break; + } + case DragTool: { + foreach(const QWeakPointer<FormEditorView> &view, m_formEditorViewList) + view->changeToDragTool(); + break; + } + case SelectTool: { + foreach(const QWeakPointer<FormEditorView> &view, m_formEditorViewList) + view->changeToSelectionTool(); + break; + } + case ResizeTool: { + foreach(const QWeakPointer<FormEditorView> &view, m_formEditorViewList) + view->changeToResizeTool(); + break; + } + case AnchorTool: { + foreach(const QWeakPointer<FormEditorView> &view, m_formEditorViewList) + view->changeToAnchorTool(); + break; + } + default: Q_ASSERT(0); + } + emit toolChanged(m_currentTool); +} + +void FormEditorMainView::changeToDragTool() +{ + setCurrentTool(DragTool); +} + + +void FormEditorMainView::changeToMoveTool() +{ + setCurrentTool(MoveTool); +} + +void FormEditorMainView::changeToMoveTool(const QPointF &/*beginPoint*/) +{ + setCurrentTool(MoveTool); +} + +void FormEditorMainView::changeToSelectionTool() +{ + setCurrentTool(SelectTool); +} + +void FormEditorMainView::changeToResizeTool() +{ + setCurrentTool(ResizeTool); +} + +void FormEditorMainView::changeToAnchorTool() +{ + setCurrentTool(AnchorTool); +} + +void FormEditorMainView::changeToTransformTools() +{ + foreach(const QWeakPointer<FormEditorView> &view, m_formEditorViewList) + if (view) + view->changeToTransformTools(); +} + +void FormEditorMainView::anchorsChanged(const NodeState &nodeState) +{ + foreach(const QWeakPointer<FormEditorView> &view, m_formEditorViewList) { + if (view) + view->anchorsChanged(nodeState); + } +} + + +void FormEditorMainView::auxiliaryDataChanged(const ModelNode &node, const QString &name, const QVariant &data) +{ + foreach(const QWeakPointer<FormEditorView> &view, m_formEditorViewList) { + if (view) + view->auxiliaryDataChanged(node, name, data); + } +} + +void FormEditorMainView::nodeSlidedToIndex(const ModelNode &node, int newIndex, int oldIndex) +{ + foreach(const QWeakPointer<FormEditorView> &view, m_formEditorViewList) { + if (view) + view->nodeSlidedToIndex(node, newIndex, oldIndex); + } +} + +ComponentAction *FormEditorMainView::componentAction() const +{ + return m_formMainEditorWidget->componentAction(); +} + +ZoomAction *FormEditorMainView::zoomAction() const +{ + return m_formMainEditorWidget->zoomAction(); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/formeditor/formeditormainview.h b/src/plugins/qmldesigner/components/formeditor/formeditormainview.h new file mode 100644 index 00000000000..05a563f09e7 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/formeditormainview.h @@ -0,0 +1,123 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef FORMEDITORMAINVIEW_H +#define FORMEDITORMAINVIEW_H + +#include <forwardview.h> + +#include <formeditormainwidget.h> +#include <formeditorview.h> +#include <QPointer> + + +namespace QmlDesigner { +class AbstractFormEditorTool; + +class FormEditorMainView : public ForwardView<FormEditorView> +{ + Q_OBJECT + +public: + FormEditorMainView(); + ~FormEditorMainView(); + + // AbstractView + void modelAttached(Model *model); + void modelAboutToBeDetached(Model *model); + void nodeCreated(const ModelNode &createdNode); + void nodeAboutToBeRemoved(const ModelNode &removedNode); + void nodeReparented(const ModelNode &node, const ModelNode &oldParent, const ModelNode &newParent); + void propertiesAdded(const NodeState &state, const QList<NodeProperty>& propertyList); + void propertiesAboutToBeRemoved(const NodeState &state, const QList<NodeProperty>& propertyList); + void propertyValuesChanged(const NodeState& state, const QList<NodeProperty> &propertyList); + void nodeIdChanged(const ModelNode& node, const QString& newId, const QString& oldId); + + void selectedNodesChanged(const QList<ModelNode> &selectedNodeList, + const QList<ModelNode> &lastSelectedNodeList); + + void modelStateAboutToBeRemoved(const ModelState &modelState); + void modelStateAdded(const ModelState &modelState); + + void nodeStatesAboutToBeRemoved(const QList<NodeState> &nodeStateList); + void nodeStatesAdded(const QList<NodeState> &nodeStateList); + + void auxiliaryDataChanged(const ModelNode &node, const QString &name, const QVariant &data); + // FormEditorMainView + FormEditorMainWidget *widget() const; + NodeInstanceView *nodeInstanceView(const ModelState &modelState) const; + + void setCurrentState(const ModelState &state); + ModelState currentState() const; + + enum EditorTool { + MoveTool, + DragTool, + SelectTool, + ResizeTool, + AnchorTool + }; + + EditorTool currentTool() const; + void setCurrentTool(EditorTool tool); + + void changeToMoveTool(); + void changeToMoveTool(const QPointF &beginPoint); + void changeToDragTool(); + void changeToSelectionTool(); + void changeToResizeTool(); + void changeToTransformTools(); + void changeToAnchorTool(); + + void anchorsChanged(const NodeState &nodeState); + void nodeSlidedToIndex(const ModelNode &node, int newIndex, int oldIndex); + + ComponentAction *componentAction() const; + ZoomAction *zoomAction() const; + +signals: + void stateChanged(const ModelState &state); + void toolChanged(EditorTool tool); + +protected: + void setupSubViews(); + void createSubView(const QmlModelState &state); + void removeSubView(const QmlModelState &state); + void resetViews(); + +private: + QWeakPointer<FormEditorMainWidget> m_formMainEditorWidget; + QList<QWeakPointer<FormEditorView> > m_formEditorViewList; + QMap<ModelState, QWeakPointer<QWidget> > m_subWindowMap; + EditorTool m_currentTool; +}; + +} // namespace QmlDesigner + +#endif // FORMEDITORMAINVIEW_H diff --git a/src/plugins/qmldesigner/components/formeditor/formeditormainwidget.cpp b/src/plugins/qmldesigner/components/formeditor/formeditormainwidget.cpp new file mode 100644 index 00000000000..ade4aace517 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/formeditormainwidget.cpp @@ -0,0 +1,156 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "formeditormainwidget.h" + +#include <QAction> +#include <formeditormainview.h> +#include <QtDebug> +#include <QToolBar> +#include <QVBoxLayout> +#include <QFile> +#include "toolbox.h" +#include "componentaction.h" +#include "zoomaction.h" +#include <QWheelEvent> +#include <cmath> + +namespace QmlDesigner { + +FormEditorMainWidget::FormEditorMainWidget(FormEditorMainView *mainView) + : QWidget(), + m_formEditorMainView(mainView), + m_stackedWidget(new QStackedWidget(this)) +{ + QFile file(":/qmldesigner/stylesheet.css"); + file.open(QFile::ReadOnly); + QString styleSheet = QLatin1String(file.readAll()); + setStyleSheet(styleSheet); + + QVBoxLayout *fillLayout = new QVBoxLayout(this); + fillLayout->setMargin(0); + fillLayout->setSpacing(0); + setLayout(fillLayout); + + QActionGroup *toolActionGroup = new QActionGroup(this); + + QAction *transformToolAction = toolActionGroup->addAction("Transform Tool (Press Key Q)"); + transformToolAction->setShortcut(Qt::Key_Q); + transformToolAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); + transformToolAction->setCheckable(true); + transformToolAction->setChecked(true); + transformToolAction->setIcon(QPixmap(":/icon/tool/transform.png")); + connect(transformToolAction, SIGNAL(triggered(bool)), SLOT(changeTransformTool(bool))); + + QAction *anchorToolAction = toolActionGroup->addAction("Anchor Tool (Press Key W)"); + anchorToolAction->setShortcut(Qt::Key_W); + anchorToolAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); + anchorToolAction->setCheckable(true); + anchorToolAction->setIcon(QPixmap(":/icon/tool/anchor.png")); + connect(anchorToolAction, SIGNAL(triggered(bool)), SLOT(changeAnchorTool(bool))); + + addActions(toolActionGroup->actions()); + + + m_componentAction = new ComponentAction(toolActionGroup); + addAction(m_componentAction.data()); + + m_zoomAction = new ZoomAction(toolActionGroup); + addAction(m_zoomAction.data()); + + ToolBox *toolBox = new ToolBox(this); + toolBox->setActions(actions()); + fillLayout->addWidget(toolBox); + + fillLayout->addWidget(m_stackedWidget.data()); +} + +void FormEditorMainWidget::addWidget(QWidget *widget) +{ + m_stackedWidget->addWidget(widget); +} + +QWidget *FormEditorMainWidget::currentWidget() const +{ + return m_stackedWidget->currentWidget(); +} + +void FormEditorMainWidget::setCurrentWidget(QWidget *widget) +{ + m_stackedWidget->setCurrentWidget(widget); +} + +void FormEditorMainWidget::setModel(Model *model) +{ + m_componentAction->setDisabled(true); + + if (model) { + m_componentAction->setModel(model->masterModel()); + m_componentAction->setEnabled(true); + } +} + +void FormEditorMainWidget::changeTransformTool(bool checked) +{ + if (checked) + m_formEditorMainView->changeToTransformTools(); +} + +void FormEditorMainWidget::changeAnchorTool(bool checked) +{ + if (checked) + m_formEditorMainView->changeToAnchorTool(); +} + +ComponentAction *FormEditorMainWidget::componentAction() const +{ + return m_componentAction.data(); +} + +ZoomAction *FormEditorMainWidget::zoomAction() const +{ + return m_zoomAction.data(); +} + +void FormEditorMainWidget::wheelEvent(QWheelEvent *event) +{ + if (event->modifiers().testFlag(Qt::ControlModifier)) { + if (event->delta() > 0) { + zoomAction()->zoomIn(); + } else { + zoomAction()->zoomOut(); + } + + event->accept(); + } else { + QWidget::wheelEvent(event); + } +} + +} diff --git a/src/plugins/qmldesigner/components/formeditor/formeditormainwidget.h b/src/plugins/qmldesigner/components/formeditor/formeditormainwidget.h new file mode 100644 index 00000000000..d07afa5fc42 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/formeditormainwidget.h @@ -0,0 +1,74 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef FORMEDITORMAINWIDGET_H +#define FORMEDITORMAINWIDGET_H + +#include <QStackedWidget> +#include <QWeakPointer> + + + +namespace QmlDesigner { + +class Model; +class FormEditorMainView; +class ComponentAction; +class ZoomAction; + +class FormEditorMainWidget : public QWidget +{ + Q_OBJECT +public: + FormEditorMainWidget(FormEditorMainView *mainView); + + void addWidget(QWidget *widget); + QWidget *currentWidget() const; + void setCurrentWidget(QWidget *widget); + void setModel(Model *model); + + ComponentAction *componentAction() const; + ZoomAction *zoomAction() const; + +protected: + void wheelEvent(QWheelEvent *event); + +private slots: + void changeTransformTool(bool checked); + void changeAnchorTool(bool checked); + +private: + QWeakPointer<FormEditorMainView> m_formEditorMainView; + QWeakPointer<QStackedWidget> m_stackedWidget; + QWeakPointer<ComponentAction> m_componentAction; + QWeakPointer<ZoomAction> m_zoomAction; +}; + +} +#endif // FORMEDITORMAINWIDGET_H diff --git a/src/plugins/qmldesigner/components/formeditor/formeditornodeinstanceview.cpp b/src/plugins/qmldesigner/components/formeditor/formeditornodeinstanceview.cpp new file mode 100644 index 00000000000..7516f55934b --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/formeditornodeinstanceview.cpp @@ -0,0 +1,48 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "formeditornodeinstanceview.h" +#include "formeditorview.h" + +#include <modelnode.h> +#include <QtCore/QDebug> + + +namespace QmlDesigner { + +FormEditorNodeInstanceView::FormEditorNodeInstanceView(QObject * parent) : NodeInstanceView(parent) +{ + +} + +FormEditorNodeInstanceView::~FormEditorNodeInstanceView() +{ +} + +} diff --git a/src/plugins/qmldesigner/components/formeditor/formeditornodeinstanceview.h b/src/plugins/qmldesigner/components/formeditor/formeditornodeinstanceview.h new file mode 100644 index 00000000000..b1c9909e92d --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/formeditornodeinstanceview.h @@ -0,0 +1,50 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef FORMEDITORNODEINSTANCE_H +#define FORMEDITORNODEINSTANCE_H + + +#include <nodeinstanceview.h> + +namespace QmlDesigner { + +class FormEditorView; + +class FormEditorNodeInstanceView : public NodeInstanceView +{ + Q_OBJECT +public: + FormEditorNodeInstanceView(QObject * parent); + ~FormEditorNodeInstanceView(); +}; + + +} +#endif //FORMEDITORNODEINSTANCE_H diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorscene.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorscene.cpp new file mode 100644 index 00000000000..9c7c69d67a5 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/formeditorscene.cpp @@ -0,0 +1,364 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "formeditorscene.h" +#include "formeditorview.h" +#include "formeditorwidget.h" +#include "formeditoritem.h" +#include "movemanipulator.h" +#include <metainfo.h> +#include <QGraphicsSceneDragDropEvent> + +#include <QEvent> +#include <QGraphicsSceneMouseEvent> +#include <QGraphicsSceneHoverEvent> +#include <QGraphicsView> + +#include <QApplication> +#include <QDebug> +#include <QList> + +#include "formeditornodeinstanceview.h" + +#include "resizehandleitem.h" +#include <QtDebug> + + + +namespace QmlDesigner { + +FormEditorScene::FormEditorScene(FormEditorWidget *view, FormEditorView *editorView) + : QGraphicsScene(), + m_editorView(editorView), + m_formLayerItem(new LayerItem(this)), + m_manipulatorLayerItem(new LayerItem(this)), + m_paintMode(NormalMode) + +{ + setSceneRect(0, 0, 1, 1); // prevent automatic calculation (causing a recursion), right size will be set later + + m_manipulatorLayerItem->setZValue(1.0); + m_formLayerItem->setZValue(0.0); + m_formLayerItem->setFlag(QGraphicsItem::ItemClipsChildrenToShape, true); + view->setScene(this); + +// setItemIndexMethod(QGraphicsScene::NoIndex); +} + +FormEditorScene::~FormEditorScene() +{ + clear(); //FormEditorItems have to be cleared before destruction + //Reason: FormEditorItems accesses FormEditorScene in destructor +} + + +FormEditorItem* FormEditorScene::itemForQmlItemNode(const QmlItemNode &qmlItemNode) const +{ + Q_ASSERT(hasItemForQmlItemNode(qmlItemNode)); + return m_qmlItemNodeItemHash.value(qmlItemNode); +} + + +QList<FormEditorItem*> FormEditorScene::itemsForQmlItemNodes(const QList<QmlItemNode> &nodeList) const +{ + QList<FormEditorItem*> itemList; + foreach (const QmlItemNode &node, nodeList) { + if (hasItemForQmlItemNode(node)) + itemList += itemForQmlItemNode(node); + } + + return itemList; +} + +QList<FormEditorItem*> FormEditorScene::allFormEditorItems() const +{ + return m_qmlItemNodeItemHash.values(); +} + +void FormEditorScene::updateAllFormEditorItems() +{ + foreach (FormEditorItem *item, allFormEditorItems()) { + item->update(); + } +} + +bool FormEditorScene::hasItemForQmlItemNode(const QmlItemNode &qmlItemNode) const +{ + return m_qmlItemNodeItemHash.contains(qmlItemNode); +} + +void FormEditorScene::removeItemFromHash(FormEditorItem *item) +{ + m_qmlItemNodeItemHash.remove(item->qmlItemNode()); +} + + +AbstractFormEditorTool* FormEditorScene::currentTool() const +{ + return m_editorView->currentTool(); +} + +//This method calculates the possible parent for reparent +FormEditorItem* FormEditorScene::calulateNewParent(FormEditorItem *formEditorItem) +{ + QList<QGraphicsItem *> list = items(formEditorItem->qmlItemNode().instanceBoundingRect().center()); + foreach (QGraphicsItem *graphicsItem, list) { + if (qgraphicsitem_cast<FormEditorItem*>(graphicsItem) && + graphicsItem->collidesWithItem(formEditorItem, Qt::ContainsItemShape)) + return qgraphicsitem_cast<FormEditorItem*>(graphicsItem); + } + return 0; +} + +void FormEditorScene::synchronizeTransformation(const QmlItemNode &qmlItemNode) +{ + FormEditorItem *item = itemForQmlItemNode(qmlItemNode); + item->updateGeometry(); + item->update(); + + if (qmlItemNode.isRootNode()) { + QRectF sceneRect(qmlItemNode.instanceBoundingRect()); + + setSceneRect(sceneRect); + formLayerItem()->update(); + manipulatorLayerItem()->update(); + } +} + +void FormEditorScene::synchronizeParent(const QmlItemNode &qmlItemNode) +{ + QmlItemNode parentNode = qmlItemNode.instanceParent().toQmlItemNode(); + if (parentNode.isValid()) + reparentItem(qmlItemNode, parentNode); +} + +void FormEditorScene::synchronizeOtherProperty(const QmlItemNode &qmlItemNode) +{ + if (hasItemForQmlItemNode(qmlItemNode)) { + FormEditorItem *item = itemForQmlItemNode(qmlItemNode); + if (item) + item->update(); + } +} + +void FormEditorScene::synchronizeState(const QmlItemNode &qmlItemNode) +{ + if (hasItemForQmlItemNode(qmlItemNode)) { + FormEditorItem *item = itemForQmlItemNode(qmlItemNode); + if (item) + item->update(); + } +} + + +FormEditorItem *FormEditorScene::addFormEditorItem(const QmlItemNode &qmlItemNode) +{ + FormEditorItem *formEditorItem = new FormEditorItem(qmlItemNode, this); + Q_ASSERT(!m_qmlItemNodeItemHash.contains(qmlItemNode)); + + m_qmlItemNodeItemHash.insert(qmlItemNode, formEditorItem); + if (qmlItemNode.isRootNode()) { + QRectF sceneRect(qmlItemNode.instanceBoundingRect()); + + setSceneRect(sceneRect); + formLayerItem()->update(); + manipulatorLayerItem()->update(); + } + + return formEditorItem; +} + + +void FormEditorScene::dropEvent(QGraphicsSceneDragDropEvent * event) +{ + currentTool()->dropEvent(event); + + if (views().first()) + views().first()->setFocus(); +} + +void FormEditorScene::dragEnterEvent(QGraphicsSceneDragDropEvent * event) +{ + currentTool()->dragEnterEvent(event); +} + +void FormEditorScene::dragLeaveEvent(QGraphicsSceneDragDropEvent * event) +{ + currentTool()->dragLeaveEvent(event); + + return; + + if (m_dragNode.isValid()) { + m_dragNode.destroy(); + } +} + +void FormEditorScene::dragMoveEvent(QGraphicsSceneDragDropEvent * event) +{ + currentTool()->dragMoveEvent(event); +} + +QList<QGraphicsItem *> FormEditorScene::removeLayerItems(const QList<QGraphicsItem *> &itemList) +{ + QList<QGraphicsItem *> itemListWithoutLayerItems; + + foreach (QGraphicsItem *item, itemList) + if (item != manipulatorLayerItem() && item != formLayerItem()) + itemListWithoutLayerItems.append(item); + + return itemListWithoutLayerItems; +} + +void FormEditorScene::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + currentTool()->mousePressEvent(removeLayerItems(items(event->scenePos())), event); +} + +void FormEditorScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + if (event->buttons()) + currentTool()->mouseMoveEvent(removeLayerItems(items(event->scenePos())), event); + else + currentTool()->hoverMoveEvent(removeLayerItems(items(event->scenePos())), event); + + event->accept(); +} + +void FormEditorScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + currentTool()->mouseReleaseEvent(removeLayerItems(items(event->scenePos())), event); + + event->accept(); + } + +void FormEditorScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) +{ + currentTool()->mouseDoubleClickEvent(removeLayerItems(items(event->scenePos())), event); + + event->accept(); +} + +void FormEditorScene::keyPressEvent(QKeyEvent *keyEvent) +{ + currentTool()->keyPressEvent(keyEvent); +} + +void FormEditorScene::keyReleaseEvent(QKeyEvent *keyEvent) +{ + currentTool()->keyReleaseEvent(keyEvent); +} + +FormEditorView *FormEditorScene::editorView() const +{ + return m_editorView; +} + +LayerItem* FormEditorScene::manipulatorLayerItem() const +{ + return m_manipulatorLayerItem.data(); +} + +LayerItem* FormEditorScene::formLayerItem() const +{ + return m_formLayerItem.data(); +} + +bool FormEditorScene::event(QEvent * event) +{ + switch(event->type()) + { + case QEvent::GraphicsSceneHoverEnter : + hoverEnterEvent(static_cast<QGraphicsSceneHoverEvent *>(event)); + return true; + case QEvent::GraphicsSceneHoverMove : + hoverMoveEvent(static_cast<QGraphicsSceneHoverEvent *>(event)); + return true; + case QEvent::GraphicsSceneHoverLeave : + hoverLeaveEvent(static_cast<QGraphicsSceneHoverEvent *>(event)); + return true; + default: return QGraphicsScene::event(event); + } + +} + +void FormEditorScene::hoverEnterEvent(QGraphicsSceneHoverEvent * /*event*/) +{ + qDebug() << __FUNCTION__; +} + +void FormEditorScene::hoverMoveEvent(QGraphicsSceneHoverEvent * /*event*/) +{ + qDebug() << __FUNCTION__; +} + +void FormEditorScene::hoverLeaveEvent(QGraphicsSceneHoverEvent * /*event*/) +{ + qDebug() << __FUNCTION__; +} + + +void FormEditorScene::reparentItem(const QmlItemNode &node, const QmlItemNode &newParent) +{ + Q_ASSERT(hasItemForQmlItemNode(node)); + Q_ASSERT(hasItemForQmlItemNode(newParent)); + FormEditorItem *item = itemForQmlItemNode(node); + FormEditorItem *parentItem = itemForQmlItemNode(newParent); + item->setParentItem(parentItem); + item->setParent(parentItem); +} + +FormEditorItem* FormEditorScene::rootFormEditorItem() const +{ + QList<QGraphicsItem*> childItemList(m_formLayerItem->childItems()); + if (!childItemList.isEmpty()) + return FormEditorItem::fromQGraphicsItem(childItemList.first()); + + return 0; +} + +FormEditorScene::PaintMode FormEditorScene::paintMode() const +{ + return m_paintMode; +} + +void FormEditorScene::setPaintMode(PaintMode paintMode) +{ + m_paintMode = paintMode; +} + +void FormEditorScene::clearFormEditorItems() +{ + foreach (QGraphicsItem *item, items()) { + if (qgraphicsitem_cast<FormEditorItem* >(item)) + delete item; + } +} + +} + diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorscene.h b/src/plugins/qmldesigner/components/formeditor/formeditorscene.h new file mode 100644 index 00000000000..89098ba7abc --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/formeditorscene.h @@ -0,0 +1,133 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef FORMEDITORSCENE_H +#define FORMEDITORSCENE_H + + +#include <QGraphicsScene> +#include <QWeakPointer> +#include <QHash> +#include <qmlitemnode.h> +#include "abstractformeditortool.h" + + +class QGraphicsSceneMouseEvent; + +namespace QmlDesigner { + +class FormEditorWidget; +class FormEditorItem; +class FormEditorView; +class LayerItem; + +class FormEditorScene : public QGraphicsScene +{ + Q_OBJECT + + friend class QmlDesigner::FormEditorItem; + friend class QmlDesigner::FormEditorView; + +public: + enum PaintMode { + NormalMode, + AnchorMode + }; + + FormEditorScene(FormEditorWidget *widget, FormEditorView *editorView); + ~FormEditorScene(); + FormEditorItem *addFormEditorItem(const QmlItemNode &qmlItemNode); + + FormEditorItem* itemForQmlItemNode(const QmlItemNode &qmlItemNode) const; + + QList<FormEditorItem*> itemsForQmlItemNodes(const QList<QmlItemNode> &nodeList) const; + QList<FormEditorItem*> allFormEditorItems() const; + + void updateAllFormEditorItems(); + + bool hasItemForQmlItemNode(const QmlItemNode &qmlItemNode) const; + + void synchronizeTransformation(const QmlItemNode &qmlItemNode); + void synchronizeParent(const QmlItemNode &qmlItemNode); + void synchronizeOtherProperty(const QmlItemNode &qmlItemNode); + void synchronizeState(const QmlItemNode &qmlItemNode); + + FormEditorItem* calulateNewParent(FormEditorItem *widget); + bool event(QEvent *event); + LayerItem* manipulatorLayerItem() const; + LayerItem* formLayerItem() const; + FormEditorView *editorView() const; + + FormEditorItem *rootFormEditorItem() const; + + void reparentItem(const QmlItemNode &node, const QmlItemNode &newParent); + + PaintMode paintMode() const; + void setPaintMode(PaintMode paintMode); + + void clearFormEditorItems(); + +protected: + void dropEvent(QGraphicsSceneDragDropEvent * event); + void dragEnterEvent(QGraphicsSceneDragDropEvent * event); + void dragLeaveEvent(QGraphicsSceneDragDropEvent * event); + void dragMoveEvent(QGraphicsSceneDragDropEvent * event); + void mousePressEvent(QGraphicsSceneMouseEvent *event); + void mouseMoveEvent(QGraphicsSceneMouseEvent *event); + void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); + void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event); + + void hoverEnterEvent(QGraphicsSceneHoverEvent *event); + void hoverMoveEvent(QGraphicsSceneHoverEvent *event); + void hoverLeaveEvent(QGraphicsSceneHoverEvent *event); + + void keyPressEvent(QKeyEvent *keyEvent); + void keyReleaseEvent(QKeyEvent *keyEvent); + +private: + QList<QGraphicsItem *> removeLayerItems(const QList<QGraphicsItem *> &itemList); + + AbstractFormEditorTool* currentTool() const; + void removeItemFromHash(FormEditorItem*); + + FormEditorView *m_editorView; + AbstractFormEditorTool *m_currentTool; + QHash<QmlItemNode, FormEditorItem*> m_qmlItemNodeItemHash; + QWeakPointer<LayerItem> m_formLayerItem; + QWeakPointer<LayerItem> m_manipulatorLayerItem; + ModelNode m_dragNode; + PaintMode m_paintMode; +}; + + + + +} +#endif //FORMEDITORSCENE_H + diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorsubwindow.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorsubwindow.cpp new file mode 100644 index 00000000000..8fe38a042e1 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/formeditorsubwindow.cpp @@ -0,0 +1,38 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "formeditorsubwindow.h" + +namespace QmlDesigner { + +FormEditorSubWindow::FormEditorSubWindow() +{ +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorsubwindow.h b/src/plugins/qmldesigner/components/formeditor/formeditorsubwindow.h new file mode 100644 index 00000000000..9376d73b893 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/formeditorsubwindow.h @@ -0,0 +1,45 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef FORMEDITORSUBWINDOW_H +#define FORMEDITORSUBWINDOW_H + +#include <QMdiSubWindow> + +namespace QmlDesigner { + +class FormEditorSubWindow : public QMdiSubWindow +{ +public: + FormEditorSubWindow(); +}; + +} // namespace QmlDesigner + +#endif // FORMEDITORSUBWINDOW_H diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp new file mode 100644 index 00000000000..7da0cb9ed5a --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp @@ -0,0 +1,531 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "selectiontool.h" +#include "movetool.h" +#include "resizetool.h" +#include "anchortool.h" +#include "dragtool.h" +#include "itemcreatortool.h" +#include "formeditorview.h" +#include "formeditorwidget.h" +#include "formeditornodeinstanceview.h" +#include "formeditoritem.h" +#include "formeditorscene.h" +#include <rewritertransaction.h> +#include <modelnode.h> +#include <modelutilities.h> +#include <itemlibraryinfo.h> +#include <metainfo.h> +#include <model.h> +#include <QApplication> +#include <QtDebug> +#include <QPair> +#include <QString> +#include <QDir> +#include <QAction> +#include <zoomaction.h> + +namespace QmlDesigner { + +FormEditorView::FormEditorView(QObject *parent) + : QmlModelView(parent), + m_formEditorWidget(new FormEditorWidget(this)), + m_scene(new FormEditorScene(m_formEditorWidget.data(), this)), + m_moveTool(new MoveTool(this)), + m_selectionTool(new SelectionTool(this)), + m_resizeTool(new ResizeTool(this)), + m_anchorTool(new AnchorTool(this)), + m_dragTool(new DragTool(this)), + m_itemCreatorTool(new ItemCreatorTool(this)), + m_currentTool(m_selectionTool) +{ + connect(widget()->zoomAction(), SIGNAL(zoomLevelChanged(double)), SLOT(updateGraphicsIndicators())); +} + +FormEditorScene* FormEditorView::scene() const +{ + return m_scene.data(); +} + +FormEditorView::~FormEditorView() +{ + delete m_selectionTool; + m_selectionTool = 0; + delete m_moveTool; + m_moveTool = 0; + delete m_resizeTool; + m_resizeTool = 0; + delete m_anchorTool; + m_anchorTool = 0; + delete m_dragTool; + m_dragTool = 0; + + + // delete scene after tools to prevent double deletion + // of items + delete m_scene.data(); + delete m_formEditorWidget.data(); +} + +void FormEditorView::modelAttached(Model *model) +{ + Q_ASSERT(model); + + QmlModelView::modelAttached(model); + + Q_ASSERT(m_scene->formLayerItem()); + + if (rootQmlObjectNode().toQmlItemNode().isValid()) + setupFormEditorItemTree(rootQmlObjectNode().toQmlItemNode()); +} + + +//This method does the setup of the inital FormEditorItem tree in the scene +void FormEditorView::setupFormEditorItemTree(const QmlItemNode &qmlItemNode) +{ + m_scene->addFormEditorItem(qmlItemNode); + + foreach (const QmlItemNode &nextNode, qmlItemNode.children()) //TODO instance children + setupFormEditorItemTree(nextNode); +} + +void FormEditorView::nodeCreated(const ModelNode &createdNode) +{ + QmlModelView::nodeCreated(createdNode); + ModelNode node(createdNode); + if (QmlItemNode(node).isValid()) //only setup QmlItems + setupFormEditorItemTree(QmlItemNode(node)); +} + +void FormEditorView::modelAboutToBeDetached(Model *model) +{ + m_selectionTool->clear(); + m_moveTool->clear(); + m_resizeTool->clear(); + m_anchorTool->clear(); + m_dragTool->clear(); + m_scene->clearFormEditorItems(); + + QmlModelView::modelAboutToBeDetached(model); +} + +void FormEditorView::nodeAboutToBeRemoved(const ModelNode &removedNode) +{ + QmlItemNode qmlItemNode(removedNode); + + if (qmlItemNode.isValid()) { + + FormEditorItem *item = m_scene->itemForQmlItemNode(qmlItemNode); + + QList<QmlItemNode> nodeList; + nodeList.append(qmlItemNode.allSubModelNodes()); + nodeList.append(qmlItemNode); + + QList<FormEditorItem*> removedItemList; + removedItemList.append(scene()->itemsForQmlItemNodes(nodeList)); + m_currentTool->itemsAboutToRemoved(removedItemList); + + delete item; + } + + QmlModelView::nodeAboutToBeRemoved(removedNode); +} + + void FormEditorView::nodeTypeChanged(const ModelNode &node,const QString &type, int majorVersion, int minorVersion) + { + QmlItemNode oldItemNode(node); + if (oldItemNode.isValid() && m_scene->hasItemForQmlItemNode(oldItemNode)) { + FormEditorItem *item = m_scene->itemForQmlItemNode(oldItemNode); + + QList<QmlItemNode> nodeList; + nodeList.append(oldItemNode.allSubModelNodes()); + nodeList.append(oldItemNode); + + QList<FormEditorItem*> removedItemList; + removedItemList.append(scene()->itemsForQmlItemNodes(nodeList)); + m_currentTool->itemsAboutToRemoved(removedItemList); + + delete item; + } + + QmlModelView::nodeTypeChanged(node, type, majorVersion, minorVersion); + + QmlItemNode newItemNode(node); + if (newItemNode.isValid()) //only setup QmlItems + setupFormEditorItemTree(newItemNode); + } + +void FormEditorView::propertiesAboutToBeRemoved(const QList<AbstractProperty>& propertyList) +{ + QmlModelView::propertiesAboutToBeRemoved(propertyList); +} +void FormEditorView::nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent, const NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags propertyChange) +{ + QmlModelView::nodeReparented(node, newPropertyParent, oldPropertyParent, propertyChange); +} + +void FormEditorView::variantPropertiesChanged(const QList<VariantProperty>& propertyList, PropertyChangeFlags propertyChange) +{ + QmlModelView::variantPropertiesChanged(propertyList, propertyChange); +} + +void FormEditorView::bindingPropertiesChanged(const QList<BindingProperty>& propertyList, PropertyChangeFlags propertyChange) +{ + QmlModelView::bindingPropertiesChanged(propertyList, propertyChange); +} + +FormEditorWidget *FormEditorView::widget() const +{ + Q_ASSERT(!m_formEditorWidget.isNull()); + return m_formEditorWidget.data(); +} + +void FormEditorView::nodeIdChanged(const ModelNode& node, const QString& newId, const QString& oldId) +{ + nodeInstanceView()->nodeIdChanged(node, newId, oldId); +} + +void FormEditorView::selectedNodesChanged(const QList<ModelNode> &selectedNodeList, + const QList<ModelNode> &lastSelectedNodeList) +{ + QmlModelView::selectedNodesChanged(selectedNodeList, lastSelectedNodeList); + + m_currentTool->setItems(scene()->itemsForQmlItemNodes(toQmlItemNodeList(selectedNodeList))); + if (scene()->paintMode() == FormEditorScene::AnchorMode) { + foreach (FormEditorItem *item, m_scene->itemsForQmlItemNodes(toQmlItemNodeList(selectedNodeList))) + item->update(); + + foreach (FormEditorItem *item, m_scene->itemsForQmlItemNodes(toQmlItemNodeList(lastSelectedNodeList))) + item->update(); + } + + m_scene->update(); +} + +AbstractFormEditorTool* FormEditorView::currentTool() const +{ + return m_currentTool; +} + +void FormEditorView::changeToMoveTool() +{ + if (m_currentTool == m_moveTool) + return; + + scene()->setPaintMode(FormEditorScene::NormalMode); + m_scene->updateAllFormEditorItems(); + setCursor(Qt::SizeAllCursor); + m_currentTool->clear(); + m_currentTool = m_moveTool; + m_currentTool->clear(); + m_currentTool->setItems(scene()->itemsForQmlItemNodes(selectedQmlItemNodes())); +} + +void FormEditorView::changeToDragTool() +{ + if (m_currentTool == m_dragTool) + return; + + scene()->setPaintMode(FormEditorScene::NormalMode); + m_scene->updateAllFormEditorItems(); + setCursor(Qt::ArrowCursor); + setCurrentState(baseState()); + m_currentTool->clear(); + m_currentTool = m_dragTool; + m_currentTool->clear(); + m_currentTool->setItems(scene()->itemsForQmlItemNodes(selectedQmlItemNodes())); +} + + +void FormEditorView::changeToMoveTool(const QPointF &beginPoint) +{ + if (m_currentTool == m_moveTool) + return; + + scene()->setPaintMode(FormEditorScene::NormalMode); + m_scene->updateAllFormEditorItems(); + setCursor(Qt::SizeAllCursor); + m_currentTool->clear(); + m_currentTool = m_moveTool; + m_currentTool->clear(); + m_currentTool->setItems(scene()->itemsForQmlItemNodes(selectedQmlItemNodes())); + m_moveTool->beginWithPoint(beginPoint); +} + +void FormEditorView::changeToSelectionTool() +{ + if (m_currentTool == m_selectionTool) + return; + + scene()->setPaintMode(FormEditorScene::NormalMode); + m_scene->updateAllFormEditorItems(); + setCursor(Qt::ArrowCursor); + m_currentTool->clear(); + m_currentTool = m_selectionTool; + m_currentTool->clear(); + m_currentTool->setItems(scene()->itemsForQmlItemNodes(selectedQmlItemNodes())); +} + +void FormEditorView::changeToItemCreatorTool() +{ + if(m_currentTool == m_itemCreatorTool) + return; + + scene()->setPaintMode(FormEditorScene::NormalMode); + m_scene->updateAllFormEditorItems(); + setCursor(Qt::CrossCursor); + setCurrentState(baseState()); + m_currentTool->clear(); + m_currentTool = m_itemCreatorTool; + m_currentTool->clear(); + setSelectedQmlItemNodes(QList<QmlItemNode>()); + m_currentTool->setItems(scene()->itemsForQmlItemNodes(selectedQmlItemNodes())); +} + +void FormEditorView::changeToSelectionTool(QGraphicsSceneMouseEvent *event) +{ + if (m_currentTool == m_selectionTool) + return; + + scene()->setPaintMode(FormEditorScene::NormalMode); + m_scene->updateAllFormEditorItems(); + setCursor(Qt::ArrowCursor); + m_currentTool->clear(); + m_currentTool = m_selectionTool; + m_currentTool->clear(); + m_currentTool->setItems(scene()->itemsForQmlItemNodes(selectedQmlItemNodes())); + + m_selectionTool->selectUnderPoint(event); +} + +void FormEditorView::changeToResizeTool() +{ + if (m_currentTool == m_resizeTool) + return; + + scene()->setPaintMode(FormEditorScene::NormalMode); + m_scene->updateAllFormEditorItems(); + setCursor(Qt::ArrowCursor); + m_currentTool->clear(); + m_currentTool = m_resizeTool; + m_currentTool->clear(); + m_currentTool->setItems(scene()->itemsForQmlItemNodes(selectedQmlItemNodes())); +} + +void FormEditorView::changeToAnchorTool() +{ + if (m_currentTool == m_anchorTool) + return; + + scene()->setPaintMode(FormEditorScene::AnchorMode); + m_scene->updateAllFormEditorItems(); + setCursor(Qt::ArrowCursor); + m_currentTool->clear(); + m_currentTool = m_anchorTool; + m_currentTool->clear(); + m_currentTool->setItems(scene()->itemsForQmlItemNodes(selectedQmlItemNodes())); +} + +void FormEditorView::changeToTransformTools() +{ + if (m_currentTool == m_moveTool || + m_currentTool == m_resizeTool || + m_currentTool == m_selectionTool) + return; + + changeToSelectionTool(); +} + +void FormEditorView::setCursor(const QCursor &cursor) +{ + m_formEditorWidget->setCursor(cursor); +} + +bool FormEditorView::isSnapButtonChecked() const +{ + return m_formEditorWidget->isSnapButtonChecked(); +} + + +void FormEditorView::nodeSlidedToIndex(const NodeListProperty &listProperty, int /*newIndex*/, int /*oldIndex*/) +{ + QList<ModelNode> newOrderModelNodeList = listProperty.toModelNodeList(); + foreach(const ModelNode &node, newOrderModelNodeList) { + FormEditorItem *item = m_scene->itemForQmlItemNode(QmlItemNode(node)); + if (item) { + FormEditorItem *oldParentItem = item->parentItem(); + item->setParentItem(0); + item->setParentItem(oldParentItem); + } + } + + m_currentTool->formEditorItemsChanged(scene()->allFormEditorItems()); +} + +void FormEditorView::auxiliaryDataChanged(const ModelNode &node, const QString &name, const QVariant &data) +{ + QmlModelView::auxiliaryDataChanged(node, name, data); + if (name == "invisible") { + FormEditorItem *item(m_scene->itemForQmlItemNode(QmlItemNode(node))); + bool isInvisible = data.toBool(); + item->setVisible(!isInvisible); + ModelNode newNode(node); + if (isInvisible) + newNode.deselectNode(); + } +} + +double FormEditorView::margins() const +{ + return m_formEditorWidget->margins(); +} + +double FormEditorView::spacing() const +{ + return m_formEditorWidget->spacing(); +} + +void FormEditorView::activateItemCreator(const QString &name) +{ + if (m_currentTool == m_itemCreatorTool) { + m_itemCreatorTool->setItemString(name); + return; + } + changeToItemCreatorTool(); + m_itemCreatorTool->setItemString(name); +} + +void FormEditorView::deActivateItemCreator() +{ + if (m_currentTool == m_itemCreatorTool) { + changeToSelectionTool(); + emit ItemCreatorDeActivated(); + } +} + +QList<ModelNode> FormEditorView::adjustStatesForModelNodes(const QList<ModelNode> &nodeList) const +{ + QList<ModelNode> adjustedNodeList; + foreach (const ModelNode &node, nodeList) + adjustedNodeList.append(node); + + return adjustedNodeList; +} + +QmlItemNode findRecursiveQmlItemNode(const QmlObjectNode &firstQmlObjectNode) +{ + QmlObjectNode qmlObjectNode = firstQmlObjectNode; + + while (true) { + QmlItemNode itemNode = qmlObjectNode.toQmlItemNode(); + if (itemNode.isValid()) + return itemNode; + if (qmlObjectNode.hasInstanceParent()) + qmlObjectNode = qmlObjectNode.instanceParent(); + else + break; + } + + return QmlItemNode(); +} + +void FormEditorView::transformChanged(const QmlObjectNode &qmlObjectNode) +{ + QmlItemNode itemNode = qmlObjectNode.toQmlItemNode(); + if (itemNode.isValid() && scene()->hasItemForQmlItemNode(itemNode)) { + m_scene->synchronizeTransformation(itemNode); + m_currentTool->formEditorItemsChanged(QList<FormEditorItem*>() << m_scene->itemForQmlItemNode(itemNode)); + } +} + +void FormEditorView::parentChanged(const QmlObjectNode &qmlObjectNode) +{ + QmlItemNode itemNode = qmlObjectNode.toQmlItemNode(); + QmlItemNode parentNode = qmlObjectNode.instanceParent().toQmlItemNode(); + if (itemNode.isValid() + && scene()->hasItemForQmlItemNode(itemNode) + && parentNode.isValid() + && scene()->hasItemForQmlItemNode(parentNode) + && scene()->itemForQmlItemNode(itemNode)->parentItem() != scene()->itemForQmlItemNode(parentNode)) { + scene()->synchronizeParent(itemNode); + m_currentTool->formEditorItemsChanged(QList<FormEditorItem*>() << m_scene->itemForQmlItemNode(itemNode)); + } +} + +void FormEditorView::otherPropertyChanged(const QmlObjectNode &qmlObjectNode) +{ + Q_ASSERT(qmlObjectNode.isValid()); + + QmlItemNode itemNode = findRecursiveQmlItemNode(qmlObjectNode); + + if (itemNode.isValid() && scene()->hasItemForQmlItemNode(itemNode)) { + m_scene->synchronizeOtherProperty(itemNode); + m_currentTool->formEditorItemsChanged(QList<FormEditorItem*>() << m_scene->itemForQmlItemNode(itemNode)); + } +} + +void FormEditorView::updateGraphicsIndicators() +{ + m_currentTool->formEditorItemsChanged(scene()->allFormEditorItems()); +} + +void FormEditorView::updateItem(const QmlObjectNode &qmlObjectNode) +{ + + Q_ASSERT(qmlObjectNode.isValid()); + + QmlItemNode itemNode = findRecursiveQmlItemNode(qmlObjectNode); + + if (itemNode.isValid() && scene()->hasItemForQmlItemNode(itemNode)) { + m_scene->synchronizeOtherProperty(itemNode); + m_currentTool->formEditorItemsChanged(QList<FormEditorItem*>() << m_scene->itemForQmlItemNode(itemNode)); + } +} + +void FormEditorView::stateChanged(const QmlModelState &newQmlModelState, const QmlModelState &oldQmlModelState) +{ + QmlModelView::stateChanged(newQmlModelState, oldQmlModelState); + + + m_formEditorWidget->anchorToolAction()->setEnabled(newQmlModelState.isBaseState()); + + if (!newQmlModelState.isBaseState() && currentTool() == m_anchorTool) { + changeToTransformTools(); + m_formEditorWidget->transformToolAction()->setChecked(true); + } + +// FormEditorItem *item = m_scene->itemForQmlItemNode(fxObjectNode); +// +// m_currentTool->formEditorItemsChanged(itemList); +} + + +} + diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorview.h b/src/plugins/qmldesigner/components/formeditor/formeditorview.h new file mode 100644 index 00000000000..82b6f18a756 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/formeditorview.h @@ -0,0 +1,141 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef FORMEDITORVIEW_H +#define FORMEDITORVIEW_H + +#include <qmlmodelview.h> + +class QGraphicsScene; +class QGraphicsSceneMouseEvent; + +namespace QmlDesigner { + +class FormEditorWidget; +class FormEditorNodeInstanceView; +class FormEditorScene; +class NodeInstanceView; + +class AbstractFormEditorTool; +class MoveTool; +class SelectionTool; +class ResizeTool; +class AnchorTool; +class DragTool; +class ItemCreatorTool; +class ItemLibraryInfo; +class QmlItemNode; + +class FormEditorView : public QmlModelView +{ + Q_OBJECT + +public: + FormEditorView(QObject *parent); + ~FormEditorView(); + + // AbstractView + void modelAttached(Model *model); + void modelAboutToBeDetached(Model *model); + + void nodeCreated(const ModelNode &createdNode); + void nodeAboutToBeRemoved(const ModelNode &removedNode); + void nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent, const NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags propertyChange); + void nodeIdChanged(const ModelNode& node, const QString& newId, const QString& oldId); + void propertiesAboutToBeRemoved(const QList<AbstractProperty>& propertyList); + void variantPropertiesChanged(const QList<VariantProperty>& propertyList, PropertyChangeFlags propertyChange); + void bindingPropertiesChanged(const QList<BindingProperty>& propertyList, PropertyChangeFlags propertyChange); + void nodeTypeChanged(const ModelNode &node,const QString &type, int majorVersion, int minorVersion); + + void selectedNodesChanged(const QList<ModelNode> &selectedNodeList, + const QList<ModelNode> &lastSelectedNodeList); + + // FormEditorView + FormEditorWidget *widget() const; + AbstractFormEditorTool *currentTool() const; + FormEditorScene *scene() const; + + void changeToMoveTool(); + void changeToMoveTool(const QPointF &beginPoint); + void changeToDragTool(); + void changeToSelectionTool(); + void changeToItemCreatorTool(); + void changeToSelectionTool(QGraphicsSceneMouseEvent *event); + void changeToResizeTool(); + void changeToAnchorTool(); + void changeToTransformTools(); + + void setCursor(const QCursor &cursor); + + bool isSnapButtonChecked() const; + + void nodeSlidedToIndex(const NodeListProperty &listProperty, int newIndex, int oldIndex); + void auxiliaryDataChanged(const ModelNode &node, const QString &name, const QVariant &data); + + double margins() const; + double spacing() const; + void deActivateItemCreator(); + +public slots: + void activateItemCreator(const QString &name); + +signals: + void ItemCreatorDeActivated(); + +protected: + void transformChanged(const QmlObjectNode &qmlObjectNode); + void parentChanged(const QmlObjectNode &qmlObjectNode); + void otherPropertyChanged(const QmlObjectNode &qmlObjectNode); + void updateItem(const QmlObjectNode &qmlObjectNode); + void stateChanged(const QmlModelState &newQmlModelState, const QmlModelState &oldQmlModelState); + +protected slots: + QList<ModelNode> adjustStatesForModelNodes(const QList<ModelNode> &nodeList) const; + void updateGraphicsIndicators(); + +private: //functions + void setupFormEditorItemTree(const QmlItemNode &qmlItemNode); + + +private: //variables + QWeakPointer<FormEditorWidget> m_formEditorWidget; + QWeakPointer<FormEditorScene> m_scene; + MoveTool *m_moveTool; + SelectionTool *m_selectionTool; + ResizeTool *m_resizeTool; + AnchorTool *m_anchorTool; + DragTool *m_dragTool; + ItemCreatorTool *m_itemCreatorTool; + AbstractFormEditorTool *m_currentTool; + +}; + +} + +#endif //FORMEDITORVIEW_H diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp new file mode 100644 index 00000000000..5217711fa4d --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp @@ -0,0 +1,221 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "formeditorwidget.h" + +#include <QWheelEvent> +#include <cmath> +#include <QCoreApplication> +#include <QPushButton> +#include <QFile> +#include <QVBoxLayout> +#include <QActionGroup> +#include <QGraphicsView> +#include <toolbox.h> +#include <zoomaction.h> +#include <formeditorgraphicsview.h> +#include <formeditorscene.h> +#include <formeditorview.h> +#include "numberseriesaction.h" + +namespace QmlDesigner { + +FormEditorWidget::FormEditorWidget(FormEditorView *view) + : QWidget(), + m_formEditorView(view) +{ + QFile file(":/qmldesigner/stylesheet.css"); + file.open(QFile::ReadOnly); + QString styleSheet = QLatin1String(file.readAll()); + setStyleSheet(styleSheet); + + QVBoxLayout *fillLayout = new QVBoxLayout(this); + fillLayout->setMargin(0); + fillLayout->setSpacing(0); + setLayout(fillLayout); + + m_toolActionGroup = new QActionGroup(this); + + m_transformToolAction = m_toolActionGroup->addAction("Transform Tool (Press Key Q)"); + m_transformToolAction->setShortcut(Qt::Key_Q); + m_transformToolAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); + m_transformToolAction->setCheckable(true); + m_transformToolAction->setChecked(true); + m_transformToolAction->setIcon(QPixmap(":/icon/tool/transform.png")); + connect(m_transformToolAction.data(), SIGNAL(triggered(bool)), SLOT(changeTransformTool(bool))); + + m_anchorToolAction = m_toolActionGroup->addAction("Anchor Tool (Press Key W)"); + m_anchorToolAction->setShortcut(Qt::Key_W); + m_anchorToolAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); + m_anchorToolAction->setCheckable(true); + m_anchorToolAction->setIcon(QPixmap(":/icon/tool/anchor.png")); + connect(m_anchorToolAction.data(), SIGNAL(triggered(bool)), SLOT(changeAnchorTool(bool))); + + addActions(m_toolActionGroup->actions()); + + QActionGroup *layoutActionGroup = new QActionGroup(this); + layoutActionGroup->setExclusive(false); + + m_snappingToolAction = layoutActionGroup->addAction("Toogle Snapping (Press Key E)"); + m_snappingToolAction->setShortcut(Qt::Key_E); + m_snappingToolAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); + m_snappingToolAction->setCheckable(true); + m_snappingToolAction->setChecked(true); + m_snappingToolAction->setIcon(QPixmap(":/icon/layout/snapping.png")); + connect(m_snappingToolAction.data(), SIGNAL(triggered(bool)), SLOT(changeSnappingTool(bool))); + + m_snappingMarginAction = new NumberSeriesAction(layoutActionGroup); + m_snappingMarginAction->addEntry("no margins (0)", 0); + m_snappingMarginAction->addEntry("small margin (2)", 2); + m_snappingMarginAction->addEntry("medium margin (6)", 6); + m_snappingMarginAction->addEntry("all in margin (10)", 10); + m_snappingMarginAction->setCurrentEntryIndex(3); + layoutActionGroup->addAction(m_snappingMarginAction.data()); + addActions(layoutActionGroup->actions()); + + m_snappingSpacingAction = new NumberSeriesAction(layoutActionGroup); + m_snappingSpacingAction->addEntry("no spacing (0)", 0); + m_snappingSpacingAction->addEntry("small spacing (2)", 2); + m_snappingSpacingAction->addEntry("medium spacing (4)", 4); + m_snappingSpacingAction->addEntry("all in spacing (6)", 6); + m_snappingSpacingAction->setCurrentEntryIndex(1); + layoutActionGroup->addAction(m_snappingSpacingAction.data()); + + addActions(layoutActionGroup->actions()); + + m_zoomAction = new ZoomAction(toolActionGroup()); + connect(m_zoomAction.data(), SIGNAL(zoomLevelChanged(double)), SLOT(setZoomLevel(double))); + addAction(m_zoomAction.data()); + + QAction *separatorAction = new QAction(toolActionGroup()); + separatorAction->setSeparator(true); + addAction(separatorAction); + + m_toolBox = new ToolBox(this); + toolBox()->setActions(actions()); + fillLayout->addWidget(toolBox()); + + m_graphicsView = new FormEditorGraphicsView(this); + fillLayout->addWidget(m_graphicsView.data()); +} + +void FormEditorWidget::enterEvent(QEvent *event) +{ + setFocus(); + QWidget::enterEvent(event); +} + +void FormEditorWidget::changeTransformTool(bool checked) +{ + if (checked) + m_formEditorView->changeToTransformTools(); +} + +void FormEditorWidget::changeAnchorTool(bool checked) +{ + if (checked && m_formEditorView->currentState().isBaseState()) + m_formEditorView->changeToAnchorTool(); +} + +void FormEditorWidget::changeSnappingTool(bool /*checked*/) +{ + // TODO +} + +void FormEditorWidget::wheelEvent(QWheelEvent *event) +{ + if (event->modifiers().testFlag(Qt::ControlModifier)) { + if (event->delta() > 0) { + zoomAction()->zoomIn(); + } else { + zoomAction()->zoomOut(); + } + + event->accept(); + } else { + QWidget::wheelEvent(event); + } + +} + +ZoomAction *FormEditorWidget::zoomAction() const +{ + return m_zoomAction.data(); +} + +QAction *FormEditorWidget::anchorToolAction() const +{ + return m_anchorToolAction.data(); +} + +QAction *FormEditorWidget::transformToolAction() const +{ + return m_transformToolAction.data(); +} + +void FormEditorWidget::setZoomLevel(double zoomLevel) +{ + m_graphicsView->resetTransform(); + + m_graphicsView->scale(zoomLevel, zoomLevel); +} + +void FormEditorWidget::setScene(FormEditorScene *scene) +{ + m_graphicsView->setScene(scene); +} + +QActionGroup *FormEditorWidget::toolActionGroup() const +{ + return m_toolActionGroup.data(); +} + +ToolBox *FormEditorWidget::toolBox() const +{ + return m_toolBox.data(); +} + +bool FormEditorWidget::isSnapButtonChecked() const +{ + return m_snappingToolAction->isChecked(); +} + +double FormEditorWidget::spacing() const +{ + return m_snappingSpacingAction->currentValue().toDouble(); +} + +double FormEditorWidget::margins() const +{ + return m_snappingMarginAction->currentValue().toDouble(); +} + +} + + diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorwidget.h b/src/plugins/qmldesigner/components/formeditor/formeditorwidget.h new file mode 100644 index 00000000000..2a718507f80 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/formeditorwidget.h @@ -0,0 +1,91 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef FORMEDITORWIDGET_H +#define FORMEDITORWIDGET_H + +#include <QWidget> + +class QActionGroup; + +namespace QmlDesigner { + +class ZoomAction; +class FormEditorView; +class FormEditorScene; +class FormEditorGraphicsView; +class ToolBox; +class NumberSeriesAction; + +class FormEditorWidget : public QWidget +{ + Q_OBJECT +public: + FormEditorWidget(FormEditorView *view); + bool isSnapButtonChecked() const; + + ZoomAction *zoomAction() const; + QAction *anchorToolAction() const; + QAction *transformToolAction() const; + + void setScene(FormEditorScene *scene); + ToolBox *toolBox() const; + + double spacing() const; + double margins() const; + + +protected: + void enterEvent(QEvent *event); + void wheelEvent(QWheelEvent *event); + QActionGroup *toolActionGroup() const; + +private slots: + void changeTransformTool(bool checked); + void changeAnchorTool(bool checked); + void setZoomLevel(double zoomLevel); + void changeSnappingTool(bool checked); + +private: + QWeakPointer<FormEditorView> m_formEditorView; + QWeakPointer<FormEditorGraphicsView> m_graphicsView; + QWeakPointer<ZoomAction> m_zoomAction; + QWeakPointer<QAction> m_anchorToolAction; + QWeakPointer<QAction> m_transformToolAction; + QWeakPointer<QActionGroup> m_toolActionGroup; + QWeakPointer<ToolBox> m_toolBox; + QWeakPointer<QAction> m_snappingToolAction; + QWeakPointer<NumberSeriesAction> m_snappingMarginAction; + QWeakPointer<NumberSeriesAction> m_snappingSpacingAction; +}; + + +} +#endif //FORMEDITORWIDGET_H + diff --git a/src/plugins/qmldesigner/components/formeditor/itemcreatortool.cpp b/src/plugins/qmldesigner/components/formeditor/itemcreatortool.cpp new file mode 100644 index 00000000000..9376ed18dc4 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/itemcreatortool.cpp @@ -0,0 +1,191 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "itemcreatortool.h" +#include "formeditorscene.h" +#include "formeditorview.h" + +#include <metainfo.h> +#include <itemlibrary.h> + +#include <QApplication> +#include <QGraphicsSceneMouseEvent> +#include <QtDebug> +#include <QClipboard> + +namespace QmlDesigner { + +ItemCreatorTool::ItemCreatorTool(FormEditorView *editorView) + : AbstractFormEditorTool(editorView), + m_rubberbandSelectionManipulator(editorView->scene()->manipulatorLayerItem(), editorView) + +{ + +} + + +ItemCreatorTool::~ItemCreatorTool() +{ +} + +void ItemCreatorTool::mousePressEvent(const QList<QGraphicsItem*> &/*itemList*/, + QGraphicsSceneMouseEvent *event) +{ + if (event->button() == Qt::LeftButton) + m_rubberbandSelectionManipulator.begin(event->scenePos()); + else + view()->deActivateItemCreator(); +} + +void ItemCreatorTool::mouseMoveEvent(const QList<QGraphicsItem*> &/*itemList*/, + QGraphicsSceneMouseEvent *event) +{ + view()->setCursor(Qt::CrossCursor); + if (m_rubberbandSelectionManipulator.isActive()) { + m_rubberbandSelectionManipulator.update(event->scenePos()); + } else { + + } +} + +void ItemCreatorTool::hoverMoveEvent(const QList<QGraphicsItem*> &/*itemList*/, + QGraphicsSceneMouseEvent * /*event*/) +{ +} + +void ItemCreatorTool::mouseReleaseEvent(const QList<QGraphicsItem*> &/*itemList*/, + QGraphicsSceneMouseEvent *event) +{ + if (event->button() == Qt::LeftButton) { + if (m_rubberbandSelectionManipulator.isActive()) { + QPointF mouseMovementVector = m_rubberbandSelectionManipulator.beginPoint() - event->scenePos(); + if (mouseMovementVector.toPoint().manhattanLength() < QApplication::startDragDistance()) { + m_rubberbandSelectionManipulator.update(event->scenePos()); + } else { + m_rubberbandSelectionManipulator.update(event->scenePos()); + QRectF rect(m_rubberbandSelectionManipulator.beginPoint(), event->scenePos()); + createAtItem(rect); + m_rubberbandSelectionManipulator.end(); + view()->deActivateItemCreator(); + } + } + } else { + view()->deActivateItemCreator(); + } +} + +void ItemCreatorTool::mouseDoubleClickEvent(const QList<QGraphicsItem*> &/*itemList*/, + QGraphicsSceneMouseEvent * /*event*/) +{ + +} + +void ItemCreatorTool::keyPressEvent(QKeyEvent *event) +{ + switch(event->key()) { + case Qt::Key_Escape: + view()->deActivateItemCreator(); + break; + } +} + +void ItemCreatorTool::keyReleaseEvent(QKeyEvent * /*keyEvent*/) +{ + +} + +void ItemCreatorTool::itemsAboutToRemoved(const QList<FormEditorItem*> &/*itemList*/) +{ + +} + +void ItemCreatorTool::clear() +{ + m_rubberbandSelectionManipulator.clear(); +} + +void ItemCreatorTool::selectedItemsChanged(const QList<FormEditorItem*> &/*itemList*/) +{ +} + +void ItemCreatorTool::formEditorItemsChanged(const QList<FormEditorItem*> &/*itemList*/) +{ +} + +void ItemCreatorTool::setItemString(const QString &itemString) +{ + m_itemString = itemString; +} + +FormEditorItem* ItemCreatorTool::calculateContainer(const QPointF &point) +{ + QList<QGraphicsItem *> list = scene()->items(point); + foreach (QGraphicsItem *item, list) { + FormEditorItem *formEditorItem = FormEditorItem::fromQGraphicsItem(item); + if (formEditorItem && formEditorItem->isContainer()) + return formEditorItem; + } + return 0; +} + +void ItemCreatorTool::createAtItem(const QRectF &rect) +{ + QPointF pos = rect.topLeft(); + + QmlItemNode parentNode = view()->rootQmlItemNode(); + FormEditorItem *parentItem = calculateContainer(pos); + if (parentItem) { + parentNode = parentItem->qmlItemNode(); + pos = parentItem->mapFromScene(pos); + } + + QStringList list = m_itemString.split("^"); + if (list.count() != 2) + return; + if (list.first() == "item") { + RewriterTransaction transaction = view()->beginRewriterTransaction(); + ItemLibraryInfo itemLibraryRepresentation = view()->model()->metaInfo().itemLibraryRepresentation(list.at(1)); + QmlItemNode newNode = view()->createQmlItemNode(itemLibraryRepresentation, pos, parentNode); + newNode.modelNode().variantProperty("width") = rect.width(); + newNode.modelNode().variantProperty("height") = rect.height(); + QList<QmlItemNode> nodeList; + nodeList.append(newNode); + view()->setSelectedQmlItemNodes(nodeList); + } else if (list.first() == "image") { + RewriterTransaction transaction = view()->beginRewriterTransaction(); + QmlItemNode newNode = view()->createQmlItemNodeFromImage(list.at(1), pos, parentNode); + newNode.modelNode().variantProperty("width") = rect.width(); + newNode.modelNode().variantProperty("height") = rect.height(); + QList<QmlItemNode> nodeList; + nodeList.append(newNode); + view()->setSelectedQmlItemNodes(nodeList); + } +} + +} //QmlDesigner diff --git a/src/plugins/qmldesigner/components/formeditor/itemcreatortool.h b/src/plugins/qmldesigner/components/formeditor/itemcreatortool.h new file mode 100644 index 00000000000..c2661039051 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/itemcreatortool.h @@ -0,0 +1,82 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef ITEMCREATORTOOL_H +#define ITEMCREATORTOOL_H + + +#include "abstractformeditortool.h" +#include "rubberbandselectionmanipulator.h" + +#include <QHash> + + +namespace QmlDesigner { + + +class ItemCreatorTool : public AbstractFormEditorTool +{ +public: + ItemCreatorTool(FormEditorView* editorView); + ~ItemCreatorTool(); + + void mousePressEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event); + void mouseMoveEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event); + void mouseReleaseEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event); + void mouseDoubleClickEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event); + void hoverMoveEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event); + void keyPressEvent(QKeyEvent *event); + void keyReleaseEvent(QKeyEvent *keyEvent); + + void itemsAboutToRemoved(const QList<FormEditorItem*> &itemList); + + void clear(); + + void selectedItemsChanged(const QList<FormEditorItem*> &itemList); + + void formEditorItemsChanged(const QList<FormEditorItem*> &itemList); + + void setItemString(const QString &itemString); + + FormEditorItem* calculateContainer(const QPointF &point); + + void createAtItem(const QRectF &rect); + +private: + RubberBandSelectionManipulator m_rubberbandSelectionManipulator; + QString m_itemString; +}; + +} +#endif // ITEMCREATORTOOL_H diff --git a/src/plugins/qmldesigner/components/formeditor/itemutilfunctions.cpp b/src/plugins/qmldesigner/components/formeditor/itemutilfunctions.cpp new file mode 100644 index 00000000000..405b617e9da --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/itemutilfunctions.cpp @@ -0,0 +1,78 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "itemutilfunctions.h" +#include <modelutilities.h> +#include <formeditorscene.h> + +#include <QRectF> +#include <QtDebug> + +namespace QmlDesigner { + +class FormEditorItem; + +QRectF boundingRectForItemList(QList<FormEditorItem*> itemList) +{ + QRectF boundingRect; + + foreach (FormEditorItem *item, itemList) + { + boundingRect = boundingRect.united(item->mapToScene(item->qmlItemNode().instanceBoundingRect()).boundingRect()); + } + + return boundingRect; +} + +//ModelNode parentNodeSemanticallyChecked(const ModelNode &childNode, const ModelNode &parentNode) +//{ +// if (ModelUtilities::canReparent(childNode, parentNode)) { +// ModelNode newParentNode; +// newParentNode = ModelUtilities::mapGraphicsViewToGraphicsScene(parentNode); +// if (newParentNode == childNode.parentNode()) +// return ModelNode(); +// if (ModelUtilities::isQWidget(childNode)) +// return ModelUtilities::reparentAndProxymize(childNode, newParentNode); +// else +// return parentNode; +// } +// +// return ModelNode(); +//} + +//FormEditorItem* mapGraphicsViewToGraphicsScene(FormEditorItem* item) +//{ +// if (item == 0) //null pointer +// return 0; +// ModelNode newNode = ModelUtilities::mapGraphicsViewToGraphicsScene(item->modelNode()); +// return item->scene()->itemForNode(newNode); +//} + + +} diff --git a/src/plugins/qmldesigner/components/formeditor/itemutilfunctions.h b/src/plugins/qmldesigner/components/formeditor/itemutilfunctions.h new file mode 100644 index 00000000000..95760f2cb85 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/itemutilfunctions.h @@ -0,0 +1,48 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef ITEMUTILFUNCTIONS_H +#define ITEMUTILFUNCTIONS_H + +#include <QRectF> +#include <QGraphicsItem> +#include "formeditoritem.h" + + +namespace QmlDesigner { + +QRectF boundingRectForItemList(QList<FormEditorItem*> itemList); + +ModelNode parentNodeSemanticallyChecked(const ModelNode &childNode, const ModelNode &parentNode); + +FormEditorItem* mapGraphicsViewToGraphicsScene(FormEditorItem* item); + +} + +#endif // ITEMUTILFUNCTIONS_H diff --git a/src/plugins/qmldesigner/components/formeditor/layeritem.cpp b/src/plugins/qmldesigner/components/formeditor/layeritem.cpp new file mode 100644 index 00000000000..5a948115316 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/layeritem.cpp @@ -0,0 +1,73 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "layeritem.h" + +#include <formeditorscene.h> + + +namespace QmlDesigner { + +LayerItem::LayerItem(FormEditorScene* scene) + : QGraphicsObject() +{ + scene->addItem(this); + setZValue(1); + setFlag(QGraphicsItem::ItemIsMovable, false); +} + +LayerItem::~LayerItem() +{ +} + +void LayerItem::paint(QPainter * /*painter*/, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/) +{ +} + +QRectF LayerItem::boundingRect() const +{ + return childrenBoundingRect(); +} + +QList<QGraphicsItem*> LayerItem::findAllChildItems() const +{ + return findAllChildItems(this); +} + +QList<QGraphicsItem*> LayerItem::findAllChildItems(const QGraphicsItem *item) const +{ + QList<QGraphicsItem*> itemList(item->childItems()); + + foreach (QGraphicsItem *childItem, item->childItems()) + itemList += findAllChildItems(childItem); + + return itemList; +} + +} diff --git a/src/plugins/qmldesigner/components/formeditor/layeritem.h b/src/plugins/qmldesigner/components/formeditor/layeritem.h new file mode 100644 index 00000000000..09881a065c2 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/layeritem.h @@ -0,0 +1,66 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef LAYERITEM_H +#define LAYERITEM_H + +#include <QGraphicsObject> +#include <QWeakPointer> + +namespace QmlDesigner { + +class FormEditorScene; + +class LayerItem : public QGraphicsObject +{ +public: + enum + { + Type = 0xEAAA + }; + LayerItem(FormEditorScene* scene); + ~LayerItem(); + void paint ( QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = 0 ); + QRectF boundingRect() const; + int type() const; + + QList<QGraphicsItem*> findAllChildItems() const; + +protected: + QList<QGraphicsItem*> findAllChildItems(const QGraphicsItem *item) const; +}; + +inline int LayerItem::type() const +{ + return Type; +} + +} + +#endif // LAYERITEM_H diff --git a/src/plugins/qmldesigner/components/formeditor/manipulatorlayeritem.h b/src/plugins/qmldesigner/components/formeditor/manipulatorlayeritem.h new file mode 100644 index 00000000000..215d1702fe8 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/manipulatorlayeritem.h @@ -0,0 +1,39 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef MANIPULATORLAYERITEM_H +#define MANIPULATORLAYERITEM_H + +class ManipulatorLayerItem +{ +public: + ManipulatorLayerItem(); +}; + +#endif // MANIPULATORLAYERITEM_H diff --git a/src/plugins/qmldesigner/components/formeditor/movemanipulator.cpp b/src/plugins/qmldesigner/components/formeditor/movemanipulator.cpp new file mode 100644 index 00000000000..fe0ca9893aa --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/movemanipulator.cpp @@ -0,0 +1,363 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "movemanipulator.h" +#include "itemutilfunctions.h" +#include "layeritem.h" +#include "formeditoritem.h" +#include "formeditorscene.h" + +#include <QPointF> +#include <QtDebug> +#include <QColor> +#include <QPen> +#include <QApplication> + +#include <limits> +#include <model.h> +#include <qmlanchors.h> + + +namespace QmlDesigner { + +MoveManipulator::MoveManipulator(LayerItem *layerItem, FormEditorView *view) + : m_layerItem(layerItem), + m_view(view), + m_isActive(false) +{ +} + +MoveManipulator::~MoveManipulator() +{ + deleteSnapLines(); +} + +QPointF MoveManipulator::beginPoint() const +{ + return m_beginPoint; +} + +void MoveManipulator::setItem(FormEditorItem* item) +{ + QList<FormEditorItem*> itemList; + itemList.append(item); + + setItems(itemList); +} + +void MoveManipulator::setItems(const QList<FormEditorItem*> &itemList) +{ + m_itemList = itemList; + if (!m_itemList.isEmpty()) { + if (m_itemList.first()->parentItem()) + m_snapper.setContainerFormEditorItem(m_itemList.first()->parentItem()); + else + m_snapper.setContainerFormEditorItem(m_itemList.first()); + m_snapper.setTransformtionSpaceFormEditorItem(m_snapper.containerFormEditorItem()); + } +} + +void MoveManipulator::updateHashes() +{ +// foreach (FormEditorItem* item, m_itemList) +// m_beginItemRectHash[item] = item->mapRectToParent(item->qmlItemNode().instanceBoundingRect()); + + foreach (FormEditorItem* item, m_itemList) { + QPointF positionInParentSpace = m_snapper.containerFormEditorItem()->mapFromScene(m_beginPositionInSceneSpaceHash.value(item)); + m_beginItemRectHash[item].translate(positionInParentSpace - m_beginPositionHash.value(item)); + m_beginPositionHash.insert(item, positionInParentSpace); + } +} + +bool MoveManipulator::itemsCanReparented() const +{ + foreach (FormEditorItem* item, m_itemList) { + if (!item->qmlItemNode().canReparent()) + return false; + } + + return true; +} + +void MoveManipulator::begin(const QPointF &beginPoint) +{ + m_isActive = true; + + m_snapper.updateSnappingLines(m_itemList); + + + foreach (FormEditorItem* item, m_itemList) + m_beginItemRectHash.insert(item, m_snapper.containerFormEditorItem()->mapRectFromItem(item, item->qmlItemNode().instanceBoundingRect())); + + foreach (FormEditorItem* item, m_itemList) { + QPointF positionInParentSpace(item->qmlItemNode().instancePosition()); + QPointF positionInScenesSpace = m_snapper.containerFormEditorItem()->mapToScene(positionInParentSpace); + m_beginPositionInSceneSpaceHash.insert(item, positionInScenesSpace); + } + + foreach (FormEditorItem* item, m_itemList) { + QPointF positionInParentSpace = m_snapper.containerFormEditorItem()->mapFromScene(m_beginPositionInSceneSpaceHash.value(item)); + m_beginPositionHash.insert(item, positionInParentSpace); + + QmlAnchors anchors(item->qmlItemNode().anchors()); + m_beginTopMarginHash.insert(item, anchors.instanceMargin(AnchorLine::Top)); + m_beginLeftMarginHash.insert(item, anchors.instanceMargin(AnchorLine::Left)); + m_beginRightMarginHash.insert(item, anchors.instanceMargin(AnchorLine::Right)); + m_beginBottomMarginHash.insert(item, anchors.instanceMargin(AnchorLine::Bottom)); + m_beginHorizontalCenterHash.insert(item, anchors.instanceMargin(AnchorLine::HorizontalCenter)); + m_beginVerticalCenterHash.insert(item, anchors.instanceMargin(AnchorLine::VerticalCenter)); + } + + m_beginPoint = beginPoint; + +// setOpacityForAllElements(0.62); + + m_rewriterTransaction = m_view->beginRewriterTransaction(); +} + + + + + +QPointF MoveManipulator::findSnappingOffset(const QList<QRectF> &boundingRectList) +{ + QPointF offset; + + QMap<double, double> verticalOffsetMap; + foreach (const QRectF &boundingRect, boundingRectList) { + double verticalOffset = m_snapper.snappedVerticalOffset(boundingRect); + if (verticalOffset < std::numeric_limits<double>::max()) + verticalOffsetMap.insert(qAbs(verticalOffset), verticalOffset); + } + + + if (!verticalOffsetMap.isEmpty()) + offset.rx() = verticalOffsetMap.begin().value(); + + + + QMap<double, double> horizontalOffsetMap; + foreach (const QRectF &boundingRect, boundingRectList) { + double horizontalOffset = m_snapper.snappedHorizontalOffset(boundingRect); + if (horizontalOffset < std::numeric_limits<double>::max()) + horizontalOffsetMap.insert(qAbs(horizontalOffset), horizontalOffset); + } + + + if (!horizontalOffsetMap.isEmpty()) + offset.ry() = horizontalOffsetMap.begin().value(); + + return offset; +} + + +void MoveManipulator::generateSnappingLines(const QList<QRectF> &boundingRectList) +{ + m_graphicsLineList = m_snapper.generateSnappingLines(boundingRectList, + m_layerItem.data(), + m_snapper.transformtionSpaceFormEditorItem()->sceneTransform()); +} + + + +QList<QRectF> MoveManipulator::tanslatedBoundingRects(const QList<QRectF> &boundingRectList, const QPointF& offsetVector) +{ + QList<QRectF> translatedBoundingRectList; + foreach (const QRectF &boundingRect, boundingRectList) + translatedBoundingRectList.append(boundingRect.translated(offsetVector)); + + return translatedBoundingRectList; +} + + + +/* + /brief updates the position of the items. +*/ +void MoveManipulator::update(const QPointF& updatePoint, Snapping useSnapping) +{ + deleteSnapLines(); //Since they position is changed and the item is moved the snapping lines are + //are obsolete. The new updated snapping lines (color and visibility) will be + //calculated in snapPoint() called in moveNode() later + + if (m_itemList.isEmpty()) { + return; + } else { + QPointF updatePointInContainerSpace(m_snapper.containerFormEditorItem()->mapFromScene(updatePoint)); + QPointF beginPointInContainerSpace(m_snapper.containerFormEditorItem()->mapFromScene(m_beginPoint)); + + QPointF offsetVector(updatePointInContainerSpace - beginPointInContainerSpace); + if (useSnapping == UseSnapping) { + offsetVector -= findSnappingOffset(tanslatedBoundingRects(m_beginItemRectHash.values(), offsetVector)); + generateSnappingLines(tanslatedBoundingRects(m_beginItemRectHash.values(), offsetVector)); + } + + foreach (FormEditorItem* item, m_itemList) { + QPointF positionInContainerSpace(m_beginPositionHash.value(item) + offsetVector); + QmlAnchors anchors(item->qmlItemNode().anchors()); + + if (anchors.instanceHasAnchor(AnchorLine::Top)) { + anchors.setMargin(AnchorLine::Top, m_beginTopMarginHash.value(item) + offsetVector.y()); + } + + if (anchors.instanceHasAnchor(AnchorLine::Left)) { + anchors.setMargin(AnchorLine::Left, m_beginLeftMarginHash.value(item) + offsetVector.x()); + } + + if (anchors.instanceHasAnchor(AnchorLine::Bottom)) { + anchors.setMargin(AnchorLine::Bottom, m_beginBottomMarginHash.value(item) - offsetVector.y()); + } + + if (anchors.instanceHasAnchor(AnchorLine::Right)) { + anchors.setMargin(AnchorLine::Right, m_beginRightMarginHash.value(item) - offsetVector.x()); + } + + if (anchors.instanceHasAnchor(AnchorLine::HorizontalCenter)) { + anchors.setMargin(AnchorLine::HorizontalCenter, m_beginHorizontalCenterHash.value(item) + offsetVector.x()); + } + + if (anchors.instanceHasAnchor(AnchorLine::VerticalCenter)) { + anchors.setMargin(AnchorLine::VerticalCenter, m_beginVerticalCenterHash.value(item) + offsetVector.y()); + } + + item->qmlItemNode().setPosition(positionInContainerSpace); + } + + + } +} + +void MoveManipulator::clear() +{ + deleteSnapLines(); + m_beginItemRectHash.clear(); + m_beginPositionHash.clear(); + m_beginPositionInSceneSpaceHash.clear(); + m_itemList.clear(); + + m_rewriterTransaction.commit(); + + m_beginTopMarginHash.clear(); + m_beginLeftMarginHash.clear(); + m_beginRightMarginHash.clear(); + m_beginBottomMarginHash.clear(); + m_beginHorizontalCenterHash.clear(); + m_beginVerticalCenterHash.clear(); +} + +void MoveManipulator::reparentTo(FormEditorItem *newParent) +{ + deleteSnapLines(); + + if (!newParent) + return; + + if (!itemsCanReparented()) + return; + + foreach (FormEditorItem* item, m_itemList) { + QmlItemNode parent(newParent->qmlItemNode()); + if (parent.isValid()) { + item->qmlItemNode().setParentProperty(parent.nodeAbstractProperty("data")); + } + } + + if (m_view->model()) { + m_snapper.setContainerFormEditorItem(newParent); + m_snapper.setTransformtionSpaceFormEditorItem(m_snapper.containerFormEditorItem()); + m_snapper.updateSnappingLines(m_itemList); + updateHashes(); + } +} + + +void MoveManipulator::end(const QPointF &/*endPoint*/) +{ + m_isActive = false; + deleteSnapLines(); +// setOpacityForAllElements(1.0); + clear(); +} + +void MoveManipulator::moveBy(double deltaX, double deltaY) +{ + foreach (FormEditorItem* item, m_itemList) { + QmlAnchors anchors(item->qmlItemNode().anchors()); + + if (anchors.instanceHasAnchor(AnchorLine::Top)) { + anchors.setMargin(AnchorLine::Top, anchors.instanceMargin(AnchorLine::Top) - deltaY); + } + + if (anchors.instanceHasAnchor(AnchorLine::Left)) { + anchors.setMargin(AnchorLine::Left, anchors.instanceMargin(AnchorLine::Left) + deltaX); + } + + if (anchors.instanceHasAnchor(AnchorLine::Bottom)) { + anchors.setMargin(AnchorLine::Bottom, anchors.instanceMargin(AnchorLine::Bottom) + deltaY); + } + + if (anchors.instanceHasAnchor(AnchorLine::Right)) { + anchors.setMargin(AnchorLine::Right, anchors.instanceMargin(AnchorLine::Right) - deltaX); + } + + if (anchors.instanceHasAnchor(AnchorLine::HorizontalCenter)) { + anchors.setMargin(AnchorLine::HorizontalCenter, anchors.instanceMargin(AnchorLine::HorizontalCenter) + deltaX); + } + + if (anchors.instanceHasAnchor(AnchorLine::VerticalCenter)) { + anchors.setMargin(AnchorLine::VerticalCenter, anchors.instanceMargin(AnchorLine::VerticalCenter) + deltaY); + } + + item->qmlItemNode().setPosition(QPointF(item->qmlItemNode().instanceValue("x").toDouble() + deltaX, + item->qmlItemNode().instanceValue("y").toDouble() + deltaY)); + } +} + +void MoveManipulator::setOpacityForAllElements(qreal opacity) +{ + foreach (FormEditorItem* item, m_itemList) + item->setOpacity(opacity); +} + +void MoveManipulator::deleteSnapLines() +{ + if (m_layerItem) { + foreach (QGraphicsItem *item, m_graphicsLineList) + m_layerItem->scene()->removeItem(item); + } + m_graphicsLineList.clear(); + m_view->scene()->update(); +} + +bool MoveManipulator::isActive() const +{ + return m_isActive; +} + +} diff --git a/src/plugins/qmldesigner/components/formeditor/movemanipulator.h b/src/plugins/qmldesigner/components/formeditor/movemanipulator.h new file mode 100644 index 00000000000..3501625b4d9 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/movemanipulator.h @@ -0,0 +1,114 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef MOVEMANIPULATOR_H +#define MOVEMANIPULATOR_H + +#include <QWeakPointer> +#include <QGraphicsItem> +#include <QGraphicsLineItem> +#include <QHash> +#include <QPointF> +#include <QRectF> + +#include "controlelement.h" +#include "formeditoritem.h" +#include "rewritertransaction.h" +#include "snapper.h" +#include "formeditorview.h" + +namespace QmlDesigner { + +class ModelNodeChangeSet; +class Model; + +class MoveManipulator +{ +public: + enum Snapping { + UseSnapping, + NoSnapping + }; + + MoveManipulator(LayerItem *layerItem, FormEditorView *view); + ~MoveManipulator(); + void setItems(const QList<FormEditorItem*> &itemList); + void setItem(FormEditorItem* item); + + void begin(const QPointF& beginPoint); + void update(const QPointF& updatePoint, Snapping useSnapping); + void reparentTo(FormEditorItem *newParent); + void end(const QPointF& endPoint); + + void moveBy(double deltaX, double deltaY); + + QPointF beginPoint() const; + + void clear(); + + bool isActive() const; + +protected: + void setOpacityForAllElements(qreal opacity); + + QPointF findSnappingOffset(const QList<QRectF> &boundingRectList); + + void deleteSnapLines(); + + QList<QRectF> tanslatedBoundingRects(const QList<QRectF> &boundingRectList, const QPointF& offset); + QPointF calculateBoundingRectMovementOffset(const QPointF& updatePoint); + QRectF boundingRect(FormEditorItem* item, const QPointF& updatePoint); + + void generateSnappingLines(const QList<QRectF> &boundingRectList); + void updateHashes(); + + bool itemsCanReparented() const; + +private: + Snapper m_snapper; + QWeakPointer<LayerItem> m_layerItem; + QWeakPointer<FormEditorView> m_view; + QList<FormEditorItem*> m_itemList; + QHash<FormEditorItem*, QRectF> m_beginItemRectHash; + QHash<FormEditorItem*, QPointF> m_beginPositionHash; + QHash<FormEditorItem*, QPointF> m_beginPositionInSceneSpaceHash; + QPointF m_beginPoint; + QHash<FormEditorItem*, double> m_beginTopMarginHash; + QHash<FormEditorItem*, double> m_beginLeftMarginHash; + QHash<FormEditorItem*, double> m_beginRightMarginHash; + QHash<FormEditorItem*, double> m_beginBottomMarginHash; + QHash<FormEditorItem*, double> m_beginHorizontalCenterHash; + QHash<FormEditorItem*, double> m_beginVerticalCenterHash; + QList<QGraphicsItem*> m_graphicsLineList; + bool m_isActive; + RewriterTransaction m_rewriterTransaction; +}; + +} +#endif // MOVEMANIPULATOR_H diff --git a/src/plugins/qmldesigner/components/formeditor/movetool.cpp b/src/plugins/qmldesigner/components/formeditor/movetool.cpp new file mode 100644 index 00000000000..09f5ec73c16 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/movetool.cpp @@ -0,0 +1,345 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "movetool.h" + +#include "formeditorscene.h" +#include "formeditorview.h" +#include "modelutilities.h" +#include "itemutilfunctions.h" + +#include "resizehandleitem.h" + +#include <QApplication> +#include <QGraphicsSceneMouseEvent> +#include <QtDebug> + +namespace QmlDesigner { + +MoveTool::MoveTool(FormEditorView *editorView) + : AbstractFormEditorTool(editorView), + m_moveManipulator(editorView->scene()->manipulatorLayerItem(), editorView), + m_selectionIndicator(editorView->scene()->manipulatorLayerItem()), + m_resizeIndicator(editorView->scene()->manipulatorLayerItem()) +{ +// view()->setCursor(Qt::SizeAllCursor); +} + + +MoveTool::~MoveTool() +{ + +} + +void MoveTool::clear() +{ + m_moveManipulator.clear(); + m_movingItems.clear(); + m_selectionIndicator.clear(); + m_resizeIndicator.clear(); +} + +void MoveTool::mousePressEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event) +{ + if (itemList.isEmpty()) + return; + m_movingItems = movingItems(items()); + if (m_movingItems.isEmpty()) + return; + + m_moveManipulator.setItems(m_movingItems); + m_moveManipulator.begin(event->scenePos()); +} + +void MoveTool::mouseMoveEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event) +{ + if (m_movingItems.isEmpty()) + return; + +// m_selectionIndicator.hide(); + m_resizeIndicator.hide(); + + FormEditorItem *containerItem = containerFormEditorItem(itemList, m_movingItems); + if (containerItem && + containerItem != m_movingItems.first()->parentItem() && + view()->currentState().isBaseState() && + !event->modifiers().testFlag(Qt::ShiftModifier)) { + + m_moveManipulator.reparentTo(containerItem); + } + + MoveManipulator::Snapping useSnapping = MoveManipulator::NoSnapping; + if (event->modifiers().testFlag(Qt::ControlModifier) != view()->isSnapButtonChecked()) + useSnapping = MoveManipulator::UseSnapping; + + m_moveManipulator.update(event->scenePos(), useSnapping); +} + +void MoveTool::hoverMoveEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent * /*event*/) +{ + if (itemList.isEmpty()) { + view()->changeToSelectionTool(); + return; + } + + ResizeHandleItem* resizeHandle = ResizeHandleItem::fromGraphicsItem(itemList.first()); + if (resizeHandle) { + view()->changeToResizeTool(); + return; + } + + if (!topSelectedItemIsMovable(itemList)) { + view()->changeToSelectionTool(); + return; + } +} + +void MoveTool::keyPressEvent(QKeyEvent *event) +{ + switch(event->key()) { + case Qt::Key_Shift: + case Qt::Key_Alt: + case Qt::Key_Control: + case Qt::Key_AltGr: + event->setAccepted(false); + return; + } + + double moveStep = 1.0; + + if (event->modifiers().testFlag(Qt::ShiftModifier)) + moveStep = 10.0; + + if (!event->isAutoRepeat()) { + QList<FormEditorItem*> movableItems(movingItems(items())); + if (movableItems.isEmpty()) + return; + + m_moveManipulator.setItems(movableItems); +// m_selectionIndicator.hide(); + m_resizeIndicator.hide(); + } + + switch(event->key()) { + case Qt::Key_Left: m_moveManipulator.moveBy(-moveStep, 0.0); break; + case Qt::Key_Right: m_moveManipulator.moveBy(moveStep, 0.0); break; + case Qt::Key_Up: m_moveManipulator.moveBy(0.0, -moveStep); break; + case Qt::Key_Down: m_moveManipulator.moveBy(0.0, moveStep); break; + } + + +} + +void MoveTool::keyReleaseEvent(QKeyEvent *keyEvent) +{ + switch(keyEvent->key()) { + case Qt::Key_Shift: + case Qt::Key_Alt: + case Qt::Key_Control: + case Qt::Key_AltGr: + keyEvent->setAccepted(false); + return; + } + + if (!keyEvent->isAutoRepeat()) { + m_moveManipulator.clear(); +// m_selectionIndicator.show(); + m_resizeIndicator.show(); + } +} + +void MoveTool::mouseReleaseEvent(const QList<QGraphicsItem*> &/*itemList*/, + QGraphicsSceneMouseEvent *event) +{ + if (m_movingItems.isEmpty()) + return; + + QLineF moveVector(event->scenePos(), m_moveManipulator.beginPoint()); + if (moveVector.length() < QApplication::startDragDistance()) + { + QPointF beginPoint(m_moveManipulator.beginPoint()); + + m_moveManipulator.end(beginPoint); + +// m_selectionIndicator.show(); + m_resizeIndicator.show(); + m_movingItems.clear(); + + view()->changeToSelectionTool(event); + } else { + m_moveManipulator.end(event->scenePos()); + + m_selectionIndicator.show(); + m_resizeIndicator.show(); + m_movingItems.clear(); + } +} + +void MoveTool::mouseDoubleClickEvent(const QList<QGraphicsItem*> & /*itemList*/, + QGraphicsSceneMouseEvent * /*event*/) +{ + +} + +void MoveTool::itemsAboutToRemoved(const QList<FormEditorItem*> &removedItemList) +{ + foreach (FormEditorItem* removedItem, removedItemList) + m_movingItems.removeOne(removedItem); +} + +void MoveTool::selectedItemsChanged(const QList<FormEditorItem*> &itemList) +{ + m_selectionIndicator.setItems(movingItems(itemList)); + m_resizeIndicator.setItems(itemList); + updateMoveManipulator(); +} + +bool MoveTool::haveSameParent(const QList<FormEditorItem*> &itemList) +{ + if (itemList.isEmpty()) + return false; + + QGraphicsItem *firstParent = itemList.first()->parentItem(); + foreach (FormEditorItem* item, itemList) + { + if (firstParent != item->parentItem()) + return false; + } + + return true; +} + +bool MoveTool::isAncestorOfAllItems(FormEditorItem* maybeAncestorItem, + const QList<FormEditorItem*> &itemList) +{ + foreach (FormEditorItem* item, itemList) + { + if (!maybeAncestorItem->isAncestorOf(item) && item != maybeAncestorItem) + return false; + } + + return true; +} + + +FormEditorItem* MoveTool::ancestorIfOtherItemsAreChild(const QList<FormEditorItem*> &itemList) +{ + if (itemList.isEmpty()) + return 0; + + + foreach (FormEditorItem* item, itemList) + { + if (isAncestorOfAllItems(item, itemList)) + return item; + } + + return 0; +} + +void MoveTool::updateMoveManipulator() +{ + if (m_moveManipulator.isActive()) + return; +} + +void MoveTool::beginWithPoint(const QPointF &beginPoint) +{ + m_movingItems = movingItems(items()); + if (m_movingItems.isEmpty()) + return; + + m_moveManipulator.setItems(m_movingItems); + m_moveManipulator.begin(beginPoint); +} + +static bool isNotAncestorOfItemInList(FormEditorItem *formEditorItem, const QList<FormEditorItem*> &itemList) +{ + foreach (FormEditorItem *item, itemList) { + if (item->qmlItemNode().isAncestorOf(formEditorItem->qmlItemNode())) + return false; + } + + return true; +} + +FormEditorItem* MoveTool::containerFormEditorItem(const QList<QGraphicsItem*> &itemUnderMouseList, + const QList<FormEditorItem*> &selectedItemList) +{ + Q_ASSERT(!selectedItemList.isEmpty()); + + foreach (QGraphicsItem* item, itemUnderMouseList) { + FormEditorItem *formEditorItem = FormEditorItem::fromQGraphicsItem(item); + if (formEditorItem + && !selectedItemList.contains(formEditorItem) + && isNotAncestorOfItemInList(formEditorItem, selectedItemList)) + if (formEditorItem->isContainer()) { + return formEditorItem; + } + + } + + return 0; +} + + +QList<FormEditorItem*> MoveTool::movingItems(const QList<FormEditorItem*> &selectedItemList) +{ + FormEditorItem* ancestorItem = ancestorIfOtherItemsAreChild(selectedItemList); + + if (ancestorItem != 0 && ancestorItem->qmlItemNode().isRootNode()) { +// view()->changeToSelectionTool(); + return QList<FormEditorItem*>(); + } + + + if (ancestorItem != 0 && ancestorItem->parentItem() != 0) { + QList<FormEditorItem*> ancestorItemList; + ancestorItemList.append(ancestorItem); + return ancestorItemList; + } + + if (!haveSameParent(selectedItemList)) { +// view()->changeToSelectionTool(); + return QList<FormEditorItem*>(); + } + + return selectedItemList; +} + +void MoveTool::formEditorItemsChanged(const QList<FormEditorItem*> &itemList) +{ + m_selectionIndicator.updateItems(itemList); + m_resizeIndicator.updateItems(itemList); +} + +} diff --git a/src/plugins/qmldesigner/components/formeditor/movetool.h b/src/plugins/qmldesigner/components/formeditor/movetool.h new file mode 100644 index 00000000000..df3c65b7916 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/movetool.h @@ -0,0 +1,97 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef MOVETOOL_H +#define MOVETOOL_H + +#include "abstractformeditortool.h" +#include "movemanipulator.h" +#include "selectionindicator.h" +#include "resizeindicator.h" + +#include <QHash> + + +namespace QmlDesigner { + + +class MoveTool : public AbstractFormEditorTool +{ +public: + MoveTool(FormEditorView* editorView); + ~MoveTool(); + + void mousePressEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event); + void mouseMoveEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event); + void mouseReleaseEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event); + void mouseDoubleClickEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event); + void hoverMoveEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event); + void keyPressEvent(QKeyEvent *event); + void keyReleaseEvent(QKeyEvent *keyEvent); + + void itemsAboutToRemoved(const QList<FormEditorItem*> &itemList); + + void selectedItemsChanged(const QList<FormEditorItem*> &itemList); + + void updateMoveManipulator(); + + + + void beginWithPoint(const QPointF &beginPoint); + + void clear(); + + void formEditorItemsChanged(const QList<FormEditorItem*> &itemList); + +protected: + static bool haveSameParent(const QList<FormEditorItem*> &itemList); + + static QList<FormEditorItem*> movingItems(const QList<FormEditorItem*> &selectedItemList); + + static FormEditorItem* containerFormEditorItem(const QList<QGraphicsItem*> &itemUnderMouseList, + const QList<FormEditorItem*> &selectedItemList); + + static bool isAncestorOfAllItems(FormEditorItem* maybeAncestorItem, + const QList<FormEditorItem*> &itemList); + static FormEditorItem* ancestorIfOtherItemsAreChild(const QList<FormEditorItem*> &itemList); + +private: + MoveManipulator m_moveManipulator; + SelectionIndicator m_selectionIndicator; + ResizeIndicator m_resizeIndicator; + QList<FormEditorItem*> m_movingItems; +}; + +} +#endif // MOVETOOL_H diff --git a/src/plugins/qmldesigner/components/formeditor/numberseriesaction.cpp b/src/plugins/qmldesigner/components/formeditor/numberseriesaction.cpp new file mode 100644 index 00000000000..ba6354f01d1 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/numberseriesaction.cpp @@ -0,0 +1,88 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "numberseriesaction.h" +#include <QStandardItemModel> +#include <QComboBox> +#include <QtDebug> + + +namespace QmlDesigner { + +NumberSeriesAction::NumberSeriesAction(QObject *parent) : + QWidgetAction(parent), + m_comboBoxModelIndex(-1) +{ +} + +void NumberSeriesAction::addEntry(const QString &text, const QVariant &value) +{ + if (m_comboBoxModel.isNull()) + m_comboBoxModel = new QStandardItemModel(this); + + QStandardItem *newItem = new QStandardItem(text); + newItem->setData(value); + m_comboBoxModel->appendRow(newItem); +} + +QVariant NumberSeriesAction::currentValue() const +{ + return m_comboBoxModel->item(m_comboBoxModelIndex)->data(); +} + +QWidget *NumberSeriesAction::createWidget(QWidget *parent) +{ + QComboBox *comboBox = new QComboBox(parent); + + comboBox->setModel(m_comboBoxModel.data()); + + comboBox->setCurrentIndex(m_comboBoxModelIndex); + connect(comboBox, SIGNAL(currentIndexChanged(int)), SLOT(emitValueChanged(int))); + + return comboBox; +} + +void NumberSeriesAction::emitValueChanged(int index) +{ + if (index == -1) + return; + + m_comboBoxModelIndex = index; + + emit valueChanged(m_comboBoxModel.data()->item(index)->data()); +} + +void NumberSeriesAction::setCurrentEntryIndex(int index) +{ + Q_ASSERT(index < m_comboBoxModel->rowCount()); + + m_comboBoxModelIndex = index; +} + +} // namespace QKinecticDesigner diff --git a/src/plugins/qmldesigner/components/formeditor/numberseriesaction.h b/src/plugins/qmldesigner/components/formeditor/numberseriesaction.h new file mode 100644 index 00000000000..e6a8c82f160 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/numberseriesaction.h @@ -0,0 +1,66 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef NUMBERSERIESACTION_H +#define NUMBERSERIESACTION_H + +#include <QWidgetAction> + +class QStandardItemModel; + +namespace QmlDesigner { + +class NumberSeriesAction : public QWidgetAction +{ +Q_OBJECT +public: + explicit NumberSeriesAction(QObject *parent = 0); + + void addEntry(const QString &text, const QVariant &value); + void setCurrentEntryIndex(int index); + + QVariant currentValue() const; + +protected: + QWidget *createWidget(QWidget *parent); + +signals: + void valueChanged(const QVariant &value); + +private slots: + void emitValueChanged(int index); + +private: + QWeakPointer<QStandardItemModel> m_comboBoxModel; + int m_comboBoxModelIndex; +}; + +} // namespace QKinecticDesigner + +#endif // NUMBERSERIESACTION_H diff --git a/src/plugins/qmldesigner/components/formeditor/onedimensionalcluster.cpp b/src/plugins/qmldesigner/components/formeditor/onedimensionalcluster.cpp new file mode 100644 index 00000000000..e77afe8a149 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/onedimensionalcluster.cpp @@ -0,0 +1,146 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "onedimensionalcluster.h" + +#include <QtDebug> + +namespace QmlDesigner { + +double sum(const QList<double> & list) +{ + double sum = 0.0; + for(QList<double>::const_iterator iterator = list.constBegin(); iterator != list.constEnd(); ++iterator) + { + sum += *iterator; + } + + return sum; +} + +OneDimensionalCluster::OneDimensionalCluster(const QList<double> & coordinateList ) + : m_coordinateList(coordinateList) +{ +} + +double OneDimensionalCluster::mean() const +{ + Q_ASSERT(!m_coordinateList.isEmpty()); + + if (m_coordinateList.size() == 1) + { + return m_coordinateList.first(); + } + + return sum(m_coordinateList) / m_coordinateList.size(); +} + +double OneDimensionalCluster::first() const +{ + Q_ASSERT(!m_coordinateList.isEmpty()); + + return m_coordinateList.first(); +} + +QList<OneDimensionalCluster> OneDimensionalCluster::createOneDimensionalClusterList(const QList<double> & oneDimensionalCoordinateList) +{ + QList<OneDimensionalCluster> oneDimensionalClusterList; + foreach (double coordinate, oneDimensionalCoordinateList) + { + QList<double> initialList; + initialList.append(coordinate); + OneDimensionalCluster cluster(initialList); + oneDimensionalClusterList.append(initialList); + } + + return oneDimensionalClusterList; +} + +QList<OneDimensionalCluster> OneDimensionalCluster::reduceOneDimensionalClusterList(const QList<OneDimensionalCluster> & unreducedClusterList, double maximumDistance) +{ + if (unreducedClusterList.size() < 2) + return unreducedClusterList; + + + QList<OneDimensionalCluster> workingList(unreducedClusterList); + QList<OneDimensionalCluster> reducedList; + while (true) + { + qSort(workingList); + reducedList.clear(); + bool clusterMerged = false; + QListIterator<OneDimensionalCluster> clusterIterator(workingList); + while (clusterIterator.hasNext()) + { + OneDimensionalCluster currentCluster = clusterIterator.next(); + if (clusterIterator.hasNext()) + { + OneDimensionalCluster nextCluster = clusterIterator.peekNext(); + if ((nextCluster.mean() - currentCluster.mean()) < maximumDistance) + { + reducedList.append(currentCluster + nextCluster); + clusterIterator.next(); + clusterMerged = true; + } + else + { + reducedList.append(currentCluster); + } + } + else + { + reducedList.append(currentCluster); + break; + } + + } + + workingList = reducedList; + + if (clusterMerged == false) + break; + } + + + return reducedList; +} + +QList<double> OneDimensionalCluster::reduceLines(const QList<double> & oneDimensionalCoordinateList, double maximumDistance) +{ + QList<OneDimensionalCluster> clusterList(createOneDimensionalClusterList(oneDimensionalCoordinateList)); + clusterList = reduceOneDimensionalClusterList(clusterList, maximumDistance); + + QList<double> lineList; + foreach (const OneDimensionalCluster &cluster, clusterList) + lineList.append(cluster.first()); + + return lineList; +} + +} diff --git a/src/plugins/qmldesigner/components/formeditor/onedimensionalcluster.h b/src/plugins/qmldesigner/components/formeditor/onedimensionalcluster.h new file mode 100644 index 00000000000..dcf179eb184 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/onedimensionalcluster.h @@ -0,0 +1,70 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef ONEDIMENSIONALCLUSTER_H +#define ONEDIMENSIONALCLUSTER_H + +#include <QList> + +namespace QmlDesigner { + +class OneDimensionalCluster +{ + friend inline OneDimensionalCluster operator+(const OneDimensionalCluster & firstCluster, const OneDimensionalCluster & secondCluster); + friend inline bool operator < (const OneDimensionalCluster & firstCluster, const OneDimensionalCluster & secondCluster); +public: + static QList<double> reduceLines(const QList<double> & oneDimensionalCoordinateList, double maximumDistance); + +private: + + OneDimensionalCluster(const QList<double> & coordinateList ); + double mean() const; + double first() const; + + + + static QList<OneDimensionalCluster> createOneDimensionalClusterList(const QList<double> & oneDimensionalCoordinateList); + static QList<OneDimensionalCluster> reduceOneDimensionalClusterList(const QList<OneDimensionalCluster> & unreducedClusterList, double maximumDistance); + + QList<double> m_coordinateList; +}; + +inline bool operator < (const OneDimensionalCluster & firstCluster, const OneDimensionalCluster & secondCluster) +{ + return firstCluster.mean() < secondCluster.mean(); +} + +inline OneDimensionalCluster operator+(const OneDimensionalCluster & firstCluster, const OneDimensionalCluster & secondCluster) +{ + + return OneDimensionalCluster(firstCluster.m_coordinateList + secondCluster.m_coordinateList); +} + +} +#endif // ONEDIMENSIONALCLUSTER_H diff --git a/src/plugins/qmldesigner/components/formeditor/resize_handle.png b/src/plugins/qmldesigner/components/formeditor/resize_handle.png new file mode 100644 index 0000000000000000000000000000000000000000..2934f25b7436ea36065f0ef5c249db9e87b04906 GIT binary patch literal 160 zcmeAS@N?(olHy`uVBq!ia0vp^>>$j+1|*LJg<l3zjKx9jP7LeL$-D$|I14-?iy0WW zg+Z8+Vb&Z8prEU#i(?4K_2iTWrsVz0{u_L|>08>v0|Y)Ngp@Y9X>dtiRFwdNdp35* x8LoHgG@eO!3hP`qS@5pkj3XemJwIZ&8Dd)GO4JMt?*R>D@O1TaS?83{1OO0JE(!nu literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/formeditor/resizecontroller.cpp b/src/plugins/qmldesigner/components/formeditor/resizecontroller.cpp new file mode 100644 index 00000000000..9929df14eed --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/resizecontroller.cpp @@ -0,0 +1,273 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "resizecontroller.h" + +#include "formeditoritem.h" +#include "layeritem.h" + +#include <resizehandleitem.h> +#include <QCursor> +#include <QGraphicsScene> + +namespace QmlDesigner { + + + +ResizeControllerData::ResizeControllerData(LayerItem *layerItem, FormEditorItem *formEditorItem) + : layerItem(layerItem), + formEditorItem(formEditorItem), + topLeftItem(0), + topRightItem(0), + bottomLeftItem(0), + bottomRightItem(0), + topItem(0), + leftItem(0), + rightItem(0), + bottomItem(0) +{ + +} + +ResizeControllerData::ResizeControllerData(const ResizeControllerData &other) + : layerItem(other.layerItem), + formEditorItem(other.formEditorItem), + topLeftItem(other.topLeftItem), + topRightItem(other.topRightItem), + bottomLeftItem(other.bottomLeftItem), + bottomRightItem(other.bottomRightItem), + topItem(other.topItem), + leftItem(other.leftItem), + rightItem(other.rightItem), + bottomItem(other.bottomItem) +{} + +ResizeControllerData::~ResizeControllerData() +{ + if (layerItem) { + layerItem->scene()->removeItem(topLeftItem); + layerItem->scene()->removeItem(topRightItem); + layerItem->scene()->removeItem(bottomLeftItem); + layerItem->scene()->removeItem(bottomRightItem); + layerItem->scene()->removeItem(topItem); + layerItem->scene()->removeItem(leftItem); + layerItem->scene()->removeItem(rightItem); + layerItem->scene()->removeItem(bottomItem); + } +} + + +ResizeController::ResizeController() + : m_data(new ResizeControllerData(0, 0)) +{ + +} + +ResizeController::ResizeController(const QSharedPointer<ResizeControllerData> &data) + : m_data(data) +{ + +} + +ResizeController::ResizeController(LayerItem *layerItem, FormEditorItem *formEditorItem) + : m_data(new ResizeControllerData(layerItem, formEditorItem)) +{ + m_data->topLeftItem = new ResizeHandleItem(layerItem, *this); + m_data->topLeftItem->setZValue(302); + m_data->topLeftItem->setCursor(Qt::SizeFDiagCursor); + + m_data->topRightItem = new ResizeHandleItem(layerItem, *this); + m_data->topRightItem->setZValue(301); + m_data->topRightItem->setCursor(Qt::SizeBDiagCursor); + + m_data->bottomLeftItem = new ResizeHandleItem(layerItem, *this); + m_data->bottomLeftItem->setZValue(301); + m_data->bottomLeftItem->setCursor(Qt::SizeBDiagCursor); + + m_data->bottomRightItem = new ResizeHandleItem(layerItem, *this); + m_data->bottomRightItem->setZValue(305); + m_data->bottomRightItem->setCursor(Qt::SizeFDiagCursor); + + m_data->topItem = new ResizeHandleItem(layerItem, *this); + m_data->topItem->setZValue(300); + m_data->topItem->setCursor(Qt::SizeVerCursor); + + m_data->leftItem = new ResizeHandleItem(layerItem, *this); + m_data->leftItem->setZValue(300); + m_data->leftItem->setCursor(Qt::SizeHorCursor); + + m_data->rightItem = new ResizeHandleItem(layerItem, *this); + m_data->rightItem->setZValue(300); + m_data->rightItem->setCursor(Qt::SizeHorCursor); + + m_data->bottomItem = new ResizeHandleItem(layerItem, *this); + m_data->bottomItem->setZValue(300); + m_data->bottomItem->setCursor(Qt::SizeVerCursor); + + updatePosition(); +} + + +bool ResizeController::isValid() const +{ + return m_data->formEditorItem != 0; +} + +void ResizeController::show() +{ + m_data->topLeftItem->show(); + m_data->topRightItem->show(); + m_data->bottomLeftItem->show(); + m_data->bottomRightItem->show(); + m_data->topItem->show(); + m_data->leftItem->show(); + m_data->rightItem->show(); + m_data->bottomItem->show(); +} +void ResizeController::hide() +{ + m_data->topLeftItem->hide(); + m_data->topRightItem->hide(); + m_data->bottomLeftItem->hide(); + m_data->bottomRightItem->hide(); + m_data->topItem->hide(); + m_data->leftItem->hide(); + m_data->rightItem->hide(); + m_data->bottomItem->hide(); +} + + +static QPointF topCenter(const QRectF &rect) +{ + return QPointF(rect.center().x(), rect.top()); +} + +static QPointF leftCenter(const QRectF &rect) +{ + return QPointF(rect.left(), rect.center().y()); +} + +static QPointF rightCenter(const QRectF &rect) +{ + return QPointF(rect.right(), rect.center().y()); +} + +static QPointF bottomCenter(const QRectF &rect) +{ + return QPointF(rect.center().x(), rect.bottom()); +} + + +void ResizeController::updatePosition() +{ + QRectF originalBoundingRect = m_data->formEditorItem->qmlItemNode().instanceBoundingRect(); + QRectF boundingRect = m_data->formEditorItem->qmlItemNode().instanceBoundingRect().adjusted(0.49, 0.49, 0.49, 0.49); + QPointF topLeftPointInLayerSpace(m_data->formEditorItem->mapToItem(m_data->layerItem.data(), + boundingRect.topLeft())); + QPointF topRightPointInLayerSpace(m_data->formEditorItem->mapToItem(m_data->layerItem.data(), + boundingRect.topRight())); + QPointF bottomLeftPointInLayerSpace(m_data->formEditorItem->mapToItem(m_data->layerItem.data(), + boundingRect.bottomLeft())); + QPointF bottomRightPointInLayerSpace(m_data->formEditorItem->mapToItem(m_data->layerItem.data(), + boundingRect.bottomRight())); + + QPointF topPointInLayerSpace(m_data->formEditorItem->mapToItem(m_data->layerItem.data(), + topCenter(boundingRect))); + QPointF leftPointInLayerSpace(m_data->formEditorItem->mapToItem(m_data->layerItem.data(), + leftCenter(boundingRect))); + + QPointF rightPointInLayerSpace(m_data->formEditorItem->mapToItem(m_data->layerItem.data(), + rightCenter(boundingRect))); + QPointF bottomPointInLayerSpace(m_data->formEditorItem->mapToItem(m_data->layerItem.data(), + bottomCenter(boundingRect))); + + + + m_data->topRightItem->setHandlePosition(topRightPointInLayerSpace, originalBoundingRect.topRight()); + m_data->topLeftItem->setHandlePosition(topLeftPointInLayerSpace, originalBoundingRect.topLeft()); + m_data->bottomLeftItem->setHandlePosition(bottomLeftPointInLayerSpace, originalBoundingRect.bottomLeft()); + m_data->bottomRightItem->setHandlePosition(bottomRightPointInLayerSpace, originalBoundingRect.bottomRight()); + m_data->topItem->setHandlePosition(topPointInLayerSpace, topCenter(originalBoundingRect)); + m_data->leftItem->setHandlePosition(leftPointInLayerSpace, leftCenter(originalBoundingRect)); + m_data->rightItem->setHandlePosition(rightPointInLayerSpace, rightCenter(originalBoundingRect)); + m_data->bottomItem->setHandlePosition(bottomPointInLayerSpace, bottomCenter(originalBoundingRect)); +} + + +FormEditorItem* ResizeController::formEditorItem() const +{ + return m_data->formEditorItem; +} + +QWeakPointer<ResizeControllerData> ResizeController::weakPointer() const +{ + return m_data; +} + +bool ResizeController::isTopLeftHandle(const ResizeHandleItem *handle) const +{ + return handle == m_data->topLeftItem; +} + +bool ResizeController::isTopRightHandle(const ResizeHandleItem *handle) const +{ + return handle == m_data->topRightItem; +} + +bool ResizeController::isBottomLeftHandle(const ResizeHandleItem *handle) const +{ + return handle == m_data->bottomLeftItem; +} + +bool ResizeController::isBottomRightHandle(const ResizeHandleItem *handle) const +{ + return handle == m_data->bottomRightItem; +} + +bool ResizeController::isTopHandle(const ResizeHandleItem *handle) const +{ + return handle == m_data->topItem; +} + +bool ResizeController::isLeftHandle(const ResizeHandleItem *handle) const +{ + return handle == m_data->leftItem; +} + +bool ResizeController::isRightHandle(const ResizeHandleItem *handle) const +{ + return handle == m_data->rightItem; +} + +bool ResizeController::isBottomHandle(const ResizeHandleItem *handle) const +{ + return handle == m_data->bottomItem; +} + +} diff --git a/src/plugins/qmldesigner/components/formeditor/resizecontroller.h b/src/plugins/qmldesigner/components/formeditor/resizecontroller.h new file mode 100644 index 00000000000..be1e44824a8 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/resizecontroller.h @@ -0,0 +1,104 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef RESIZECONTROLLER_H +#define RESIZECONTROLLER_H + +#include <QWeakPointer> +#include <QSharedPointer> + + +namespace QmlDesigner { + +class FormEditorItem; +class LayerItem; +class ResizeHandleItem; + + +class ResizeControllerData +{ +public: + ResizeControllerData(LayerItem *layerItem, + FormEditorItem *formEditorItem); + ResizeControllerData(const ResizeControllerData &other); + ~ResizeControllerData(); + + + QWeakPointer<LayerItem> layerItem; + FormEditorItem *formEditorItem; + ResizeHandleItem *topLeftItem; + ResizeHandleItem *topRightItem; + ResizeHandleItem *bottomLeftItem; + ResizeHandleItem *bottomRightItem; + ResizeHandleItem *topItem; + ResizeHandleItem *leftItem; + ResizeHandleItem *rightItem; + ResizeHandleItem *bottomItem; +}; + + + + +class ResizeController +{ +public: + friend class ResizeHandleItem; + + ResizeController(); + ResizeController(LayerItem *layerItem, FormEditorItem *formEditorItem); + + void show(); + void hide(); + + void updatePosition(); + + bool isValid() const; + + FormEditorItem *formEditorItem() const; + + bool isTopLeftHandle(const ResizeHandleItem *handle) const; + bool isTopRightHandle(const ResizeHandleItem *handle) const; + bool isBottomLeftHandle(const ResizeHandleItem *handle) const; + bool isBottomRightHandle(const ResizeHandleItem *handle) const; + + bool isTopHandle(const ResizeHandleItem *handle) const; + bool isLeftHandle(const ResizeHandleItem *handle) const; + bool isRightHandle(const ResizeHandleItem *handle) const; + bool isBottomHandle(const ResizeHandleItem *handle) const; + +private: // functions + ResizeController(const QSharedPointer<ResizeControllerData> &data); + QWeakPointer<ResizeControllerData> weakPointer() const; +private: // variables + QSharedPointer<ResizeControllerData> m_data; +}; + +} + +#endif // RESIZECONTROLLER_H diff --git a/src/plugins/qmldesigner/components/formeditor/resizehandleitem.cpp b/src/plugins/qmldesigner/components/formeditor/resizehandleitem.cpp new file mode 100644 index 00000000000..ae388cf2cdf --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/resizehandleitem.cpp @@ -0,0 +1,118 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "resizehandleitem.h" + +#include <formeditoritem.h> +#include <QCursor> + +namespace QmlDesigner { + +ResizeHandleItem::ResizeHandleItem(QGraphicsItem *parent, const ResizeController &resizeController) + : QGraphicsPixmapItem(QPixmap(":/icon/handle/resize_handle.png"), parent), + m_resizeControllerData(resizeController.weakPointer()) +{ + setShapeMode(QGraphicsPixmapItem::BoundingRectShape); + setOffset(-pixmap().rect().center()); + setFlag(QGraphicsItem::ItemIsMovable, true); + setFlag(QGraphicsItem::ItemIgnoresTransformations, true); +} + +void ResizeHandleItem::setHandlePosition(const QPointF & globalPosition, const QPointF & itemSpacePosition) +{ + m_itemSpacePosition = itemSpacePosition; + setPos(globalPosition); +} + +QRectF ResizeHandleItem::boundingRect() const +{ + return QGraphicsPixmapItem::boundingRect().adjusted(-1, -1, 1, 1); +} + +QPainterPath ResizeHandleItem::shape() const +{ + return QGraphicsItem::shape(); +} + +ResizeController ResizeHandleItem::resizeController() const +{ + Q_ASSERT(!m_resizeControllerData.isNull()); + return ResizeController(m_resizeControllerData.toStrongRef()); +} + +ResizeHandleItem* ResizeHandleItem::fromGraphicsItem(QGraphicsItem *item) +{ + return qgraphicsitem_cast<ResizeHandleItem*>(item); +} + +bool ResizeHandleItem::isTopLeftHandle() const +{ + return resizeController().isTopLeftHandle(this); +} + +bool ResizeHandleItem::isTopRightHandle() const +{ + return resizeController().isTopRightHandle(this); +} + +bool ResizeHandleItem::isBottomLeftHandle() const +{ + return resizeController().isBottomLeftHandle(this); +} + +bool ResizeHandleItem::isBottomRightHandle() const +{ + return resizeController().isBottomRightHandle(this); +} + +bool ResizeHandleItem::isTopHandle() const +{ + return resizeController().isTopHandle(this); +} + +bool ResizeHandleItem::isLeftHandle() const +{ + return resizeController().isLeftHandle(this); +} + +bool ResizeHandleItem::isRightHandle() const +{ + return resizeController().isRightHandle(this); +} + +bool ResizeHandleItem::isBottomHandle() const +{ + return resizeController().isBottomHandle(this); +} + +QPointF ResizeHandleItem::itemSpacePosition() const +{ + return m_itemSpacePosition; +} +} diff --git a/src/plugins/qmldesigner/components/formeditor/resizehandleitem.h b/src/plugins/qmldesigner/components/formeditor/resizehandleitem.h new file mode 100644 index 00000000000..3177e3aa24d --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/resizehandleitem.h @@ -0,0 +1,84 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef RESIZEHANDLEITEM_H +#define RESIZEHANDLEITEM_H + +#include <QGraphicsPixmapItem> + +#include "resizecontroller.h" + +namespace QmlDesigner { + +class ResizeHandleItem : public QGraphicsPixmapItem +{ +public: + enum + { + Type = 0xEAEA + }; + + + ResizeHandleItem(QGraphicsItem *parent, const ResizeController &resizeController); + + void setHandlePosition(const QPointF & globalPosition, const QPointF & itemSpacePosition); + + int type() const; + QRectF boundingRect() const; + QPainterPath shape() const; + + ResizeController resizeController() const; + + static ResizeHandleItem* fromGraphicsItem(QGraphicsItem *item); + + bool isTopLeftHandle() const; + bool isTopRightHandle() const; + bool isBottomLeftHandle() const; + bool isBottomRightHandle() const; + + bool isTopHandle() const; + bool isLeftHandle() const; + bool isRightHandle() const; + bool isBottomHandle() const; + + QPointF itemSpacePosition() const; + +private: + QWeakPointer<ResizeControllerData> m_resizeControllerData; + QPointF m_itemSpacePosition; +}; + +inline int ResizeHandleItem::type() const +{ + return Type; +} + +} + +#endif // RESIZEHANDLEITEM_H diff --git a/src/plugins/qmldesigner/components/formeditor/resizeindicator.cpp b/src/plugins/qmldesigner/components/formeditor/resizeindicator.cpp new file mode 100644 index 00000000000..9add9a65c7c --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/resizeindicator.cpp @@ -0,0 +1,91 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "resizeindicator.h" + +#include "formeditoritem.h" + +namespace QmlDesigner { + +ResizeIndicator::ResizeIndicator(LayerItem *layerItem) + : m_layerItem(layerItem) +{ + Q_ASSERT(layerItem); +} + +ResizeIndicator::~ResizeIndicator() +{ + m_itemControllerHash.clear(); +} + +void ResizeIndicator::show() +{ + QHashIterator<FormEditorItem*, ResizeController> itemControllerIterator(m_itemControllerHash); + while (itemControllerIterator.hasNext()) { + ResizeController controller = itemControllerIterator.next().value(); + controller.show(); + } +} +void ResizeIndicator::hide() +{ + QHashIterator<FormEditorItem*, ResizeController> itemControllerIterator(m_itemControllerHash); + while (itemControllerIterator.hasNext()) { + ResizeController controller = itemControllerIterator.next().value(); + controller.hide(); + } +} + +void ResizeIndicator::clear() +{ + m_itemControllerHash.clear(); +} + +void ResizeIndicator::setItems(const QList<FormEditorItem*> &itemList) +{ + clear(); + + foreach (FormEditorItem* item, itemList) { + if (item->qmlItemNode().isRootNode()) + continue; + ResizeController controller(m_layerItem, item); + m_itemControllerHash.insert(item, controller); + } +} + +void ResizeIndicator::updateItems(const QList<FormEditorItem*> &itemList) +{ + foreach (FormEditorItem* item, itemList) { + if (m_itemControllerHash.contains(item)) { + ResizeController controller(m_itemControllerHash.value(item)); + controller.updatePosition(); + } + } +} + +} diff --git a/src/plugins/qmldesigner/components/formeditor/resizeindicator.h b/src/plugins/qmldesigner/components/formeditor/resizeindicator.h new file mode 100644 index 00000000000..e8881ec3e73 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/resizeindicator.h @@ -0,0 +1,81 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef RESIZEINDICATOR_H +#define RESIZEINDICATOR_H + +#include <QHash> +#include <QPair> + +#include "resizecontroller.h" + +class QGraphicsRectItem; + +namespace QmlDesigner { + +class FormEditorItem; +class LayerItem; + +class ResizeIndicator +{ +public: + enum Orientation { + Top = 1, + Right = 2, + Bottom = 4, + Left = 8 + }; + + ResizeIndicator(LayerItem *layerItem); + ~ResizeIndicator(); + + void show(); + void hide(); + + void clear(); + + void setItems(const QList<FormEditorItem*> &itemList); + void updateItems(const QList<FormEditorItem*> &itemList); + +// +// QPair<FormEditorItem*,Orientation> pick(QGraphicsRectItem* pickedItem) const; +// +// void show(); +// void hide(); + + + +private: + QHash<FormEditorItem*, ResizeController> m_itemControllerHash; + + LayerItem *m_layerItem; +}; + +} +#endif // SCALEINDICATOR_H diff --git a/src/plugins/qmldesigner/components/formeditor/resizemanipulator.cpp b/src/plugins/qmldesigner/components/formeditor/resizemanipulator.cpp new file mode 100644 index 00000000000..b0f21896461 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/resizemanipulator.cpp @@ -0,0 +1,504 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "resizemanipulator.h" + +#include "formeditoritem.h" +#include "formeditorscene.h" +#include "qmlanchors.h" +#include <QtDebug> +#include <model.h> +#include "mathutils.h" + +#include <limits> + +namespace QmlDesigner { + +ResizeManipulator::ResizeManipulator(LayerItem *layerItem, FormEditorView *view) + : m_view(view), + m_beginTopMargin(0.0), + m_beginLeftMargin(0.0), + m_beginRightMargin(0.0), + m_beginBottomMargin(0.0), + m_layerItem(layerItem), + m_resizeHandle(0) +{ +} + +ResizeManipulator::~ResizeManipulator() +{ + deleteSnapLines(); +} + +void ResizeManipulator::setHandle(ResizeHandleItem *resizeHandle) +{ + Q_ASSERT(resizeHandle); + m_resizeHandle = resizeHandle; + m_resizeController = resizeHandle->resizeController(); + m_snapper.setContainerFormEditorItem(m_resizeController.formEditorItem()->parentItem()); + m_snapper.setTransformtionSpaceFormEditorItem(m_resizeController.formEditorItem()); + Q_ASSERT(m_resizeController.isValid()); +} + +void ResizeManipulator::removeHandle() +{ + m_resizeController = ResizeController(); + m_resizeHandle = 0; +} + +void ResizeManipulator::begin(const QPointF &/*beginPoint*/) +{ + if (m_resizeController.isValid()) { + m_beginBoundingRect = m_resizeController.formEditorItem()->qmlItemNode().instanceBoundingRect(); + m_beginToSceneTransform = m_resizeController.formEditorItem()->qmlItemNode().instanceSceneTransform(); + m_beginFromSceneTransform = m_beginToSceneTransform.inverted(); + m_beginToParentTransform = m_resizeController.formEditorItem()->qmlItemNode().instanceTransform(); + m_rewriterTransaction = m_view->beginRewriterTransaction(); + m_snapper.updateSnappingLines(m_resizeController.formEditorItem()); + m_beginBottomRightPoint = m_beginToParentTransform.map(m_resizeController.formEditorItem()->qmlItemNode().instanceBoundingRect().bottomRight()); + + QmlAnchors anchors(m_resizeController.formEditorItem()->qmlItemNode().anchors()); + m_beginTopMargin = anchors.instanceMargin(AnchorLine::Top); + m_beginLeftMargin = anchors.instanceMargin(AnchorLine::Left); + m_beginRightMargin = anchors.instanceMargin(AnchorLine::Right); + m_beginBottomMargin = anchors.instanceMargin(AnchorLine::Bottom); + } +} + +//static QSizeF mapSizeToParent(const QSizeF &size, QGraphicsItem *item) +//{ +// QPointF sizeAsPoint(size.width(), size.height()); +// sizeAsPoint = item->mapToParent(sizeAsPoint); +// return QSizeF(sizeAsPoint.x(), sizeAsPoint.y()); +//} + +void ResizeManipulator::update(const QPointF& updatePoint, Snapping useSnapping) +{ + const double minimumWidth = 15.0; + const double minimumHeight = 15.0; + + deleteSnapLines(); + + if (m_resizeController.isValid()) { + + FormEditorItem *formEditorItem = m_resizeController.formEditorItem(); + FormEditorItem *containerItem = m_snapper.containerFormEditorItem(); + + if (!containerItem) + return; + + QPointF updatePointInLocalSpace = m_beginFromSceneTransform.map(updatePoint); + QmlAnchors anchors(formEditorItem->qmlItemNode().anchors()); + + QRectF boundingRect(m_beginBoundingRect); + if (m_resizeHandle->isBottomRightHandle()) { + boundingRect.setBottomRight(updatePointInLocalSpace); + + if (useSnapping == UseSnapping) { + double rightOffset = m_snapper.snapRightOffset(boundingRect); + if (rightOffset < std::numeric_limits<double>::max()) + updatePointInLocalSpace.rx() -= rightOffset; + + double bottomOffset = m_snapper.snapBottomOffset(boundingRect); + if (bottomOffset < std::numeric_limits<double>::max()) + updatePointInLocalSpace.ry() -= bottomOffset; + } + boundingRect.setBottomRight(updatePointInLocalSpace); + + if (anchors.instanceHasAnchor(AnchorLine::HorizontalCenter)) { + boundingRect.setLeft(boundingRect.left() - (updatePointInLocalSpace.x() - m_beginBoundingRect.right())); + } + + if (anchors.instanceHasAnchor(AnchorLine::VerticalCenter)) { + boundingRect.setTop(boundingRect.top() - (updatePointInLocalSpace.y() - m_beginBoundingRect.bottom())); + } + + if (boundingRect.width() < minimumWidth) + boundingRect.setWidth(minimumWidth); + if (boundingRect.height() < minimumHeight) + boundingRect.setHeight(minimumHeight); + + formEditorItem->qmlItemNode().setSize(boundingRect.size()); + + + if (anchors.instanceHasAnchor(AnchorLine::Bottom)) { + anchors.setMargin(AnchorLine::Bottom, + m_beginBottomMargin - (m_beginToParentTransform.map(boundingRect.bottomRight()) - m_beginBottomRightPoint).y()); + } + + if (anchors.instanceHasAnchor(AnchorLine::Right)) { + anchors.setMargin(AnchorLine::Right, + m_beginRightMargin - (m_beginToParentTransform.map(boundingRect.bottomRight()) - m_beginBottomRightPoint).x()); + } + } else if (m_resizeHandle->isTopLeftHandle()) { + boundingRect.setTopLeft(updatePointInLocalSpace); + + if (useSnapping == UseSnapping) { + double leftOffset = m_snapper.snapLeftOffset(boundingRect); + if (leftOffset < std::numeric_limits<double>::max()) + updatePointInLocalSpace.rx() -= leftOffset; + + double topOffset = m_snapper.snapTopOffset(boundingRect); + if (topOffset < std::numeric_limits<double>::max()) + updatePointInLocalSpace.ry() -= topOffset; + } + boundingRect.setTopLeft(updatePointInLocalSpace); + + if (anchors.instanceHasAnchor(AnchorLine::HorizontalCenter)) { + boundingRect.setRight(boundingRect.right() - (updatePointInLocalSpace.x() - m_beginBoundingRect.left())); + } + + if (anchors.instanceHasAnchor(AnchorLine::VerticalCenter)) { + boundingRect.setBottom(boundingRect.bottom() - (updatePointInLocalSpace.y() - m_beginBoundingRect.top())); + } + + + if (boundingRect.width() < minimumWidth) + boundingRect.setLeft(boundingRect.left() - minimumWidth + boundingRect.width()); + if (boundingRect.height() < minimumHeight) + boundingRect.setTop(boundingRect.top() - minimumHeight + boundingRect.height()); + + formEditorItem->qmlItemNode().setSize(boundingRect.size()); + formEditorItem->qmlItemNode().setPosition(m_beginToParentTransform.map(boundingRect.topLeft())); + + + if (anchors.instanceHasAnchor(AnchorLine::Top)) { + anchors.setMargin(AnchorLine::Top, + m_beginTopMargin + (-m_beginToParentTransform.map(m_beginBoundingRect.topLeft()).y() + m_beginToParentTransform.map(boundingRect.topLeft()).y())); + } + + if (anchors.instanceHasAnchor(AnchorLine::Left)) { + anchors.setMargin(AnchorLine::Left, + m_beginLeftMargin + (-m_beginToParentTransform.map(m_beginBoundingRect.topLeft()).x() + m_beginToParentTransform.map(boundingRect.topLeft()).x())); + } + + } else if (m_resizeHandle->isTopRightHandle()) { + boundingRect.setTopRight(updatePointInLocalSpace); + + if (useSnapping == UseSnapping) { + double rightOffset = m_snapper.snapRightOffset(boundingRect); + if (rightOffset < std::numeric_limits<double>::max()) + updatePointInLocalSpace.rx() -= rightOffset; + + double topOffset = m_snapper.snapTopOffset(boundingRect); + if (topOffset < std::numeric_limits<double>::max()) + updatePointInLocalSpace.ry() -= topOffset; + } + boundingRect.setTopRight(updatePointInLocalSpace); + + if (anchors.instanceHasAnchor(AnchorLine::HorizontalCenter)) { + boundingRect.setLeft(boundingRect.left() - (updatePointInLocalSpace.x() - m_beginBoundingRect.right())); + } + + if (anchors.instanceHasAnchor(AnchorLine::VerticalCenter)) { + boundingRect.setBottom(boundingRect.bottom() - (updatePointInLocalSpace.y() - m_beginBoundingRect.top())); + } + + if (boundingRect.height() < minimumHeight) + boundingRect.setTop(boundingRect.top() - minimumHeight + boundingRect.height()); + if (boundingRect.width() < minimumWidth) + boundingRect.setWidth(minimumWidth); + + formEditorItem->qmlItemNode().setSize(boundingRect.size()); + formEditorItem->qmlItemNode().setPosition(m_beginToParentTransform.map(boundingRect.topLeft())); + + if (anchors.instanceHasAnchor(AnchorLine::Top)) { + anchors.setMargin(AnchorLine::Top, + m_beginTopMargin + (-m_beginToParentTransform.map(m_beginBoundingRect.topLeft()).y() + m_beginToParentTransform.map(boundingRect.topLeft()).y())); + } + + if (anchors.instanceHasAnchor(AnchorLine::Right)) { + anchors.setMargin(AnchorLine::Right, + m_beginRightMargin - (m_beginToParentTransform.map(boundingRect.bottomRight()) - m_beginBottomRightPoint).x()); + } + } else if (m_resizeHandle->isBottomLeftHandle()) { + boundingRect.setBottomLeft(updatePointInLocalSpace); + + if (useSnapping == UseSnapping) { + double leftOffset = m_snapper.snapLeftOffset(boundingRect); + if (leftOffset < std::numeric_limits<double>::max()) + updatePointInLocalSpace.rx() -= leftOffset; + + double bottomOffset = m_snapper.snapBottomOffset(boundingRect); + if (bottomOffset < std::numeric_limits<double>::max()) + updatePointInLocalSpace.ry() -= bottomOffset; + } + + boundingRect.setBottomLeft(updatePointInLocalSpace); + + if (anchors.instanceHasAnchor(AnchorLine::HorizontalCenter)) { + boundingRect.setRight(boundingRect.right() - (updatePointInLocalSpace.x() - m_beginBoundingRect.left())); + } + + if (anchors.instanceHasAnchor(AnchorLine::VerticalCenter)) { + boundingRect.setTop(boundingRect.top() - (updatePointInLocalSpace.y() - m_beginBoundingRect.bottom())); + } + + if (boundingRect.height() < minimumHeight) + boundingRect.setHeight(minimumHeight); + if (boundingRect.width() < minimumWidth) + boundingRect.setLeft(boundingRect.left() - minimumWidth + boundingRect.width()); + + formEditorItem->qmlItemNode().setSize(boundingRect.size()); + formEditorItem->qmlItemNode().setPosition(m_beginToParentTransform.map(boundingRect.topLeft())); + + if (anchors.instanceHasAnchor(AnchorLine::Left)) { + anchors.setMargin(AnchorLine::Left, + m_beginLeftMargin + (-m_beginToParentTransform.map(m_beginBoundingRect.topLeft()).x() + m_beginToParentTransform.map(boundingRect.topLeft()).x())); + } + + if (anchors.instanceHasAnchor(AnchorLine::Bottom)) { + anchors.setMargin(AnchorLine::Bottom, + m_beginBottomMargin - (m_beginToParentTransform.map(boundingRect.bottomRight()) - m_beginBottomRightPoint).y()); + } + } else if (m_resizeHandle->isBottomHandle()) { + boundingRect.setBottom(updatePointInLocalSpace.y()); + + if (useSnapping == UseSnapping) { + double bottomOffset = m_snapper.snapBottomOffset(boundingRect); + if (bottomOffset < std::numeric_limits<double>::max()) + updatePointInLocalSpace.ry() -= bottomOffset; + } + + boundingRect.setBottom(updatePointInLocalSpace.y()); + + if (anchors.instanceHasAnchor(AnchorLine::VerticalCenter)) { + boundingRect.setTop(boundingRect.top() - (updatePointInLocalSpace.y() - m_beginBoundingRect.bottom())); + } + + if (boundingRect.height() < minimumHeight) + boundingRect.setHeight(minimumHeight); + + formEditorItem->qmlItemNode().setSize(boundingRect.size()); + + if (anchors.instanceHasAnchor(AnchorLine::Bottom)) { + anchors.setMargin(AnchorLine::Bottom, + m_beginBottomMargin - (m_beginToParentTransform.map(boundingRect.bottomRight()) - m_beginBottomRightPoint).y()); + } + } else if (m_resizeHandle->isTopHandle()) { + boundingRect.setTop(updatePointInLocalSpace.y()); + + if (useSnapping == UseSnapping) { + double topOffset = m_snapper.snapTopOffset(boundingRect); + if (topOffset < std::numeric_limits<double>::max()) + updatePointInLocalSpace.ry() -= topOffset; + } + + boundingRect.setTop(updatePointInLocalSpace.y()); + + if (anchors.instanceHasAnchor(AnchorLine::VerticalCenter)) { + boundingRect.setBottom(boundingRect.bottom() - (updatePointInLocalSpace.y() - m_beginBoundingRect.top())); + } + + if (boundingRect.height() < minimumHeight) + boundingRect.setTop(boundingRect.top() - minimumHeight + boundingRect.height()); + + formEditorItem->qmlItemNode().setSize(boundingRect.size()); + formEditorItem->qmlItemNode().setPosition(m_beginToParentTransform.map(boundingRect.topLeft())); + + if (anchors.instanceHasAnchor(AnchorLine::Top)) { + anchors.setMargin(AnchorLine::Top, + m_beginTopMargin + (-m_beginToParentTransform.map(m_beginBoundingRect.topLeft()).y() + m_beginToParentTransform.map(boundingRect.topLeft()).y())); + } + } else if (m_resizeHandle->isRightHandle()) { + boundingRect.setRight(updatePointInLocalSpace.x()); + + if (useSnapping == UseSnapping) { + double rightOffset = m_snapper.snapRightOffset(boundingRect); + if (rightOffset < std::numeric_limits<double>::max()) + updatePointInLocalSpace.rx() -= rightOffset; + } + + boundingRect.setRight(updatePointInLocalSpace.x()); + + if (anchors.instanceHasAnchor(AnchorLine::HorizontalCenter)) { + boundingRect.setLeft(boundingRect.left() - (updatePointInLocalSpace.x() - m_beginBoundingRect.right())); + } + + if (boundingRect.width() < minimumWidth) + boundingRect.setWidth(minimumWidth); + + formEditorItem->qmlItemNode().setSize(boundingRect.size()); + + + if (anchors.instanceHasAnchor(AnchorLine::Right)) { + anchors.setMargin(AnchorLine::Right, + m_beginRightMargin - (m_beginToParentTransform.map(boundingRect.bottomRight()) - m_beginBottomRightPoint).x()); + } + } else if (m_resizeHandle->isLeftHandle()) { + boundingRect.setLeft(updatePointInLocalSpace.x()); + + if (useSnapping == UseSnapping) { + double leftOffset = m_snapper.snapLeftOffset(boundingRect); + if (leftOffset < std::numeric_limits<double>::max()) + updatePointInLocalSpace.rx() -= leftOffset; + } + + boundingRect.setLeft(updatePointInLocalSpace.x()); + + if (anchors.instanceHasAnchor(AnchorLine::HorizontalCenter)) { + boundingRect.setRight(boundingRect.right() - (updatePointInLocalSpace.x() - m_beginBoundingRect.left())); + } + + if (boundingRect.width() < minimumWidth) + boundingRect.setLeft(boundingRect.left() - minimumWidth + boundingRect.width()); + + formEditorItem->qmlItemNode().setSize(boundingRect.size()); + formEditorItem->qmlItemNode().setPosition(m_beginToParentTransform.map(boundingRect.topLeft())); + + if (anchors.instanceHasAnchor(AnchorLine::Left)) { + anchors.setMargin(AnchorLine::Left, + m_beginLeftMargin + (-m_beginToParentTransform.map(m_beginBoundingRect.topLeft()).x() + m_beginToParentTransform.map(boundingRect.topLeft()).x())); + } + } + + if (useSnapping == UseSnapping) + m_graphicsLineList = m_snapper.generateSnappingLines(boundingRect, + m_layerItem.data(), + m_beginToSceneTransform); + } +} + +void ResizeManipulator::end() +{ + m_rewriterTransaction.commit(); + clear(); + removeHandle(); +} + +void ResizeManipulator::moveBy(double deltaX, double deltaY) +{ + if (resizeHandle()) { + QmlItemNode qmlItemNode(m_resizeController.formEditorItem()->qmlItemNode()); + QmlAnchors anchors(qmlItemNode.anchors()); + + if (m_resizeController.isLeftHandle(resizeHandle()) + || m_resizeController.isTopLeftHandle(resizeHandle()) + || m_resizeController.isBottomLeftHandle(resizeHandle())) { + qmlItemNode.setVariantProperty("x", round((qmlItemNode.instanceValue("x").toDouble() + deltaX), 4)); + qmlItemNode.setVariantProperty("width", round(qmlItemNode.instanceValue("width").toDouble() - deltaX, 4)); + + + if (anchors.instanceHasAnchor(AnchorLine::Left)) { + anchors.setMargin(AnchorLine::Left, anchors.instanceMargin(AnchorLine::Left) + deltaX); + } + + if (anchors.instanceHasAnchor(AnchorLine::HorizontalCenter)) { + qmlItemNode.setVariantProperty("width", round(qmlItemNode.instanceValue("width").toDouble() - (deltaX * 2), 4)); + } + } + + if (m_resizeController.isRightHandle(resizeHandle()) + || m_resizeController.isTopRightHandle(resizeHandle()) + || m_resizeController.isBottomRightHandle(resizeHandle())) { + qmlItemNode.setVariantProperty("width", round(qmlItemNode.instanceValue("width").toDouble() + deltaX, 4)); + + if (anchors.instanceHasAnchor(AnchorLine::Right)) { + anchors.setMargin(AnchorLine::Right, round(anchors.instanceMargin(AnchorLine::Right) - deltaX, 4)); + } + + if (anchors.instanceHasAnchor(AnchorLine::HorizontalCenter)) { + qmlItemNode.setVariantProperty("width", round(qmlItemNode.instanceValue("width").toDouble() + (deltaX * 2), 4)); + } + } + + + if (m_resizeController.isTopHandle(resizeHandle()) + || m_resizeController.isTopLeftHandle(resizeHandle()) + || m_resizeController.isTopRightHandle(resizeHandle())) { + qmlItemNode.setVariantProperty("y", round(qmlItemNode.instanceValue("y").toDouble() + deltaY, 4)); + qmlItemNode.setVariantProperty("height", round(qmlItemNode.instanceValue("height").toDouble() - deltaY, 4)); + + if (anchors.instanceHasAnchor(AnchorLine::Top)) { + anchors.setMargin(AnchorLine::Top, anchors.instanceMargin(AnchorLine::Top) + deltaY); + } + + if (anchors.instanceHasAnchor(AnchorLine::VerticalCenter)) { + qmlItemNode.setVariantProperty("height", round(qmlItemNode.instanceValue("height").toDouble() - (deltaY * 2), 4)); + } + } + + if (m_resizeController.isBottomHandle(resizeHandle()) + || m_resizeController.isBottomLeftHandle(resizeHandle()) + || m_resizeController.isBottomRightHandle(resizeHandle())) { + qmlItemNode.setVariantProperty("height", round(qmlItemNode.instanceValue("height").toDouble() + deltaY, 4)); + + if (anchors.instanceHasAnchor(AnchorLine::Bottom)) { + anchors.setMargin(AnchorLine::Bottom, anchors.instanceMargin(AnchorLine::Bottom) - deltaY); + } + + if (anchors.instanceHasAnchor(AnchorLine::VerticalCenter)) { + qmlItemNode.setVariantProperty("height", round(qmlItemNode.instanceValue("height").toDouble() + (deltaY * 2), 4)); + } + } + + } +} + +bool ResizeManipulator::isInvalidSize(const QSizeF & size) +{ + if (size.width() < 15 || size.height() < 15) + return true; + + return false; +} + +void ResizeManipulator::deleteSnapLines() +{ + if (m_layerItem) { + foreach (QGraphicsItem *item, m_graphicsLineList) + m_layerItem->scene()->removeItem(item); + } + + m_graphicsLineList.clear(); + m_view->scene()->update(); +} + +ResizeHandleItem *ResizeManipulator::resizeHandle() +{ + return m_resizeHandle; +} + +void ResizeManipulator::clear() +{ + m_rewriterTransaction.commit(); + + deleteSnapLines(); + m_beginBoundingRect = QRectF(); + m_beginFromSceneTransform = QTransform(); + m_beginToSceneTransform = QTransform(); + m_beginToParentTransform = QTransform(); + m_beginTopMargin = 0.0; + m_beginLeftMargin = 0.0; + m_beginRightMargin = 0.0; + m_beginBottomMargin = 0.0; + removeHandle(); +} + +} diff --git a/src/plugins/qmldesigner/components/formeditor/resizemanipulator.h b/src/plugins/qmldesigner/components/formeditor/resizemanipulator.h new file mode 100644 index 00000000000..913966537f1 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/resizemanipulator.h @@ -0,0 +1,91 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef RESIZEMANIPULATOR_H +#define RESIZEMANIPULATOR_H + +#include <QWeakPointer> +#include "resizehandleitem.h" +#include <snapper.h> +#include "rewritertransaction.h" +#include "formeditorview.h" + +namespace QmlDesigner { + +class ResizeHandleItem; +class Model; + +class ResizeManipulator +{ +public: + enum Snapping { + UseSnapping, + NoSnapping + }; + + ResizeManipulator(LayerItem *layerItem, FormEditorView *view); + ~ResizeManipulator(); + + void setHandle(ResizeHandleItem *resizeHandle); + void removeHandle(); + + void begin(const QPointF& beginPoint); + void update(const QPointF& updatePoint, Snapping useSnapping); + void end(); + + void moveBy(double deltaX, double deltaY); + + void clear(); + +protected: + bool isInvalidSize(const QSizeF & size); + void deleteSnapLines(); + ResizeHandleItem *resizeHandle(); + +private: + Snapper m_snapper; + QWeakPointer<FormEditorView> m_view; + QList<QGraphicsItem*> m_graphicsLineList; + ResizeController m_resizeController; // hold the controller so that the handle cant be deleted + QTransform m_beginFromSceneTransform; + QTransform m_beginToSceneTransform; + QTransform m_beginToParentTransform; + QRectF m_beginBoundingRect; + QPointF m_beginBottomRightPoint; + double m_beginTopMargin; + double m_beginLeftMargin; + double m_beginRightMargin; + double m_beginBottomMargin; + QWeakPointer<LayerItem> m_layerItem; + ResizeHandleItem *m_resizeHandle; + RewriterTransaction m_rewriterTransaction; +}; + +} +#endif // RESIZEMANIPULATOR_H diff --git a/src/plugins/qmldesigner/components/formeditor/resizetool.cpp b/src/plugins/qmldesigner/components/formeditor/resizetool.cpp new file mode 100644 index 00000000000..bd7d5157e1f --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/resizetool.cpp @@ -0,0 +1,177 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "resizetool.h" + +#include "formeditorscene.h" +#include "formeditorview.h" + +#include "resizehandleitem.h" + +#include <QApplication> +#include <QGraphicsSceneMouseEvent> +#include <QtDebug> + +namespace QmlDesigner { + +ResizeTool::ResizeTool(FormEditorView *editorView) + : AbstractFormEditorTool(editorView), + m_selectionIndicator(editorView->scene()->manipulatorLayerItem()), + m_resizeIndicator(editorView->scene()->manipulatorLayerItem()), + m_resizeManipulator(editorView->scene()->manipulatorLayerItem(), editorView) +{ +} + + +ResizeTool::~ResizeTool() +{ +} + +void ResizeTool::mousePressEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event) +{ + if (itemList.isEmpty()) + return; + + ResizeHandleItem *resizeHandle = ResizeHandleItem::fromGraphicsItem(itemList.first()); + if (resizeHandle) { + m_resizeManipulator.setHandle(resizeHandle); + m_resizeManipulator.begin(event->scenePos()); + m_selectionIndicator.hide(); + m_resizeIndicator.hide(); + } +} + +void ResizeTool::mouseMoveEvent(const QList<QGraphicsItem*> &, + QGraphicsSceneMouseEvent *event) +{ + + + ResizeManipulator::Snapping useSnapping = ResizeManipulator::NoSnapping; + if (event->modifiers().testFlag(Qt::ControlModifier) != view()->isSnapButtonChecked()) + useSnapping = ResizeManipulator::UseSnapping; + + m_resizeManipulator.update(event->scenePos(), useSnapping); +} + +void ResizeTool::hoverMoveEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent * /*event*/) +{ + if (itemList.isEmpty()) + return; + ResizeHandleItem* resizeHandle = ResizeHandleItem::fromGraphicsItem(itemList.first()); + if (resizeHandle) { + m_resizeManipulator.setHandle(resizeHandle); + } else { + view()->changeToSelectionTool(); + return; + } +} + +void ResizeTool::mouseReleaseEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent * /*event*/) +{ + if (itemList.isEmpty()) + return; + + m_selectionIndicator.show(); + m_resizeIndicator.show(); + m_resizeManipulator.end(); +} + +void ResizeTool::mouseDoubleClickEvent(const QList<QGraphicsItem*> & /*itemList*/, + QGraphicsSceneMouseEvent * /*event*/) +{ +} + +void ResizeTool::keyPressEvent(QKeyEvent * event) +{ + switch(event->key()) { + case Qt::Key_Shift: + case Qt::Key_Alt: + case Qt::Key_Control: + case Qt::Key_AltGr: + event->setAccepted(false); + return; + } + + double moveStep = 1.0; + + if (event->modifiers().testFlag(Qt::ShiftModifier)) + moveStep = 10.0; + + switch(event->key()) { + case Qt::Key_Left: m_resizeManipulator.moveBy(-moveStep, 0.0); break; + case Qt::Key_Right: m_resizeManipulator.moveBy(moveStep, 0.0); break; + case Qt::Key_Up: m_resizeManipulator.moveBy(0.0, -moveStep); break; + case Qt::Key_Down: m_resizeManipulator.moveBy(0.0, moveStep); break; + } + +} + +void ResizeTool::keyReleaseEvent(QKeyEvent * keyEvent) +{ + switch(keyEvent->key()) { + case Qt::Key_Shift: + case Qt::Key_Alt: + case Qt::Key_Control: + case Qt::Key_AltGr: + keyEvent->setAccepted(false); + return; + } + +// if (!keyEvent->isAutoRepeat()) +// m_resizeManipulator.clear(); +} + +void ResizeTool::itemsAboutToRemoved(const QList<FormEditorItem*> & /*itemList*/) +{ + +} + +void ResizeTool::selectedItemsChanged(const QList<FormEditorItem*> & /*itemList*/) +{ + m_selectionIndicator.setItems(items()); + m_resizeIndicator.setItems(items()); +} + +void ResizeTool::clear() +{ + m_selectionIndicator.clear(); + m_resizeIndicator.clear(); + m_resizeManipulator.clear(); +} + +void ResizeTool::formEditorItemsChanged(const QList<FormEditorItem*> &itemList) +{ + m_selectionIndicator.updateItems(itemList); + m_resizeIndicator.updateItems(itemList); +} + +} diff --git a/src/plugins/qmldesigner/components/formeditor/resizetool.h b/src/plugins/qmldesigner/components/formeditor/resizetool.h new file mode 100644 index 00000000000..f376748ac0d --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/resizetool.h @@ -0,0 +1,76 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef RESIZETOOL_H +#define RESIZETOOL_H + +#include "abstractformeditortool.h" +#include "selectionindicator.h" +#include "resizeindicator.h" +#include "resizemanipulator.h" + + +namespace QmlDesigner { + +class ResizeTool : public AbstractFormEditorTool +{ +public: + ResizeTool(FormEditorView* editorView); + ~ResizeTool(); + + void mousePressEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event); + void mouseMoveEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event); + void mouseReleaseEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event); + void mouseDoubleClickEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event); + void hoverMoveEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event); + void keyPressEvent(QKeyEvent *event); + void keyReleaseEvent(QKeyEvent *keyEvent); + + void itemsAboutToRemoved(const QList<FormEditorItem*> &itemList); + + void selectedItemsChanged(const QList<FormEditorItem*> &itemList); + + void clear(); + + void formEditorItemsChanged(const QList<FormEditorItem*> &itemList); + +private: + SelectionIndicator m_selectionIndicator; + ResizeIndicator m_resizeIndicator; + ResizeManipulator m_resizeManipulator; +}; + +} + +#endif // RESIZETOOL_H diff --git a/src/plugins/qmldesigner/components/formeditor/rubberbandselectionmanipulator.cpp b/src/plugins/qmldesigner/components/formeditor/rubberbandselectionmanipulator.cpp new file mode 100644 index 00000000000..f9ca6b0831d --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/rubberbandselectionmanipulator.cpp @@ -0,0 +1,153 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "rubberbandselectionmanipulator.h" + +#include "model.h" +#include "formeditorscene.h" + +namespace QmlDesigner { + +RubberBandSelectionManipulator::RubberBandSelectionManipulator(LayerItem *layerItem, FormEditorView *editorView) + : m_selectionRectangleElement(layerItem), + m_editorView(editorView), + m_beginFormEditorItem(0), + m_isActive(false) +{ + m_selectionRectangleElement.hide(); +} + +void RubberBandSelectionManipulator::clear() +{ + m_selectionRectangleElement.clear(); + m_isActive = false; + m_beginPoint = QPointF(); + m_itemList.clear(); + m_oldSelectionList.clear(); +} + +FormEditorItem *RubberBandSelectionManipulator::topFormEditorItem(const QList<QGraphicsItem*> &itemList) +{ + foreach (QGraphicsItem *item, itemList) { + FormEditorItem *formEditorItem = FormEditorItem::fromQGraphicsItem(item); + + if (formEditorItem) + { + return formEditorItem; + } + } + + return m_editorView->scene()->rootFormEditorItem(); +} + +void RubberBandSelectionManipulator::begin(const QPointF& beginPoint) +{ + m_beginPoint = beginPoint; + m_selectionRectangleElement.setRect(m_beginPoint, m_beginPoint); + m_selectionRectangleElement.show(); + m_isActive = true; + m_beginFormEditorItem = topFormEditorItem(m_editorView->scene()->items(beginPoint)); + m_oldSelectionList = m_editorView->selectedQmlItemNodes(); +} + +void RubberBandSelectionManipulator::update(const QPointF& updatePoint) +{ + m_selectionRectangleElement.setRect(m_beginPoint, updatePoint); +} + +void RubberBandSelectionManipulator::end() +{ + m_oldSelectionList.clear(); + m_selectionRectangleElement.hide(); + m_isActive = false; +} + +void RubberBandSelectionManipulator::select(SelectionType selectionType) +{ + if (!m_beginFormEditorItem) + return; + + QList<QGraphicsItem*> itemList = m_editorView->scene()->items(m_selectionRectangleElement.rect(), + Qt::IntersectsItemBoundingRect); + QList<QmlItemNode> newNodeList; + + foreach (QGraphicsItem* item, itemList) + { + FormEditorItem *formEditorItem = FormEditorItem::fromQGraphicsItem(item); + + if (formEditorItem + && m_beginFormEditorItem->childItems().contains(formEditorItem) + && !formEditorItem->qmlItemNode().isRootNode()) + { + newNodeList.append(formEditorItem->qmlItemNode()); + } + } + + if (newNodeList.isEmpty() && !m_beginFormEditorItem->qmlItemNode().isRootNode()) + newNodeList.append(m_beginFormEditorItem->qmlItemNode()); + + QList<QmlItemNode> nodeList; + + switch(selectionType) { + case AddToSelection: { + nodeList.append(m_oldSelectionList); + nodeList.append(newNodeList); + } + break; + case ReplaceSelection: { + nodeList.append(newNodeList); + } + break; + case RemoveFromSelection: { + QSet<QmlItemNode> oldSelectionSet(m_oldSelectionList.toSet()); + QSet<QmlItemNode> newSelectionSet(newNodeList.toSet()); + nodeList.append(oldSelectionSet.subtract(newSelectionSet).toList()); + } + } + + m_editorView->setSelectedQmlItemNodes(nodeList); +} + + +void RubberBandSelectionManipulator::setItems(const QList<FormEditorItem*> &itemList) +{ + m_itemList = itemList; +} + +QPointF RubberBandSelectionManipulator::beginPoint() const +{ + return m_beginPoint; +} + +bool RubberBandSelectionManipulator::isActive() const +{ + return m_isActive; +} + +} diff --git a/src/plugins/qmldesigner/components/formeditor/rubberbandselectionmanipulator.h b/src/plugins/qmldesigner/components/formeditor/rubberbandselectionmanipulator.h new file mode 100644 index 00000000000..bd8d9b6b2ae --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/rubberbandselectionmanipulator.h @@ -0,0 +1,83 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef RUBBERBANDSELECTIONMANIPULATOR_H +#define RUBBERBANDSELECTIONMANIPULATOR_H + + +#include "selectionrectangle.h" +#include "formeditorview.h" +#include "formeditoritem.h" + +namespace QmlDesigner { + + +class RubberBandSelectionManipulator +{ +public: + enum SelectionType { + ReplaceSelection, + AddToSelection, + RemoveFromSelection + }; + + + RubberBandSelectionManipulator(LayerItem *layerItem, FormEditorView *editorView); + + void setItems(const QList<FormEditorItem*> &itemList); + + void begin(const QPointF& beginPoint); + void update(const QPointF& updatePoint); + void end(); + + void clear(); + + void select(SelectionType selectionType); + + QPointF beginPoint() const; + + bool isActive() const; + +protected: + FormEditorItem *topFormEditorItem(const QList<QGraphicsItem*> &itemList); + + +private: + QList<FormEditorItem*> m_itemList; + QList<QmlItemNode> m_oldSelectionList; + SelectionRectangle m_selectionRectangleElement; + QPointF m_beginPoint; + FormEditorView *m_editorView; + FormEditorItem *m_beginFormEditorItem; + bool m_isActive; +}; + +} + +#endif // RUBBERBANDSELECTIONMANIPULATOR_H diff --git a/src/plugins/qmldesigner/components/formeditor/scaleitem.cpp b/src/plugins/qmldesigner/components/formeditor/scaleitem.cpp new file mode 100644 index 00000000000..def9aeb5ee2 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/scaleitem.cpp @@ -0,0 +1,49 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "scaleitem.h" + +#include "layeritem.h" + +namespace QmlDesigner { + +ScaleItem::ScaleItem(LayerItem *layerItem, ScaleIndicator *indicator) + : QGraphicsRectItem(layerItem), + m_indicator(indicator) +{ + Q_ASSERT(layerItem); + Q_ASSERT(indicator); +} + +ScaleIndicator* ScaleItem::indicator() const +{ + return m_indicator; +} + +} diff --git a/src/plugins/qmldesigner/components/formeditor/scaleitem.h b/src/plugins/qmldesigner/components/formeditor/scaleitem.h new file mode 100644 index 00000000000..c06d2170e26 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/scaleitem.h @@ -0,0 +1,53 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef SCALEITEM_H +#define SCALEITEM_H + +#include <QGraphicsRectItem> + +namespace QmlDesigner { + +class LayerItem; +class ScaleIndicator; + +class ScaleItem : public QGraphicsRectItem +{ +public: + ScaleItem(LayerItem *layerItem, ScaleIndicator *indicator); + + ScaleIndicator* indicator() const; + +private: + ScaleIndicator* m_indicator; +}; + +}; + +#endif // SCALEITEM_H diff --git a/src/plugins/qmldesigner/components/formeditor/scalemanipulator.cpp b/src/plugins/qmldesigner/components/formeditor/scalemanipulator.cpp new file mode 100644 index 00000000000..0c329f22b92 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/scalemanipulator.cpp @@ -0,0 +1,45 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "scalemanipulator.h" + + +namespace QmlDesigner { + +ScaleManipulator::ScaleManipulator(LayerItem *layerItem, FormEditorItem *formEditorItem) + : m_layerItem(layerItem), + m_formEditorItem(formEditorItem) +{ +} + +ScaleManipulator::~ScaleManipulator() +{ +} + +} diff --git a/src/plugins/qmldesigner/components/formeditor/scalemanipulator.h b/src/plugins/qmldesigner/components/formeditor/scalemanipulator.h new file mode 100644 index 00000000000..9d853d7f35c --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/scalemanipulator.h @@ -0,0 +1,63 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef SCALEMANIPULATOR_H +#define SCALEMANIPULATOR_H + +#include <QPointF> +#include <QList> + + + +class QGraphicsLineItem; + +namespace QmlDesigner { + +class LayerItem; +class FormEditorItem; + +class ScaleManipulator +{ +public: + ScaleManipulator(LayerItem *layerItem, FormEditorItem *formEditorItem); + virtual ~ScaleManipulator(); + + + void begin(const QPointF& beginPoint); + void update(const QPointF& updatePoint); + void reparentTo(FormEditorItem *newParent); + void end(const QPointF& updatePoint); + +private: + LayerItem *m_layerItem; + FormEditorItem *m_formEditorItem; +}; + +} +#endif // SCALEMANIPULATOR_H diff --git a/src/plugins/qmldesigner/components/formeditor/selectionindicator.cpp b/src/plugins/qmldesigner/components/formeditor/selectionindicator.cpp new file mode 100644 index 00000000000..d71ef6cca27 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/selectionindicator.cpp @@ -0,0 +1,118 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "selectionindicator.h" + +#include <QPen> +#include <cmath> +#include <QGraphicsScene> +#include <formeditorview.h> +#include <formeditorwidget.h> +#include <zoomaction.h> + +namespace QmlDesigner { + +SelectionIndicator::SelectionIndicator(LayerItem *layerItem) + : m_layerItem(layerItem) +{ +} + +SelectionIndicator::~SelectionIndicator() +{ + clear(); +} + +void SelectionIndicator::show() +{ + foreach (QGraphicsPolygonItem *item, m_indicatorShapeHash.values()) + item->show(); +} + +void SelectionIndicator::hide() +{ + foreach (QGraphicsPolygonItem *item, m_indicatorShapeHash.values()) + item->hide(); +} + +void SelectionIndicator::clear() +{ + if (m_layerItem) { + foreach (QGraphicsItem *item, m_indicatorShapeHash.values()) + m_layerItem->scene()->removeItem(item); + } + m_indicatorShapeHash.clear(); +} + +static void alignVertices(QPolygonF &polygon, double factor) +{ + QMutableVectorIterator<QPointF> iterator(polygon); + while (iterator.hasNext()) { + QPointF &vertex = iterator.next(); + vertex.setX(std::floor(vertex.x()) + factor); + vertex.setY(std::floor(vertex.y()) + factor); + } + +} + +void SelectionIndicator::setItems(const QList<FormEditorItem*> &itemList) +{ + clear(); + + foreach (FormEditorItem *item, itemList) { + QGraphicsPolygonItem *newSelectionIndicatorGraphicsItem = new QGraphicsPolygonItem(m_layerItem.data()); + m_indicatorShapeHash.insert(item, newSelectionIndicatorGraphicsItem); + QPolygonF boundingRectInSceneSpace(item->mapToScene(item->qmlItemNode().instanceBoundingRect())); + alignVertices(boundingRectInSceneSpace, 0.5 / item->formEditorView()->widget()->zoomAction()->zoomLevel()); + QPolygonF boundingRectInLayerItemSpace = m_layerItem->mapFromScene(boundingRectInSceneSpace); + newSelectionIndicatorGraphicsItem->setPolygon(boundingRectInLayerItemSpace); + newSelectionIndicatorGraphicsItem->setFlag(QGraphicsItem::ItemIsSelectable, false); + + QPen pen; + pen.setColor(QColor(108, 141, 221)); + newSelectionIndicatorGraphicsItem->setPen(pen); + } +} + + + +void SelectionIndicator::updateItems(const QList<FormEditorItem*> &itemList) +{ + foreach (FormEditorItem *item, itemList) { + if (m_indicatorShapeHash.contains(item)) { + QGraphicsPolygonItem *indicatorGraphicsItem = m_indicatorShapeHash.value(item); + QPolygonF boundingRectInSceneSpace(item->mapToScene(item->qmlItemNode().instanceBoundingRect())); + alignVertices(boundingRectInSceneSpace, 0.5 / item->formEditorView()->widget()->zoomAction()->zoomLevel()); + QPolygonF boundingRectInLayerItemSpace = m_layerItem->mapFromScene(boundingRectInSceneSpace); + indicatorGraphicsItem->setPolygon(boundingRectInLayerItemSpace); + } + } +} + +} + diff --git a/src/plugins/qmldesigner/components/formeditor/selectionindicator.h b/src/plugins/qmldesigner/components/formeditor/selectionindicator.h new file mode 100644 index 00000000000..27e641b0a8d --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/selectionindicator.h @@ -0,0 +1,65 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef SELECTIONINDICATOR_H +#define SELECTIONINDICATOR_H + +#include <QWeakPointer> +#include <QGraphicsPolygonItem> +#include "layeritem.h" +#include "formeditoritem.h" + + + +namespace QmlDesigner { + + +class SelectionIndicator +{ +public: + SelectionIndicator(LayerItem *layerItem); + ~SelectionIndicator(); + + void show(); + void hide(); + + void clear(); + + void setItems(const QList<FormEditorItem*> &itemList); + void updateItems(const QList<FormEditorItem*> &itemList); + +private: + QHash<FormEditorItem*, QGraphicsPolygonItem *> m_indicatorShapeHash; + QWeakPointer<LayerItem> m_layerItem; + +}; + +} + +#endif // SELECTIONINDICATOR_H diff --git a/src/plugins/qmldesigner/components/formeditor/selectionrectangle.cpp b/src/plugins/qmldesigner/components/formeditor/selectionrectangle.cpp new file mode 100644 index 00000000000..058de243e04 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/selectionrectangle.cpp @@ -0,0 +1,87 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "selectionrectangle.h" + +#include <QPen> +#include <QGraphicsScene> +#include <QtDebug> +#include <cmath> +#include <QGraphicsScene> + +namespace QmlDesigner { + +SelectionRectangle::SelectionRectangle(LayerItem *layerItem) + : m_controlShape(new QGraphicsRectItem(layerItem)), + m_layerItem(layerItem) +{ + m_controlShape->setPen(QPen(Qt::black)); + m_controlShape->setBrush(QColor(128, 128, 128, 50)); +} + +SelectionRectangle::~SelectionRectangle() +{ + if (m_layerItem) + m_layerItem->scene()->removeItem(m_controlShape); +} + +void SelectionRectangle::clear() +{ + hide(); +} +void SelectionRectangle::show() +{ + m_controlShape->show(); +} + +void SelectionRectangle::hide() +{ + m_controlShape->hide(); +} + +QRectF SelectionRectangle::rect() const +{ + return m_controlShape->mapFromScene(m_controlShape->rect()).boundingRect(); +} + +void SelectionRectangle::setRect(const QPointF &firstPoint, + const QPointF &secondPoint) +{ + double firstX = std::floor(firstPoint.x()) + 0.5; + double firstY = std::floor(firstPoint.y()) + 0.5; + double secondX = std::floor(secondPoint.x()) + 0.5; + double secondY = std::floor(secondPoint.y()) + 0.5; + QPointF topLeftPoint(firstX < secondX ? firstX : secondX, firstY < secondY ? firstY : secondY); + QPointF bottomRightPoint(firstX > secondX ? firstX : secondX, firstY > secondY ? firstY : secondY); + + QRectF rect(topLeftPoint, bottomRightPoint); + m_controlShape->setRect(rect); +} + +} diff --git a/src/plugins/qmldesigner/components/formeditor/selectionrectangle.h b/src/plugins/qmldesigner/components/formeditor/selectionrectangle.h new file mode 100644 index 00000000000..91839be73fc --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/selectionrectangle.h @@ -0,0 +1,63 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef SELECTIONRECTANGLE_H +#define SELECTIONRECTANGLE_H + +#include <QWeakPointer> +#include <QGraphicsRectItem> +#include "layeritem.h" + + +namespace QmlDesigner { + +class SelectionRectangle +{ +public: + SelectionRectangle(LayerItem *layerItem); + ~SelectionRectangle(); + + void show(); + void hide(); + + void clear(); + + void setRect(const QPointF &firstPoint, + const QPointF &secondPoint); + + QRectF rect() const; + +private: + QGraphicsRectItem *m_controlShape; + QWeakPointer<LayerItem> m_layerItem; +}; + +} + +#endif // SELECTIONRECTANGLE_H diff --git a/src/plugins/qmldesigner/components/formeditor/selectiontool.cpp b/src/plugins/qmldesigner/components/formeditor/selectiontool.cpp new file mode 100644 index 00000000000..a3e7c1ee60c --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/selectiontool.cpp @@ -0,0 +1,280 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "selectiontool.h" +#include "formeditorscene.h" +#include "formeditorview.h" + +#include "resizehandleitem.h" + + +#include <QApplication> +#include <QGraphicsSceneMouseEvent> +#include <QtDebug> +#include <QClipboard> + +namespace QmlDesigner { + +SelectionTool::SelectionTool(FormEditorView *editorView) + : AbstractFormEditorTool(editorView), + m_rubberbandSelectionManipulator(editorView->scene()->manipulatorLayerItem(), editorView), + m_singleSelectionManipulator(editorView), + m_selectionIndicator(editorView->scene()->manipulatorLayerItem()), + m_resizeIndicator(editorView->scene()->manipulatorLayerItem()) + +{ +// view()->setCursor(Qt::CrossCursor); +} + + +SelectionTool::~SelectionTool() +{ +} + +void SelectionTool::mousePressEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event) +{ + m_mousePressTimer.start(); + FormEditorItem* formEditorItem = topFormEditorItem(itemList); + if (formEditorItem && !formEditorItem->qmlItemNode().hasChildren()) { + m_singleSelectionManipulator.begin(event->scenePos()); + + if (event->modifiers().testFlag(Qt::ControlModifier)) + m_singleSelectionManipulator.select(SingleSelectionManipulator::RemoveFromSelection); + else if (event->modifiers().testFlag(Qt::ShiftModifier)) + m_singleSelectionManipulator.select(SingleSelectionManipulator::AddToSelection); + else + m_singleSelectionManipulator.select(SingleSelectionManipulator::InvertSelection); + } else { + if (event->modifiers().testFlag(Qt::AltModifier)) { + m_singleSelectionManipulator.begin(event->scenePos()); + + if (event->modifiers().testFlag(Qt::ControlModifier)) + m_singleSelectionManipulator.select(SingleSelectionManipulator::RemoveFromSelection); + else if (event->modifiers().testFlag(Qt::ShiftModifier)) + m_singleSelectionManipulator.select(SingleSelectionManipulator::AddToSelection); + else + m_singleSelectionManipulator.select(SingleSelectionManipulator::InvertSelection); + + m_singleSelectionManipulator.end(event->scenePos()); + view()->changeToMoveTool(event->scenePos()); + } else { + m_rubberbandSelectionManipulator.begin(event->scenePos()); + } + } +} + +void SelectionTool::mouseMoveEvent(const QList<QGraphicsItem*> &/*itemList*/, + QGraphicsSceneMouseEvent *event) +{ + if (m_singleSelectionManipulator.isActive()) { + QPointF mouseMovementVector = m_singleSelectionManipulator.beginPoint() - event->scenePos(); + if ((mouseMovementVector.toPoint().manhattanLength() > QApplication::startDragDistance()) + && (m_mousePressTimer.elapsed() > QApplication::startDragTime())) { + m_singleSelectionManipulator.end(event->scenePos()); + view()->changeToMoveTool(m_singleSelectionManipulator.beginPoint()); + return; + } + } else if (m_rubberbandSelectionManipulator.isActive()) { + QPointF mouseMovementVector = m_rubberbandSelectionManipulator.beginPoint() - event->scenePos(); + if ((mouseMovementVector.toPoint().manhattanLength() > QApplication::startDragDistance()) + && (m_mousePressTimer.elapsed() > QApplication::startDragTime())) { + m_rubberbandSelectionManipulator.update(event->scenePos()); + + if (event->modifiers().testFlag(Qt::ControlModifier)) + m_rubberbandSelectionManipulator.select(RubberBandSelectionManipulator::RemoveFromSelection); + else if (event->modifiers().testFlag(Qt::ShiftModifier)) + m_rubberbandSelectionManipulator.select(RubberBandSelectionManipulator::AddToSelection); + else + m_rubberbandSelectionManipulator.select(RubberBandSelectionManipulator::ReplaceSelection); + } + } +} + +void SelectionTool::hoverMoveEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent * /*event*/) +{ + if (itemList.isEmpty()) + return; + + ResizeHandleItem* resizeHandle = ResizeHandleItem::fromGraphicsItem(itemList.first()); + if (resizeHandle) { + view()->changeToResizeTool(); + return; + } + + if (topSelectedItemIsMovable(itemList)) + view()->changeToMoveTool(); +} + +void SelectionTool::mouseReleaseEvent(const QList<QGraphicsItem*> &/*itemList*/, + QGraphicsSceneMouseEvent *event) +{ + if (m_singleSelectionManipulator.isActive()) { + qDebug() << __FUNCTION__ << "single"; + m_singleSelectionManipulator.end(event->scenePos()); + } + else if (m_rubberbandSelectionManipulator.isActive()) { + + QPointF mouseMovementVector = m_rubberbandSelectionManipulator.beginPoint() - event->scenePos(); + if (mouseMovementVector.toPoint().manhattanLength() < QApplication::startDragDistance()) { + m_singleSelectionManipulator.begin(event->scenePos()); + + if (event->modifiers().testFlag(Qt::ControlModifier)) + m_singleSelectionManipulator.select(SingleSelectionManipulator::RemoveFromSelection); + else if (event->modifiers().testFlag(Qt::ShiftModifier)) + m_singleSelectionManipulator.select(SingleSelectionManipulator::AddToSelection); + else + m_singleSelectionManipulator.select(SingleSelectionManipulator::InvertSelection); + + m_singleSelectionManipulator.end(event->scenePos()); + } else { + m_rubberbandSelectionManipulator.update(event->scenePos()); + + if (event->modifiers().testFlag(Qt::ControlModifier)) + m_rubberbandSelectionManipulator.select(RubberBandSelectionManipulator::RemoveFromSelection); + else if (event->modifiers().testFlag(Qt::ShiftModifier)) + m_rubberbandSelectionManipulator.select(RubberBandSelectionManipulator::AddToSelection); + else + m_rubberbandSelectionManipulator.select(RubberBandSelectionManipulator::ReplaceSelection); + + m_rubberbandSelectionManipulator.end(); + } + } + +} + +void SelectionTool::mouseDoubleClickEvent(const QList<QGraphicsItem*> &/*itemList*/, + QGraphicsSceneMouseEvent * /*event*/) +{ + +} + +void SelectionTool::keyPressEvent(QKeyEvent *event) +{ + switch(event->key()) { + case Qt::Key_Left: + case Qt::Key_Right: + case Qt::Key_Up: + case Qt::Key_Down: + view()->changeToMoveTool(); + view()->currentTool()->keyPressEvent(event); + break; + case Qt::Key_C: + if (event->modifiers().testFlag(Qt::ControlModifier)) { + copySelectedNodeToClipBoard(); + break; + } + case Qt::Key_V: + if (event->modifiers().testFlag(Qt::ControlModifier)) { + pasteClipBoard(); + break; + } + case Qt::Key_X: + if (event->modifiers().testFlag(Qt::ControlModifier)) { + cutSelectedNodeToClipBoard(); + break; + } + } +} + +void SelectionTool::keyReleaseEvent(QKeyEvent * /*keyEvent*/) +{ + +} + +void SelectionTool::copySelectedNodeToClipBoard() +{ + // QClipboard *clipboard = QApplication::clipboard(); +} + +void SelectionTool::cutSelectedNodeToClipBoard() +{ + // QClipboard *clipboard = QApplication::clipboard(); +} + +void SelectionTool::pasteClipBoard() +{ + // QClipboard *clipboard = QApplication::clipboard(); +} + +void SelectionTool::itemsAboutToRemoved(const QList<FormEditorItem*> &/*itemList*/) +{ + +} + +//QVariant SelectionTool::itemChange(const QList<QGraphicsItem*> &itemList, +// QGraphicsItem::GraphicsItemChange change, +// const QVariant &value ) +//{ +// qDebug() << Q_FUNC_INFO; +// return QVariant(); +//} + +//void SelectionTool::update() +//{ +// +//} + + +void SelectionTool::clear() +{ + m_rubberbandSelectionManipulator.clear(), + m_singleSelectionManipulator.clear(); + m_selectionIndicator.clear(); + m_resizeIndicator.clear(); +} + +void SelectionTool::selectedItemsChanged(const QList<FormEditorItem*> &itemList) +{ + m_selectionIndicator.setItems(itemList); + m_resizeIndicator.setItems(itemList); +} + +void SelectionTool::formEditorItemsChanged(const QList<FormEditorItem*> &itemList) +{ + m_selectionIndicator.updateItems(itemList); + m_resizeIndicator.updateItems(itemList); +} + +void SelectionTool::selectUnderPoint(QGraphicsSceneMouseEvent *event) +{ + m_singleSelectionManipulator.begin(event->scenePos()); + + if (event->modifiers().testFlag(Qt::ControlModifier)) + m_singleSelectionManipulator.select(SingleSelectionManipulator::RemoveFromSelection); + else if (event->modifiers().testFlag(Qt::ShiftModifier)) + m_singleSelectionManipulator.select(SingleSelectionManipulator::AddToSelection); + else + m_singleSelectionManipulator.select(SingleSelectionManipulator::ReplaceSelection); + + m_singleSelectionManipulator.end(event->scenePos()); +} + +} diff --git a/src/plugins/qmldesigner/components/formeditor/selectiontool.h b/src/plugins/qmldesigner/components/formeditor/selectiontool.h new file mode 100644 index 00000000000..062eb20fbc9 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/selectiontool.h @@ -0,0 +1,93 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef SELECTIONTOOL_H +#define SELECTIONTOOL_H + + +#include "abstractformeditortool.h" +#include "movemanipulator.h" +#include "rubberbandselectionmanipulator.h" +#include "singleselectionmanipulator.h" +#include "selectionindicator.h" +#include "resizeindicator.h" + +#include <QHash> +#include <QTime> + +namespace QmlDesigner { + + +class SelectionTool : public AbstractFormEditorTool +{ +public: + SelectionTool(FormEditorView* editorView); + ~SelectionTool(); + + void mousePressEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event); + void mouseMoveEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event); + void mouseReleaseEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event); + void mouseDoubleClickEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event); + void hoverMoveEvent(const QList<QGraphicsItem*> &itemList, + QGraphicsSceneMouseEvent *event); + void keyPressEvent(QKeyEvent *event); + void keyReleaseEvent(QKeyEvent *keyEvent); + + void itemsAboutToRemoved(const QList<FormEditorItem*> &itemList); + // QVariant itemChange(const QList<QGraphicsItem*> &itemList, +// QGraphicsItem::GraphicsItemChange change, +// const QVariant &value ); + +// void update(); + + void clear(); + + void selectedItemsChanged(const QList<FormEditorItem*> &itemList); + + void formEditorItemsChanged(const QList<FormEditorItem*> &itemList); + + void selectUnderPoint(QGraphicsSceneMouseEvent *event); + + void copySelectedNodeToClipBoard(); + void cutSelectedNodeToClipBoard(); + void pasteClipBoard(); +private: + RubberBandSelectionManipulator m_rubberbandSelectionManipulator; + SingleSelectionManipulator m_singleSelectionManipulator; + SelectionIndicator m_selectionIndicator; + ResizeIndicator m_resizeIndicator; + QTime m_mousePressTimer; +}; + +} +#endif // SELECTIONTOOL_H diff --git a/src/plugins/qmldesigner/components/formeditor/singleselectionmanipulator.cpp b/src/plugins/qmldesigner/components/formeditor/singleselectionmanipulator.cpp new file mode 100644 index 00000000000..43625eb9550 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/singleselectionmanipulator.cpp @@ -0,0 +1,132 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "singleselectionmanipulator.h" + +#include "model.h" +#include "nodemetainfo.h" +#include "formeditorscene.h" +#include "formeditorview.h" +#include <QtDebug> + +namespace QmlDesigner { + +SingleSelectionManipulator::SingleSelectionManipulator(FormEditorView *editorView) + : m_editorView(editorView), + m_isActive(false) +{ +} + + +void SingleSelectionManipulator::begin(const QPointF &beginPoint) +{ + m_beginPoint = beginPoint; + m_isActive = true; + m_oldSelectionList = m_editorView->selectedQmlItemNodes(); +} + +void SingleSelectionManipulator::update(const QPointF &/*updatePoint*/) +{ + m_oldSelectionList.clear(); +} + +void SingleSelectionManipulator::clear() +{ + m_beginPoint = QPointF(); + m_oldSelectionList.clear(); +} + + +void SingleSelectionManipulator::end(const QPointF &/*updatePoint*/) +{ + m_oldSelectionList.clear(); + m_isActive = false; +} + +void SingleSelectionManipulator::select(SelectionType selectionType) +{ + QList<QGraphicsItem*> itemList = m_editorView->scene()->items(m_beginPoint); + + QmlItemNode selectedNode; + + foreach(QGraphicsItem* item, itemList) + { + FormEditorItem *formEditorItem = FormEditorItem::fromQGraphicsItem(item); + + if (formEditorItem + && !formEditorItem->qmlItemNode().isRootNode() + && (formEditorItem->qmlItemNode().hasShowContent())) + { + selectedNode = formEditorItem->qmlItemNode(); + break; + } + } + + QList<QmlItemNode> nodeList; + + switch(selectionType) { + case AddToSelection: { + nodeList.append(m_oldSelectionList); + if (selectedNode.isValid()) + nodeList.append(selectedNode); + } + break; + case ReplaceSelection: { + if (selectedNode.isValid()) + nodeList.append(selectedNode); + } + break; + case RemoveFromSelection: { + nodeList.append(m_oldSelectionList); + if (selectedNode.isValid()) + nodeList.removeAll(selectedNode); + } + break; + case InvertSelection: { + if (selectedNode.isValid() + && !m_oldSelectionList.contains(selectedNode)) + nodeList.append(selectedNode); + } + } + + m_editorView->setSelectedQmlItemNodes(nodeList); +} + + +bool SingleSelectionManipulator::isActive() const +{ + return m_isActive; +} + +QPointF SingleSelectionManipulator::beginPoint() const +{ + return m_beginPoint; +} + +} diff --git a/src/plugins/qmldesigner/components/formeditor/singleselectionmanipulator.h b/src/plugins/qmldesigner/components/formeditor/singleselectionmanipulator.h new file mode 100644 index 00000000000..a97abaa1453 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/singleselectionmanipulator.h @@ -0,0 +1,72 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef SINGLESELECTIONMANIPULATOR_H +#define SINGLESELECTIONMANIPULATOR_H + +#include "selectionrectangle.h" +#include "formeditorview.h" + +namespace QmlDesigner { + +class SingleSelectionManipulator +{ +public: + SingleSelectionManipulator(FormEditorView *editorView); + +// void setItems(const QList<FormEditorItem*> &itemList); + + enum SelectionType { + ReplaceSelection, + AddToSelection, + RemoveFromSelection, + InvertSelection + }; + + void begin(const QPointF& beginPoint); + void update(const QPointF& updatePoint); + void end(const QPointF& updatePoint); + + void select(SelectionType selectionType); + + void clear(); + + QPointF beginPoint() const; + + bool isActive() const; + +private: + QList<QmlItemNode> m_oldSelectionList; + QPointF m_beginPoint; + FormEditorView *m_editorView; + bool m_isActive; +}; + +} +#endif // SINGLESELECTIONMANIPULATOR_H diff --git a/src/plugins/qmldesigner/components/formeditor/snapper.cpp b/src/plugins/qmldesigner/components/formeditor/snapper.cpp new file mode 100644 index 00000000000..506146836c4 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/snapper.cpp @@ -0,0 +1,626 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "snapper.h" + +#include <QtDebug> + +#include <limits> +#include <cmath> +#include <QLineF> +#include <QPen> +#include <QApplication> + +namespace QmlDesigner { + +Snapper::Snapper() + : m_containerFormEditorItem(0), + m_transformtionSpaceFormEditorItem(0), + m_snappingDistance(5.0) +{ +} + +void Snapper::updateSnappingLines(const QList<FormEditorItem*> &exceptionList) +{ + if (m_containerFormEditorItem) + m_containerFormEditorItem->updateSnappingLines(exceptionList, m_transformtionSpaceFormEditorItem); +} + +void Snapper::updateSnappingLines(FormEditorItem* exceptionItem) +{ + QList<FormEditorItem*> exceptionList; + exceptionList.append(exceptionItem); + updateSnappingLines(exceptionList); +} + + +void Snapper::setContainerFormEditorItem(FormEditorItem *formEditorItem) +{ + m_containerFormEditorItem = formEditorItem; +} + + +void Snapper::setTransformtionSpaceFormEditorItem(FormEditorItem *formEditorItem) +{ + m_transformtionSpaceFormEditorItem = formEditorItem; +} + +FormEditorItem *Snapper::transformtionSpaceFormEditorItem() const +{ + return m_transformtionSpaceFormEditorItem; +} + +double Snapper::snappedVerticalOffset(const QRectF &boundingRect) const +{ + double offset = std::numeric_limits<double>::max(); + + offset = qMin(offset, snappedOffsetForLines(containerFormEditorItem()->leftSnappingLines(), + boundingRect.left())); + + offset = qMin(offset, snappedOffsetForOffsetLines(containerFormEditorItem()->rightSnappingOffsets(), + Qt::Vertical, + boundingRect.left(), + boundingRect.top(), + boundingRect.bottom())); + + offset = qMin(offset, snappedOffsetForLines(containerFormEditorItem()->rightSnappingLines(), + boundingRect.right())); + + offset = qMin(offset, snappedOffsetForOffsetLines(containerFormEditorItem()->leftSnappingOffsets(), + Qt::Vertical, + boundingRect.right(), + boundingRect.top(), + boundingRect.bottom())); + + offset = qMin(offset, snappedOffsetForLines(containerFormEditorItem()->verticalCenterSnappingLines(), + boundingRect.center().x())); + + return offset; +} + +double Snapper::snappedHorizontalOffset(const QRectF &boundingRect) const +{ + double offset = std::numeric_limits<double>::max(); + + offset = qMin(offset, snappedOffsetForLines(containerFormEditorItem()->topSnappingLines(), + boundingRect.top())); + + offset = qMin(offset, snappedOffsetForOffsetLines(containerFormEditorItem()->bottomSnappingOffsets(), + Qt::Horizontal, + boundingRect.top(), + boundingRect.left(), + boundingRect.right())); + + offset = qMin(offset, snappedOffsetForLines(containerFormEditorItem()->bottomSnappingLines(), + boundingRect.bottom())); + + offset = qMin(offset, snappedOffsetForOffsetLines(containerFormEditorItem()->topSnappingOffsets(), + Qt::Horizontal, + boundingRect.bottom(), + boundingRect.left(), + boundingRect.right())); + + offset = qMin(offset, snappedOffsetForLines(containerFormEditorItem()->horizontalCenterSnappingLines(), + boundingRect.center().y())); + return offset; +} + + +double Snapper::snapTopOffset(const QRectF &boundingRect) const +{ + double offset = std::numeric_limits<double>::max(); + + offset = qMin(offset, snappedOffsetForLines(containerFormEditorItem()->topSnappingLines(), + boundingRect.top())); + + offset = qMin(offset, snappedOffsetForOffsetLines(containerFormEditorItem()->bottomSnappingOffsets(), + Qt::Horizontal, + boundingRect.top(), + boundingRect.left(), + boundingRect.right())); + return offset; +} + +double Snapper::snapRightOffset(const QRectF &boundingRect) const +{ + double offset = std::numeric_limits<double>::max(); + + offset = qMin(offset, snappedOffsetForLines(containerFormEditorItem()->rightSnappingLines(), + boundingRect.right())); + + offset = qMin(offset, snappedOffsetForOffsetLines(containerFormEditorItem()->leftSnappingOffsets(), + Qt::Vertical, + boundingRect.right(), + boundingRect.top(), + boundingRect.bottom())); + return offset; +} + +double Snapper::snapLeftOffset(const QRectF &boundingRect) const +{ + double offset = std::numeric_limits<double>::max(); + + offset = qMin(offset, snappedOffsetForLines(containerFormEditorItem()->leftSnappingLines(), + boundingRect.left())); + + offset = qMin(offset, snappedOffsetForOffsetLines(containerFormEditorItem()->rightSnappingOffsets(), + Qt::Vertical, + boundingRect.left(), + boundingRect.top(), + boundingRect.bottom())); + return offset; +} + +double Snapper::snapBottomOffset(const QRectF &boundingRect) const +{ + double offset = std::numeric_limits<double>::max(); + + offset = qMin(offset, snappedOffsetForLines(containerFormEditorItem()->bottomSnappingLines(), + boundingRect.bottom())); + + offset = qMin(offset, snappedOffsetForOffsetLines(containerFormEditorItem()->topSnappingOffsets(), + Qt::Horizontal, + boundingRect.bottom(), + boundingRect.left(), + boundingRect.right())); + return offset; + +} + + +QList<QLineF> Snapper::verticalSnappedLines(const QRectF &boundingRect, QList<QRectF> *boundingRects) const +{ + QList<QLineF> lineList = findSnappingLines(containerFormEditorItem()->leftSnappingLines(), + Qt::Vertical, + boundingRect.left(), + boundingRect.top(), + boundingRect.bottom(), + boundingRects); + + lineList += findSnappingOffsetLines(containerFormEditorItem()->rightSnappingOffsets(), + Qt::Vertical, + boundingRect.left(), + boundingRect.top(), + boundingRect.bottom(), + boundingRects); + + + lineList += findSnappingLines(containerFormEditorItem()->rightSnappingLines(), + Qt::Vertical, + boundingRect.right(), + boundingRect.top(), + boundingRect.bottom(), + boundingRects); + + lineList += findSnappingOffsetLines(containerFormEditorItem()->leftSnappingOffsets(), + Qt::Vertical, + boundingRect.right(), + boundingRect.top(), + boundingRect.bottom(), + boundingRects); + + lineList += findSnappingLines(containerFormEditorItem()->verticalCenterSnappingLines(), + Qt::Vertical, + boundingRect.center().x(), + boundingRect.top(), + boundingRect.bottom(), + boundingRects); + + return lineList; +} + +QList<QLineF> Snapper::horizontalSnappedLines(const QRectF &boundingRect, QList<QRectF> *boundingRects) const +{ + QList<QLineF> lineList = findSnappingLines(containerFormEditorItem()->topSnappingLines(), + Qt::Horizontal, + boundingRect.top(), + boundingRect.left(), + boundingRect.right()); + + lineList += findSnappingOffsetLines(containerFormEditorItem()->bottomSnappingOffsets(), + Qt::Horizontal, + boundingRect.top(), + boundingRect.left(), + boundingRect.right(), + boundingRects); + + + lineList += findSnappingLines(containerFormEditorItem()->bottomSnappingLines(), + Qt::Horizontal, + boundingRect.bottom(), + boundingRect.left(), + boundingRect.right(), + boundingRects); + + lineList += findSnappingOffsetLines(containerFormEditorItem()->topSnappingOffsets(), + Qt::Horizontal, + boundingRect.bottom(), + boundingRect.left(), + boundingRect.right(), + boundingRects); + + lineList += findSnappingLines(containerFormEditorItem()->horizontalCenterSnappingLines(), + Qt::Horizontal, + boundingRect.center().y(), + boundingRect.left(), + boundingRect.right(), + boundingRects); + + return lineList; +} + + + +FormEditorItem *Snapper::containerFormEditorItem() const +{ + return m_containerFormEditorItem; +} + +QLineF Snapper::createSnapLine(Qt::Orientation orientation, + double snapLine, + double lowerLimit, + double upperLimit, + const QRectF &itemRect) const +{ + if (orientation == Qt::Horizontal) { + double lowerX(qMin(lowerLimit, itemRect.left())); + double upperX(qMax(upperLimit, itemRect.right())); + return QLineF(lowerX, snapLine, upperX, snapLine); + } else { + double lowerY(qMin(lowerLimit, itemRect.top())); + double upperY(qMax(upperLimit, itemRect.bottom())); + return QLineF(snapLine, lowerY, snapLine, upperY); + } +} + +static bool compareLines(double snapLine, double lineToSnap) +{ +// if (qAbs(snapLine - lineToSnap) < 1.0) +// return true; +// +// return false; + return qFuzzyCompare(snapLine, lineToSnap); +} + +QList<QLineF> Snapper::findSnappingLines(const SnapLineMap &snappingLineMap, + Qt::Orientation orientation, + double snapLine, + double lowerLimit, + double upperLimit, + QList<QRectF> *boundingRects) const +{ + QList<QLineF> lineList; + + SnapLineMapIterator snappingLineIterator(snappingLineMap); + while (snappingLineIterator.hasNext()) { + snappingLineIterator.next(); + + if (compareLines(snapLine, snappingLineIterator.key())) { // near distance snapping lines + lineList += createSnapLine(orientation, + snappingLineIterator.key(), + lowerLimit, + upperLimit, + snappingLineIterator.value().first); + if (boundingRects != 0) + boundingRects->append(snappingLineIterator.value().first); + } + } + + return lineList; +} + + +QList<QLineF> Snapper::findSnappingOffsetLines(const SnapLineMap &snappingOffsetMap, + Qt::Orientation orientation, + double snapLine, + double lowerLimit, + double upperLimit, + QList<QRectF> *boundingRects) const +{ + QList<QLineF> lineList; + + SnapLineMapIterator snappingOffsetIterator(snappingOffsetMap); + while (snappingOffsetIterator.hasNext()) { + snappingOffsetIterator.next(); + + const QRectF &formEditorItemRect(snappingOffsetIterator.value().first); + double formEditorItemRectLowerLimit; + double formEditorItemRectUpperLimit; + if (orientation == Qt::Horizontal) { + formEditorItemRectLowerLimit = formEditorItemRect.left(); + formEditorItemRectUpperLimit = formEditorItemRect.right(); + } else { + formEditorItemRectLowerLimit = formEditorItemRect.top(); + formEditorItemRectUpperLimit = formEditorItemRect.bottom(); + } + + + if (qFuzzyCompare(snapLine, snappingOffsetIterator.key()) && + !(lowerLimit > formEditorItemRectUpperLimit || + upperLimit < formEditorItemRectLowerLimit)) { + lineList += createSnapLine(orientation, + snapLine, + lowerLimit, + upperLimit, + formEditorItemRect); + if (boundingRects != 0) + boundingRects->append(snappingOffsetIterator.value().first); + } + } + + + return lineList; +} + +double Snapper::snappedOffsetForLines(const SnapLineMap &snappingLineMap, + double value) const +{ + QMultiMap<double, double> minimumSnappingLineMap; + + SnapLineMapIterator snappingLineIterator(snappingLineMap); + while (snappingLineIterator.hasNext()) { + snappingLineIterator.next(); + double snapLine = snappingLineIterator.key(); + double offset = value - snapLine; + double distance = qAbs(offset); + + if (distance < snappingDistance()) { + minimumSnappingLineMap.insert(distance, offset); + } + } + + if (!minimumSnappingLineMap.isEmpty()) { + return minimumSnappingLineMap.begin().value(); + } + + return std::numeric_limits<double>::max(); +} + + +double Snapper::snappedOffsetForOffsetLines(const SnapLineMap &snappingOffsetMap, + Qt::Orientation orientation, + double value, + double lowerLimit, + double upperLimit) const +{ + QMultiMap<double, double> minimumSnappingLineMap; + + SnapLineMapIterator snappingOffsetIterator(snappingOffsetMap); + while (snappingOffsetIterator.hasNext()) { + snappingOffsetIterator.next(); + double snapLine = snappingOffsetIterator.key(); + const QRectF &formEditorItemRect(snappingOffsetIterator.value().first); + double formEditorItemRectLowerLimit; + double formEditorItemRectUpperLimit; + if (orientation == Qt::Horizontal) { + formEditorItemRectLowerLimit = formEditorItemRect.left(); + formEditorItemRectUpperLimit = formEditorItemRect.right(); + } else { + formEditorItemRectLowerLimit = formEditorItemRect.top(); + formEditorItemRectUpperLimit = formEditorItemRect.bottom(); + } + + double offset = value - snapLine; + double distance = qAbs(offset); + + if (distance < snappingDistance() && + !(lowerLimit > formEditorItemRectUpperLimit || + upperLimit < formEditorItemRectLowerLimit)) { + + minimumSnappingLineMap.insert(distance, offset); + } + } + + if (!minimumSnappingLineMap.isEmpty()) { + return minimumSnappingLineMap.begin().value(); + } + + return std::numeric_limits<double>::max(); +} + + +void Snapper::setSnappingDistance(double snappingDistance) +{ + m_snappingDistance = snappingDistance; +} + +double Snapper::snappingDistance() const +{ + return m_snappingDistance; +} + +static bool lineXLessThan(const QLineF &firstLine, const QLineF &secondLine) +{ + return firstLine.x1() < secondLine.x2(); +} + +static bool lineYLessThan(const QLineF &firstLine, const QLineF &secondLine) +{ + return firstLine.y1() < secondLine.y2(); +} + + +static QLineF mergedHorizontalLine(const QList<QLineF> &lineList) +{ + if (lineList.count() == 1) + return lineList.first(); + + double minimumX = std::numeric_limits<double>::max(); + double maximumX = std::numeric_limits<double>::min(); + foreach(const QLineF &line, lineList) { + minimumX = qMin(minimumX, line.x1()); + minimumX = qMin(minimumX, line.x2()); + maximumX = qMax(maximumX, line.x1()); + maximumX = qMax(maximumX, line.x2()); + } + + double y(lineList.first().y1()); + return QLineF(minimumX, y, maximumX, y);; +} + +static QLineF mergedVerticalLine(const QList<QLineF> &lineList) +{ + if (lineList.count() == 1) + return lineList.first(); + + double minimumY = std::numeric_limits<double>::max(); + double maximumY = std::numeric_limits<double>::min(); + foreach(const QLineF &line, lineList) { + minimumY = qMin(minimumY, line.y1()); + minimumY = qMin(minimumY, line.y2()); + maximumY = qMax(maximumY, line.y1()); + maximumY = qMax(maximumY, line.y2()); + } + + double x(lineList.first().x1()); + return QLineF(x, minimumY, x, maximumY);; +} + +static QList<QLineF> mergedHorizontalLines(const QList<QLineF> &lineList) +{ + QList<QLineF> mergedLineList; + + QList<QLineF> sortedLineList(lineList); + qSort(sortedLineList.begin(), sortedLineList.end(), lineYLessThan); + + QList<QLineF> lineWithTheSameY; + QListIterator<QLineF> sortedLineListIterator(sortedLineList); + while (sortedLineListIterator.hasNext()) { + QLineF line = sortedLineListIterator.next(); + lineWithTheSameY.append(line); + + if (sortedLineListIterator.hasNext()) { + QLineF nextLine = sortedLineListIterator.peekNext(); + if (!qFuzzyCompare(line.y1(), nextLine.y1())) { + mergedLineList.append(mergedHorizontalLine(lineWithTheSameY)); + lineWithTheSameY.clear(); + } + } else { + mergedLineList.append(mergedHorizontalLine(lineWithTheSameY)); + } + } + + return mergedLineList; +} + + + + + + +static QList<QLineF> mergedVerticalLines(const QList<QLineF> &lineList) +{ + QList<QLineF> mergedLineList; + + QList<QLineF> sortedLineList(lineList); + qSort(sortedLineList.begin(), sortedLineList.end(), lineXLessThan); + + QList<QLineF> lineWithTheSameX; + QListIterator<QLineF> sortedLineListIterator(sortedLineList); + while (sortedLineListIterator.hasNext()) { + QLineF line = sortedLineListIterator.next(); + lineWithTheSameX.append(line); + + if (sortedLineListIterator.hasNext()) { + QLineF nextLine = sortedLineListIterator.peekNext(); + if (!qFuzzyCompare(line.x1(), nextLine.x1())) { + mergedLineList.append(mergedVerticalLine(lineWithTheSameX)); + lineWithTheSameX.clear(); + } + } else { + mergedLineList.append(mergedVerticalLine(lineWithTheSameX)); + } + } + + return mergedLineList; +} + +QList<QGraphicsItem*> Snapper::generateSnappingLines(const QRectF &boundingRect, + QGraphicsItem *layerItem, + const QTransform &transform) +{ + QList<QRectF> boundingRectList; + boundingRectList.append(boundingRect); + + return generateSnappingLines(boundingRectList, layerItem, transform); +} + +static void alignLine(QLineF &line) +{ + line.setP1(QPointF(std::floor(line.p1().x()) + 0.5, + std::floor(line.p1().y()) + 0.5)); + line.setP2(QPointF(std::floor(line.p2().x()) + 0.5, + std::floor(line.p2().y()) + 0.5)); +} + +QList<QGraphicsItem*> Snapper::generateSnappingLines(const QList<QRectF> &boundingRectList, + QGraphicsItem *layerItem, + const QTransform &transform) +{ + QList<QGraphicsItem*> graphicsItemList; + QList<QLineF> lineList; + foreach(const QRectF &boundingRect, boundingRectList) { + QList<QRectF> snappedBoundingRectList; + lineList += mergedHorizontalLines(horizontalSnappedLines(boundingRect, &snappedBoundingRectList)); + lineList += mergedVerticalLines(verticalSnappedLines(boundingRect, &snappedBoundingRectList)); + +// snappedBoundingRectList.append(boundingRect); +// foreach(const QRectF &snappedBoundingRect, snappedBoundingRectList) { +// QPolygonF rect = transform.map(snappedBoundingRect); +// alignVertices(rect); +// QGraphicsPolygonItem * item = new QGraphicsPolygonItem(rect, layerItem); +// item->setZValue(20); + +// QColor brushColor(QApplication::palette().highlight().color()); +// QColor brushColor(Qt::gray); +// brushColor.setAlphaF(0.25); +// QBrush brush(brushColor); +// item->setBrush(brush); +// item->setPen(Qt::NoPen); +// graphicsItemList.append(item); +// } + } + + foreach(const QLineF &line, lineList) { + QLineF lineInTransformationSpace = transform.map(line); + alignLine(lineInTransformationSpace); + QGraphicsLineItem * lineItem = new QGraphicsLineItem(lineInTransformationSpace, layerItem); + lineItem->setZValue(40); + QPen linePen; +// linePen.setStyle(Qt::DashLine); + linePen.setColor("#5d2dd7"); + lineItem->setPen(linePen); + + graphicsItemList.append(lineItem); + } + + return graphicsItemList; +} + +} diff --git a/src/plugins/qmldesigner/components/formeditor/snapper.h b/src/plugins/qmldesigner/components/formeditor/snapper.h new file mode 100644 index 00000000000..a176b4e6e12 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/snapper.h @@ -0,0 +1,112 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef SNAPPER_H +#define SNAPPER_H + +#include "formeditoritem.h" + +class QLineF; + +namespace QmlDesigner { + + +class Snapper +{ +public: + Snapper(); + + void setContainerFormEditorItem(FormEditorItem *formEditorItem); + FormEditorItem *containerFormEditorItem() const; + + void setTransformtionSpaceFormEditorItem(FormEditorItem *formEditorItem); + FormEditorItem *transformtionSpaceFormEditorItem() const; + + double snappedVerticalOffset(const QRectF &boundingRect) const; + double snappedHorizontalOffset(const QRectF &boundingRect) const; + + double snapTopOffset(const QRectF &boundingRect) const; + double snapRightOffset(const QRectF &boundingRect) const; + double snapLeftOffset(const QRectF &boundingRect) const; + double snapBottomOffset(const QRectF &boundingRect) const; + + QList<QLineF> horizontalSnappedLines(const QRectF &boundingRect, QList<QRectF> *boundingRects = 0) const; + QList<QLineF> verticalSnappedLines(const QRectF &boundingRect, QList<QRectF> *boundingRects = 0) const; + + void setSnappingDistance(double snappingDistance); + double snappingDistance() const; + + void updateSnappingLines(const QList<FormEditorItem*> &exceptionList); + void updateSnappingLines(FormEditorItem* exceptionItem); + + QList<QGraphicsItem*> generateSnappingLines(const QList<QRectF> &boundingRectList, + QGraphicsItem *layerItem, + const QTransform &transform); + QList<QGraphicsItem*> generateSnappingLines(const QRectF &boundingRect, + QGraphicsItem *layerItem, + const QTransform &transform); +protected: + double snappedOffsetForLines(const SnapLineMap &snappingLineMap, + double value) const; + + double snappedOffsetForOffsetLines(const SnapLineMap &snappingOffsetMap, + Qt::Orientation orientation, + double value, + double lowerLimit, + double upperLimit) const; + + QList<QLineF> findSnappingLines(const SnapLineMap &snappingLineMap, + Qt::Orientation orientation, + double snapLine, + double lowerLimit, + double upperLimit, + QList<QRectF> *boundingRects = 0) const; + + QList<QLineF> findSnappingOffsetLines(const SnapLineMap &snappingOffsetMap, + Qt::Orientation orientation, + double snapLine, + double lowerLimit, + double upperLimit, + QList<QRectF> *boundingRects = 0) const; + + QLineF createSnapLine(Qt::Orientation orientation, + double snapLine, + double lowerEdge, + double upperEdge, + const QRectF &itemRect) const; + +// bool canSnap(QList<double> snappingLineList, double value) const; +private: + FormEditorItem *m_containerFormEditorItem; + FormEditorItem *m_transformtionSpaceFormEditorItem; + double m_snappingDistance; +}; + +} +#endif // SNAPPER_H diff --git a/src/plugins/qmldesigner/components/formeditor/snapping.png b/src/plugins/qmldesigner/components/formeditor/snapping.png new file mode 100644 index 0000000000000000000000000000000000000000..ceee8b94bb4fde9fb33bacfb4c453efe1402b836 GIT binary patch literal 359 zcmeAS@N?(olHy`uVBq!ia0vp^d?3uh1|;P@bT0xa#^NA%Cx&(BWL^R}Y)RhkE)4%c zaKYZ?lYt_f1s;*b3=G`DAk4@xYmNj^kiEpy*OmPyw;VsCfFbjQM?j(Ho-U3d8t3Ou zvhQj!5NY2pE97|Loc0Z$#Pt2<Cni4gxtEZy5Y>^P+7ZE`q7cRMNR03ByEkY0cFVVX znDo8+e?`IJ*l(MIubbB1|IG98jdGV#-Q$w~7q;mhn=w1LXW{Ez@`twTKiwPuo}W*l z>|^Z)`;WC2QQ2Fr9j~@IwZC=IDlLX{&##}0oF;hO&{064=Ds@5Vduz8CPzQpJga;b zGEMdLradB($scU`GiPaZHBAocvN>OAayIw2#Bss3rq`mbl`dVSH6`fxDb1w}><0{Z zJTxZ;C9QSkXe{P?F5`PSrdIw<)+CkoUw>KZY!jROvko5udYQq~)z4*}Q$iB}oLrCm literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/formeditor/snappinglinecreator.cpp b/src/plugins/qmldesigner/components/formeditor/snappinglinecreator.cpp new file mode 100644 index 00000000000..7c3b9e36a3e --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/snappinglinecreator.cpp @@ -0,0 +1,190 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "snappinglinecreator.h" + +#include <QGraphicsItem> +#include "onedimensionalcluster.h" +#include "formeditoritem.h" +#include "formeditorview.h" + +namespace QmlDesigner { + +SnappingLineCreator::SnappingLineCreator(FormEditorItem *formEditorItem) + : m_formEditorItem(formEditorItem), + m_topOffset(formEditorItem->formEditorView()->spacing()), + m_bottomOffset(formEditorItem->formEditorView()->spacing()), + m_leftOffset(formEditorItem->formEditorView()->spacing()), + m_rightOffset(formEditorItem->formEditorView()->spacing()), + m_topMargin(formEditorItem->formEditorView()->margins()), + m_bottomMargin(formEditorItem->formEditorView()->margins()), + m_leftMargin(formEditorItem->formEditorView()->margins()), + m_rightMargin(formEditorItem->formEditorView()->margins()) +{ + Q_ASSERT(m_formEditorItem); +} + +void SnappingLineCreator::clearLines() +{ + m_topLineMap.clear(); + m_bottomLineMap.clear(); + m_leftLineMap.clear(); + m_rightLineMap.clear(); + m_horizontalCenterLineMap.clear(); + m_verticalCenterLineMap.clear(); + + m_topOffsetMap.clear(); + m_bottomOffsetMap.clear(); + m_leftOffsetMap.clear(); + m_rightOffsetMap.clear(); + +} + +void SnappingLineCreator::addLines(const QRectF &rectInSceneSpace, FormEditorItem *item) +{ + m_topLineMap.insert(rectInSceneSpace.top(), qMakePair(rectInSceneSpace, item)); + m_bottomLineMap.insert(rectInSceneSpace.bottom(), qMakePair(rectInSceneSpace, item)); + m_leftLineMap.insert(rectInSceneSpace.left(), qMakePair(rectInSceneSpace, item)); + m_rightLineMap.insert(rectInSceneSpace.right(), qMakePair(rectInSceneSpace, item)); + QPointF centerPoint(rectInSceneSpace.center()); + m_horizontalCenterLineMap.insert(centerPoint.y(), qMakePair(rectInSceneSpace, item)); + m_verticalCenterLineMap.insert(centerPoint.x(), qMakePair(rectInSceneSpace, item)); +} + +void SnappingLineCreator::addOffsets(const QRectF &rectInSceneSpace, FormEditorItem *item) +{ + m_topOffsetMap.insert(rectInSceneSpace.top() - m_topOffset, qMakePair(rectInSceneSpace, item)); + m_bottomOffsetMap.insert(rectInSceneSpace.bottom() + m_bottomOffset, qMakePair(rectInSceneSpace, item)); + m_leftOffsetMap.insert(rectInSceneSpace.left() - m_leftOffset, qMakePair(rectInSceneSpace, item)); + m_rightOffsetMap.insert(rectInSceneSpace.right() + m_rightOffset, qMakePair(rectInSceneSpace, item)); +} + +void SnappingLineCreator::generateLines(const QList<FormEditorItem*> &exceptionList, + FormEditorItem *transformationSpaceItem) +{ + Q_ASSERT(transformationSpaceItem); + { + QRectF containerBoundingRectInTransformationSpace = m_formEditorItem->mapRectToItem(transformationSpaceItem, + m_formEditorItem->boundingRect()); + + addLines(containerBoundingRectInTransformationSpace, m_formEditorItem); + containerBoundingRectInTransformationSpace.adjust(m_leftMargin, m_topMargin, -m_rightMargin, -m_rightMargin); + addLines(containerBoundingRectInTransformationSpace, m_formEditorItem); + } + + foreach (FormEditorItem *item, m_formEditorItem->childFormEditorItems()) { + if (exceptionList.contains(item)) + continue; + QRectF boundingRectInContainerSpace; + + boundingRectInContainerSpace = item->mapRectToItem(transformationSpaceItem, item->boundingRect()); + + boundingRectInContainerSpace = boundingRectInContainerSpace.toRect(); // round to integer + + addLines(boundingRectInContainerSpace, item); + addOffsets(boundingRectInContainerSpace, item); + } +} + +void SnappingLineCreator::setMargins(double margin) +{ + m_topMargin = margin; + m_bottomMargin = margin; + m_leftMargin = margin; + m_rightMargin = margin; +} + +void SnappingLineCreator::setSpacing(double spacing) +{ + m_topOffset = spacing; + m_bottomOffset = spacing; + m_leftOffset = spacing; + m_rightOffset = spacing; +} + +void SnappingLineCreator::update(const QList<FormEditorItem*> &exceptionList, + FormEditorItem *transformationSpaceItem) +{ + clearLines(); + setMargins(m_formEditorItem->formEditorView()->margins()); + setSpacing(m_formEditorItem->formEditorView()->spacing()); + generateLines(exceptionList, transformationSpaceItem); +} + +SnapLineMap SnappingLineCreator::topLines() const +{ + return m_topLineMap; +} + +SnapLineMap SnappingLineCreator::bottomLines() const +{ + return m_bottomLineMap; +} + +SnapLineMap SnappingLineCreator::leftLines() const +{ + return m_leftLineMap; +} + +SnapLineMap SnappingLineCreator::rightLines() const +{ + return m_rightLineMap; +} + +SnapLineMap SnappingLineCreator::horizontalCenterLines() const +{ + return m_horizontalCenterLineMap; +} + +SnapLineMap SnappingLineCreator::verticalCenterLines() const +{ + return m_verticalCenterLineMap; +} + +SnapLineMap SnappingLineCreator::topOffsets() const +{ + return m_topOffsetMap; +} + +SnapLineMap SnappingLineCreator::bottomOffsets() const +{ + return m_bottomOffsetMap; +} + +SnapLineMap SnappingLineCreator::leftOffsets() const +{ + return m_leftOffsetMap; +} + +SnapLineMap SnappingLineCreator::rightOffsets() const +{ + return m_rightOffsetMap; +} + +} diff --git a/src/plugins/qmldesigner/components/formeditor/snappinglinecreator.h b/src/plugins/qmldesigner/components/formeditor/snappinglinecreator.h new file mode 100644 index 00000000000..7d8eb3b51be --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/snappinglinecreator.h @@ -0,0 +1,102 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef SNAPPINGLINECREATOR_H +#define SNAPPINGLINECREATOR_H + +#include "layeritem.h" +#include <QPair> + +namespace QmlDesigner { + +class FormEditorItem; + +typedef QMultiMap<double, QPair<QRectF, FormEditorItem*> > SnapLineMap; +typedef QMapIterator<double, QPair<QRectF, FormEditorItem*> > SnapLineMapIterator; + +class FormEditorItem; + +class SnappingLineCreator +{ +public: + SnappingLineCreator(FormEditorItem *formEditorItem); + + void update(const QList<FormEditorItem*> &exceptionList, + FormEditorItem *transformationSpaceItem); + + + SnapLineMap topLines() const; + SnapLineMap bottomLines() const; + SnapLineMap leftLines() const; + SnapLineMap rightLines() const; + SnapLineMap horizontalCenterLines() const; + SnapLineMap verticalCenterLines() const; + + SnapLineMap topOffsets() const; + SnapLineMap bottomOffsets() const; + SnapLineMap leftOffsets() const; + SnapLineMap rightOffsets() const; + + void setMargins(double margin); + void setSpacing(double spacing); + +protected: + void addLines(const QRectF &rectInSceneSpace, FormEditorItem *item); + void addOffsets(const QRectF &rectInSceneSpace, FormEditorItem *item); + + void clearLines(); + void generateLines(const QList<FormEditorItem*> &exceptionList, + FormEditorItem *transformationSpaceItem); + +private: + SnapLineMap m_topLineMap; + SnapLineMap m_bottomLineMap; + SnapLineMap m_leftLineMap; + SnapLineMap m_rightLineMap; + SnapLineMap m_horizontalCenterLineMap; + SnapLineMap m_verticalCenterLineMap; + + SnapLineMap m_topOffsetMap; + SnapLineMap m_bottomOffsetMap; + SnapLineMap m_leftOffsetMap; + SnapLineMap m_rightOffsetMap; + + FormEditorItem *m_formEditorItem; + double m_topOffset; + double m_bottomOffset; + double m_leftOffset; + double m_rightOffset; + double m_topMargin; + double m_bottomMargin; + double m_leftMargin; + double m_rightMargin; +}; + +} +#endif // SNAPPINGLINECREATOR_H diff --git a/src/plugins/qmldesigner/components/formeditor/toolbox.cpp b/src/plugins/qmldesigner/components/formeditor/toolbox.cpp new file mode 100644 index 00000000000..04d4486b789 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/toolbox.cpp @@ -0,0 +1,81 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "toolbox.h" + +#include <QToolBar> +#include <QHBoxLayout> +#include <QPainter> +#include <QtDebug> +#include <QFile> + +namespace QmlDesigner { + +ToolBox::ToolBox(QWidget *parentWidget) + : QWidget(parentWidget), + m_toolBar(new QToolBar("Sidebar", this)) +{ + QHBoxLayout *fillLayout = new QHBoxLayout(this); + fillLayout->setMargin(0); + setLayout(fillLayout); + //setContentsMargins(4, 12, 4, 4); + + m_toolBar->setFloatable(true); + m_toolBar->setMovable(true); + m_toolBar->setOrientation(Qt::Horizontal); + m_toolBar->setIconSize(QSize(24, 24)); + fillLayout->addWidget(m_toolBar); + + +// QPalette toolPalette(palette()); +// QColor newColor(palette().color(QPalette::Window)); +// newColor.setAlphaF(0.7); +// toolPalette.setColor(QPalette::Window, newColor); +// setPalette(toolPalette); +// setBackgroundRole(QPalette::Window); +// setAutoFillBackground(true); +// setAttribute(Qt::WA_NoSystemBackground, true); + +} + + + +void ToolBox::setActions(const QList<QAction*> &actions) +{ + m_toolBar->clear(); + m_toolBar->addActions(actions); + resize(sizeHint()); +} + +void ToolBox::addAction(QAction *action) +{ + m_toolBar->addAction(action); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/formeditor/toolbox.h b/src/plugins/qmldesigner/components/formeditor/toolbox.h new file mode 100644 index 00000000000..98fed3a2719 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/toolbox.h @@ -0,0 +1,52 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef TOOLBOX_H +#define TOOLBOX_H + +#include <QWidget> + +class QToolBar; + +namespace QmlDesigner { + +class ToolBox : public QWidget +{ +public: + ToolBox(QWidget *parentWidget); + void setActions(const QList<QAction*> &actions); + void addAction(QAction *action); + +private: + QToolBar *m_toolBar; +}; + +} // namespace QmlDesigner + +#endif // TOOLBOX_H diff --git a/src/plugins/qmldesigner/components/formeditor/transform.png b/src/plugins/qmldesigner/components/formeditor/transform.png new file mode 100644 index 0000000000000000000000000000000000000000..e9e7662b3a310c7dc5956020989f92105f8215f3 GIT binary patch literal 17296 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 literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/formeditor/zoomaction.cpp b/src/plugins/qmldesigner/components/formeditor/zoomaction.cpp new file mode 100644 index 00000000000..9a6560927f4 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/zoomaction.cpp @@ -0,0 +1,116 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "zoomaction.h" + +#include <QComboBox> +#include <QLineEdit> +#include <QEvent> +#include <QCoreApplication> + +namespace QmlDesigner { + + +ZoomAction::ZoomAction(QObject *parent) + : QWidgetAction(parent), + m_zoomLevel(1.0), + m_currentComboBoxIndex(-1) +{ + +} + +double ZoomAction::zoomLevel() const +{ + return m_zoomLevel; +} + +void ZoomAction::zoomIn() +{ + if (m_currentComboBoxIndex > 0) + emit indexChanged(m_currentComboBoxIndex - 1); +} + +void ZoomAction::zoomOut() +{ + if (m_currentComboBoxIndex < (m_comboBoxModel->rowCount() - 1)) + emit indexChanged(m_currentComboBoxIndex + 1); +} + +void ZoomAction::setZoomLevel(double zoomLevel) +{ + if (zoomLevel < .1) { + m_zoomLevel = 0.1; + } else if (zoomLevel > 16.0) { + m_zoomLevel = 16.0; + } else { + m_zoomLevel = zoomLevel; + } + + emit zoomLevelChanged(m_zoomLevel); +} + + +QWidget *ZoomAction::createWidget(QWidget *parent) +{ + QComboBox *comboBox = new QComboBox(parent); + + if (m_comboBoxModel.isNull()) { + m_comboBoxModel = comboBox->model(); + comboBox->addItem("10 %", 0.1); + comboBox->addItem("25 %", 0.25); + comboBox->addItem("50 %", 0.5); + comboBox->addItem("100 %", 1.0); + comboBox->addItem("200 %", 2.0); + comboBox->addItem("400 %", 4.0); + comboBox->addItem("800 %", 8.0); + comboBox->addItem("1600 %", 16.0); + + } else { + comboBox->setModel(m_comboBoxModel.data()); + } + + comboBox->setCurrentIndex(3); + connect(comboBox, SIGNAL(currentIndexChanged(int)), SLOT(emitZoomLevelChanged(int))); + connect(this, SIGNAL(indexChanged(int)), comboBox, SLOT(setCurrentIndex(int))); + + return comboBox; +} + +void ZoomAction::emitZoomLevelChanged(int index) +{ + m_currentComboBoxIndex = index; + + if (index == -1) + return; + + QModelIndex modelIndex(m_comboBoxModel.data()->index(index, 0)); + setZoomLevel(m_comboBoxModel.data()->data(modelIndex, Qt::UserRole).toDouble()); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/formeditor/zoomaction.h b/src/plugins/qmldesigner/components/formeditor/zoomaction.h new file mode 100644 index 00000000000..02ceb9acc0e --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/zoomaction.h @@ -0,0 +1,68 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef ZOOMACTION_H +#define ZOOMACTION_H + +#include <QWidgetAction> + +class QAbstractItemModel; + +namespace QmlDesigner { + +class ZoomAction : public QWidgetAction +{ + Q_OBJECT + +public: + ZoomAction(QObject *parent); + + double zoomLevel() const; + + void zoomIn(); + void zoomOut(); + +protected: + QWidget *createWidget(QWidget *parent); + void setZoomLevel(double zoomLevel); +signals: + void zoomLevelChanged(double zoom); + void indexChanged(int); +private slots: + void emitZoomLevelChanged(int index); + +private: + QWeakPointer<QAbstractItemModel> m_comboBoxModel; + double m_zoomLevel; + int m_currentComboBoxIndex; +}; + +} // namespace QmlDesigner + +#endif // ZOOMACTION_H diff --git a/src/plugins/qmldesigner/components/integration/allpropertiesviewcontroller.cpp b/src/plugins/qmldesigner/components/integration/allpropertiesviewcontroller.cpp new file mode 100644 index 00000000000..c1956ee6a02 --- /dev/null +++ b/src/plugins/qmldesigner/components/integration/allpropertiesviewcontroller.cpp @@ -0,0 +1,64 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include <QtGui/QStackedWidget> + +#include <allpropertiesbox.h> + +#include "designdocumentcontroller.h" +#include "allpropertiesviewcontroller.h" + +namespace QmlDesigner { + +AllPropertiesViewController::AllPropertiesViewController(QObject* parent): + StackedUtilityPanelController(parent) +{ + setObjectName("PropertiesViewController"); +} + +QWidget* AllPropertiesViewController::stackedPageWidget(DesignDocumentController* designDocumentController) const +{ + return designDocumentController->allPropertiesBox(); +} + +void AllPropertiesViewController::showNewLookProperties() +{ + AllPropertiesBox* propsBox = dynamic_cast<AllPropertiesBox*>(dynamic_cast<QStackedWidget*>(contentWidget())->currentWidget()); + if (propsBox) + propsBox->showNewLook(); +} + +void AllPropertiesViewController::showTraditionalProperties() +{ + AllPropertiesBox* propsBox = dynamic_cast<AllPropertiesBox*>(dynamic_cast<QStackedWidget*>(contentWidget())->currentWidget()); + if (propsBox) + propsBox->showTraditional(); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/integration/allpropertiesviewcontroller.h b/src/plugins/qmldesigner/components/integration/allpropertiesviewcontroller.h new file mode 100644 index 00000000000..15eff733d09 --- /dev/null +++ b/src/plugins/qmldesigner/components/integration/allpropertiesviewcontroller.h @@ -0,0 +1,54 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef ALLPROPERTIESVIEWCONTROLLER_H +#define ALLPROPERTIESVIEWCONTROLLER_H + +#include "stackedutilitypanelcontroller.h" + +namespace QmlDesigner { + +class AllPropertiesViewController : public StackedUtilityPanelController +{ + Q_OBJECT + +public: + AllPropertiesViewController(QObject* parent = 0); + +public slots: + void showNewLookProperties(); + void showTraditionalProperties(); + +protected: + class QWidget* stackedPageWidget(class DesignDocumentController* designDocumentController) const; +}; + +} // namespace QmlDesigner + +#endif // ALLPROPERTIESVIEWCONTROLLER_H diff --git a/src/plugins/qmldesigner/components/integration/componentaction.cpp b/src/plugins/qmldesigner/components/integration/componentaction.cpp new file mode 100644 index 00000000000..450cab497e4 --- /dev/null +++ b/src/plugins/qmldesigner/components/integration/componentaction.cpp @@ -0,0 +1,74 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "componentaction.h" + +#include <QComboBox> +#include "componentview.h" +#include <QStandardItemModel> +#include <QtDebug> + +namespace QmlDesigner { + +ComponentAction::ComponentAction(QObject *parent) + : QWidgetAction(parent), + m_componentView(new ComponentView(this)) +{ +} + +void ComponentAction::setModel(Model* model) +{ + if (model == m_componentView->model()) + return; + + blockSignals(true); + + if (model) + model->attachView(m_componentView.data()); + else if (m_componentView->model()) + m_componentView->model()->detachView(m_componentView.data()); + + blockSignals(false); +} + +QWidget *ComponentAction::createWidget(QWidget *parent) +{ + QComboBox *comboBox = new QComboBox(parent); + comboBox->setModel(m_componentView->standardItemModel()); + connect(comboBox, SIGNAL(currentIndexChanged(int)), SLOT(emitCurrentComponentChanged(int))); + + return comboBox; +} + +void ComponentAction::emitCurrentComponentChanged(int index) +{ + emit currentComponentChanged(m_componentView->modelNode(index)); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/integration/componentaction.h b/src/plugins/qmldesigner/components/integration/componentaction.h new file mode 100644 index 00000000000..e4ee74645fc --- /dev/null +++ b/src/plugins/qmldesigner/components/integration/componentaction.h @@ -0,0 +1,66 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef COMPONENTACTION_H +#define COMPONENTACTION_H + +#include <QWidgetAction> +#include <QWeakPointer> + +class QComboBox; + +namespace QmlDesigner { + +class Model; +class ComponentView; +class ModelNode; + +class ComponentAction : public QWidgetAction +{ + Q_OBJECT +public: + ComponentAction(QObject *parent); + void setModel(Model* model); + +protected: + QWidget *createWidget(QWidget *parent); + +signals: + void currentComponentChanged(const ModelNode &node); + +private slots: + void emitCurrentComponentChanged(int index); + +private: + QWeakPointer<ComponentView> m_componentView; +}; + +} // namespace QmlDesigner + +#endif // COMPONENTACTION_H diff --git a/src/plugins/qmldesigner/components/integration/componentview.cpp b/src/plugins/qmldesigner/components/integration/componentview.cpp new file mode 100644 index 00000000000..ccd0a395188 --- /dev/null +++ b/src/plugins/qmldesigner/components/integration/componentview.cpp @@ -0,0 +1,179 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "componentview.h" +#include <QtDebug> + +#include <nodemetainfo.h> +#include <QStandardItemModel> + +// silence gcc warnings about unused parameters + +namespace QmlDesigner { + +ComponentView::ComponentView(QObject *parent) + : AbstractView(parent), + m_standardItemModel(new QStandardItemModel(this)), + m_listChanged(false) +{ +} + + + +void ComponentView::nodeAboutToBeRemoved(const ModelNode &removedNode) +{ + for (int index = 1; index < m_standardItemModel->rowCount(); index++) { + QStandardItem *item = m_standardItemModel->item(index); + if (item->data(ModelNodeRole).value<ModelNode>() == removedNode) + m_standardItemModel->removeRow(index); + } +} + +QStandardItemModel *ComponentView::standardItemModel() const +{ + return m_standardItemModel; +} + +ModelNode ComponentView::modelNode(int index) const +{ + if (m_standardItemModel->hasIndex(index, 0)) { + QStandardItem *item = m_standardItemModel->item(index, 0); + return item->data(ModelNodeRole).value<ModelNode>(); + } + + return ModelNode(); +} + +void ComponentView::appendWholeDocumentAsComponent() +{ + QStandardItem *item = new QStandardItem("Whole Document"); + item->setData(QVariant::fromValue(rootModelNode()), ModelNodeRole); + item->setEditable(false); + m_standardItemModel->appendRow(item); +} + +void ComponentView::modelAttached(Model *model) +{ + if (AbstractView::model() == model) + return; + + AbstractView::modelAttached(model); + + Q_ASSERT(model->masterModel()); + appendWholeDocumentAsComponent(); + searchForComponentAndAddToList(rootModelNode()); +} + +void ComponentView::modelAboutToBeDetached(Model *model) +{ + m_standardItemModel->clear(); + AbstractView::modelAboutToBeDetached(model); +} + +void ComponentView::nodeCreated(const ModelNode &createdNode) +{ + searchForComponentAndAddToList(createdNode); +} + +void ComponentView::searchForComponentAndAddToList(const ModelNode &node) +{ + QList<ModelNode> nodeList; + nodeList.append(node); + nodeList.append(node.allSubModelNodes()); + + + foreach (const ModelNode &childNode, nodeList) { + if (childNode.type() == "Qt/Component") { + if (!childNode.id().isEmpty()) { + QStandardItem *item = new QStandardItem(childNode.id()); + item->setData(QVariant::fromValue(childNode), ModelNodeRole); + item->setEditable(false); + m_standardItemModel->appendRow(item); + } + } else if (node.metaInfo().isComponent() && !m_componentList.contains(node.type())) { + m_componentList.append(node.type()); + m_componentList.sort(); + m_listChanged = true; + } + } +} + +void ComponentView::nodeRemoved(const ModelNode & /*removedNode*/, const NodeAbstractProperty & /*parentProperty*/, PropertyChangeFlags /*propertyChange*/) +{ + +} + +//void ComponentView::searchForComponentAndRemoveFromList(const ModelNode &node) +//{ +// QList<ModelNode> nodeList; +// nodeList.append(node); +// nodeList.append(node.allSubModelNodes()); +// +// +// foreach (const ModelNode &childNode, nodeList) { +// if (node.type() == "Qt/Component") { +// if (!node.id().isEmpty()) { +// for(int row = 0; row < m_standardItemModel->rowCount(); row++) { +// if (m_standardItemModel->item(row)->text() == node.id()) +// m_standardItemModel->removeRow(row); +// } +// } +// } else if (node.metaInfo().isComponent() && !m_componentList.contains(node.type())) { +// m_componentList.append(node.type()); +// m_componentList.sort(); +// m_listChanged = true; +// } +// } +//} + +void ComponentView::nodeReparented(const ModelNode &/*node*/, const NodeAbstractProperty &/*newPropertyParent*/, const NodeAbstractProperty &/*oldPropertyParent*/, AbstractView::PropertyChangeFlags /*propertyChange*/) {} +void ComponentView::nodeIdChanged(const ModelNode& /*node*/, const QString& /*newId*/, const QString& /*oldId*/) {} +void ComponentView::propertiesAboutToBeRemoved(const QList<AbstractProperty>& /*propertyList*/) {} +void ComponentView::propertiesRemoved(const QList<AbstractProperty>& /*propertyList*/) {} +void ComponentView::variantPropertiesChanged(const QList<VariantProperty>& /*propertyList*/, PropertyChangeFlags /*propertyChange*/) {} +void ComponentView::bindingPropertiesChanged(const QList<BindingProperty>& /*propertyList*/, PropertyChangeFlags /*propertyChange*/) {} +void ComponentView::nodeTypeChanged(const ModelNode &/*node*/, const QString &/*type*/, int /*majorVersion*/, int /*minorVersion*/) {} + + + +void ComponentView::selectedNodesChanged(const QList<ModelNode> &/*selectedNodeList*/, + const QList<ModelNode> &/*lastSelectedNodeList*/) {} + +void ComponentView::fileUrlChanged(const QUrl &/*oldUrl*/, const QUrl &/*newUrl*/) {} + +void ComponentView::nodeSlidedToIndex(const NodeListProperty &/*listProperty*/, int /*newIndex*/, int /*oldIndex*/) {} + +void ComponentView::importsChanged() {} + +void ComponentView::auxiliaryDataChanged(const ModelNode &/*node*/, const QString &/*name*/, const QVariant &/*data*/) {} + +void ComponentView::customNotification(const AbstractView */*view*/, const QString &/*identifier*/, const QList<ModelNode> &/*nodeList*/, const QList<QVariant> &/*data*/) {} + + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/integration/componentview.h b/src/plugins/qmldesigner/components/integration/componentview.h new file mode 100644 index 00000000000..b4ec1e83610 --- /dev/null +++ b/src/plugins/qmldesigner/components/integration/componentview.h @@ -0,0 +1,105 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef COMPONENTVIEW_H +#define COMPONENTVIEW_H + +#include <abstractview.h> +#include <modelnode.h> + +#include <QStringList> + +class QStandardItemModel; + +namespace QmlDesigner { + +class ComponentView : public AbstractView +{ + Q_OBJECT + +public: + enum UserRoles + { + ModelNodeRole = Qt::UserRole + }; + + ComponentView(QObject *parent); + + void modelAttached(Model *model); + void modelAboutToBeDetached(Model *model); + + void nodeCreated(const ModelNode &createdNode); + void nodeAboutToBeRemoved(const ModelNode &removedNode); + void nodeRemoved(const ModelNode &removedNode, const NodeAbstractProperty &parentProperty, PropertyChangeFlags propertyChange); + void nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent, const NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags propertyChange); + void nodeIdChanged(const ModelNode& node, const QString& newId, const QString& oldId); + void propertiesAboutToBeRemoved(const QList<AbstractProperty>& propertyList); + void propertiesRemoved(const QList<AbstractProperty>& propertyList); + void variantPropertiesChanged(const QList<VariantProperty>& propertyList, PropertyChangeFlags propertyChange); + void bindingPropertiesChanged(const QList<BindingProperty>& propertyList, PropertyChangeFlags propertyChange); + void nodeTypeChanged(const ModelNode &node,const QString &type, int majorVersion, int minorVersion); + + + + void selectedNodesChanged(const QList<ModelNode> &selectedNodeList, + const QList<ModelNode> &lastSelectedNodeList); + + void fileUrlChanged(const QUrl &oldUrl, const QUrl &newUrl); + + void nodeSlidedToIndex(const NodeListProperty &listProperty, int newIndex, int oldIndex); + + void importsChanged(); + + void auxiliaryDataChanged(const ModelNode &node, const QString &name, const QVariant &data); + + void customNotification(const AbstractView *view, const QString &identifier, const QList<ModelNode> &nodeList, const QList<QVariant> &data); + + + QStandardItemModel *standardItemModel() const; + + ModelNode modelNode(int index) const; + +signals: + void componentListChanged(const QStringList &componentList); + +private: //functions + void updateModel(); + void searchForComponentAndAddToList(const ModelNode &node); +// void searchForComponentAndRemoveFromList(const ModelNode &node); + void appendWholeDocumentAsComponent(); + +private: + QStringList m_componentList; + QStandardItemModel *m_standardItemModel; + bool m_listChanged; +}; + +} // namespace QmlDesigner + +#endif // COMPONENTVIEW_H diff --git a/src/plugins/qmldesigner/components/integration/designdocumentcontroller.cpp b/src/plugins/qmldesigner/components/integration/designdocumentcontroller.cpp new file mode 100644 index 00000000000..b0602c8689e --- /dev/null +++ b/src/plugins/qmldesigner/components/integration/designdocumentcontroller.cpp @@ -0,0 +1,815 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "designdocumentcontroller.h" +#include "designdocumentcontrollerview.h" +#include "xuifiledialog.h" +#include "componentview.h" +#include "subcomponentmanager.h" +#include "model/viewlogger.h" + +#include <allpropertiesbox.h> +#include <itemlibrary.h> +#include <navigatorview.h> +#include <stateseditorwidget.h> +#include <formeditorview.h> +#include <formeditorwidget.h> +#include <plaintexteditmodifier.h> +#include <componenttextmodifier.h> +#include <metainfo.h> +#include <invalidargumentexception.h> +#include <componentaction.h> +#include <qmlobjectnode.h> +#include <rewriterview.h> +#include <nodelistproperty.h> +#include <toolbox.h> + +#include <QtCore/QCoreApplication> +#include <QtCore/QDir> +#include <QtCore/QFile> +#include <QtCore/QFileInfo> +#include <QtCore/QUrl> +#include <QtCore/QProcess> +#include <QtCore/QTemporaryFile> +#include <QtCore/QtDebug> +#include <QtCore/QEvent> + +#include <QtGui/QBoxLayout> +#include <QtGui/QComboBox> +#include <QtGui/QErrorMessage> +#include <QtGui/QFileDialog> +#include <QtGui/QLabel> +#include <QtGui/QMdiArea> +#include <QtGui/QMdiSubWindow> +#include <QtGui/QMessageBox> +#include <QtGui/QUndoStack> +#include <QtGui/QPlainTextEdit> + +enum { + debug = false +}; + +namespace QmlDesigner { + +class DesignDocumentControllerPrivate { +public: + QWeakPointer<FormEditorView> formEditorView; + + QWeakPointer<ItemLibrary> itemLibrary; + QWeakPointer<NavigatorView> navigator; + QWeakPointer<AllPropertiesBox> allPropertiesBox; + QWeakPointer<StatesEditorWidget> statesEditorWidget; + QWeakPointer<QStackedWidget> stackedWidget; + + QWeakPointer<QmlDesigner::Model> model; + QWeakPointer<QmlDesigner::Model> subComponentModel; + QWeakPointer<QmlDesigner::Model> masterModel; + QWeakPointer<QPlainTextEdit> textEdit; + QWeakPointer<RewriterView> rewriterView; + QmlDesigner::PlainTextEditModifier *textModifier; + QmlDesigner::ComponentTextModifier *componentTextModifier; + QWeakPointer<SubComponentManager> subComponentManager; + QWeakPointer<Internal::ViewLogger> viewLogger; + + QWeakPointer<QProcess> previewProcess; + QWeakPointer<QProcess> previewWithDebugProcess; + + QString fileName; + QUrl searchPath; + bool documentLoaded; + bool syncBlocked; + +}; + +/** + \class QmlDesigner::DesignDocumentController + + DesignDocumentController acts as a facade to a model representing a qml document, + and the different views/widgets accessing it. + */ +DesignDocumentController::DesignDocumentController(QObject *parent) : + QObject(parent), + m_d(new DesignDocumentControllerPrivate) +{ + m_d->itemLibrary = new ItemLibrary; + m_d->navigator = new NavigatorView(this); + m_d->allPropertiesBox = new AllPropertiesBox; + m_d->statesEditorWidget = new StatesEditorWidget; + m_d->stackedWidget = new QStackedWidget; + + m_d->documentLoaded = false; + m_d->syncBlocked = false; +} + +DesignDocumentController::~DesignDocumentController() +{ + delete m_d->formEditorView.data(); + + // deleting the widgets should be safe because these are QWeakPointer's + delete m_d->itemLibrary.data(); + delete m_d->allPropertiesBox.data(); + delete m_d->statesEditorWidget.data(); + delete m_d->stackedWidget.data(); + + delete m_d->model.data(); + delete m_d->subComponentModel.data(); + + delete m_d->rewriterView.data(); + // m_d->textEdit is child of stackedWidget or owned by external widget + delete m_d->textModifier; + + if (m_d->componentTextModifier) //componentTextModifier might not be created + delete m_d->componentTextModifier; + + delete m_d->previewProcess.data(); + delete m_d->previewWithDebugProcess.data(); + + delete m_d; +} + +Model *DesignDocumentController::model() const +{ + return m_d->model.data(); +} + +Model *DesignDocumentController::masterModel() const +{ + return m_d->masterModel.data(); +} + +QWidget *DesignDocumentController::documentWidget() const +{ + return m_d->stackedWidget.data(); +} + +void DesignDocumentController::togglePreview(bool visible) +{ + if (!m_d->documentLoaded) + return; + + if (visible) { + Q_ASSERT(!m_d->previewProcess); + + QString directory; + if (!m_d->fileName.isEmpty()) { + directory = QFileInfo(m_d->fileName).absoluteDir().path(); + } else { + directory = QDir::tempPath(); + } + m_d->previewProcess = createPreviewProcess(directory); + connect(m_d->previewProcess.data(), SIGNAL(finished(int, QProcess::ExitStatus)), + this, SLOT(emitPreviewVisibilityChanged())); + } else { + Q_ASSERT(m_d->previewProcess); + + delete m_d->previewProcess.data(); + } +} + +void DesignDocumentController::toggleWithDebugPreview(bool visible) +{ + if (!m_d->documentLoaded) + return; + + if (visible) { + Q_ASSERT(!m_d->previewWithDebugProcess); + + QString directory; + if (!m_d->fileName.isEmpty()) { + directory = QFileInfo(m_d->fileName).absoluteDir().path(); + } else { + directory = QDir::tempPath(); + } + m_d->previewWithDebugProcess = createPreviewWithDebugProcess(directory); + connect(m_d->previewWithDebugProcess.data(), SIGNAL(finished(int, QProcess::ExitStatus)), + this, SLOT(emitPreviewWithDebugVisibilityChanged())); + } else { + Q_ASSERT(m_d->previewWithDebugProcess); + + delete m_d->previewWithDebugProcess.data(); + } +} + +bool DesignDocumentController::previewVisible() const +{ + if (!m_d->documentLoaded) + return false; + + return m_d->previewProcess; +} + +bool DesignDocumentController::previewWithDebugVisible() const +{ + if (!m_d->documentLoaded) + return false; + + return m_d->previewWithDebugProcess; +} + + +/*! + Returns whether the model is automatically updated if the text editor changes. + */ +bool DesignDocumentController::isModelSyncBlocked() const +{ + return m_d->syncBlocked; +} + +/*! + Switches whether the model (and therefore the views) are updated if the text editor + changes. + + If the synchronization is enabled again, the model is automatically resynchronized + with the current state of the text editor. + */ +void DesignDocumentController::blockModelSync(bool block) +{ + if (m_d->syncBlocked == block) + return; + + m_d->syncBlocked = block; + + if (m_d->textModifier) { + if (m_d->syncBlocked) { + m_d->textModifier->deactivateChangeSignals(); + } else { + m_d->textModifier->reactivateChangeSignals(); + } + } +} + +/*! + Returns any errors that happened when parsing the latest qml file. + */ +QList<RewriterView::Error> DesignDocumentController::qmlErrors() const +{ + return m_d->rewriterView->errors(); +} + +void DesignDocumentController::emitPreviewVisibilityChanged() +{ + emit previewVisibilityChanged(false); +} + +void DesignDocumentController::emitPreviewWithDebugVisibilityChanged() +{ + emit previewWithDebugVisibilityChanged(false); +} + +QString DesignDocumentController::displayName() const +{ + if (fileName().isEmpty()) + return tr("-New Form-"); + else + return fileName(); +} + +QString DesignDocumentController::fileName() const +{ + return m_d->fileName; +} + +void DesignDocumentController::setFileName(const QString &fileName) +{ + m_d->fileName = fileName; + + if (QFileInfo(fileName).exists()) { + m_d->searchPath = QUrl::fromLocalFile(fileName); + } else { + m_d->searchPath = QUrl(fileName); + } + + if (m_d->model) + m_d->model->setFileUrl(m_d->searchPath); + + if (m_d->itemLibrary) + m_d->itemLibrary->setResourcePath(QFileInfo(fileName).absolutePath()); + emit displayNameChanged(displayName()); +} + +QList<RewriterView::Error> DesignDocumentController::loadMaster(QPlainTextEdit *edit) +{ + Q_CHECK_PTR(edit); + + m_d->textEdit = edit; + + connect(edit, SIGNAL(undoAvailable(bool)), + this, SIGNAL(undoAvailable(bool))); + connect(edit, SIGNAL(redoAvailable(bool)), + this, SIGNAL(redoAvailable(bool))); + connect(edit, SIGNAL(modificationChanged(bool)), + this, SIGNAL(dirtyStateChanged(bool))); + + m_d->textModifier = new PlainTextEditModifier(m_d->textEdit.data()); + + m_d->componentTextModifier = 0; + + //m_d->masterModel = Model::create(m_d->textModifier, m_d->searchPath, errors); + + m_d->masterModel = Model::create("Item", 4, 6); + m_d->masterModel->setFileUrl(m_d->searchPath); + + m_d->subComponentModel = Model::create("Item", 4, 6); + m_d->subComponentModel->setFileUrl(m_d->searchPath); + + m_d->subComponentManager = new SubComponentManager(m_d->masterModel->metaInfo(), this); + m_d->subComponentManager->update(m_d->searchPath, m_d->textModifier->text().toUtf8()); + + m_d->rewriterView = new RewriterView(RewriterView::Amend, m_d->masterModel.data()); + m_d->rewriterView->setTextModifier( m_d->textModifier); + connect(m_d->rewriterView.data(), SIGNAL(errorsChanged(const QList<RewriterView::Error> &)), + this, SIGNAL(qmlErrorsChanged(const QList<RewriterView::Error> &))); + + m_d->masterModel->attachView(m_d->rewriterView.data()); + m_d->model = m_d->masterModel; + +#if defined(VIEWLOGGER) + m_d->viewLogger = new Internal::ViewLogger(m_d->model.data()); + m_d->masterModel->attachView(m_d->viewLogger.data()); +#endif + + // m_d->masterModel->detachView(m_d->rewriterView.data()); + + QList<RewriterView::Error> errors = m_d->rewriterView->errors(); + if (errors.isEmpty()) + loadCurrentModel(); + + return errors; +} + +void DesignDocumentController::changeCurrentModelTo(const ModelNode &componentNode) +{ + Q_ASSERT(m_d->masterModel); + QWeakPointer<Model> oldModel = m_d->model; + Q_ASSERT(oldModel.data()); + + if (m_d->model == m_d->subComponentModel) { + //change back to master model + m_d->model->detachView(m_d->rewriterView.data()); + m_d->rewriterView->setTextModifier(m_d->textModifier); + m_d->model = m_d->masterModel; + m_d->model->attachView(m_d->rewriterView.data()); + } + if (!componentNode.isRootNode()) { + Q_ASSERT(m_d->model == m_d->masterModel); + Q_ASSERT(componentNode.isValid()); + //change to subcomponent model + ModelNode rootModelNode = componentNode.view()->rootModelNode(); + Q_ASSERT(rootModelNode.isValid()); + if (m_d->componentTextModifier) + delete m_d->componentTextModifier; + + int componentStartOffset = m_d->rewriterView->firstDefinitionInsideOffset(componentNode); + int componentEndOffset = componentStartOffset + m_d->rewriterView->firstDefinitionInsideLength(componentNode); + int rootStartOffset = m_d->rewriterView->nodeOffset(rootModelNode); + + m_d->componentTextModifier = new ComponentTextModifier (m_d->textModifier, componentStartOffset, componentEndOffset, rootStartOffset); + + + m_d->model->detachView(m_d->rewriterView.data()); + + m_d->rewriterView->setTextModifier(m_d->componentTextModifier); + + m_d->subComponentModel->attachView(m_d->rewriterView.data()); + + Q_ASSERT(m_d->rewriterView->rootModelNode().isValid()); + + m_d->model = m_d->subComponentModel; + } + + Q_ASSERT(m_d->masterModel); + Q_ASSERT(m_d->model); + + loadCurrentModel(); +} + + void DesignDocumentController::loadCurrentModel() + { + Q_ASSERT(m_d->masterModel); + Q_ASSERT(m_d->model); + m_d->model->setMasterModel(m_d->masterModel.data()); + + m_d->allPropertiesBox->setModel(m_d->model.data()); + + m_d->model->attachView(m_d->navigator.data()); + m_d->itemLibrary->setMetaInfo(m_d->model->metaInfo()); + + if (m_d->formEditorView .isNull()) { + m_d->formEditorView = new FormEditorView(this); + m_d->stackedWidget->addWidget(m_d->formEditorView->widget()); + ComponentAction *componentAction = new ComponentAction(m_d->formEditorView->widget()); + componentAction->setModel(m_d->model.data()); + m_d->formEditorView->widget()->toolBox()->addAction(componentAction); + connect(componentAction, SIGNAL(currentComponentChanged(ModelNode)), SLOT(changeCurrentModelTo(ModelNode))); //TODO component action + connect(m_d->itemLibrary.data(), SIGNAL(itemActivated(const QString&)), m_d->formEditorView.data(), SLOT(activateItemCreator(const QString&))); + } + + m_d->model->attachView(m_d->formEditorView.data()); + + + if (!m_d->textEdit->parent()) // hack to prevent changing owner of external text edit + m_d->stackedWidget->addWidget(m_d->textEdit.data()); + + // Will call setCurrentState (formEditorView etc has to be constructed first) + m_d->statesEditorWidget->setup(m_d->model.data()); + + m_d->documentLoaded = true; + Q_ASSERT(m_d->masterModel); + } + +QList<RewriterView::Error> DesignDocumentController::loadMaster(const QByteArray &qml) +{ + QPlainTextEdit *textEdit = new QPlainTextEdit; + textEdit->setReadOnly(true); + textEdit->setPlainText(QString(qml)); + return loadMaster(textEdit); +} + +void DesignDocumentController::saveAs(QWidget *parent) +{ + QFileInfo oldFileInfo(m_d->fileName); + XUIFileDialog::runSaveFileDialog(oldFileInfo.path(), parent, this, SLOT(doRealSaveAs(QString))); +} + +void DesignDocumentController::doRealSaveAs(const QString &fileName) +{ + if (fileName.isNull()) + return; + + QFileInfo fileInfo(fileName); + if (fileInfo.exists() && !fileInfo.isWritable()) { + QMessageBox msgBox(documentWidget()); + msgBox.setIcon(QMessageBox::Warning); + msgBox.setText(tr("Cannot save to file \"%1\": permission denied.").arg(fileInfo.baseName())); + msgBox.exec(); + return; + } else if (!fileInfo.exists() && !fileInfo.dir().exists()) { + QMessageBox msgBox(documentWidget()); + msgBox.setIcon(QMessageBox::Warning); + msgBox.setText(tr("Parent folder \"%1\" for file \"%2\" does not exist.") + .arg(fileInfo.dir().dirName()) + .arg(fileInfo.baseName())); + msgBox.exec(); + return; + } + + setFileName(fileName); + save(documentWidget()); +} + +bool DesignDocumentController::save(QWidget *parent) +{ +// qDebug() << "Saving document to file \"" << m_d->fileName << "\"..."; +// + if (m_d->fileName.isEmpty()) { + saveAs(parent); + return true; + } + QFile file(m_d->fileName); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + showError(tr("Cannot write file: \"%1\".").arg(m_d->fileName), parent); + return false; + } + + QString errorMessage; + bool result = save(&file, &errorMessage); + if (!result) + showError(errorMessage, parent); + return result; +} + +bool DesignDocumentController::isDirty() const +{ + if (m_d->textEdit) + return m_d->textEdit->document()->isModified(); + else + return false; +} + +bool DesignDocumentController::isUndoAvailable() const +{ + return m_d->textEdit->document()->isUndoAvailable(); +} + +bool DesignDocumentController::isRedoAvailable() const +{ + return m_d->textEdit->document()->isRedoAvailable(); +} + +void DesignDocumentController::close() +{ + documentWidget()->close(); + m_d->documentLoaded = false; + emit designDocumentClosed(); +} + +void DesignDocumentController::deleteSelected() +{ + if (!m_d->model) + return; + + if (m_d->formEditorView) { + QList<ModelNode> toDelete = m_d->formEditorView->selectedModelNodes(); + foreach (ModelNode node, toDelete) { + if (node.isValid() && !node.isRootNode() && QmlObjectNode(node).isValid()) + QmlObjectNode(node).destroy(); + } + } +} + +void DesignDocumentController::copySelected() +{ + QScopedPointer<Model> model(Model::create("Qt/Rectangle")); + Q_ASSERT(model); + + DesignDocumentControllerView view; + + m_d->model->attachView(&view); + + if (view.selectedModelNodes().isEmpty()) + return; + + QList<ModelNode> selectedNodes(view.selectedModelNodes()); + + foreach (const ModelNode &node, selectedNodes) { + foreach (const ModelNode &node2, selectedNodes) { + if (node.isAncestorOf(node2)) + selectedNodes.removeAll(node2); + } + } + + if (selectedNodes.count() == 1) { + ModelNode selectedNode(selectedNodes.first()); + + if (!selectedNode.isValid()) + return; + + m_d->model->detachView(&view); + + model->attachView(&view); + view.replaceModel(selectedNode); + + Q_ASSERT(view.rootModelNode().isValid()); + Q_ASSERT(view.rootModelNode().type() != "empty"); + + view.toClipboard(); + } else { //multi items selected + m_d->model->detachView(&view); + model->attachView(&view); + + foreach (ModelNode node, view.rootModelNode().allDirectSubModelNodes()) { + node.destroy(); + } + view.rootModelNode().changeType("Qt/Rectangle", 4, 6); + view.rootModelNode().setId("designer__Selection"); + + foreach (const ModelNode &selectedNode, selectedNodes) { + ModelNode newNode(view.insertModel(selectedNode)); + view.rootModelNode().nodeListProperty("data").reparentHere(newNode); + } + + view.toClipboard(); + } +} + +void DesignDocumentController::cutSelected() +{ + copySelected(); + deleteSelected(); +} + +void DesignDocumentController::paste() +{ + QScopedPointer<Model> model(Model::create("empty")); + Q_ASSERT(model); + + if (!m_d->model) + return; + + DesignDocumentControllerView view; + model->attachView(&view); + + view.fromClipboard(); + + ModelNode rootNode(view.rootModelNode()); + + if (rootNode.type() == "empty") + return; + + if (rootNode.id() == "designer__Selection") { + QList<ModelNode> selectedNodes = rootNode.allDirectSubModelNodes(); + model->detachView(&view); + m_d->model->attachView(&view); + + ModelNode selectedNode; + + if (!view.selectedModelNodes().isEmpty()) + selectedNode = view.selectedModelNodes().first(); + + if (!selectedNode.isValid()) + selectedNode = view.rootModelNode(); + foreach (const ModelNode &node, selectedNodes) { + foreach (const ModelNode &node2, selectedNodes) { + if (node.isAncestorOf(node2)) + selectedNodes.removeAll(node2); + } + + } + foreach (const ModelNode &node, selectedNodes) { + QString defaultProperty(selectedNode.metaInfo().defaultProperty()); + ModelNode pastedNode(view.insertModel(node)); + selectedNode.nodeListProperty(defaultProperty).reparentHere(pastedNode); + } + } else { + model->detachView(&view); + m_d->model->attachView(&view); + ModelNode pastedNode(view.insertModel(rootNode)); + ModelNode selectedNode; + + if (!view.selectedModelNodes().isEmpty()) + selectedNode = view.selectedModelNodes().first(); + + if (!selectedNode.isValid()) + selectedNode = view.rootModelNode(); + + QString defaultProperty(selectedNode.metaInfo().defaultProperty()); + selectedNode.nodeListProperty(defaultProperty).reparentHere(pastedNode); + } +} + +void DesignDocumentController::showError(const QString &message, QWidget *parent) const +{ + if (!parent) + parent = m_d->stackedWidget.data(); + + QMessageBox msgBox(parent); + msgBox.setWindowFlags(Qt::Sheet | Qt::MSWindowsFixedSizeDialogHint); + msgBox.setWindowTitle("Invalid qml"); + msgBox.setText(message); + msgBox.setIcon(QMessageBox::Warning); + msgBox.exec(); +} + +QWidget *DesignDocumentController::itemLibrary() const +{ + return m_d->itemLibrary.data(); +} + +QWidget *DesignDocumentController::navigator() const +{ + return m_d->navigator->widget(); +} + +QWidget *DesignDocumentController::allPropertiesBox() const +{ + return m_d->allPropertiesBox.data(); +} + +QWidget *DesignDocumentController::statesEditorWidget() const +{ + return m_d->statesEditorWidget.data(); +} + +RewriterView *DesignDocumentController::rewriterView() const +{ + return m_d->rewriterView.data(); +} + +void DesignDocumentController::undo() +{ + m_d->textEdit->undo(); +} + +void DesignDocumentController::redo() +{ + m_d->textEdit->redo(); +} + +#ifdef ENABLE_TEXT_VIEW +void DesignDocumentController::showText() +{ + m_d->stackedWidget->setCurrentWidget(m_d->textEdit.data()); +} +#endif // ENABLE_TEXT_VIEW + +#ifdef ENABLE_TEXT_VIEW +void DesignDocumentController::showForm() +{ + m_d->stackedWidget->setCurrentWidget(m_d->formEditorView->widget()); +} +#endif // ENABLE_TEXT_VIEW + +QProcess *DesignDocumentController::createPreviewProcess(const QString &dirPath) +{ + Q_ASSERT(QDir(dirPath).exists()); + + QProcess *previewProcess = new QProcess(this); + previewProcess->setWorkingDirectory(dirPath); + + QTemporaryFile *temporaryFile = new QTemporaryFile(QDir(dirPath).absoluteFilePath("bauhaus_tmp")); + temporaryFile->setParent(previewProcess); + + temporaryFile->open(); + + m_d->textModifier->save(temporaryFile); + + QStringList qmlViewerArgumentList; +// qmlViewerArgumentList.append("-L"); +// qmlViewerArgumentList.append(dirPath); + previewProcess->setWorkingDirectory(dirPath); + qmlViewerArgumentList.append(temporaryFile->fileName()); + + temporaryFile->close(); + + previewProcess->start(QString("%1/qmlviewer").arg(QCoreApplication::applicationDirPath()), qmlViewerArgumentList); + if (!previewProcess->waitForStarted()) + previewProcess->start("qmlviewer", qmlViewerArgumentList); + if (!previewProcess->waitForStarted()) + QMessageBox::warning(documentWidget(), "qmlviewer not found", "qmlviewer should be in the PATH or in the same directory like the bauhaus binary."); + + return previewProcess; +} + +QProcess *DesignDocumentController::createPreviewWithDebugProcess(const QString &dirPath) +{ + Q_ASSERT(QDir(dirPath).exists()); + + QProcess *previewProcess = new QProcess(this); + previewProcess->setWorkingDirectory(dirPath); + + QProcess *debugger = new QProcess(previewProcess); + + QTemporaryFile *temporaryFile = new QTemporaryFile(QDir(dirPath).absoluteFilePath("bauhaus_tmp")); + temporaryFile->setParent(previewProcess); + + temporaryFile->open(); + + m_d->textModifier->save(temporaryFile); + + QStringList environmentList = QProcess::systemEnvironment(); + environmentList.append("QML_DEBUG_SERVER_PORT=3768"); + previewProcess->setEnvironment(environmentList); + + QStringList qmlViewerArgumentList; +// qmlViewerArgumentList.append("-L"); +// qmlViewerArgumentList.append(libraryPath); + qmlViewerArgumentList.append(temporaryFile->fileName()); + + temporaryFile->close(); + + debugger->setWorkingDirectory(dirPath); + debugger->start(QString("%1/qmldebugger").arg(QCoreApplication::applicationDirPath())); + if (!debugger->waitForStarted()) + debugger->start("qmldebugger", qmlViewerArgumentList); + if (!debugger->waitForStarted()) + QMessageBox::warning(documentWidget(), "qmldebugger not found", "qmldebugger should in the PATH or in the same directory like the bauhaus binary."); + + previewProcess->setWorkingDirectory(dirPath); + previewProcess->start(QString("%1/qmlviewer").arg(QCoreApplication::applicationDirPath()), qmlViewerArgumentList); + if (!previewProcess->waitForStarted()) + previewProcess->start("qmlviewer", qmlViewerArgumentList); + if (!previewProcess->waitForStarted()) + QMessageBox::warning(documentWidget(), "qmlviewer not found", "qmlviewer should in the PATH or in the same directory like the bauhaus binary."); + + return previewProcess; +} + +bool DesignDocumentController::save(QIODevice *device, QString * /*errorMessage*/) +{ + if (m_d->model) { + QByteArray data = m_d->textEdit->toPlainText().toLatin1(); + device->write(data); + m_d->textEdit->setPlainText(data); // clear undo/redo history + return true; + } else { + return false; + } +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/integration/designdocumentcontroller.h b/src/plugins/qmldesigner/components/integration/designdocumentcontroller.h new file mode 100644 index 00000000000..355cf79e00c --- /dev/null +++ b/src/plugins/qmldesigner/components/integration/designdocumentcontroller.h @@ -0,0 +1,142 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef DesignDocumentController_h +#define DesignDocumentController_h + +#include "rewriterview.h" + +#include <QtCore/QObject> +#include <QtCore/QString> + +class QUndoStack; +class QWidget; +class QIODevice; +class QProcess; +class QPlainTextEdit; +class QmlError; + +namespace QmlDesigner { + +class Model; +class ModelNode; +class TextModifier; +class QmlObjectNode; +class RewriterView; + +class DesignDocumentController: public QObject +{ + Q_OBJECT + +public: + DesignDocumentController(QObject *parent); + ~DesignDocumentController(); + + QString displayName() const; + QString fileName() const; + void setFileName(const QString &fileName); + + QList<RewriterView::Error> loadMaster(QPlainTextEdit *edit); + QList<RewriterView::Error> loadMaster(const QByteArray &qml); + void loadCurrentModel(); + void close(); + + bool isDirty() const; + bool isUndoAvailable() const; + bool isRedoAvailable() const; + + Model *model() const; + Model *masterModel() const; + + QWidget *documentWidget() const; + QWidget *itemLibrary() const; + QWidget *navigator() const; + QWidget *allPropertiesBox() const; + QWidget *statesEditorWidget() const; + + RewriterView *rewriterView() const; + + bool previewVisible() const; + bool previewWithDebugVisible() const; + + bool isModelSyncBlocked() const; + void blockModelSync(bool block); + + QList<RewriterView::Error> qmlErrors() const; + +signals: + void displayNameChanged(const QString &newFileName); + void dirtyStateChanged(bool newState); + + void undoAvailable(bool isAvailable); + void redoAvailable(bool isAvailable); + void previewVisibilityChanged(bool visible); + void previewWithDebugVisibilityChanged(bool visible); + void designDocumentClosed(); + void qmlErrorsChanged(const QList<RewriterView::Error> &errors); + + void fileToOpen(const QString &path); + +public slots: + bool save(QWidget *parent = 0); + void saveAs(QWidget *parent = 0); + void deleteSelected(); + void copySelected(); + void cutSelected(); + void paste(); + + void togglePreview(bool visible); + void toggleWithDebugPreview(bool visible); + + void emitPreviewVisibilityChanged(); + void emitPreviewWithDebugVisibilityChanged(); + void undo(); + void redo(); + +#ifdef ENABLE_TEXT_VIEW + void showText(); + void showForm(); +#endif // ENABLE_TEXT_VIEW + +private slots: + void doRealSaveAs(const QString &fileName); + void showError(const QString &message, QWidget *parent = 0) const; + void changeCurrentModelTo(const ModelNode &node); + +private: + QProcess *createPreviewProcess(const QString &dirPath); + QProcess *createPreviewWithDebugProcess(const QString &dirPath); + bool save(QIODevice *device, QString *errorMessage); + + class DesignDocumentControllerPrivate *m_d; +}; + +} // namespace QmlDesigner + +#endif // DesignDocumentController_h diff --git a/src/plugins/qmldesigner/components/integration/designdocumentcontrollerview.cpp b/src/plugins/qmldesigner/components/integration/designdocumentcontrollerview.cpp new file mode 100644 index 00000000000..187328bad8b --- /dev/null +++ b/src/plugins/qmldesigner/components/integration/designdocumentcontrollerview.cpp @@ -0,0 +1,137 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "designdocumentcontrollerview.h" +#include <rewriterview.h> +#include <plaintexteditmodifier.h> + +#include <QApplication> +#include <QPlainTextEdit> +#include <QClipboard> +#include <QDebug> + +namespace QmlDesigner { + +void DesignDocumentControllerView::nodeCreated(const ModelNode & /*createdNode*/) {}; +void DesignDocumentControllerView::nodeAboutToBeRemoved(const ModelNode & /*removedNode*/) {}; +void DesignDocumentControllerView::nodeRemoved(const ModelNode & /*removedNode*/, const NodeAbstractProperty & /*parentProperty*/, AbstractView::PropertyChangeFlags /*propertyChange*/) {}; +void DesignDocumentControllerView::nodeReparented(const ModelNode & /*node*/, const NodeAbstractProperty & /*newPropertyParent*/, const NodeAbstractProperty & /*oldPropertyParent*/, AbstractView::PropertyChangeFlags /*propertyChange*/) {}; +void DesignDocumentControllerView::nodeIdChanged(const ModelNode& /*node*/, const QString& /*newId*/, const QString& /*oldId*/) {}; +void DesignDocumentControllerView::propertiesAboutToBeRemoved(const QList<AbstractProperty>& /*propertyList*/) {}; +void DesignDocumentControllerView::propertiesRemoved(const QList<AbstractProperty>& /*propertyList*/) {}; +void DesignDocumentControllerView::variantPropertiesChanged(const QList<VariantProperty>& /*propertyList*/, AbstractView::PropertyChangeFlags /*propertyChange*/) {}; +void DesignDocumentControllerView::bindingPropertiesChanged(const QList<BindingProperty>& /*propertyList*/, AbstractView::PropertyChangeFlags /*propertyChange*/) {}; +void DesignDocumentControllerView::nodeTypeChanged(const ModelNode & /*node*/,const QString & /*type*/, int /*majorVersion*/, int /*minorVersion*/) {}; + +void DesignDocumentControllerView::selectedNodesChanged(const QList<ModelNode> & /*selectedNodeList*/, + const QList<ModelNode> & /*lastSelectedNodeList*/) {}; + +void DesignDocumentControllerView::nodeSlidedToIndex(const NodeListProperty & /*listProperty*/, int /*newIndex*/, int /*oldIndex*/) {}; + +static QStringList arrayToStringList(const QByteArray &byteArray) +{ + QString str(QString::fromLatin1(byteArray)); + return str.split("\n"); +} + +static QByteArray stringListToArray(const QStringList &stringList) +{ + QString str; + foreach (const QString &subString, stringList) + str += subString + "\n"; + return str.toLatin1(); +} + +void DesignDocumentControllerView::toClipboard() const +{ + QClipboard *clipboard = QApplication::clipboard(); + + QMimeData *data = new QMimeData; + + data->setText(toText()); + QStringList imports; + foreach (const Import &import, model()->imports()) + imports.append(import.toString()); + + data->setData("QmlDesigner::imports", stringListToArray(imports)); + clipboard->setMimeData(data); +} + +void DesignDocumentControllerView::fromClipboard() +{ + QClipboard *clipboard = QApplication::clipboard(); + fromText(clipboard->text()); + QStringList imports = arrayToStringList(clipboard->mimeData()->data("QmlDesigner::imports")); +// foreach (const QString &importString, imports) { +// Import import(Import::createLibraryImport(); +// model()->addImport(import); //### imports +// } +} + + +QString DesignDocumentControllerView::toText() const +{ + QScopedPointer<Model> model(Model::create("Qt/Rectangle")); + QPlainTextEdit textEdit; + textEdit.setPlainText("import Qt 4.6; Item {}"); + PlainTextEditModifier modifier(&textEdit); + + QScopedPointer<RewriterView> rewriterView(new RewriterView(RewriterView::Amend, 0)); + rewriterView->setTextModifier(&modifier); + model->attachView(rewriterView.data()); + + ModelMerger merger(rewriterView.data()); + + merger.replaceModel(rootModelNode()); + + ModelNode rewriterNode(rewriterView->rootModelNode()); + + //get the text of the root item without imports + return rewriterView->extractText(QList<ModelNode>() << rewriterNode).value(rewriterNode); +} + +void DesignDocumentControllerView::fromText(QString text) +{ + QScopedPointer<Model> model(Model::create("Qt/Rectangle")); + QPlainTextEdit textEdit; + QString imports("import Qt 4.6;\n"); + textEdit.setPlainText(imports + text); + PlainTextEditModifier modifier(&textEdit); + + QScopedPointer<RewriterView> rewriterView(new RewriterView(RewriterView::Amend, 0)); + rewriterView->setTextModifier(&modifier); + model->attachView(rewriterView.data()); + + if (rewriterView->errors().isEmpty() && rewriterView->rootModelNode().isValid()) { + ModelMerger merger(this); + merger.replaceModel(rewriterView->rootModelNode()); + } +} + +}// namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/integration/designdocumentcontrollerview.h b/src/plugins/qmldesigner/components/integration/designdocumentcontrollerview.h new file mode 100644 index 00000000000..6b4c89ea627 --- /dev/null +++ b/src/plugins/qmldesigner/components/integration/designdocumentcontrollerview.h @@ -0,0 +1,78 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef DESIGNDOCUMENTCONTROLLERVIEW_H +#define DESIGNDOCUMENTCONTROLLERVIEW_H + +#include <abstractview.h> +#include <modelmerger.h> + +namespace QmlDesigner { + +class DesignDocumentControllerView : public AbstractView +{ + Q_OBJECT +public: + DesignDocumentControllerView(QObject *parent = 0) + : AbstractView(parent), m_modelMerger(this) {} + + virtual void nodeCreated(const ModelNode &createdNode); + virtual void nodeAboutToBeRemoved(const ModelNode &removedNode); + virtual void nodeRemoved(const ModelNode &removedNode, const NodeAbstractProperty &parentProperty, PropertyChangeFlags propertyChange); + virtual void nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent, const NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags propertyChange); + virtual void nodeIdChanged(const ModelNode& node, const QString& newId, const QString& oldId); + virtual void propertiesAboutToBeRemoved(const QList<AbstractProperty>& propertyList); + virtual void propertiesRemoved(const QList<AbstractProperty>& propertyList); + virtual void variantPropertiesChanged(const QList<VariantProperty>& propertyList, PropertyChangeFlags propertyChange); + virtual void bindingPropertiesChanged(const QList<BindingProperty>& propertyList, PropertyChangeFlags propertyChange); + virtual void nodeTypeChanged(const ModelNode &node,const QString &type, int majorVersion, int minorVersion); + + virtual void selectedNodesChanged(const QList<ModelNode> &selectedNodeList, + const QList<ModelNode> &lastSelectedNodeList); + + virtual void nodeSlidedToIndex(const NodeListProperty &listProperty, int newIndex, int oldIndex); + + ModelNode insertModel(const ModelNode &modelNode) + { return m_modelMerger.insertModel(modelNode); } + void replaceModel(const ModelNode &modelNode) + { m_modelMerger.replaceModel(modelNode); } + + void toClipboard() const; + void fromClipboard(); + + QString toText() const; + void fromText(QString text); + +private: + ModelMerger m_modelMerger; +}; + +}// namespace QmlDesigner + +#endif // DESIGNDOCUMENTCONTROLLERVIEW_H diff --git a/src/plugins/qmldesigner/components/integration/documentcloser.cpp b/src/plugins/qmldesigner/components/integration/documentcloser.cpp new file mode 100644 index 00000000000..c7c0c5636b7 --- /dev/null +++ b/src/plugins/qmldesigner/components/integration/documentcloser.cpp @@ -0,0 +1,139 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "documentcloser.h" + +#include <QtCore/QCoreApplication> +#include <QtCore/QDebug> +#include <QtCore/QFileInfo> + +#include <QtGui/QMessageBox> + +#include "designdocumentcontroller.h" + +namespace QmlDesigner { + +DocumentCloser::DocumentCloser(): + m_quitWhenDone(false) +{ +} + +void DocumentCloser::close(DesignDocumentController* designDocument) +{ + DocumentCloser* closer = new DocumentCloser; + closer->m_designDocuments.append(designDocument); + closer->runClose(designDocument); +} + +void DocumentCloser::runClose(DesignDocumentController* designDocument) +{ + bool isDirty = designDocument->isDirty(); + +//#ifndef QT_NO_DEBUG +// isDirty=false; +//#endif + + if (isDirty) { + QMessageBox* msgBox = new QMessageBox(designDocument->documentWidget()); + QString txt = tr("Do you want to save the changes you made in the document \"%1\"?"); + QString shortFileName = QFileInfo(designDocument->fileName()).baseName(); + msgBox->setText(txt.arg(shortFileName)); + msgBox->setInformativeText(tr("Your changes will be lost if you don't save them.")); + msgBox->setIcon(QMessageBox::Question); + msgBox->setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); + msgBox->setDefaultButton(QMessageBox::Save); + msgBox->setEscapeButton(QMessageBox::Cancel); + msgBox->resize(451, 131); + msgBox->open(this, SLOT(choiceMade())); + } else { + designDocument->close(); + closeDone(); + } +} + +void DocumentCloser::choiceMade() +{ + QMessageBox* msgBox = dynamic_cast<QMessageBox*>(sender()); + if (!msgBox) + return; + + switch (msgBox->result()) { + case QMessageBox::Save: + m_designDocuments.first()->save(); + + case QMessageBox::Discard: + m_designDocuments.first()->close(); + closeDone(); + break; + + case QMessageBox::Cancel: + default: + closeCancelled(); + } +} + +void DocumentCloser::closeCancelled() +{ + delete this; +} + +void DocumentCloser::closeDone() +{ + if (!m_designDocuments.isEmpty()) // should always be the case, but let's be safe. + m_designDocuments.removeFirst(); + + bool quit = m_quitWhenDone; + + if (m_designDocuments.isEmpty()) { + delete this; + + if (quit) { + qApp->quit(); + } + } else { + runClose(m_designDocuments.first().data()); + } +} + +void DocumentCloser::close(QList<QWeakPointer<DesignDocumentController> > designDocuments, bool quitWhenAllEditorsClosed) +{ + if (designDocuments.isEmpty()) { + if (quitWhenAllEditorsClosed) { + qApp->quit(); + } + } else { + DocumentCloser* closer = new DocumentCloser; + closer->m_designDocuments = designDocuments; + closer->m_quitWhenDone = quitWhenAllEditorsClosed; + closer->runClose(closer->m_designDocuments.first().data()); + } +} + +} // namespace QmlDesigner + diff --git a/src/plugins/qmldesigner/components/integration/documentcloser.h b/src/plugins/qmldesigner/components/integration/documentcloser.h new file mode 100644 index 00000000000..35933af1f04 --- /dev/null +++ b/src/plugins/qmldesigner/components/integration/documentcloser.h @@ -0,0 +1,67 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef DOCUMENTCLOSER_H +#define DOCUMENTCLOSER_H + +#include <QWeakPointer> +#include <QtCore/QList> +#include <QtCore/QObject> + + +#include "designdocumentcontroller.h" + +namespace QmlDesigner { + +class DocumentCloser : public QObject +{ + Q_OBJECT + +public: + static void close(DesignDocumentController* designDocument); + static void close(QList<QWeakPointer<DesignDocumentController> > designDocuments, bool quitWhenAllEditorsClosed); + +private: + DocumentCloser(); + + void runClose(DesignDocumentController* designDocument); + void closeDone(); + void closeCancelled(); + +private slots: + void choiceMade(); + +private: + QList<QWeakPointer<DesignDocumentController> > m_designDocuments; + bool m_quitWhenDone; +}; + +} // namespace QmlDesigner + +#endif // DOCUMENTCLOSER_H diff --git a/src/plugins/qmldesigner/components/integration/integration.pri b/src/plugins/qmldesigner/components/integration/integration.pri new file mode 100644 index 00000000000..0247e2df106 --- /dev/null +++ b/src/plugins/qmldesigner/components/integration/integration.pri @@ -0,0 +1,34 @@ +VPATH += $$PWD +INCLUDEPATH += $$PWD +SOURCES += \ + integrationcore.cpp \ + multipledocumentscontroller.cpp \ + designdocumentcontroller.cpp \ + designdocumentcontrollerview.cpp \ + navigatorcontroller.cpp \ + allpropertiesviewcontroller.cpp \ + utilitypanelcontroller.cpp \ + widgetboxcontroller.cpp \ + stackedutilitypanelcontroller.cpp \ + stateseditorcontroller.cpp \ + documentcloser.cpp \ + componentaction.cpp \ + componentview.cpp \ + xuifiledialog.cpp + +HEADERS += \ + integrationcore.h \ + multipledocumentscontroller.h \ + designdocumentcontrollerview.h \ + designdocumentcontroller.h \ + navigatorcontroller.h \ + allpropertiesviewcontroller.h \ + utilitypanelcontroller.h \ + widgetboxcontroller.h \ + stackedutilitypanelcontroller.h \ + stateseditorcontroller.h \ + documentcloser.h \ + componentaction.h \ + componentview.h \ + xuifiledialog.h + diff --git a/src/plugins/qmldesigner/components/integration/integrationcore.cpp b/src/plugins/qmldesigner/components/integration/integrationcore.cpp new file mode 100644 index 00000000000..518cf23ecd4 --- /dev/null +++ b/src/plugins/qmldesigner/components/integration/integrationcore.cpp @@ -0,0 +1,94 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "integrationcore.h" +#include "pluginmanager.h" +#include "itemlibrary.h" +#include "navigatorwidget.h" +#include "metainfo.h" + +#include <QtCore/QCoreApplication> +#include <QtCore/QObject> + +namespace QmlDesigner { + +class CorePrivate +{ + public: + CorePrivate(); + ~CorePrivate(); + + static IntegrationCore *m_instance; + + PluginManager m_pluginManager; +}; + +CorePrivate::CorePrivate() +{ +} + +CorePrivate::~CorePrivate() +{ + MetaInfo::clearGlobal(); +} + +IntegrationCore *CorePrivate::m_instance = 0; + +/*! + \class QmlDesigner::Core + + Convenience class to access the plugin manager singleton, + and manage the lifecycle of static data (e.g. in the metatype system). +*/ + +IntegrationCore::IntegrationCore() : + m_d(new CorePrivate) +{ + Q_ASSERT(CorePrivate::m_instance == 0); + CorePrivate::m_instance = this; +} + +IntegrationCore::~IntegrationCore() +{ + CorePrivate::m_instance = 0; + delete m_d; +} + +IntegrationCore *IntegrationCore::instance() +{ + Q_ASSERT(CorePrivate::m_instance); + return CorePrivate::m_instance; +} + +PluginManager *IntegrationCore::pluginManager() const +{ + return &m_d->m_pluginManager; +} + +} diff --git a/src/plugins/qmldesigner/components/integration/integrationcore.h b/src/plugins/qmldesigner/components/integration/integrationcore.h new file mode 100644 index 00000000000..170eb6ac48d --- /dev/null +++ b/src/plugins/qmldesigner/components/integration/integrationcore.h @@ -0,0 +1,61 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef QMLDESIGNERCORE_H +#define QMLDESIGNERCORE_H + +#include <QtCore/QObject> + +class QWidget; +class QDialog; + +namespace QmlDesigner { + +class ItemLibrary; +class FormWindowManager; +class PluginManager; +class CorePrivate; + +class IntegrationCore +{ + Q_DISABLE_COPY(IntegrationCore) +public: + IntegrationCore(); + ~IntegrationCore(); + + PluginManager *pluginManager() const; + + static IntegrationCore *instance(); + +private: + CorePrivate *m_d; +}; +} + +#endif // QMLDESIGNERCORE_H diff --git a/src/plugins/qmldesigner/components/integration/multipledocumentscontroller.cpp b/src/plugins/qmldesigner/components/integration/multipledocumentscontroller.cpp new file mode 100644 index 00000000000..4e3b34bf471 --- /dev/null +++ b/src/plugins/qmldesigner/components/integration/multipledocumentscontroller.cpp @@ -0,0 +1,353 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "multipledocumentscontroller.h" + +#include <QtCore/QFileInfo> +#include <QtGui/QTextEdit> +#include <QtGui/QMessageBox> + +#include <QmlError> + +#include <model.h> +#include "designdocumentcontroller.h" +#include "documentcloser.h" + + +namespace QmlDesigner { + +static QString createTitle(const QString& fileName) +{ + return QFileInfo(fileName).baseName(); +} + +MultipleDocumentsController::MultipleDocumentsController(QWidget* parent): + QObject(parent), + m_tabWidget(new QTabWidget), + m_undoAction(new QAction(tr("&Undo"), this)), + m_redoAction(new QAction(tr("&Redo"), this)) +{ + m_tabWidget->setDocumentMode(true); + m_tabWidget->setTabsClosable(true); + m_tabWidget->setMovable(true); + + connect(tabWidget(), SIGNAL(currentChanged(int)), this, SLOT(currentTabChanged(int))); + connect(tabWidget(), SIGNAL(tabCloseRequested(int)), this, SLOT(tabCloseRequested(int))); +} + +MultipleDocumentsController::~MultipleDocumentsController() +{ +} + +QTabWidget *MultipleDocumentsController::tabWidget() const +{ + return m_tabWidget.data(); +} + +DesignDocumentController* MultipleDocumentsController::findActiveDocument(QWidget* documentWidget) +{ + foreach (const QWeakPointer<DesignDocumentController> &controller, m_documentControllers) + if (controller->documentWidget() == documentWidget) + return controller.data(); + + return 0; +} + +void MultipleDocumentsController::rewireDocumentActions(DesignDocumentController* oldActiveDocument, DesignDocumentController* newActiveDocument) +{ + if (oldActiveDocument) { + disconnect(undoAction(), SIGNAL(triggered()), oldActiveDocument, SLOT(undo())); + disconnect(redoAction(), SIGNAL(triggered()), oldActiveDocument, SLOT(redo())); +#ifdef ENABLE_TEXT_VIEW + disconnect(m_showTextAction.data(), SIGNAL(triggered()), oldActiveDocument, SLOT(showText())); + disconnect(m_showFormAction.data(), SIGNAL(triggered()), oldActiveDocument, SLOT(showForm())); +#endif // ENABLE_TEXT_VIEW + } + + if (newActiveDocument) { + connect(undoAction(), SIGNAL(triggered()), newActiveDocument, SLOT(undo())); + connect(redoAction(), SIGNAL(triggered()), newActiveDocument, SLOT(redo())); +#ifdef ENABLE_TEXT_VIEW + connect(m_showTextAction.data(), SIGNAL(triggered()), newActiveDocument, SLOT(showText())); + connect(m_showFormAction.data(), SIGNAL(triggered()), newActiveDocument, SLOT(showForm())); + if (m_showTextAction->isChecked()) + newActiveDocument->showText(); + else + newActiveDocument->showForm(); +#endif // ENABLE_TEXT_VIEW + } +} + +void MultipleDocumentsController::currentTabChanged(int newCurrentTab) +{ + DesignDocumentController* oldActiveDocument = 0; + if (!m_documentControllers.isEmpty()) { + oldActiveDocument = m_documentControllers.first().data(); + } + + if (newCurrentTab == -1) { + emit activeDocumentChanged(0); + if (oldActiveDocument && oldActiveDocument->previewVisible()) + emit previewVisibilityChanged(false); + if (oldActiveDocument && oldActiveDocument->previewWithDebugVisible()) + emit previewWithDebugVisibilityChanged(false); + + if (oldActiveDocument) + rewireDocumentActions(oldActiveDocument, 0); + } else { + DesignDocumentController* newActiveDocument = findActiveDocument(m_tabWidget->currentWidget()); + m_undoAction->setEnabled(newActiveDocument->isUndoAvailable()); + m_redoAction->setEnabled(newActiveDocument->isRedoAvailable()); + + rewireDocumentActions(oldActiveDocument, newActiveDocument); + + m_documentControllers.removeOne(newActiveDocument); + m_documentControllers.prepend(newActiveDocument); + emit activeDocumentChanged(newActiveDocument); + if (oldActiveDocument && oldActiveDocument->previewVisible()) + emit previewVisibilityChanged(newActiveDocument->previewVisible()); + if (oldActiveDocument && oldActiveDocument->previewWithDebugVisible()) + emit previewWithDebugVisibilityChanged(newActiveDocument->previewWithDebugVisible()); + } +} + +void MultipleDocumentsController::tabCloseRequested(int tabIndex) +{ + DesignDocumentController* controller = findActiveDocument(m_tabWidget->widget(tabIndex)); + + if (controller) + DocumentCloser::close(controller); +} + +void MultipleDocumentsController::documentDisplayNameChanged(const QString &/*fileName*/) +{ + DesignDocumentController *documentController = qobject_cast<DesignDocumentController*>(sender()); + Q_ASSERT(documentController); + documentDirtyStateChanged(documentController->isDirty()); +} + +void MultipleDocumentsController::documentDirtyStateChanged(bool newState) +{ + DesignDocumentController *documentController = qobject_cast<DesignDocumentController*>(sender()); + Q_ASSERT(documentController); + + int tabIdx = m_tabWidget->indexOf(documentController->documentWidget()); + + if (newState) + m_tabWidget->setTabText(tabIdx, tr("* %1").arg(createTitle(documentController->displayName()))); + else + m_tabWidget->setTabText(tabIdx, createTitle(documentController->displayName())); +} + +void MultipleDocumentsController::documentUndoAvailable(bool isAvailable) +{ + DesignDocumentController *documentController = qobject_cast<DesignDocumentController*>(sender()); + Q_ASSERT(documentController); + + if (m_documentControllers.first().data() == documentController) + m_undoAction->setEnabled(isAvailable); +} + +void MultipleDocumentsController::documentRedoAvailable(bool isAvailable) +{ + DesignDocumentController *documentController = qobject_cast<DesignDocumentController*>(sender()); + Q_ASSERT(documentController); + + if (m_documentControllers.first().data() == documentController) + m_redoAction->setEnabled(isAvailable); +} + +void MultipleDocumentsController::documentPreviewVisibilityChanged(bool visible) +{ + DesignDocumentController *documentController = qobject_cast<DesignDocumentController*>(sender()); + Q_ASSERT(documentController); + + if (m_documentControllers.first().data() == documentController) + emit previewVisibilityChanged(visible); +} + +void MultipleDocumentsController::documentPreviewWithDebugVisibilityChanged(bool visible) +{ + DesignDocumentController *documentController = qobject_cast<DesignDocumentController*>(sender()); + Q_ASSERT(documentController); + + if (m_documentControllers.first().data() == documentController) + emit previewWithDebugVisibilityChanged(visible); +} + +void MultipleDocumentsController::open(DesignDocumentController* documentController) +{ + m_documentControllers.append(documentController); + + int newTabIndex = m_tabWidget->addTab(documentController->documentWidget(), createTitle(documentController->displayName())); + m_tabWidget->setCurrentIndex(newTabIndex); + + connect(documentController, SIGNAL(displayNameChanged(QString)), + this, SLOT(documentDisplayNameChanged(QString))); + connect(documentController, SIGNAL(dirtyStateChanged(bool)), + this, SLOT(documentDirtyStateChanged(bool))); + connect(documentController, SIGNAL(undoAvailable(bool)), + this, SLOT(documentUndoAvailable(bool))); + connect(documentController, SIGNAL(redoAvailable(bool)), + this, SLOT(documentRedoAvailable(bool))); + connect(documentController, SIGNAL(previewVisibilityChanged(bool)), + this, SLOT(documentPreviewVisibilityChanged(bool))); + connect(documentController, SIGNAL(previewWithDebugVisibilityChanged(bool)), + this, SLOT(documentPreviewWithDebugVisibilityChanged(bool))); + connect(documentController, SIGNAL(designDocumentClosed()), + this, SLOT(designDocumentClosed())); + connect(documentController, SIGNAL(qmlErrorsChanged(QList<RewriterView::Error>)), + this, SLOT(designDocumentError(QList<RewriterView::Error>))); + + emit documentCountChanged(m_documentControllers.size()); +} + +void MultipleDocumentsController::designDocumentClosed() +{ + DesignDocumentController *documentController = qobject_cast<DesignDocumentController*>(sender()); + Q_ASSERT(documentController); + + rewireDocumentActions(documentController, 0); + + m_tabWidget->removeTab(m_tabWidget->indexOf(documentController->documentWidget())); + m_documentControllers.removeOne(documentController); + emit documentClosed(documentController); + emit documentCountChanged(m_documentControllers.size()); +} + +void MultipleDocumentsController::designDocumentError(const QList<RewriterView::Error> &error) +{ + DesignDocumentController *documentController = qobject_cast<DesignDocumentController*>(sender()); + Q_ASSERT(documentController); + + if (error.isEmpty()) + return; + + QMessageBox msgBox(documentController->documentWidget()); + msgBox.setWindowFlags(Qt::Sheet | Qt::MSWindowsFixedSizeDialogHint); + msgBox.setWindowTitle("Invalid qml"); + msgBox.setText(error.first().toString()); + msgBox.setIcon(QMessageBox::Warning); + msgBox.exec(); +} + +void MultipleDocumentsController::doSave() +{ + if (!m_documentControllers.isEmpty()) + m_documentControllers.first()->save(dynamic_cast<QWidget*>(parent())); +} + +void MultipleDocumentsController::doSaveAs() +{ + QWidget* parentWindow = dynamic_cast<QWidget*>(parent()); + + if (!m_documentControllers.isEmpty()) + m_documentControllers.first()->saveAs(parentWindow); +} + +void MultipleDocumentsController::doPreview(bool visible) +{ + if (m_documentControllers.isEmpty()) + return; + + m_documentControllers.first()->togglePreview(visible); +} + +void MultipleDocumentsController::doPreviewWithDebug(bool visible) +{ + if (m_documentControllers.isEmpty()) + return; + + m_documentControllers.first()->toggleWithDebugPreview(visible); +} + +void MultipleDocumentsController::doDelete() +{ + m_documentControllers.first()->deleteSelected(); +} + +void MultipleDocumentsController::doCopy() +{ + m_documentControllers.first()->copySelected(); +} + +void MultipleDocumentsController::doCut() +{ + m_documentControllers.first()->cutSelected(); +} + +void MultipleDocumentsController::doPaste() +{ + m_documentControllers.first()->paste(); +} + +void MultipleDocumentsController::closeAll(bool quitWhenAllEditorsClosed) +{ + DocumentCloser::close(m_documentControllers, quitWhenAllEditorsClosed); +} + +QAction* MultipleDocumentsController::undoAction() +{ + return m_undoAction.data(); +} + +QAction* MultipleDocumentsController::redoAction() +{ + return m_redoAction.data(); +} + +#ifdef ENABLE_TEXT_VIEW +QAction* MultipleDocumentsController::showFormAction(QMenu* parent) +{ + if (!m_showFormAction) { + m_showFormAction = new QAction(tr("&Form"), parent); + m_showFormAction->setShortcut(QKeySequence("Ctrl+Shift+F")); + m_showFormAction->setCheckable(true); + } + + return m_showFormAction.data(); +} +#endif // ENABLE_TEXT_VIEW + +#ifdef ENABLE_TEXT_VIEW +QAction* MultipleDocumentsController::showTextAction(QMenu* parent) +{ + if (!m_showTextAction) { + m_showTextAction = new QAction(tr("&Text"), parent); + m_showTextAction->setShortcut(QKeySequence("Ctrl+Shift+T")); + m_showTextAction->setCheckable(true); + } + + return m_showTextAction.data(); +} + +#endif // ENABLE_TEXT_VIEW + +} // namespace QmlDesigner + diff --git a/src/plugins/qmldesigner/components/integration/multipledocumentscontroller.h b/src/plugins/qmldesigner/components/integration/multipledocumentscontroller.h new file mode 100644 index 00000000000..91347d69188 --- /dev/null +++ b/src/plugins/qmldesigner/components/integration/multipledocumentscontroller.h @@ -0,0 +1,120 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef MultipleDocumentsController_h +#define MultipleDocumentsController_h + +#include <rewriterview.h> + +#include <QWeakPointer> +#include <QtCore/QObject> +#include <QtCore/QList> +#include <QWeakPointer> + +#include <QtGui/QAction> +#include <QtGui/QMenu> +#include <QtGui/QTabWidget> +#include <QtGui/QUndoGroup> + +class QmlError; + +namespace QmlDesigner { + +class DesignDocumentController; + +class MultipleDocumentsController : public QObject +{ + Q_OBJECT + +public: + MultipleDocumentsController(QWidget* parent = 0); // a bit of a hack + ~MultipleDocumentsController(); + + QTabWidget* tabWidget() const; + + QAction* undoAction(); + QAction* redoAction(); +#ifdef ENABLE_TEXT_VIEW + QAction* showFormAction(QMenu* parent); + QAction* showTextAction(QMenu* parent); +#endif // ENABLE_TEXT_VIEW + void open(DesignDocumentController* documentController); + void closeAll(bool quitWhenAllEditorsClosed); + unsigned activeDocumentCount() const { return m_documentControllers.size(); } + +signals: + void activeDocumentChanged(DesignDocumentController* documentController); + void documentClosed(DesignDocumentController* documentController); + void documentCountChanged(unsigned newDocumentCount); + + void previewVisibilityChanged(bool); + void previewWithDebugVisibilityChanged(bool); + +public slots: + void doSave(); + void doSaveAs(); + void doPreview(bool visible); + void doPreviewWithDebug(bool visible); + void doDelete(); + void doCopy(); + void doCut(); + void doPaste(); + + void documentDisplayNameChanged(const QString &fileName); + void documentDirtyStateChanged(bool newState); + void documentUndoAvailable(bool isAvailable); + void documentRedoAvailable(bool isAvailable); + void documentPreviewVisibilityChanged(bool visible); + void documentPreviewWithDebugVisibilityChanged(bool visible); + +protected slots: + void currentTabChanged(int newCurrentTab); + void tabCloseRequested(int tabIndex); + void designDocumentClosed(); + void designDocumentError(const QList<RewriterView::Error> &errors); + +private: + DesignDocumentController* findActiveDocument(QWidget* documentWidget); + void rewireDocumentActions(DesignDocumentController* oldActiveDocument, DesignDocumentController* newActiveDocument); + +private: + QWeakPointer<QTabWidget> m_tabWidget; + QWeakPointer<QAction> m_undoAction; + QWeakPointer<QAction> m_redoAction; +#ifdef ENABLE_TEXT_VIEW + QWeakPointer<QAction> m_showTextAction; + QWeakPointer<QAction> m_showFormAction; +#endif // ENABLE_TEXT_VIEW + + QList<QWeakPointer<DesignDocumentController> > m_documentControllers; +}; + +} // namespace QmlDesigner + +#endif // MultipleDocumentsController_h diff --git a/src/plugins/qmldesigner/components/integration/navigatorcontroller.cpp b/src/plugins/qmldesigner/components/integration/navigatorcontroller.cpp new file mode 100644 index 00000000000..e09f028ba8d --- /dev/null +++ b/src/plugins/qmldesigner/components/integration/navigatorcontroller.cpp @@ -0,0 +1,53 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include <navigatorwidget.h> + +#include "designdocumentcontroller.h" +#include "navigatorcontroller.h" + +namespace QmlDesigner { + +NavigatorController::NavigatorController(QObject* parent): + StackedUtilityPanelController(parent) +{ + setObjectName("navigatorController"); + contentWidget()->setObjectName("navigatorStack"); +} + +QWidget* NavigatorController::stackedPageWidget(DesignDocumentController* designDocumentController) const +{ + if (designDocumentController) + return designDocumentController->navigator(); + else + return 0; +} + +} // namespace QmlDesigner + diff --git a/src/plugins/qmldesigner/components/integration/navigatorcontroller.h b/src/plugins/qmldesigner/components/integration/navigatorcontroller.h new file mode 100644 index 00000000000..7c44b3d6982 --- /dev/null +++ b/src/plugins/qmldesigner/components/integration/navigatorcontroller.h @@ -0,0 +1,48 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef NAVIGATORCONTROLLER_H +#define NAVIGATORCONTROLLER_H + +#include "stackedutilitypanelcontroller.h" + +namespace QmlDesigner { + +class NavigatorController : public StackedUtilityPanelController +{ +public: + NavigatorController(QObject* parent = 0); + +protected: + class QWidget* stackedPageWidget(class DesignDocumentController* designDocumentController) const; +}; + +} // namespace QmlDesigner + +#endif // NAVIGATORCONTROLLER_H diff --git a/src/plugins/qmldesigner/components/integration/stackedutilitypanelcontroller.cpp b/src/plugins/qmldesigner/components/integration/stackedutilitypanelcontroller.cpp new file mode 100644 index 00000000000..b708ff2f8b7 --- /dev/null +++ b/src/plugins/qmldesigner/components/integration/stackedutilitypanelcontroller.cpp @@ -0,0 +1,76 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include <QtGui/QStackedWidget> + +#include "designdocumentcontroller.h" +#include "stackedutilitypanelcontroller.h" + +namespace QmlDesigner { + +StackedUtilityPanelController::StackedUtilityPanelController(QObject* parent): + UtilityPanelController(parent), + m_stackedWidget(new QStackedWidget) +{ + m_stackedWidget->setLineWidth(0); + m_stackedWidget->setMidLineWidth(0); + m_stackedWidget->setFrameStyle(QFrame::NoFrame); +} + +void StackedUtilityPanelController::show(DesignDocumentController* designDocumentController) +{ + if (!designDocumentController) + return; + + QWidget* page = stackedPageWidget(designDocumentController); + + if (!m_stackedWidget->children().contains(page)) + m_stackedWidget->addWidget(page); + + m_stackedWidget->setCurrentWidget(page); + page->show(); +} + +void StackedUtilityPanelController::close(DesignDocumentController* designDocumentController) +{ + QWidget* page = stackedPageWidget(designDocumentController); + + if (m_stackedWidget->children().contains(page)) { + page->hide(); + m_stackedWidget->removeWidget(page); + } +} + +QWidget* StackedUtilityPanelController::contentWidget() const +{ + return m_stackedWidget; +} + +} // namespace QmlDesigner + diff --git a/src/plugins/qmldesigner/components/integration/stackedutilitypanelcontroller.h b/src/plugins/qmldesigner/components/integration/stackedutilitypanelcontroller.h new file mode 100644 index 00000000000..971b50c61af --- /dev/null +++ b/src/plugins/qmldesigner/components/integration/stackedutilitypanelcontroller.h @@ -0,0 +1,60 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef StackedUtilityPanelController_h +#define StackedUtilityPanelController_h + +#include "utilitypanelcontroller.h" + +namespace QmlDesigner { + +class DesignDocumentController; + +class StackedUtilityPanelController : public UtilityPanelController +{ + Q_OBJECT + +public: + StackedUtilityPanelController(QObject* parent = 0); + +public slots: + void show(DesignDocumentController* designDocumentController); + void close(DesignDocumentController* designDocumentController); + +protected: + virtual QWidget* contentWidget() const; + virtual QWidget* stackedPageWidget(DesignDocumentController* designDocumentController) const = 0; + +private: + class QStackedWidget* m_stackedWidget; +}; + +} // namespace QmlDesigner + +#endif // StackedUtilityPanelController_h diff --git a/src/plugins/qmldesigner/components/integration/stateseditorcontroller.cpp b/src/plugins/qmldesigner/components/integration/stateseditorcontroller.cpp new file mode 100644 index 00000000000..a403dd2e1dc --- /dev/null +++ b/src/plugins/qmldesigner/components/integration/stateseditorcontroller.cpp @@ -0,0 +1,50 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "designdocumentcontroller.h" +#include "stateseditorcontroller.h" + +namespace QmlDesigner { + +StatesEditorController::StatesEditorController(QObject* parent): + StackedUtilityPanelController(parent) +{ + setObjectName("StatesEditorController"); + contentWidget()->setObjectName("statesEditorStack"); +} + +QWidget* StatesEditorController::stackedPageWidget(DesignDocumentController* designDocumentController) const +{ + if (designDocumentController) + return designDocumentController->statesEditorWidget(); + else + return 0; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/integration/stateseditorcontroller.h b/src/plugins/qmldesigner/components/integration/stateseditorcontroller.h new file mode 100644 index 00000000000..ce90b29b733 --- /dev/null +++ b/src/plugins/qmldesigner/components/integration/stateseditorcontroller.h @@ -0,0 +1,48 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef STATESEDITORCONTROLLER_H +#define STATESEDITORCONTROLLER_H + +#include "stackedutilitypanelcontroller.h" + +namespace QmlDesigner { + +class StatesEditorController : public StackedUtilityPanelController +{ +public: + StatesEditorController(QObject* parent = 0); + +protected: + class QWidget* stackedPageWidget(class DesignDocumentController* designDocumentController) const; +}; + +} // namespace QmlDesigner + +#endif // STATESEDITORCONTROLLER_H diff --git a/src/plugins/qmldesigner/components/integration/utilitypanelcontroller.cpp b/src/plugins/qmldesigner/components/integration/utilitypanelcontroller.cpp new file mode 100644 index 00000000000..1f430e6ae53 --- /dev/null +++ b/src/plugins/qmldesigner/components/integration/utilitypanelcontroller.cpp @@ -0,0 +1,54 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include <QtDebug> +#include <QEvent> +#include <QtGui/QDockWidget> +#include <QtGui/QVBoxLayout> +#include <QtGui/QWidget> + +#include "utilitypanelcontroller.h" + +namespace QmlDesigner { + +UtilityPanelController::UtilityPanelController(QObject* parent): + QObject(parent) +{ +} + +UtilityPanelController::~UtilityPanelController() +{ +} + +QWidget* UtilityPanelController::widget() +{ + return contentWidget(); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/integration/utilitypanelcontroller.h b/src/plugins/qmldesigner/components/integration/utilitypanelcontroller.h new file mode 100644 index 00000000000..878eacac563 --- /dev/null +++ b/src/plugins/qmldesigner/components/integration/utilitypanelcontroller.h @@ -0,0 +1,55 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef UtilityPanelController_h +#define UtilityPanelController_h + +#include <QtCore/QObject> +#include <QtCore/QString> +#include <QtGui/QWidget> + +namespace QmlDesigner { + +class UtilityPanelController : public QObject +{ + Q_OBJECT + +public: + UtilityPanelController(QObject* parent = 0); + virtual ~UtilityPanelController() = 0; + + QWidget* widget(); + +protected: + virtual class QWidget* contentWidget() const = 0; +}; + +} // namespace QmlDesigner + +#endif // UtilityPanelController_h diff --git a/src/plugins/qmldesigner/components/integration/widgetboxcontroller.cpp b/src/plugins/qmldesigner/components/integration/widgetboxcontroller.cpp new file mode 100644 index 00000000000..1c7066c0d54 --- /dev/null +++ b/src/plugins/qmldesigner/components/integration/widgetboxcontroller.cpp @@ -0,0 +1,51 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include <itemlibrary.h> + +#include "designdocumentcontroller.h" +#include "widgetboxcontroller.h" + +namespace QmlDesigner { + +ItemLibraryController::ItemLibraryController(QObject* parent): + StackedUtilityPanelController(parent) +{ + setObjectName("ItemLibraryController"); +} + +QWidget* ItemLibraryController::stackedPageWidget(DesignDocumentController* designDocumentController) const +{ + if (designDocumentController) + return designDocumentController->itemLibrary(); + else + return 0; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/integration/widgetboxcontroller.h b/src/plugins/qmldesigner/components/integration/widgetboxcontroller.h new file mode 100644 index 00000000000..27d69d62886 --- /dev/null +++ b/src/plugins/qmldesigner/components/integration/widgetboxcontroller.h @@ -0,0 +1,48 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef ItemLibraryCONTROLLER_H +#define ItemLibraryCONTROLLER_H + +#include "stackedutilitypanelcontroller.h" + +namespace QmlDesigner { + +class ItemLibraryController : public StackedUtilityPanelController +{ +public: + ItemLibraryController(QObject* parent = 0); + +protected: + class QWidget* stackedPageWidget(class DesignDocumentController* designDocumentController) const; +}; + +} // namespace QmlDesigner + +#endif // ItemLibraryCONTROLLER_H diff --git a/src/plugins/qmldesigner/components/integration/xuifiledialog.cpp b/src/plugins/qmldesigner/components/integration/xuifiledialog.cpp new file mode 100644 index 00000000000..720ef5403f8 --- /dev/null +++ b/src/plugins/qmldesigner/components/integration/xuifiledialog.cpp @@ -0,0 +1,91 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include <QDebug> +#include <QtCore/QDir> +#include <QtCore/QObject> + +#include "xuifiledialog.h" + +namespace QmlDesigner { + +void XUIFileDialog::runOpenFileDialog(const QString& path, QWidget* parent, QObject* receiver, const char* member) +{ + QString dir = path; + if (dir.isNull()) + dir = XUIFileDialog::defaultFolder(); + + QString caption = QObject::tr("Open file", "XUIFileDialog::runOpenFileDialog"); + QString fileName = QFileDialog::getOpenFileName(parent, caption, dir, XUIFileDialog::fileNameFilters().join(";;"), 0, QFileDialog::ReadOnly); + + QmlDesigner::Internal::SignalEmitter emitter; + QObject::connect(&emitter, SIGNAL(fileNameSelected(QString)), receiver, member); + emitter.emitFileNameSelected(fileName); +} + +void XUIFileDialog::runSaveFileDialog(const QString& path, QWidget* parent, QObject* receiver, const char* member) +{ + QString dir = path; + if (dir.isNull()) + dir = XUIFileDialog::defaultFolder(); + +#ifdef Q_WS_MAC + QFileDialog *dialog = new QFileDialog(parent, Qt::Sheet); + dialog->setFileMode(QFileDialog::AnyFile); + dialog->setAcceptMode(QFileDialog::AcceptSave); + dialog->setNameFilters(XUIFileDialog::fileNameFilters()); + dialog->setDirectory(dir); + dialog->open(receiver, member); +#else // !Q_WS_MAC + QString caption = QObject::tr("Save file", "XUIFileDialog::runSaveFileDialog"); + QString fileName = QFileDialog::getSaveFileName(parent, caption, dir, XUIFileDialog::fileNameFilters().join(";;")); + + QmlDesigner::Internal::SignalEmitter emitter; + QObject::connect(&emitter, SIGNAL(fileNameSelected(QString)), receiver, member); + emitter.emitFileNameSelected(fileName); +#endif // Q_WS_MAC +} + +QStringList XUIFileDialog::fileNameFilters() +{ + QStringList filters; + + filters + << QObject::tr("Declarative UI files (*.qml)", "XUIFileDialog::fileNameFilters") + << QObject::tr("All files (*)", "XUIFileDialog::fileNameFilters"); + + return filters; +} + +QString XUIFileDialog::defaultFolder() +{ + return QDir::currentPath(); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/integration/xuifiledialog.h b/src/plugins/qmldesigner/components/integration/xuifiledialog.h new file mode 100644 index 00000000000..bcd0ff52f85 --- /dev/null +++ b/src/plugins/qmldesigner/components/integration/xuifiledialog.h @@ -0,0 +1,65 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef XUIFILEDIALOG_H +#define XUIFILEDIALOG_H + +#include <QtCore/QString> +#include <QtCore/QStringList> + +#include <QtGui/QFileDialog> + +namespace QmlDesigner { + +class XUIFileDialog +{ +public: + static void runOpenFileDialog(const QString& path, QWidget* parent, QObject* receiver, const char* member); + static void runSaveFileDialog(const QString& path, QWidget* parent, QObject* receiver, const char* member); + + static QStringList fileNameFilters(); + static QString defaultFolder(); +}; + +namespace Internal { +class SignalEmitter: public QObject +{ + Q_OBJECT + +public: + void emitFileNameSelected(const QString& fileName) { emit fileNameSelected(fileName); } + +signals: + void fileNameSelected(const QString&); +}; + +} // namespace Internal +} // namespace QmlDesigner + +#endif // XUIFILEDIALOG_H diff --git a/src/plugins/qmldesigner/components/itemlibrary/customdraganddrop.cpp b/src/plugins/qmldesigner/components/itemlibrary/customdraganddrop.cpp new file mode 100644 index 00000000000..a080096665f --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/customdraganddrop.cpp @@ -0,0 +1,301 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "customdraganddrop.h" + +#include <QtCore/QMimeData> +#include <QtCore/QPoint> +#include <QLabel> + +#include <QMouseEvent> +#include <QApplication> +#include <QDebug> +#include <QPainter> + +namespace QmlDesigner { + +namespace QmlDesignerItemLibraryDragAndDrop { + +void CustomDragAndDropIcon::startDrag() +{ + m_size = m_icon.size(); + setPixmap(m_icon); + m_iconAlpha = 1; + m_previewAlpha = 0; + + QPoint pos = QCursor::pos(); + QWidget *widget = QApplication::topLevelAt(pos); + setParent(widget); + raise(); + show(); + + grabMouse(); +} + +void CustomDragAndDropIcon::mouseReleaseEvent(QMouseEvent *event) +{ + QPoint globalPos = event->globalPos(); + releaseMouse(); + move(-1000, -1000); //-1000, -1000 is used to hide because hiding causes propblems with the mouse grabber + QWidget* target = QApplication::widgetAt(globalPos - QPoint(2,2)); //-(2, 2) because: + // otherwise we just get this widget + + if (CustomDragAndDrop::isAccepted()) // if the target widget accepted the enter event, + // then create a drop event + CustomDragAndDrop::drop(target, globalPos); + CustomDragAndDrop::endCustomDrag(); +} + +void CustomDragAndDropIcon::mouseMoveEvent(QMouseEvent *event) +{ + QPoint globalPos = event->globalPos(); + QWidget * widget = QApplication::topLevelAt(globalPos); //grap the "current" top level widget + if (widget) { + setParent(widget); //set the current top level widget as parent + QPoint pos = parentWidget()->mapFromGlobal(globalPos); + if ((pos.y() > 30 && CustomDragAndDrop::isVisible())) //do not allow dragging over the menubar + move(pos); + else + move(-1000, -1000); //no hiding because of mouse grabbing + setPixmap(currentImage()); + resize(m_size); + show(); + update(); + } + else { + move(-1000, -1000); //if no top level widget is found we are out of the main window + } + QWidget* target = QApplication::widgetAt(globalPos - QPoint(2,2)); //-(2, 2) because: + // otherwise we just get this widget + if (target != m_oldTarget) { + if (CustomDragAndDrop::isAccepted()) + CustomDragAndDrop::leave(m_oldTarget, globalPos); // create DragLeave event if drag enter + // event was accepted + bool wasAccepted = CustomDragAndDrop::isAccepted(); + CustomDragAndDrop::enter(target, globalPos); // create and handle the create enter event + releaseMouse(); //to set the cursor we have to disable the mouse grabber + if (CustomDragAndDrop::isAccepted()) { //setting the right cursor and trigger animation + setCursor(Qt::CrossCursor); + if (!wasAccepted) + enter(); //trigger animation if enter event was accepted + } else { + setCursor(Qt::ForbiddenCursor); + if (wasAccepted) + leave(); // trigger animation if we leave a widget that accepted + // the drag enter event + } + grabMouse(); //enable the mouse grabber again - after the curser is set + } else { + if (CustomDragAndDrop::isAccepted()) // create DragMoveEvents if the current widget + // accepted the DragEnter event + CustomDragAndDrop::move(target, globalPos); + } + m_oldTarget = target; +} + + +QPixmap CustomDragAndDropIcon::currentImage() +{ + //blend the two images (icon and preview) according to alpha values + QPixmap pixmap(m_size); + pixmap.fill(Qt::white); + QPainter p(&pixmap); + if (CustomDragAndDrop::isAccepted()) { + p.setOpacity(m_previewAlpha); + p.drawPixmap(0 ,0 , m_size.width(), m_size.height(), m_preview); + p.setOpacity(m_iconAlpha); + p.drawPixmap(0, 0, m_size.width(), m_size.height(), m_icon); + } else { + p.setOpacity(m_iconAlpha); + p.drawPixmap(0, 0, m_size.width(), m_size.height(), m_icon); + p.setOpacity(m_previewAlpha); + p.drawPixmap(0 ,0 , m_size.width(), m_size.height(), m_preview); + } + return pixmap; +} + +void CustomDragAndDropIcon::enter() +{ + connect(&m_timeLine, SIGNAL( frameChanged (int)), this, SLOT(animateDrag(int))); + m_timeLine.setFrameRange(0, 10); + m_timeLine.setDuration(250); + m_timeLine.setLoopCount(1); + m_timeLine.setCurveShape(QTimeLine::EaseInCurve); + m_timeLine.start(); + m_size = m_icon.size(); + m_iconAlpha = 1; + m_previewAlpha = 0; +} + +void CustomDragAndDropIcon::leave() +{ + connect(&m_timeLine, SIGNAL( frameChanged (int)), this, SLOT(animateDrag(int))); + m_timeLine.setFrameRange(0, 10); + m_timeLine.setDuration(250); + m_timeLine.setLoopCount(1); + m_timeLine.setCurveShape(QTimeLine::EaseInCurve); + m_timeLine.start(); + m_size = m_preview.size(); + m_iconAlpha = 0; + m_previewAlpha = 1; +} + +void CustomDragAndDropIcon::animateDrag(int frame) +{ + //interpolation of m_size and alpha values + if (CustomDragAndDrop::isAccepted()) { + //small -> big + m_iconAlpha = 1.0 - qreal(frame) / 10.0; + m_previewAlpha = qreal(frame) / 10.0; + int width = qreal(m_preview.width()) * (qreal(frame) / 10.0) + qreal(m_icon.width()) * (1.0 - qreal(frame) / 10.0); + int height = qreal(m_preview.height()) * (qreal(frame) / 10.0) + qreal(m_icon.height()) * (1 - qreal(frame) / 10.0); + m_size = QSize(width, height); + } else { + //big -> small + m_previewAlpha = 1.0 - qreal(frame) / 10.0; + m_iconAlpha = qreal(frame) / 10.0; + int width = qreal(m_icon.width()) * (qreal(frame) / 10.0) + qreal(m_preview.width()) * (1.0 - qreal(frame) / 10.0); + int height = qreal(m_icon.height()) * (qreal(frame) / 10.0) + qreal(m_preview.height()) * (1 - qreal(frame) / 10.0); + m_size = QSize(width, height); + } + QPoint p = pos(); + setPixmap(currentImage()); + resize(m_size); + move(p); + update(); //redrawing needed +} + + +class CustomDragAndDropGuard { //This guard destroys the singleton in its destructor +public: //This should avoid that a memory leak is reported + ~CustomDragAndDropGuard() { + if (CustomDragAndDrop::m_instance != 0) + delete CustomDragAndDrop::m_instance; + } +}; + + +CustomDragAndDrop* CustomDragAndDrop::m_instance = 0; + +CustomDragAndDrop::CustomDragAndDrop() : m_customDragActive(0), m_mimeData(0), m_accepted(false) +{ + m_widget = new CustomDragAndDropIcon(QApplication::topLevelWidgets().first()); + m_widget->move(-1000, 1000); + m_widget->resize(32, 32); + m_widget->show(); +} + + +CustomDragAndDrop* CustomDragAndDrop::instance() +{ + static CustomDragAndDropGuard guard; //The destructor destroys the singleton. See above + if (m_instance == 0) + m_instance = new CustomDragAndDrop(); + return m_instance; +} + +void CustomDragAndDrop::endCustomDrag() +{ + instance()->m_customDragActive = false; +} + +void CustomDragAndDrop::startCustomDrag(const QPixmap icon, const QPixmap preview, QMimeData * mimeData) +{ + if (instance()->m_customDragActive) { + qWarning("CustomDragAndDrop::startCustomDrag drag is active"); + return ; + } + instance()->m_customDragActive = true; + instance()-> m_accepted = false; + show(); + instance()->m_mimeData = mimeData; + instance()->m_widget->setIcon(icon); + instance()->m_widget->setPreview(preview); + instance()->m_widget->startDrag(); + +} + +bool CustomDragAndDrop::customDragActive() +{ + return instance()->m_customDragActive; +} + +bool CustomDragAndDrop::isAccepted() +{ + return instance()->m_accepted; +} + +void CustomDragAndDrop::enter(QWidget *target, QPoint globalPos) +{ + if (target) { + QPoint pos = target->mapFromGlobal(globalPos); + QDragEnterEvent event(pos, Qt::MoveAction, instance()->m_mimeData ,Qt::RightButton, Qt::NoModifier); + QApplication::sendEvent(target, &event); + instance()-> m_accepted = event.isAccepted(); //if the event is accepted we enter the "accepted" state + } else { + instance()-> m_accepted = false; + } +} + +void CustomDragAndDrop::leave(QWidget *target, QPoint globalPos) +{ + if (target) { + QPoint pos = target->mapFromGlobal(globalPos); + QDragLeaveEvent event; + QApplication::sendEvent(target, &event); + } else { + qWarning() << "CustomDragAndDrop::leave leaving invalid target"; + } +} + +void CustomDragAndDrop::move(QWidget *target, QPoint globalPos) +{ + if (target) { + QPoint pos = target->mapFromGlobal(globalPos); + QDragMoveEvent event(pos, Qt::MoveAction, instance()->m_mimeData ,Qt::RightButton, Qt::NoModifier); + QApplication::sendEvent(target, &event); + } else { + qWarning() << "CustomDragAndDrop::move move in invalid target"; + } +} + +void CustomDragAndDrop::drop(QWidget *target, QPoint globalPos) +{ + if (target) { + QPoint pos = target->mapFromGlobal(globalPos); + QDropEvent event(pos, Qt::MoveAction, instance()->m_mimeData ,Qt::RightButton, Qt::NoModifier); + QApplication::sendEvent(target, &event); + } else { + qWarning() << "CustomDragAndDrop::drop dropping in invalid target"; + } +} + +} //namespace QmlDesignerItemLibraryDragAndDrop +} //namespave QmlDesigner + diff --git a/src/plugins/qmldesigner/components/itemlibrary/customdraganddrop.h b/src/plugins/qmldesigner/components/itemlibrary/customdraganddrop.h new file mode 100644 index 00000000000..df50fe6c32f --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/customdraganddrop.h @@ -0,0 +1,127 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include <QPixmap> +#include <QLabel> +#include <QTimeLine> +#include <QDrag> + +class QMimeData; + +namespace QmlDesigner { + +namespace QmlDesignerItemLibraryDragAndDrop { + +class CustomDragAndDropIcon : public QLabel { //this class shows the icon for the drag drop operation + Q_OBJECT +public: + CustomDragAndDropIcon(QWidget *parent = 0) : QLabel(parent), m_oldTarget(0) + { + } + void setPreview(const QPixmap &pixmap) + { m_preview = pixmap; } + void setIcon(const QPixmap &pixmap) + { m_icon = pixmap; } + + void enter(); + void leave(); + void startDrag(); + +public slots: + void animateDrag(int frame); + +protected: + virtual void mouseMoveEvent(QMouseEvent *); + virtual void mouseReleaseEvent(QMouseEvent *); +private: + QPixmap currentImage(); + + QWidget *m_oldTarget; + QPixmap m_preview; + QPixmap m_icon; + QTimeLine m_timeLine; + QSize m_size; + qreal m_iconAlpha, m_previewAlpha; +}; + + +class CustomDragAndDrop { //this class handles the drag and drop logic +public: + static void startCustomDrag(const QPixmap icon, const QPixmap preview, QMimeData * mimeData); + static bool customDragActive(); + static bool isAccepted(); + static void endCustomDrag(); + static void enter(QWidget *target, QPoint globalPos); + static void leave(QWidget *target, QPoint globalPos); + static void move(QWidget *target, QPoint globalPos); + static void drop(QWidget *target, QPoint globalPos); + static void hide() + { instance()->m_isVisible = false; } + static void show() + { instance()->m_isVisible = true; } + static bool isVisible() + { return instance()->m_isVisible; } + +private: + CustomDragAndDrop(); + static CustomDragAndDrop* instance(); + static CustomDragAndDrop* m_instance; + + CustomDragAndDropIcon *m_widget; + bool m_customDragActive; + QPoint m_pos; + QMimeData *m_mimeData; + bool m_accepted; + bool m_isVisible; + + friend class CustomDragAndDropGuard; +}; + +} //namespace QmlDesignerItemLibraryDragAndDrop + +class CustomItemLibraryDrag : public QDrag { + Q_OBJECT + public: + CustomItemLibraryDrag(QWidget * dragSource) : QDrag(dragSource), m_mimeData(0) + {} + void setPixmap(const QPixmap &pixmap) + { m_pixmap = pixmap; } + void setPreview(const QPixmap &pixmap) + { m_preview = pixmap; } + void setMimeData(QMimeData *mimeData) + { m_mimeData = mimeData; } + void exec() + { QmlDesignerItemLibraryDragAndDrop::CustomDragAndDrop::startCustomDrag(m_pixmap, m_preview, m_mimeData); } + +private: + QPixmap m_pixmap, m_preview; + QMimeData *m_mimeData; + }; + +} //namespave QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/default-icon.png b/src/plugins/qmldesigner/components/itemlibrary/default-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a90779f02a090f925796852ade5478ed33f22cda GIT binary patch literal 341 zcmV-b0jmCqP)<h;3K|Lk000e1NJLTq000&M000&U0ssI2OV;~r0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUy_en%SRCwB?lsgiHAPk0gXS|EOm1AQ~ z<q2#(lf7O*8%xH<(%zf+LXFD|8$nqXQaI7@e<mUDF3a-eX5O}KzVBP>hhgv-WBa~a zBS3y4AQ30Tx~>Az_r0Mq2m<ba{DJ`A5P>BZ#Ih`w2&jj@&-0AeMNuUDuIr>0d=c<x zft;oZucMT5CywJV4BNI<vjDjcAqj9)Mu@D&aa8q$5LnPO&21qk0=H8|BuN6%s;YGD z)Mvp%Pv4CSc{+rT2(_JQn!>XviXRBgj;MQavm>~j(rAI@dH&yYnh*G8I&l_Rmbo4? n3p)|)W3IPWUDqzx?;*ed!{Rpa=`bfP00000NkvXXu0mjfBNC9Q literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrary.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrary.cpp new file mode 100644 index 00000000000..3616992fcd6 --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrary.cpp @@ -0,0 +1,319 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "itemlibrary.h" + +#include "itemlibrarymodel.h" +#include "customdraganddrop.h" + +#include "ui_itemlibrary.h" +#include <metainfo.h> +#include <itemlibraryinfo.h> +#include <formeditorscene.h> + +#include <QtCore/QDebug> +#include <QtGui/QContextMenuEvent> +#include <QtGui/QMenu> +#include <QtGui/QAction> +#include <QtGui/QGraphicsView> +#include <QtGui/QGraphicsScene> +#include <QtGui/QGraphicsItem> +#include <QtGui/QPixmap> +#include <QSortFilterProxyModel> +#include <QLabel> +#include <QMainWindow> +#include <QMenuBar> +#include <QFile> +#include <QDirModel> +#include <QFileIconProvider> + + +namespace QmlDesigner { + +class MyFileIconProvider : public QFileIconProvider +{ +public: + MyFileIconProvider() : QFileIconProvider() + {} + virtual QIcon icon ( const QFileInfo & info ) const + { + QPixmap pixmap(info.absoluteFilePath()); + if (pixmap.isNull()) + return QFileIconProvider::icon(info); + else return pixmap; //pixmap.scaled(128, 128, Qt::KeepAspectRatio); + } +}; + + + +class GrabHelper { + Q_DISABLE_COPY(GrabHelper) +public: + GrabHelper(); + QPixmap grabItem(QGraphicsItem *item); + +private: + QGraphicsScene m_scene; + QGraphicsView m_view; +}; + +GrabHelper::GrabHelper() +{ + m_view.setScene(&m_scene); + m_view.setFrameShape(QFrame::NoFrame); + m_view.setAlignment(Qt::AlignLeft|Qt::AlignTop); + m_view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + m_view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); +} + +QPixmap GrabHelper::grabItem(QGraphicsItem *item) +{ + if (item->scene()) { + qWarning("%s: WARNING: Attempt to grab item that is part of another scene!", Q_FUNC_INFO); + return QPixmap(); + } + // Temporarily add the item, resize the view widget and grab it. + m_scene.addItem(item); + item->setPos(0.0, 0.0); + const QSize size = item->boundingRect().size().toSize(); + QPixmap rc; + if (!size.isEmpty()) { // We have seen horses barf... + m_view.resize(size); + rc = QPixmap::grabWidget(&m_view); + } + m_scene.removeItem(item); + return rc; +} + +// ---------- ItemLibraryPrivate +class ItemLibraryPrivate { +public: + ItemLibraryPrivate(QObject *object); + ~ItemLibraryPrivate(); + + Ui::ItemLibrary m_ui; + Internal::ItemLibraryModel *m_itemLibraryModel; + QDirModel *m_resourcesDirModel; + QSortFilterProxyModel *m_filterProxy; + GrabHelper *m_grabHelper; + QString m_resourcePath; +}; + +ItemLibraryPrivate::ItemLibraryPrivate(QObject *object) : + m_itemLibraryModel(0), + m_grabHelper(0) +{ + m_resourcePath = QDir::currentPath(); + Q_UNUSED(object); +} + +ItemLibraryPrivate::~ItemLibraryPrivate() +{ + delete m_grabHelper; +} + +ItemLibrary::ItemLibrary(QWidget *parent) : + QFrame(parent), + m_d(new ItemLibraryPrivate(this)) +{ + m_d->m_ui.setupUi(this); + m_d->m_itemLibraryModel = new Internal::ItemLibraryModel(this); + m_d->m_resourcesDirModel = new QDirModel(this); + m_d->m_filterProxy = new QSortFilterProxyModel(this); + m_d->m_filterProxy->setSourceModel(m_d->m_itemLibraryModel); + m_d->m_ui.ItemLibraryTreeView->setModel(m_d->m_filterProxy); + m_d->m_filterProxy->setDynamicSortFilter(true); + m_d->m_ui.ItemLibraryTreeView->setRealModel(m_d->m_itemLibraryModel); + m_d->m_ui.ItemLibraryTreeView->setIconSize(QSize(64, 64)); + m_d->m_ui.ItemLibraryTreeView->setSortingEnabled(true); + m_d->m_ui.ItemLibraryTreeView->setHeaderHidden(true); + m_d->m_ui.ItemLibraryTreeView->setIndentation(10); + m_d->m_ui.ItemLibraryTreeView->setAnimated(true); + m_d->m_ui.ItemLibraryTreeView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + m_d->m_ui.ItemLibraryTreeView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + m_d->m_ui.ItemLibraryTreeView->setAttribute(Qt::WA_MacShowFocusRect, false); + m_d->m_filterProxy->setFilterCaseSensitivity(Qt::CaseInsensitive); + m_d->m_filterProxy->setFilterRole(Qt::UserRole); + m_d->m_filterProxy->setSortRole(Qt::DisplayRole); + connect(m_d->m_ui.lineEdit, SIGNAL(textChanged(QString)), m_d->m_filterProxy, SLOT(setFilterRegExp(QString))); + connect(m_d->m_ui.lineEdit, SIGNAL(textChanged(QString)), this, SLOT(setNameFilter(QString))); + connect(m_d->m_ui.lineEdit, SIGNAL(textChanged(QString)), this, SLOT(expandAll())); + connect(m_d->m_ui.buttonItems, SIGNAL(toggled (bool)), this, SLOT(itemLibraryButton())); + connect(m_d->m_ui.buttonResources, SIGNAL(toggled (bool)), this, SLOT(resourcesButton())); + connect(m_d->m_ui.ItemLibraryTreeView, SIGNAL(itemActivated(const QString&)), this, SIGNAL(itemActivated(const QString&))); + m_d->m_ui.lineEdit->setDragEnabled(false); + setNameFilter(""); + + MyFileIconProvider *fileIconProvider = new MyFileIconProvider(); + m_d->m_resourcesDirModel->setIconProvider(fileIconProvider); + + setWindowTitle(tr("Library", "Title of library view")); + + { + QFile file(":/qmldesigner/stylesheet.css"); + file.open(QFile::ReadOnly); + QString styleSheet = QLatin1String(file.readAll()); + setStyleSheet(styleSheet); + + QFile fileTool(":/qmldesigner/toolbutton.css"); + fileTool.open(QFile::ReadOnly); + styleSheet = QLatin1String(fileTool.readAll()); + m_d->m_ui.buttonItems->setStyleSheet(styleSheet); + m_d->m_ui.buttonResources->setStyleSheet(styleSheet); + } + + { + QFile file(":/qmldesigner/scrollbar.css"); + file.open(QFile::ReadOnly); + QString styleSheet = QLatin1String(file.readAll()); + m_d->m_ui.ItemLibraryTreeView->setStyleSheet(styleSheet); + } + + m_d->m_ui.buttonItems->setChecked(true); +} + +ItemLibrary::~ItemLibrary() +{ + delete m_d; +} + +void ItemLibrary::setNameFilter(const QString &nameFilter) +{ + QStringList nameFilterList; + nameFilterList.append(nameFilter + "*.gif"); + nameFilterList.append(nameFilter + "*.png"); + nameFilterList.append(nameFilter + "*.jpg"); + nameFilterList.append(nameFilter + "*."); + m_d->m_resourcesDirModel->setFilter(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot); + m_d->m_resourcesDirModel->setNameFilters(nameFilterList); + m_d->m_ui.ItemLibraryTreeView->setRootIndex(m_d->m_resourcesDirModel->index(m_d->m_resourcePath)); +} + +void ItemLibrary::itemLibraryButton() +{ + if (m_d->m_ui.buttonItems->isChecked()) { + m_d->m_filterProxy->setSourceModel(m_d->m_itemLibraryModel); + m_d->m_ui.ItemLibraryTreeView->setModel(m_d->m_filterProxy); + m_d->m_ui.ItemLibraryTreeView->setIconSize(QSize(64, 64)); + m_d->m_ui.buttonResources->setChecked(false); + m_d->m_ui.ItemLibraryTreeView->setRealModel(m_d->m_itemLibraryModel); + expandAll(); + } +} + +void ItemLibrary::resourcesButton() +{ + if (m_d->m_ui.buttonResources->isChecked()) { + m_d->m_ui.ItemLibraryTreeView->setModel(m_d->m_resourcesDirModel); + m_d->m_ui.ItemLibraryTreeView->setIconSize(QSize(32, 32)); + m_d->m_ui.buttonItems->setChecked(false); + m_d->m_ui.ItemLibraryTreeView->setRootIndex(m_d->m_resourcesDirModel->index(m_d->m_resourcePath)); + m_d->m_ui.ItemLibraryTreeView->setColumnHidden(1, true); + m_d->m_ui.ItemLibraryTreeView->setColumnHidden(2, true); + m_d->m_ui.ItemLibraryTreeView->setColumnHidden(3, true); + expandAll(); + } +} + +void ItemLibrary::addItemLibraryInfo(const ItemLibraryInfo &itemLibraryInfo) +{ + m_d->m_itemLibraryModel->addItemLibraryInfo(itemLibraryInfo); +} + +void ItemLibrary::setResourcePath(const QString &resourcePath) +{ + m_d->m_resourcePath = resourcePath; +} + +void ItemLibrary::setMetaInfo(const MetaInfo &metaInfo) +{ + m_d->m_itemLibraryModel->clear(); + + foreach (const QString &type, metaInfo.itemLibraryItems()) { + NodeMetaInfo nodeInfo = metaInfo.nodeMetaInfo(type); + + QList<ItemLibraryInfo> itemLibraryRepresentationList = metaInfo.itemLibraryRepresentations(nodeInfo); + + if (!metaInfo.hasNodeMetaInfo(type)) + qWarning() << "ItemLibrary: type not declared: " << type; + if (!itemLibraryRepresentationList.isEmpty() && metaInfo.hasNodeMetaInfo(type)) { + foreach (ItemLibraryInfo itemLibraryRepresentation, itemLibraryRepresentationList) { + QImage image(64, 64, QImage::Format_RGB32); // = m_d->m_queryView->paintObject(nodeInfo, itemLibraryRepresentation.properties()); TODO + if (!image.isNull()) { + QPainter p(&image); + QPen pen(Qt::gray); + pen.setWidth(2); + p.setPen(pen); + p.drawRect(1, 1, image.width() - 2, image.height() - 2); + } + QIcon icon = itemLibraryRepresentation.icon(); + if (itemLibraryRepresentation.icon().isNull()) + itemLibraryRepresentation.setIcon(QIcon(":/ItemLibrary/images/default-icon.png")); + + if (itemLibraryRepresentation.category().isEmpty()) + itemLibraryRepresentation.setCategory(nodeInfo.category()); + if (!image.isNull()) { + itemLibraryRepresentation.setDragIcon(QPixmap::fromImage(image)); + addItemLibraryInfo(itemLibraryRepresentation); + } + } + } else { + QImage image; // = m_d->m_queryView->paintObject(nodeInfo); TODO we have to render image + QIcon icon = nodeInfo.icon(); + if (icon.isNull()) + icon = QIcon(":/ItemLibrary/images/default-icon.png"); + + ItemLibraryInfo itemLibraryInfo; + itemLibraryInfo.setName(type); + itemLibraryInfo.setTypeName(nodeInfo.typeName()); + itemLibraryInfo.setCategory(nodeInfo.category()); + itemLibraryInfo.setIcon(icon); + itemLibraryInfo.setMajorVersion(nodeInfo.majorVersion()); + itemLibraryInfo.setMinorVersion(nodeInfo.minorVersion()); + itemLibraryInfo.setDragIcon(QPixmap::fromImage(image)); + addItemLibraryInfo(itemLibraryInfo); + } + } + expandAll(); +} + +void ItemLibrary::expandAll() +{ + m_d->m_ui.ItemLibraryTreeView->expandToDepth(1); +} + +void ItemLibrary::contextMenuEvent (QContextMenuEvent *event) +{ + event->accept(); + QMenu menu; + menu.addAction(tr("About plugins...")); + menu.exec(event->globalPos()); +} + +} diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrary.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibrary.h new file mode 100644 index 00000000000..309e120490b --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrary.h @@ -0,0 +1,80 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef ITEMLIBRARY_H +#define ITEMLIBRARY_H + +#include <QtGui/QFrame> + +class QGraphicsItem; +class QPixmap; +class QMimeData; + +namespace QmlDesigner { + +class ItemLibraryPrivate; +class MetaInfo; +class ItemLibraryInfo; + +class ItemLibrary : public QFrame +{ + Q_OBJECT + Q_DISABLE_COPY(ItemLibrary) +public: + ItemLibrary(QWidget *parent = 0); + virtual ~ItemLibrary(); + + void addItemLibraryInfo(const ItemLibraryInfo &ItemLibraryInfo); + void setMetaInfo(const MetaInfo &metaInfo); + + // Helper for creating widget box items. Note that this temporarily + // adds the item to a scene, so, the item must not be associated + // with a scene. + +public Q_SLOTS: + void expandAll(); + void itemLibraryButton(); + void resourcesButton(); + void setNameFilter(const QString &nameFilter); + void setResourcePath(const QString &resourcePath); +signals: + void itemActivated(const QString& itemName); + +protected: + virtual void contextMenuEvent (QContextMenuEvent * event); + +private: + ItemLibraryPrivate *m_d; +}; + +//class ItemLibraryFilter : public QObject + +} + +#endif // ITEMLIBRARY_H diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrary.pri b/src/plugins/qmldesigner/components/itemlibrary/itemlibrary.pri new file mode 100644 index 00000000000..b8212323f11 --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrary.pri @@ -0,0 +1,12 @@ +###################################################################### +# Automatically generated by qmake (2.01a) Fri Mar 6 12:55:36 2009 +###################################################################### + +VPATH += $$PWD +INCLUDEPATH += $$PWD + +# Input +HEADERS += itemlibrary.h itemlibrarymodel.h customdraganddrop.h +FORMS += itemlibrary.ui +SOURCES += itemlibrary.cpp itemlibrarymodel.cpp customdraganddrop.cpp +RESOURCES += itemlibrary.qrc diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrary.qrc b/src/plugins/qmldesigner/components/itemlibrary/itemlibrary.qrc new file mode 100644 index 00000000000..c1d136b81d4 --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrary.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/ItemLibrary/images" > + <file>default-icon.png</file> + </qresource> +</RCC> diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrary.ui b/src/plugins/qmldesigner/components/itemlibrary/itemlibrary.ui new file mode 100644 index 00000000000..029896d37b0 --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrary.ui @@ -0,0 +1,122 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>QmlDesigner::ItemLibrary</class> + <widget class="QFrame" name="QmlDesigner::ItemLibrary"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>497</width> + <height>792</height> + </rect> + </property> + <property name="windowTitle"> + <string>ItemLibrary</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <property name="leftMargin"> + <number>2</number> + </property> + <property name="topMargin"> + <number>6</number> + </property> + <property name="rightMargin"> + <number>2</number> + </property> + <property name="bottomMargin"> + <number>2</number> + </property> + <property name="horizontalSpacing"> + <number>4</number> + </property> + <item row="0" column="0"> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Fixed</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>6</width> + <height>27</height> + </size> + </property> + </spacer> + </item> + <item row="0" column="1"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Filter: </string> + </property> + </widget> + </item> + <item row="0" column="2"> + <widget class="QLineEdit" name="lineEdit"/> + </item> + <item row="0" column="3"> + <widget class="QToolButton" name="buttonItems"> + <property name="minimumSize"> + <size> + <width>80</width> + <height>30</height> + </size> + </property> + <property name="text"> + <string>Items</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="0" column="4"> + <widget class="QToolButton" name="buttonResources"> + <property name="minimumSize"> + <size> + <width>80</width> + <height>30</height> + </size> + </property> + <property name="text"> + <string>Resources</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="checked"> + <bool>false</bool> + </property> + <property name="autoExclusive"> + <bool>false</bool> + </property> + </widget> + </item> + <item row="1" column="0" colspan="5"> + <widget class="Line" name="line"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item row="2" column="0" colspan="5"> + <widget class="QmlDesigner::Internal::ItemLibraryTreeView" name="ItemLibraryTreeView"> + <property name="enabled"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + <layoutdefault spacing="0" margin="0"/> + <customwidgets> + <customwidget> + <class>QmlDesigner::Internal::ItemLibraryTreeView</class> + <extends>QTreeView</extends> + <header>itemlibrarymodel.h</header> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp new file mode 100644 index 00000000000..89e81f8313e --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp @@ -0,0 +1,297 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "itemlibrarymodel.h" +#include "itemlibrary.h" +#include "customdraganddrop.h" + +#include <QtCore/QMimeData> +#include <QtCore/QDebug> + +#include <QtGui/QImage> +#include <QtGui/QPixmap> +#include <QtGui/QDrag> +#include <QSortFilterProxyModel> +#include <QPainter> +#include <QLabel> +#include <itemlibraryinfo.h> +#include <QDirModel> + + +enum { debug = 0 }; +// Store data and a type enumeration along with the QStandardItem +enum ItemType { CategoryItem, WidgetItem }; +enum Roles { TypeRole = Qt::UserRole + 1, + DataRole = Qt::UserRole + 2, + DragPixmapRole = Qt::UserRole + 3}; + + +static inline ItemType itemType(const QStandardItem *item) +{ + return static_cast<ItemType>(item->data(TypeRole).toInt()); +} + +static inline QmlDesigner::ItemLibraryInfo widgetItemData(const QStandardItem *item) +{ + const QVariant data = item->data(DataRole); + if (!data.isValid()) + return QmlDesigner::ItemLibraryInfo(); + return qvariant_cast<QmlDesigner::ItemLibraryInfo>(data); +} + + + +namespace QmlDesigner { +namespace Internal { + +// Cache a drag pixmap on the icon using the DragPixmapRole data field. +static QImage cachedDragImage(const ItemLibraryInfo &ItemLibraryInfo, + QStandardItem *item) +{ + const QVariant cached = item->data(DragPixmapRole); + if (cached.type() != QVariant::Invalid) + return qvariant_cast<QImage>(cached); + // TODO: Grab using factory + const QIcon icon = ItemLibraryInfo.dragIcon(); + if (icon.isNull()) + return QImage(); + const QList<QSize> sizes = icon.availableSizes(); + if (sizes.isEmpty()) + return QImage(); + const QImage image = icon.pixmap(sizes.front()).toImage(); + item->setData(image, DragPixmapRole); + return image; +} + +ItemLibraryModel::ItemLibraryModel(QObject *parent) : + QStandardItemModel(parent) +{ + setSupportedDragActions(Qt::CopyAction); +} + +static inline QStandardItem *categoryToItem(const QString &g) +{ + QStandardItem *rc = new QStandardItem(g); + rc->setFlags(Qt::ItemIsEnabled); + rc->setData(QVariant(CategoryItem), TypeRole); + rc->setData(g, Qt::UserRole); + return rc; +} + + +static QStandardItem *customWidgetDataToItem(const ItemLibraryInfo &ItemLibraryInfo) +{ + QStandardItem *item = new QStandardItem(ItemLibraryInfo.name()); + const QIcon icon = ItemLibraryInfo.icon(); + if (!icon.isNull() && !icon.availableSizes().empty()) { + item->setIcon(icon); + if (icon.availableSizes().count() == 1) { + item->setSizeHint(icon.availableSizes().first() + QSize(1, 1)); + } + } + + item->setFlags(Qt::ItemIsEnabled|Qt::ItemIsDragEnabled|Qt::ItemIsSelectable); + item->setData(qVariantFromValue(ItemLibraryInfo), DataRole); + item->setData(QVariant(WidgetItem), TypeRole); + item->setData(ItemLibraryInfo.name(), Qt::UserRole); + return item; +} + + +void ItemLibraryModel::addItemLibraryInfo(const ItemLibraryInfo &itemLibraryInfo) +{ + QStandardItem *categoryItem = findCategoryItem(itemLibraryInfo.category()); + if (!categoryItem) { + categoryItem = categoryToItem(itemLibraryInfo.category()); + appendRow(categoryItem); + } + categoryItem->appendRow(customWidgetDataToItem(itemLibraryInfo)); + QString filterList = categoryItem->data(Qt::UserRole).toString(); + filterList += itemLibraryInfo.name(); + categoryItem->setData(filterList, Qt::UserRole); +} + +QStandardItem *ItemLibraryModel::findCategoryItem(const QString &category) +{ + const QStandardItem *root = invisibleRootItem(); + const int rowCount = root->rowCount(); + for (int i = 0 ; i < rowCount; i++) { + QStandardItem *categoryItem = root->child(i, 0); + if (categoryItem->text() == category) + return categoryItem; + } + return 0; +} + +Qt::DropActions ItemLibraryModel::supportedDragActions() const +{ + return Qt::CopyAction; +} + +Qt::DropActions ItemLibraryModel::supportedDropActions() const +{ + return Qt::IgnoreAction; +} + +QStringList ItemLibraryModel::mimeTypes () const +{ + if (debug) + qDebug() << Q_FUNC_INFO; + return QStringList(QLatin1String("text/xml")); +} + +QByteArray ItemLibraryInfoToByteArray(const ItemLibraryInfo &ItemLibraryInfo) +{ + QByteArray byteArray; + QDataStream stream(&byteArray, QIODevice::WriteOnly); + + stream << ItemLibraryInfo; + + return byteArray; +} + +QMimeData *ItemLibraryModel::mimeData(const QModelIndexList &indexes) const +{ + if (debug) + qDebug() << Q_FUNC_INFO << indexes.size(); + if (indexes.size() != 1) + return 0; + QStandardItem *item = itemFromIndex (indexes.front()); + if (!item || itemType(item) != WidgetItem) + return 0; + QMimeData *mimeData = new QMimeData; + + ItemLibraryInfo ItemLibraryInfo(widgetItemData(item)); + + const QImage image = cachedDragImage(ItemLibraryInfo, item); + if (!image.isNull()) + mimeData->setImageData(image); + + + mimeData->setData("application/vnd.bauhaus.itemlibraryinfo", ItemLibraryInfoToByteArray(ItemLibraryInfo)); + mimeData->removeFormat("text/plain"); + + return mimeData; +} + +ItemLibraryTreeView::ItemLibraryTreeView(QWidget *parent) : + QTreeView(parent) +{ + setDragEnabled(true); + setDragDropMode(QAbstractItemView::DragOnly); + connect(this, SIGNAL(clicked(const QModelIndex &)), this, SLOT(activateItem(const QModelIndex &))); +} + +// We need to implement startDrag ourselves since we cannot +// otherwise influence drag pixmap and hotspot in the standard +// implementation. +void ItemLibraryTreeView::startDrag(Qt::DropActions /* supportedActions */) +{ + if (debug) + qDebug() << Q_FUNC_INFO; + QMimeData *mimeData = model()->mimeData(selectedIndexes()); + if (!mimeData) + return; + + if (qobject_cast<QSortFilterProxyModel*>(model())) { + QModelIndex index = qobject_cast<QSortFilterProxyModel*>(model())->mapToSource(selectedIndexes().front()); + + QStandardItem *item = m_model->itemFromIndex(index); + + if (!item) + return; + + CustomItemLibraryDrag *drag = new CustomItemLibraryDrag(this); + const QImage image = qvariant_cast<QImage>(mimeData->imageData()); + drag->setPixmap(item->icon().pixmap(32, 32)); + drag->setPreview(QPixmap::fromImage(image)); + drag->setMimeData(mimeData); + + drag->exec(); + } else { + QDirModel *dirModel = qobject_cast<QDirModel*>(model()); + Q_ASSERT(dirModel); + QFileInfo fileInfo = dirModel->fileInfo(selectedIndexes().front()); + QPixmap pixmap(fileInfo.absoluteFilePath()); + if (!pixmap.isNull()) { + CustomItemLibraryDrag *drag = new CustomItemLibraryDrag(this); + drag->setPreview(pixmap); + drag->setPixmap(QIcon(pixmap).pixmap(128, 128)); + QMimeData *mimeData = new QMimeData; + mimeData->setData("application/vnd.bauhaus.libraryresource", fileInfo.absoluteFilePath().toLatin1()); + drag->setMimeData(mimeData); + drag->exec(); + } + } +} + +static ItemLibraryInfo ItemLibraryInfoFromData(const QByteArray &data) +{ + QDataStream stream(data); + + ItemLibraryInfo itemLibraryInfo; + stream >> itemLibraryInfo; + + return itemLibraryInfo; +} + +void ItemLibraryTreeView::activateItem( const QModelIndex & /*index*/) +{ + QMimeData *mimeData = model()->mimeData(selectedIndexes()); + if (!mimeData) + return; + + QString name; + if (qobject_cast<QSortFilterProxyModel*>(model())) { + QModelIndex index = qobject_cast<QSortFilterProxyModel*>(model())->mapToSource(selectedIndexes().front()); + + QStandardItem *item = m_model->itemFromIndex(index); + + if (!item) + return; + + ItemLibraryInfo itemLibraryInfo = ItemLibraryInfoFromData(mimeData->data("application/vnd.bauhaus.itemlibraryinfo")); + QString type = itemLibraryInfo.name(); + + name = "item^" + type; + emit itemActivated(name); + } else { + QDirModel *dirModel = qobject_cast<QDirModel*>(model()); + Q_ASSERT(dirModel); + QFileInfo fileInfo = dirModel->fileInfo(selectedIndexes().front()); + QPixmap pixmap(fileInfo.absoluteFilePath()); + if (!pixmap.isNull()) { + name = "image^" + fileInfo.absoluteFilePath(); + emit itemActivated(name); + } + } +} + +} // namespace Internal +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h new file mode 100644 index 00000000000..f969fdf8859 --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h @@ -0,0 +1,89 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef ITEMLIBRARYMODEL_H +#define ITEMLIBRARYMODEL_H + +#include <QtGui/QStandardItemModel> +#include <QtGui/QTreeView> +#include <QDrag> +#include <QDebug> +#include <QTimeLine> + +class QLabel; + +namespace QmlDesigner { + +class ItemLibraryInfo; + +namespace Internal { + +// QStandardItemModel-based model for the widget box. +class ItemLibraryModel : public QStandardItemModel +{ + Q_OBJECT +public: + explicit ItemLibraryModel(QObject *parent = 0); + void addItemLibraryInfo(const ItemLibraryInfo &ItemLibraryInfo); + + + QStandardItem *findCategoryItem(const QString &category); + + virtual Qt::DropActions supportedDragActions() const; + virtual Qt::DropActions supportedDropActions() const; + + virtual QStringList mimeTypes() const; + virtual QMimeData *mimeData(const QModelIndexList &indexes) const; +}; + +// ItemLibraryTreeView with Drag implementation +class ItemLibraryTreeView : public QTreeView { + Q_OBJECT + public: + explicit ItemLibraryTreeView(QWidget *parent = 0); + + virtual void startDrag(Qt::DropActions supportedActions); + + void setRealModel(QStandardItemModel *model) { m_model = model; } + +signals: + void itemActivated(const QString &itemName); +private slots: + void activateItem( const QModelIndex & index); + +private: + QPixmap m_smallImage, m_bigImage; + QStandardItemModel *m_model; +}; + + + +} // namespace Internal +} // namespace QmlDesigner +#endif // ITEMLIBRARYMODEL_H diff --git a/src/plugins/qmldesigner/components/logger/logger.cpp b/src/plugins/qmldesigner/components/logger/logger.cpp new file mode 100644 index 00000000000..93c91944d23 --- /dev/null +++ b/src/plugins/qmldesigner/components/logger/logger.cpp @@ -0,0 +1,171 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "logger.h" + +#include <qapplication.h> +#include <stdio.h> +#include <stdlib.h> + +#include <QTime> +#include <QFile> + +#ifdef Q_OS_WIN +#include <windows.h> +#endif + +QLogger* QLogger::m_instance; + +void QLogger::loggerMessageOutput(QtMsgType type, const char *msg) +{ + switch (type) { + case QtDebugMsg: + if (!QLogger::instance()->m_silent) + QLogger::instance()->output(msg); + break; + case QtWarningMsg: + fprintf(stderr, "Warning: %s\n", msg); + break; + case QtCriticalMsg: + fprintf(stderr, "Critical: %s\n", msg); + break; + case QtFatalMsg: + fprintf(stderr, "Fatal: %s\n", msg); + abort(); + } +} + +void QLogger::setLevel(int level) +{ + instance()->m_level = level; +} + +void QLogger::setSilent(bool silent) +{ + instance()->m_silent = silent; +} + +void QLogger::setModul(const QString &modul) +{ + instance()->m_modul = modul; +} + +void QLogger::setFilename(const QString &filename) +{ + instance()->m_fileName = filename; + if (instance()->m_file) { + instance()->m_file->close(); + delete instance()->m_file; + } + instance()->m_file = new QFile(filename); + instance()->m_fileName = filename; + instance()->m_file->open(QIODevice::WriteOnly); +} + +void QLogger::setEnabled(bool enabled) +{ + instance()->m_enabled = enabled; +} + +void QLogger::setFlush(int msec) +{ + instance()->m_flushTime = msec; +} + +void QLogger::flush() +{ + instance()->m_lastFlush = QTime::currentTime().elapsed(); + if (instance()->m_file) { + foreach (QString s, instance()->m_buffer) { + s += QLatin1String("\n"); + instance()->m_file->write (s.toAscii()); + } + instance()->m_file->flush(); + } else { + foreach ( QString s, instance()->m_buffer) { + s += QLatin1String("\n"); +#ifdef Q_OS_WIN + OutputDebugStringW((TCHAR*)s.utf16()); +#else + fprintf(stderr, "Debug: %s\n", s.toAscii().constData()); +#endif + } + } + instance()->m_buffer.clear(); +} + +QLogger::QLogger() : m_level(0), m_modul(), m_fileName(), m_file(0), m_enabled(true), m_silent(false), m_flushTime(1000) +{ + qInstallMsgHandler(loggerMessageOutput); + m_timer = new QTime(); + m_timer->start(); + m_lastFlush = m_timer->elapsed(); +} + +QLogger::~QLogger() +{ + flush(); + if (m_file) { + m_file->close(); + delete m_file; + } + delete m_timer; +} + +QLogger* QLogger::instance() +{ + if (!m_instance) { + m_instance = new QLogger(); + } + return m_instance; +} + +void QLogger::output(const char *msg) +{ + + QString s = QString::fromAscii(msg); + if (m_enabled && (m_modul.isEmpty() || s.contains(m_modul, Qt::CaseInsensitive))) { + int level = 0; + if (s.contains("LEVEL=1")) { + s = s.remove("LEVEL=1"); + level = 1; + } else if (s.contains("LEVEL=2")) { + s = s.remove("LEVEL=2"); + level = 2; + } else if (s.contains("LEVEL=3")) { + s = s.remove("LEVEL=3"); + level = 3; + } + if (m_level >= level) + m_buffer.append(s); + } + int time = m_timer->elapsed(); + if (time > m_lastFlush + m_flushTime) + flush(); +} diff --git a/src/plugins/qmldesigner/components/logger/logger.h b/src/plugins/qmldesigner/components/logger/logger.h new file mode 100644 index 00000000000..73f13cdb72a --- /dev/null +++ b/src/plugins/qmldesigner/components/logger/logger.h @@ -0,0 +1,81 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef QLOGGER_H +#define QLOGGER_H + +#include <QString> +#include <QStringList> + + +#define qLogger(str) qDebug() << QString("GLOBAL %1, %2").arg(__FUNC__).arg(#str); +#define qLogger(module, str) qDebug() << QString("%1, %2 %3").arg(__FUNC__).arg(#str).arg(#module); + +class QFile; +class QTime; + +class QLogger { +public: + static void enable() { + setEnabled(true); + } + static void disable() { + setEnabled(false); + } + static void setLevel(int level); + static void setModul(const QString &modul); + static void setFilename(const QString &filename); + static void setEnabled(bool enabled); + static void setFlush(int msec); + static void flush(); + static void setSilent(bool silent); + ~QLogger(); + +private: + QLogger(); + + static QLogger* instance(); + static QLogger* m_instance; + void output(const char *msg); + static void loggerMessageOutput(QtMsgType type, const char *msg); + + int m_level; + QString m_modul; + QString m_fileName; + QFile* m_file; + bool m_enabled; + bool m_silent; + int m_flushTime; + int m_lastFlush; + QStringList m_buffer; + QTime *m_timer; + +}; + +#endif // QLOGGER_H diff --git a/src/plugins/qmldesigner/components/logger/logger.pri b/src/plugins/qmldesigner/components/logger/logger.pri new file mode 100644 index 00000000000..7e9c5355a31 --- /dev/null +++ b/src/plugins/qmldesigner/components/logger/logger.pri @@ -0,0 +1,6 @@ +VPATH += $$PWD +INCLUDEPATH += $$PWD +HEADERS += logger.h +SOURCES += logger.cpp + + diff --git a/src/plugins/qmldesigner/components/navigator/navigator.pri b/src/plugins/qmldesigner/components/navigator/navigator.pri new file mode 100644 index 00000000000..e58d573acf7 --- /dev/null +++ b/src/plugins/qmldesigner/components/navigator/navigator.pri @@ -0,0 +1,9 @@ +VPATH += $$PWD +INCLUDEPATH += $$PWD +SOURCES += navigatorview.cpp \ + navigatortreemodel.cpp \ + navigatorwidget.cpp + +HEADERS += navigatorview.h \ + navigatortreemodel.h \ + navigatorwidget.h diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp new file mode 100644 index 00000000000..ef9b8601439 --- /dev/null +++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp @@ -0,0 +1,393 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "navigatortreemodel.h" + +#include <nodeabstractproperty.h> +#include <nodelistproperty.h> +#include <abstractview.h> +#include <qmlitemnode.h> +#include <invalididexception.h> + +#include <QMimeData> + +namespace QmlDesigner { + +NavigatorTreeModel::NavigatorTreeModel(QObject *parent) + : QStandardItemModel(parent) +{ + invisibleRootItem()->setFlags(Qt::NoItemFlags); + + setHorizontalHeaderItem(0, new QStandardItem(tr("Name"))); + setHorizontalHeaderItem(1, new QStandardItem(tr("Type"))); + setHorizontalHeaderItem(2, new QStandardItem(tr("Show in Editor"))); + + setSupportedDragActions(Qt::MoveAction); + + connect(this, SIGNAL(itemChanged(QStandardItem*)), + this, SLOT(handleChangedItem(QStandardItem*))); +} + +NavigatorTreeModel::~NavigatorTreeModel() +{ +} + +Qt::DropActions NavigatorTreeModel::supportedDropActions() const +{ + return Qt::MoveAction; +} + +QStringList NavigatorTreeModel::mimeTypes() const +{ + QStringList types; + types << "application/vnd.modelnode.list"; + return types; +} + +QMimeData *NavigatorTreeModel::mimeData(const QModelIndexList &indexList) const +{ + QMimeData *mimeData = new QMimeData(); + QByteArray encodedData; + + QSet<QModelIndex> rowAlreadyUsedSet; + + QDataStream stream(&encodedData, QIODevice::WriteOnly); + + foreach (const QModelIndex &index, indexList) { + if (!index.isValid()) + continue; + QModelIndex idIndex = index.sibling(index.row(), 0); + if (rowAlreadyUsedSet.contains(idIndex)) + continue; + + rowAlreadyUsedSet.insert(idIndex); + stream << idIndex.data(Qt::UserRole).toUInt(); + } + + mimeData->setData("application/vnd.modelnode.list", encodedData); + + return mimeData; +} + +static bool isAnchestorInList(const ModelNode &node, const QList<ModelNode> &nodeList) +{ + foreach (const ModelNode &nodeInList, nodeList) { + if (nodeInList.isAncestorOf(node)) + return true; + } + + return false; +} + +bool NavigatorTreeModel::dropMimeData(const QMimeData *data, + Qt::DropAction action, + int row, + int column, + const QModelIndex &parentIndex) +{ + if (action == Qt::IgnoreAction) + return true; + if (action != Qt::MoveAction) + return false; + if (!data->hasFormat("application/vnd.modelnode.list")) + return false; + if (column > 1) + return false; + if (parentIndex.model() != this) + return false; + + int beginRow = 0; + + QModelIndex parentIdIndex = parentIndex; + parentIdIndex = parentIdIndex.sibling(parentIdIndex.row(), 0); + + Q_ASSERT(parentIdIndex.isValid()); + + if (row > -1) + beginRow = row; + else if (parentIdIndex.isValid()) + beginRow = rowCount(parentIdIndex); + else + beginRow = rowCount(QModelIndex()); + + + QByteArray encodedData = data->data("application/vnd.modelnode.list"); + QDataStream stream(&encodedData, QIODevice::ReadOnly); + + uint nodeHash = parentIdIndex.data(Qt::UserRole).toUInt(); + QmlItemNode parentItemNode(nodeForHash(nodeHash)); + + QList<ModelNode> nodeList; + + while (!stream.atEnd()) { + uint nodeHash; + stream >> nodeHash; + ModelNode node(nodeForHash(nodeHash)); + if (!node.isValid() || node.isAncestorOf(parentItemNode)) + return false; + nodeList.append(node); + } + + RewriterTransaction transaction = m_view->beginRewriterTransaction(); + foreach (const ModelNode &node, nodeList) { + if (!isAnchestorInList(node, nodeList)) { + if (node.parentProperty().parentModelNode() != parentItemNode) { + QmlItemNode itemNode(node); + itemNode.setParent(parentItemNode); + } + + if (node.parentProperty().isNodeListProperty()) { + int index = node.parentProperty().toNodeListProperty().toModelNodeList().indexOf(node); + node.parentProperty().toNodeListProperty().slide(index, beginRow); + } + } + } + + return false; // don't let the view do drag&drop on its own +} + +NavigatorTreeModel::ItemRow NavigatorTreeModel::createItemRow(const ModelNode &node) +{ + Q_ASSERT(node.isValid()); + + uint hash = qHash(node); + + QStandardItem *idItem = new QStandardItem; + idItem->setDragEnabled(true); + idItem->setDropEnabled(node.metaInfo().isContainer()); + idItem->setEditable(true); + idItem->setData(hash, Qt::UserRole); + + QStandardItem *typeItem = new QStandardItem; + typeItem->setDragEnabled(true); + idItem->setDropEnabled(node.metaInfo().isContainer()); + typeItem->setEditable(false); + typeItem->setData(hash, Qt::UserRole); + + QStandardItem *visibilityItem = new QStandardItem; + visibilityItem->setDropEnabled(node.metaInfo().isContainer()); + visibilityItem->setCheckable(true); + visibilityItem->setData(hash, Qt::UserRole); + + return ItemRow(idItem, typeItem, visibilityItem); +} + +void NavigatorTreeModel::updateItemRow(const ModelNode &node, ItemRow items) +{ + items.idItem->setText(node.id()); + items.typeItem->setText(node.simplifiedTypeName()); + items.visibilityItem->setCheckState(node.auxiliaryData("invisible").toBool() ? Qt::Unchecked : Qt::Checked); +} + +/** + Update the information shown for a node / property + */ +void NavigatorTreeModel::updateItemRow(const ModelNode &node) +{ + if (!containsNode(node)) + return; + updateItemRow(node, itemRowForNode(node)); +} + +/** + Updates the sibling position of the item, depending on the position in the model. + */ +void NavigatorTreeModel::updateItemRowOrder(const ModelNode &node) +{ + if (!containsNode(node)) + return; + ItemRow itemRow = itemRowForNode(node); + int currentRow = itemRow.idItem->row(); + int newRow = currentRow; + if (node.parentProperty().parentModelNode().isValid()) + newRow = modelNodeChildren(node.parentProperty().parentModelNode()).indexOf(node); + Q_ASSERT(newRow >= 0); + + if (currentRow != newRow) { + QStandardItem *parentIdItem = itemRow.idItem->parent(); + QList<QStandardItem*> items = parentIdItem->takeRow(currentRow); + parentIdItem->insertRow(newRow, items); + } +} + +void NavigatorTreeModel::handleChangedItem(QStandardItem *item) +{ + uint nodeHash = item->data(Qt::UserRole).toUInt(); + Q_ASSERT(nodeHash && containsNodeHash(nodeHash)); + ModelNode node = nodeForHash(nodeHash); + + ItemRow itemRow = itemRowForNode(node); + if (item == itemRow.idItem) { + try { + if (ModelNode::isValidId(item->text())) + node.setId(item->text()); + else + item->setText(node.id()); + } catch (InvalidIdException &) { + item->setText(node.id()); + } + } else if (item == itemRow.visibilityItem) { + bool invisible = (item->checkState() == Qt::Unchecked); + + node.setAuxiliaryData("invisible", invisible); + } +} + +ModelNode NavigatorTreeModel::nodeForHash(uint hash) const +{ + ModelNode node = m_nodeHash.value(hash); + Q_ASSERT(node.isValid()); + return node; +} + +bool NavigatorTreeModel::containsNodeHash(uint hash) const +{ + return m_nodeHash.contains(hash); +} + +bool NavigatorTreeModel::containsNode(const ModelNode &node) const +{ + return m_nodeItemHash.contains(node); +} + +NavigatorTreeModel::ItemRow NavigatorTreeModel::itemRowForNode(const ModelNode &node) +{ + Q_ASSERT(node.isValid()); + return m_nodeItemHash.value(node); +} + +void NavigatorTreeModel::setView(AbstractView *view) +{ + m_view = view; + addSubTree(view->rootModelNode()); +} + +void NavigatorTreeModel::clearView() +{ + m_view.clear(); + m_nodeHash.clear(); + m_nodeItemHash.clear(); + clear(); +} + +QModelIndex NavigatorTreeModel::indexForNode(const ModelNode &node) const +{ + Q_ASSERT(node.isValid()); + if (!containsNode(node)) + return QModelIndex(); + ItemRow row = m_nodeItemHash.value(node); + return row.idItem->index(); +} + +ModelNode NavigatorTreeModel::nodeForIndex(const QModelIndex &index) const +{ + Q_ASSERT(index.isValid()); + uint hash = index.data(Qt::UserRole).toUInt(); + Q_ASSERT(hash); + Q_ASSERT(containsNodeHash(hash)); + return nodeForHash(hash); +} + +bool NavigatorTreeModel::isInTree(const ModelNode &node) const +{ + return m_nodeHash.keys().contains(qHash(node)); +} + +/** + Adds node & all children to the visible tree hierarchy (if node should be visible at all). + + It always adds the node to the _end_ of the list of items. + */ +void NavigatorTreeModel::addSubTree(const ModelNode &node) +{ + Q_ASSERT(node.isValid()); + Q_ASSERT(!containsNodeHash(qHash(node))); + + // only add items that are in the modelNodeChildren list (that means, visible in the editor) + if (!node.isRootNode() + && !modelNodeChildren(node.parentProperty().parentModelNode()).contains(node)) { + return; + } + + ItemRow newRow = createItemRow(node); + m_nodeHash.insert(qHash(node), node); + m_nodeItemHash.insert(node, newRow); + + updateItemRow(node, newRow); + + foreach (const ModelNode &childNode, modelNodeChildren(node)) + addSubTree(childNode); + + // We assume that the node is always added to the _end_ of the property list. + if (node.hasParentProperty()) { + ItemRow parentRow = itemRowForNode(node.parentProperty().parentModelNode()); + if (parentRow.idItem) + parentRow.idItem->appendRow(newRow.toList()); + } else { + appendRow(newRow.toList()); + } +} + +/** + Deletes visual representation for the node (subtree). + */ +void NavigatorTreeModel::removeSubTree(const ModelNode &node) +{ + Q_ASSERT(node.isValid()); + + if (!containsNode(node)) + return; + + QList<QStandardItem*> rowList; + ItemRow itemRow = itemRowForNode(node); + if (itemRow.idItem->parent()) { + rowList = itemRow.idItem->parent()->takeRow(itemRow.idItem->row()); + } + + foreach (const ModelNode &node, modelNodeChildren(node)) { + removeSubTree(node); + } + + qDeleteAll(rowList); + + m_nodeHash.remove(qHash(node)); + m_nodeItemHash.remove(node); +} + +QList<ModelNode> NavigatorTreeModel::modelNodeChildren(const ModelNode &parentNode) +{ + QList<ModelNode> children; + if (QmlItemNode(parentNode).isValid()) + foreach (const QmlItemNode &childNode, QmlItemNode(parentNode).children()) + children << childNode.modelNode(); + return children; +} + +} + diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.h b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.h new file mode 100644 index 00000000000..d4212ffd997 --- /dev/null +++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.h @@ -0,0 +1,115 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef NAVIGATORTREEMODEL_H +#define NAVIGATORTREEMODEL_H + +#include <modelnode.h> +#include <nodemetainfo.h> + +#include <QStandardItem> +#include <QStandardItemModel> + +namespace QmlDesigner { + +class Model; +class AbstractView; +class ModelNode; + +class NavigatorTreeModel : public QStandardItemModel +{ + Q_OBJECT + + struct ItemRow { + ItemRow() + : idItem(0), typeItem(0), visibilityItem(0) {} + ItemRow(QStandardItem *id, QStandardItem *type, QStandardItem *visibility) + : idItem(id), typeItem(type), visibilityItem(visibility) {} + + QList<QStandardItem*> toList() const { + return QList<QStandardItem*>() << idItem << typeItem << visibilityItem; + } + + QStandardItem *idItem; + QStandardItem *typeItem; + QStandardItem *visibilityItem; + }; + +public: + NavigatorTreeModel(QObject *parent = 0); + ~NavigatorTreeModel(); + + Qt::DropActions supportedDropActions() const; + + QStringList mimeTypes() const; + QMimeData *mimeData(const QModelIndexList &indexes) const; + bool dropMimeData(const QMimeData *data, + Qt::DropAction action, + int row, + int column, + const QModelIndex &parent); + + void setView(AbstractView *view); + void clearView(); + + QModelIndex indexForNode(const ModelNode &node) const; + ModelNode nodeForIndex(const QModelIndex &index) const; + + bool isInTree(const ModelNode &node) const; + + void addSubTree(const ModelNode &node); + void removeSubTree(const ModelNode &node); + void updateItemRow(const ModelNode &node); + void updateItemRowOrder(const ModelNode &node); + +private slots: + void handleChangedItem(QStandardItem *item); + +private: + bool containsNodeHash(uint hash) const; + ModelNode nodeForHash(uint hash) const; + + bool containsNode(const ModelNode &node) const; + ItemRow itemRowForNode(const ModelNode &node); + + ItemRow createItemRow(const ModelNode &node); + void updateItemRow(const ModelNode &node, ItemRow row); + + QList<ModelNode> modelNodeChildren(const ModelNode &parentNode); + +private: + + QHash<ModelNode, ItemRow> m_nodeItemHash; + QHash<uint, ModelNode> m_nodeHash; + QWeakPointer<AbstractView> m_view; +}; + +} // namespace QmlDesigner + +#endif // NAVIGATORTREEMODEL_H diff --git a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp new file mode 100644 index 00000000000..9e9c4661b05 --- /dev/null +++ b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp @@ -0,0 +1,227 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "navigatorview.h" +#include "navigatortreemodel.h" +#include "navigatorwidget.h" + +#include <nodeproperty.h> + + +namespace QmlDesigner { + +NavigatorView::NavigatorView(QObject* parent) : + QmlModelView(parent), + m_blockSelectionChangedSignal(false), + m_widget(new NavigatorWidget), + m_treeModel(new NavigatorTreeModel(this)) +{ + m_widget->setTreeModel(m_treeModel.data()); + + connect(treeWidget()->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(changeSelection(QItemSelection,QItemSelection))); + treeWidget()->setIndentation(treeWidget()->indentation() * 0.5); +} + +NavigatorView::~NavigatorView() +{ + if (m_widget && !m_widget->parent()) + delete m_widget.data(); +} + +QWidget *NavigatorView::widget() +{ + return m_widget.data(); +} + +void NavigatorView::modelAttached(Model *model) +{ + QmlModelView::modelAttached(model); + + m_treeModel->setView(this); + + treeWidget()->expandAll(); +} + +void NavigatorView::modelAboutToBeDetached(Model *model) +{ + m_treeModel->clearView(); + QmlModelView::modelAboutToBeDetached(model); +} + +void NavigatorView::nodeAboutToBeRemoved(const ModelNode &removedNode) +{ + if (m_treeModel->isInTree(removedNode)) + m_treeModel->removeSubTree(removedNode); + QmlModelView::nodeAboutToBeRemoved(removedNode); +} + +void NavigatorView::nodeReparented(const ModelNode &node, const NodeAbstractProperty & /*newPropertyParent*/, const NodeAbstractProperty & /*oldPropertyParent*/, AbstractView::PropertyChangeFlags /*propertyChange*/) +{ + bool blocked = blockSelectionChangedSignal(true); + + if (m_treeModel->isInTree(node)) + m_treeModel->removeSubTree(node); + if (node.isInHierarchy()) + m_treeModel->addSubTree(node); + + // make sure selection is in sync again + updateItemSelection(); + + blockSelectionChangedSignal(blocked); +} + +void NavigatorView::nodeIdChanged(const ModelNode& node, const QString& newId, const QString& oldId) +{ + QmlModelView::nodeIdChanged(node, newId, oldId); + if (m_treeModel->isInTree(node)) + m_treeModel->updateItemRow(node); +} + +void NavigatorView::propertiesAboutToBeRemoved(const QList<AbstractProperty>& propertyList) +{ + QmlModelView::propertiesAboutToBeRemoved(propertyList); + foreach (const AbstractProperty &property, propertyList) { + if (property.isNodeProperty()) { + NodeProperty nodeProperty(property.toNodeProperty()); + m_treeModel->removeSubTree(nodeProperty.modelNode()); + } else if (property.isNodeListProperty()) { + NodeListProperty nodeListProperty(property.toNodeListProperty()); + foreach (const ModelNode &node, nodeListProperty.toModelNodeList()) { + m_treeModel->removeSubTree(node); + } + } + } +} + +void NavigatorView::nodeTypeChanged(const ModelNode &node,const QString &type, int majorVersion, int minorVersion) +{ + QmlModelView::nodeTypeChanged(node, type, majorVersion, minorVersion); + if (m_treeModel->isInTree(node)) + m_treeModel->updateItemRow(node); +} + +void NavigatorView::auxiliaryDataChanged(const ModelNode &node, const QString &name, const QVariant &data) +{ + QmlModelView::auxiliaryDataChanged(node, name, data); + if (m_treeModel->isInTree(node)) + m_treeModel->updateItemRow(node); +} + +void NavigatorView::nodeSlidedToIndex(const NodeListProperty &listProperty, int newIndex, int oldIndex) +{ + QmlModelView::nodeSlidedToIndex(listProperty, newIndex, oldIndex); + + int nodeIndex = newIndex; + if (oldIndex < newIndex) + --nodeIndex; + ModelNode node = listProperty.toModelNodeList().at(nodeIndex); + if (m_treeModel->isInTree(node)) + m_treeModel->updateItemRowOrder(node); +} + +void NavigatorView::stateChanged(const QmlModelState &/*newQmlModelState*/, const QmlModelState &/*oldQmlModelState*/) +{ +} + +void NavigatorView::changeSelection(const QItemSelection & /*newSelection*/, const QItemSelection &/*deselected*/) +{ + if (m_blockSelectionChangedSignal) + return; + QSet<ModelNode> nodeSet; + foreach (const QModelIndex &index, treeWidget()->selectionModel()->selectedIndexes()) { + nodeSet.insert(m_treeModel->nodeForIndex(index)); + } + + bool blocked = blockSelectionChangedSignal(true); + setSelectedModelNodes(nodeSet.toList()); + blockSelectionChangedSignal(blocked); +} + +void NavigatorView::selectedNodesChanged(const QList<ModelNode> &selectedNodeList, const QList<ModelNode> &lastSelectedNodeList) +{ + QmlModelView::selectedNodesChanged(selectedNodeList, lastSelectedNodeList); + + updateItemSelection(); +} + +void NavigatorView::updateItemSelection() +{ + QItemSelection itemSelection; + foreach (const ModelNode &node, selectedModelNodes()) { + const QModelIndex index = m_treeModel->indexForNode(node); + if (index.isValid()) { + const QModelIndex beginIndex(m_treeModel->index(index.row(), 0, index.parent())); + const QModelIndex endIndex(m_treeModel->index(index.row(), m_treeModel->columnCount(index.parent()) - 1, index.parent())); + if (beginIndex.isValid() && endIndex.isValid()) + itemSelection.select(beginIndex, endIndex); + } + } + + bool blocked = blockSelectionChangedSignal(true); + treeWidget()->selectionModel()->select(itemSelection, QItemSelectionModel::ClearAndSelect); + blockSelectionChangedSignal(blocked); + + // make sure selected nodes a visible + foreach(const QModelIndex &selectedIndex, itemSelection.indexes()) { + if (selectedIndex.column() == 0) + expandRecursively(selectedIndex); + } +} + +QTreeView *NavigatorView::treeWidget() +{ + if (m_widget) + return m_widget->treeView(); + return 0; +} + +NavigatorTreeModel *NavigatorView::treeModel() +{ + return m_treeModel.data(); +} + +// along the lines of QObject::blockSignals +bool NavigatorView::blockSelectionChangedSignal(bool block) +{ + bool oldValue = m_blockSelectionChangedSignal; + m_blockSelectionChangedSignal = block; + return oldValue; +} + +void NavigatorView::expandRecursively(const QModelIndex &index) +{ + QModelIndex currentIndex = index; + while (currentIndex.isValid()) { + if (!treeWidget()->isExpanded(currentIndex)) + treeWidget()->expand(currentIndex); + currentIndex = currentIndex.parent(); + } +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/navigator/navigatorview.h b/src/plugins/qmldesigner/components/navigator/navigatorview.h new file mode 100644 index 00000000000..c8545f802f0 --- /dev/null +++ b/src/plugins/qmldesigner/components/navigator/navigatorview.h @@ -0,0 +1,98 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef NAVIGATORVIEW_H +#define NAVIGATORVIEW_H + +#include <qmlmodelview.h> + +#include <QWeakPointer> + +class QTreeView; +class QStandardItem; +class QItemSelection; +class QModelIndex; + +namespace QmlDesigner { + +class NavigatorWidget; +class NavigatorTreeModel; + +class NavigatorView : public QmlModelView +{ + Q_OBJECT + +public: + NavigatorView(QObject* parent = 0); + ~NavigatorView(); + + QWidget *widget(); + + // AbstractView + void modelAttached(Model *model); + void modelAboutToBeDetached(Model *model); + + void nodeAboutToBeRemoved(const ModelNode &removedNode); + void nodeReparented(const ModelNode &node, const ModelNode &oldParent, const ModelNode &newParent); + + void nodeSlidedToIndex(const ModelNode &node, int newIndex, int oldIndex); + void nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent, const NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags propertyChange); + void nodeTypeChanged(const ModelNode &node,const QString &type, int majorVersion, int minorVersion); + void nodeIdChanged(const ModelNode& node, const QString& newId, const QString& oldId); + void propertiesAboutToBeRemoved(const QList<AbstractProperty>& propertyList); + + void selectedNodesChanged(const QList<ModelNode> &selectedNodeList, + const QList<ModelNode> &lastSelectedNodeList); + void auxiliaryDataChanged(const ModelNode &node, const QString &name, const QVariant &data); + void nodeSlidedToIndex(const NodeListProperty &listProperty, int newIndex, int oldIndex); + + void stateChanged(const QmlModelState &newQmlModelState, const QmlModelState &oldQmlModelState); + +private slots: +// void handleChangedItem(QStandardItem * item); + void changeSelection(const QItemSelection &selected, const QItemSelection &deselected); + void updateItemSelection(); + +protected: //functions + QTreeView *treeWidget(); + NavigatorTreeModel *treeModel(); + bool blockSelectionChangedSignal(bool block); + void expandRecursively(const QModelIndex &index); + +private: + bool m_blockSelectionChangedSignal; + + QWeakPointer<NavigatorWidget> m_widget; + QWeakPointer<NavigatorTreeModel> m_treeModel; + + friend class TestNavigator; +}; + +} +#endif // NAVIGATORVIEW_H diff --git a/src/plugins/qmldesigner/components/navigator/navigatorwidget.cpp b/src/plugins/qmldesigner/components/navigator/navigatorwidget.cpp new file mode 100644 index 00000000000..36a08386491 --- /dev/null +++ b/src/plugins/qmldesigner/components/navigator/navigatorwidget.cpp @@ -0,0 +1,88 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include <QtGui/QBoxLayout> +#include <QtGui/QTreeView> +#include <QtGui/QHeaderView> +#include <QFile> +#include <model.h> + +#include "navigatorwidget.h" + + +namespace QmlDesigner { + +NavigatorWidget::NavigatorWidget(QWidget* parent) : + QFrame(parent), + m_treeView(new QTreeView) +{ + m_treeView->setDragEnabled(true); + m_treeView->setAcceptDrops(true); + m_treeView->setSelectionMode(QAbstractItemView::ExtendedSelection); + m_treeView->header()->setStretchLastSection(false); + + QVBoxLayout *layout = new QVBoxLayout; + layout->setSpacing(0); + layout->setMargin(0); + layout->addWidget(m_treeView); + + setLayout(layout); + + setWindowTitle(tr("Navigator", "Title of navigator view")); + + { + QFile file(":/qmldesigner/stylesheet.css"); + file.open(QFile::ReadOnly); + setStyleSheet(file.readAll()); + } + + { + QFile file(":/qmldesigner/scrollbar.css"); + file.open(QFile::ReadOnly); + m_treeView->setStyleSheet(file.readAll()); + } +} + +NavigatorWidget::~NavigatorWidget() +{ + +} + +void NavigatorWidget::setTreeModel(QAbstractItemModel* model) +{ + m_treeView->setModel(model); + m_treeView->header()->setResizeMode(0, QHeaderView::Stretch); +} + +QTreeView *NavigatorWidget::treeView() +{ + return m_treeView; +} + +} diff --git a/src/plugins/qmldesigner/components/navigator/navigatorwidget.h b/src/plugins/qmldesigner/components/navigator/navigatorwidget.h new file mode 100644 index 00000000000..04207027b40 --- /dev/null +++ b/src/plugins/qmldesigner/components/navigator/navigatorwidget.h @@ -0,0 +1,56 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef NAVIGATORWIDGET_H +#define NAVIGATORWIDGET_H + +#include <QFrame> +#include <QAbstractItemModel> +#include <QTreeView> + +namespace QmlDesigner { + +class Model; + +class NavigatorWidget: public QFrame +{ +public: + NavigatorWidget(QWidget* parent = 0); + virtual ~NavigatorWidget(); + + void setTreeModel(QAbstractItemModel *model); + QTreeView *treeView(); + +private: + QTreeView *m_treeView; +}; + +} + +#endif // NAVIGATORWIDGET_H diff --git a/src/plugins/qmldesigner/components/pluginmanager/iplugin.cpp b/src/plugins/qmldesigner/components/pluginmanager/iplugin.cpp new file mode 100644 index 00000000000..70250f09840 --- /dev/null +++ b/src/plugins/qmldesigner/components/pluginmanager/iplugin.cpp @@ -0,0 +1,36 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "iplugin.h" + +// Put docs in here +namespace QmlDesigner { + + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/pluginmanager/iplugin.h b/src/plugins/qmldesigner/components/pluginmanager/iplugin.h new file mode 100644 index 00000000000..4c2dd453150 --- /dev/null +++ b/src/plugins/qmldesigner/components/pluginmanager/iplugin.h @@ -0,0 +1,56 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef IPLUGIN_H +#define IPLUGIN_H + +#include <QtCore/QObject> + +#define QMLDESIGNER_PLUGIN_INTERFACE "com.Nokia.QmlDesigner.IPlugin.v10" + +namespace QmlDesigner { + +// QmlDesigner "base" plugin with initialization method in which +// it can retriece the core via its static accessor and do magic. + +class IPlugin +{ +public: + virtual ~IPlugin() {} + + virtual bool isInitialized() const = 0; + + virtual bool initialize(QString *errorMessage) = 0; +}; + +} // namespace QmlDesigner + +Q_DECLARE_INTERFACE(QmlDesigner::IPlugin, QMLDESIGNER_PLUGIN_INTERFACE) + +#endif // IPLUGIN_H diff --git a/src/plugins/qmldesigner/components/pluginmanager/pluginmanager.cpp b/src/plugins/qmldesigner/components/pluginmanager/pluginmanager.cpp new file mode 100644 index 00000000000..1bff0ac3de0 --- /dev/null +++ b/src/plugins/qmldesigner/components/pluginmanager/pluginmanager.cpp @@ -0,0 +1,174 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "pluginmanager.h" +#include "iplugin.h" +#include "pluginpath.h" +#include <metainfo.h> + +#include <QtCore/QCoreApplication> +#include <QtCore/QObject> +#include <QtCore/QSharedData> +#include <QtCore/QDir> +#include <QtCore/QStringList> +#include <QtCore/QDebug> +#include <QWeakPointer> +#include <QtCore/QPluginLoader> +#include <QtCore/QFileInfo> +#include <QtCore/QLibraryInfo> + +#include <QtGui/QStandardItemModel> +#include <QtGui/QStandardItem> +#include <QtGui/QTreeView> +#include <QtGui/QVBoxLayout> +#include <QtGui/QDialog> +#include <QtGui/QLabel> +#include <QtGui/QDialogButtonBox> + +enum { debug = 0 }; + +namespace QmlDesigner { + +// Initialize and create instance of a plugin from scratch, +// that is, make sure the library is loaded and has an instance +// of the IPlugin type. Once something fails, mark it as failed +// ignore it from then on. +//static IPlugin *instance(PluginData &p) +//{ +// // Go stale once something fails +// if (p.failed) +// return 0; +// // Pull up the plugin, retrieve IPlugin instance. +// if (!p.instanceGuard) { +// p.instance = 0; +// QPluginLoader loader(p.path); +// if (!(loader.isLoaded() || loader.load())) { +// p.failed = true; +// p.errorMessage = loader.errorString(); +// return 0; +// } +// QObject *object = loader.instance(); +// if (!object) { +// p.failed = true; +// p.errorMessage = QCoreApplication::translate("PluginManager", "Failed to create instance."); +// return 0; +// } +// IPlugin *iplugin = qobject_cast<IPlugin *>(object); +// if (!iplugin) { +// p.failed = true; +// p.errorMessage = QCoreApplication::translate("PluginManager", "Not a QmlDesigner plugin."); +// delete object; +// return 0; +// } +// p.instanceGuard = object; +// p.instance = iplugin; +// } +// // Ensure it is initialized +// if (!p.instance->isInitialized()) { +// if (!p.instance->initialize(&p.errorMessage)) { +// p.failed = true; +// delete p.instance; +// p.instance = 0; +// return 0; +// } +// } +// return p.instance; +//} + + +// ---- PluginManager[Private] +class PluginManagerPrivate { +public: + typedef QList<PluginPath> PluginPathList; + PluginPathList m_paths; +}; + +PluginManager::PluginManager() : + m_d(new PluginManagerPrivate) +{ +} + +PluginManager::~PluginManager() +{ + delete m_d; +} + +PluginManager::IPluginList PluginManager::instances() +{ + if (debug) + qDebug() << '>' << Q_FUNC_INFO << QLibraryInfo::buildKey(); + IPluginList rc; + const PluginManagerPrivate::PluginPathList::iterator end = m_d->m_paths.end(); + for (PluginManagerPrivate::PluginPathList::iterator it = m_d->m_paths.begin(); it != end; ++it) + it->getInstances(&rc); + if (debug) + qDebug() << '<' << Q_FUNC_INFO << rc.size(); + return rc; +} + +void PluginManager::setPluginPaths(const QStringList &paths) +{ + foreach (const QString &path, paths) { + const QDir dir(path); + if (!dir.exists()) + continue; + m_d->m_paths.push_back(PluginPath(dir)); + } + + // also register path in widgetpluginmanager + MetaInfo::setPluginPaths(paths); +} + +QAbstractItemModel *PluginManager::createModel(QObject *parent) +{ + QStandardItemModel *model = new QStandardItemModel(parent); + const PluginManagerPrivate::PluginPathList::iterator end = m_d->m_paths.end(); + for (PluginManagerPrivate::PluginPathList::iterator it = m_d->m_paths.begin(); it != end; ++it) + model->appendRow(it->createModelItem()); + return model; +} + +QDialog *PluginManager::createAboutPluginDialog(QWidget *parent) +{ + QDialog *rc = new QDialog(parent); + rc->setWindowFlags(rc->windowFlags() & ~Qt::WindowContextHelpButtonHint & Qt::Sheet); + rc->setWindowTitle(QCoreApplication::tr("About plugins")); + QTreeView *treeView = new QTreeView; + treeView->setModel(createModel(treeView)); + treeView->expandAll(); + + QVBoxLayout *layout = new QVBoxLayout(rc); + layout->addWidget(treeView); + QDialogButtonBox *bb = new QDialogButtonBox(QDialogButtonBox::Close); + layout->addWidget(bb); + QObject::connect(bb, SIGNAL(rejected()), rc, SLOT(reject())); + return rc; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/pluginmanager/pluginmanager.h b/src/plugins/qmldesigner/components/pluginmanager/pluginmanager.h new file mode 100644 index 00000000000..62afc2c5a58 --- /dev/null +++ b/src/plugins/qmldesigner/components/pluginmanager/pluginmanager.h @@ -0,0 +1,75 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef PLUGINMANAGER_H +#define PLUGINMANAGER_H + + +#include <QtCore/QObject> +#include <QtCore/QList> + +class QString; +class QAbstractItemModel; +class QDialog; + +namespace QmlDesigner { + +class IPlugin; + +class PluginManagerPrivate; + +// PluginManager: Loads the plugin libraries on demand "as lazy as +// possible", that is, directories are scanned and +// instances are created only when instances() is called. + +class PluginManager +{ + Q_DISABLE_COPY(PluginManager) +public: + typedef QList<IPlugin *> IPluginList; + + PluginManager(); + ~PluginManager(); + + void setPluginPaths(const QStringList &paths); + + IPluginList instances(); + + QDialog *createAboutPluginDialog(QWidget *parent); + +private: + // Convenience to create a model for an "About Plugins" + // dialog. Forces plugin initialization. + QAbstractItemModel *createModel(QObject *parent = 0); + + PluginManagerPrivate *m_d; +}; + +} // namespace QmlDesigner +#endif // PLUGINMANAGER_H diff --git a/src/plugins/qmldesigner/components/pluginmanager/pluginmanager.pri b/src/plugins/qmldesigner/components/pluginmanager/pluginmanager.pri new file mode 100644 index 00000000000..3160b88feb7 --- /dev/null +++ b/src/plugins/qmldesigner/components/pluginmanager/pluginmanager.pri @@ -0,0 +1,10 @@ +VPATH += $$PWD +INCLUDEPATH += $$PWD + +SOURCES += pluginmanager.cpp \ + pluginpath.cpp \ + iplugin.cpp + +HEADERS += pluginmanager.h \ + pluginpath.h \ + iplugin.h diff --git a/src/plugins/qmldesigner/components/pluginmanager/pluginpath.cpp b/src/plugins/qmldesigner/components/pluginmanager/pluginpath.cpp new file mode 100644 index 00000000000..ee069bfae58 --- /dev/null +++ b/src/plugins/qmldesigner/components/pluginmanager/pluginpath.cpp @@ -0,0 +1,193 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "pluginpath.h" +#include <iplugin.h> +#include <QtCore/QLibrary> +#include <QWeakPointer> +#include <QtCore/QPluginLoader> +#include <QtCore/QFileInfo> +#include <QtCore/QLibraryInfo> +#include <QtCore/QCoreApplication> +#include <QtCore/QObject> +#include <QtCore/QSharedData> +#include <QDebug> + +enum { debug = 1 }; + +namespace QmlDesigner { + +// Initialize and create instance of a plugin from scratch, +// that is, make sure the library is loaded and has an instance +// of the IPlugin type. Once something fails, mark it as failed +// ignore it from then on. +static IPlugin *instance(PluginData &p) +{ + // Go stale once something fails + if (p.failed) + return 0; + // Pull up the plugin, retrieve IPlugin instance. + if (!p.instanceGuard) { + p.instance = 0; + QPluginLoader loader(p.path); + if (!(loader.isLoaded() || loader.load())) { + p.failed = true; + p.errorMessage = loader.errorString(); + return 0; + } + QObject *object = loader.instance(); + if (!object) { + p.failed = true; + p.errorMessage = QCoreApplication::translate("WidgetPluginManager", "Failed to create instance."); + return 0; + } + IPlugin *iplugin = qobject_cast<IPlugin *>(object); + if (!iplugin) { + p.failed = true; + p.errorMessage = QCoreApplication::translate("WidgetPluginManager", "Not a QmlDesigner plugin."); + delete object; + return 0; + } + p.instanceGuard = object; + p.instance = iplugin; + } + // Ensure it is initialized + /*if (!p.instance->isInitialized()) { + if (!p.instance->initialize(&p.errorMessage)) { + p.failed = true; + delete p.instance; + p.instance = 0; + return 0; + } + }*/ + return p.instance; +} + +PluginData::PluginData(const QString &p) : + path(p), + failed(false), + instance(0) +{ +} + + +PluginPath::PluginPath(const QDir &path) : + m_path(path), + m_loaded(false) +{ +} + +// Determine a unique list of library files in that directory +QStringList PluginPath::libraryFilePaths(const QDir &dir) +{ + const QFileInfoList infoList = dir.entryInfoList(QDir::Files|QDir::Readable|QDir::NoDotAndDotDot); + if (infoList.empty()) + return QStringList(); + // Load symbolic links but make sure all file names are unique as not + // to fall for something like 'libplugin.so.1 -> libplugin.so' + QStringList result; + const QFileInfoList::const_iterator icend = infoList.constEnd(); + for (QFileInfoList::const_iterator it = infoList.constBegin(); it != icend; ++it) { + QString fileName; + if (it->isSymLink()) { + const QFileInfo linkTarget = QFileInfo(it->symLinkTarget()); + if (linkTarget.exists() && linkTarget.isFile()) + fileName = linkTarget.absoluteFilePath(); + } else { + fileName = it->absoluteFilePath(); + } + if (!fileName.isEmpty() && QLibrary::isLibrary(fileName) && !result.contains(fileName)) + result += fileName; + } + + qDebug() << "Library file paths: " << result; + + return result; +} + +void PluginPath::clear() +{ + m_loaded = false; + m_plugins.clear(); +} + +void PluginPath::ensureLoaded() +{ + if (!m_loaded) { + const QStringList libraryFiles = libraryFilePaths(m_path); + if (debug) + qDebug() << "Checking " << libraryFiles.size() << " plugins " << m_path.absolutePath(); + foreach (const QString &libFile, libraryFiles) + m_plugins.push_back(PluginData(libFile)); + m_loaded = true; + } +} + +void PluginPath::getInstances(PluginManager::IPluginList *list) +{ + ensureLoaded(); + // Compile list of instances + if (m_plugins.empty()) + return; + const PluginDataList::iterator end = m_plugins.end(); + for (PluginDataList::iterator it = m_plugins.begin(); it != end; ++it) + if (IPlugin *i = instance(*it)) + list->push_back(i); +} + +QStandardItem *PluginPath::createModelItem() +{ + ensureLoaded(); + // Create a list of plugin lib files with classes. + // If there are failed ones, create a separate "Failed" + // category at the end + QStandardItem *pathItem = new QStandardItem(m_path.absolutePath()); + QStandardItem *failedCategory = 0; + const PluginDataList::iterator end = m_plugins.end(); + for (PluginDataList::iterator it = m_plugins.begin(); it != end; ++it) { + QStandardItem *pluginItem = new QStandardItem(QFileInfo(it->path).fileName()); + if (instance(*it)) { + pluginItem->appendRow(new QStandardItem(QString::fromLatin1(it->instanceGuard->metaObject()->className()))); + pathItem->appendRow(pluginItem); + } else { + pluginItem->setToolTip(it->errorMessage); + if (!failedCategory) { + const QString failed = QCoreApplication::translate("PluginManager", "Failed Plugins"); + failedCategory = new QStandardItem(failed); + } + failedCategory->appendRow(pluginItem); + } + } + if (failedCategory) + pathItem->appendRow(failedCategory); + return pathItem; +} + +} // namespace QmlDesigner + diff --git a/src/plugins/qmldesigner/components/pluginmanager/pluginpath.h b/src/plugins/qmldesigner/components/pluginmanager/pluginpath.h new file mode 100644 index 00000000000..226183d7d8a --- /dev/null +++ b/src/plugins/qmldesigner/components/pluginmanager/pluginpath.h @@ -0,0 +1,98 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef PLUGINPATH_H +#define PLUGINPATH_H + +#include "pluginmanager.h" + +#include <QtCore/QObject> +#include <QWeakPointer> +#include <QtCore/QList> +#include <QtCore/QDir> +#include <QtGui/QStandardItem> + +QT_BEGIN_NAMESPACE +class QString; +class QAbstractItemModel; +QT_END_NAMESPACE + +namespace QmlDesigner { + +class IPlugin; + +// Dumb plugin data structure. Note that whereas QObjects can +// casted to an interface, QPointer does not work with the +// interface class, so, we need a separate QPointer as a guard +// to detect the deletion of a plugin instance which can happen +// in theory. +struct PluginData { + PluginData(const QString &p = QString()); + + QString path; + bool failed; + QString errorMessage; + QWeakPointer<QObject> instanceGuard; + IPlugin *instance; +}; + + +// PluginPath: Manages a plugin directory. It does nothing +// on construction following the "as lazy as possible" principle. +// In the "loaded" stage, it scans the directories and creates +// a list of PluginData for the libraries found. +// getInstances() will return the fully initialized list of +// IPlugins. + +class PluginPath { +public: + explicit PluginPath(const QDir &path); + + + void getInstances(PluginManager::IPluginList *list); + + QDir path() const { return m_path; } + + // Convenience to populate a "About Plugin" dialog with + // plugins from that path. Forces initialization. + QStandardItem *createModelItem(); + +private: + typedef QList<PluginData> PluginDataList; + + static QStringList libraryFilePaths(const QDir &dir); + void clear(); + void ensureLoaded(); + + QDir m_path; + bool m_loaded; + PluginDataList m_plugins; +}; +} // namespace QmlDesigner +#endif // PLUGINPATH_H diff --git a/src/plugins/qmldesigner/components/propertyeditor/allpropertiesbox.cpp b/src/plugins/qmldesigner/components/propertyeditor/allpropertiesbox.cpp new file mode 100644 index 00000000000..effc8539f83 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/allpropertiesbox.cpp @@ -0,0 +1,85 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include <QtGui/QBoxLayout> + +#include <model.h> + +#include "application.h" +#include "allpropertiesbox.h" +#include "propertyeditor.h" + +namespace QmlDesigner { + +class AllPropertiesBoxPrivate +{ + friend class AllPropertiesBox; + +private: + PropertyEditor* propertiesEditor; +}; + +AllPropertiesBox::AllPropertiesBox(QWidget* parent): + QStackedWidget(parent), + m_d(new AllPropertiesBoxPrivate) +{ + m_d->propertiesEditor = new PropertyEditor(this); + m_d->propertiesEditor->setQmlDir(Application::sharedDirPath() + QLatin1String("/propertyeditor")); + + m_newLookIndex = addWidget(m_d->propertiesEditor->createPropertiesPage()); + + setCurrentIndex(m_newLookIndex); + + setWindowTitle(tr("Properties", "Title of properties view.")); +} + +AllPropertiesBox::~AllPropertiesBox() +{ + delete m_d; +} + +void AllPropertiesBox::setModel(Model* model) +{ + if (model) + model->attachView(m_d->propertiesEditor); + else if (m_d->propertiesEditor->model()) + m_d->propertiesEditor->model()->detachView(m_d->propertiesEditor); +} + +void AllPropertiesBox::showNewLook() +{ + setCurrentIndex(m_newLookIndex); +} + +void AllPropertiesBox::showTraditional() +{ + setCurrentIndex(m_traditionalIndex); +} + +} diff --git a/src/plugins/qmldesigner/components/propertyeditor/allpropertiesbox.h b/src/plugins/qmldesigner/components/propertyeditor/allpropertiesbox.h new file mode 100644 index 00000000000..ab88c3b6c96 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/allpropertiesbox.h @@ -0,0 +1,64 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef allpropertiesbox_h +#define allpropertiesbox_h + +#include <QtGui/QStackedWidget> + +namespace QmlDesigner { + +class FormWindow; +class Model; +class ModelState; + +class AllPropertiesBox: public QStackedWidget +{ + Q_OBJECT + +public: + AllPropertiesBox(QWidget* parent = 0); + virtual ~AllPropertiesBox(); + + void showNewLook(); + void showTraditional(); + + void setModel(Model *model); + +private: + +private: + class AllPropertiesBoxPrivate* m_d; + int m_newLookIndex; + int m_traditionalIndex; +}; + +} + +#endif // allpropertiesbox_h diff --git a/src/plugins/qmldesigner/components/propertyeditor/basiclayouts.cpp b/src/plugins/qmldesigner/components/propertyeditor/basiclayouts.cpp new file mode 100644 index 00000000000..fe082751edf --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/basiclayouts.cpp @@ -0,0 +1,83 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#include "basiclayouts.h" + + +QT_BEGIN_NAMESPACE +QML_DEFINE_TYPE(Qt,4,6,QBoxLayout,QBoxLayoutObject); + +QBoxLayoutObject::QBoxLayoutObject(QObject *parent) +: QLayoutObject(parent), _widgets(this), _layout(0) +{ +} + +QBoxLayoutObject::QBoxLayoutObject(QBoxLayout *layout, QObject *parent) +: QLayoutObject(parent), _widgets(this), _layout(layout) +{ +} + +QLayout *QBoxLayoutObject::layout() const +{ + return _layout; +} + +void QBoxLayoutObject::addWidget(QWidget *wid) +{ + _layout->addWidget(wid); +} + +void QBoxLayoutObject::clearWidget() +{ +} + +QML_DEFINE_TYPE(Qt,4,6,QHBoxLayout,QHBoxLayoutObject); +QHBoxLayoutObject::QHBoxLayoutObject(QObject *parent) +: QBoxLayoutObject(new QHBoxLayout, parent) +{ +} + + +QML_DEFINE_TYPE(Qt,4,6,QVBoxLayout,QVBoxLayoutObject); +QVBoxLayoutObject::QVBoxLayoutObject(QObject *parent) +: QBoxLayoutObject(new QVBoxLayout, parent) +{ +} +QT_END_NAMESPACE diff --git a/src/plugins/qmldesigner/components/propertyeditor/basiclayouts.h b/src/plugins/qmldesigner/components/propertyeditor/basiclayouts.h new file mode 100644 index 00000000000..f57dc2612fc --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/basiclayouts.h @@ -0,0 +1,197 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#ifndef BASICLAYOUTS_H +#define BASICLAYOUTS_H + +#include <qlayoutobject.h> +#include <QHBoxLayout> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QBoxLayoutObject : public QLayoutObject +{ + Q_OBJECT + + Q_PROPERTY(QmlList<QWidget *> *children READ children) + + Q_PROPERTY(int topMargin READ topMargin WRITE setTopMargin) + Q_PROPERTY(int bottomMargin READ bottomMargin WRITE setBottomMargin) + Q_PROPERTY(int leftMargin READ leftMargin WRITE setLeftMargin) + Q_PROPERTY(int rightMargin READ rightMargin WRITE setRightMargin) + Q_PROPERTY(int spacing READ spacing WRITE setSpacing) + + Q_CLASSINFO("DefaultProperty", "children") +public: + QBoxLayoutObject(QObject *parent=0); + QBoxLayoutObject(QBoxLayout *, QObject *parent=0); + virtual QLayout *layout() const; + + QmlList<QWidget *> *children() { return &_widgets; } + +private: + friend class WidgetList; + void addWidget(QWidget *); + void clearWidget(); + + //XXX need to provide real implementations once QBoxLayoutObject is finished + class WidgetList : public QmlList<QWidget *> + { + public: + WidgetList(QBoxLayoutObject *o) + : obj(o) {} + + virtual void append(QWidget *w) { obj->addWidget(w); } + virtual void clear() { obj->clearWidget(); } + virtual int count() const { return 0; } + virtual void removeAt(int) {} + virtual QWidget *at(int) const { return 0; } + virtual void insert(int, QWidget *) {} + + private: + QBoxLayoutObject *obj; + }; + + void getMargins() + { + _layout->getContentsMargins(&mLeft, &mTop, &mRight, &mBottom); + } + + void setMargins() + { + _layout->setContentsMargins(mLeft, mTop, mRight, mBottom); + } + + int topMargin() + { + getMargins(); + return mTop; + } + + void setTopMargin(int margin) + { + getMargins(); + mTop = margin; + setMargins(); + } + + int bottomMargin() + { + getMargins(); + return mBottom; + } + + void setBottomMargin(int margin) + { + getMargins(); + mBottom = margin; + setMargins(); + } + + int leftMargin() + { + getMargins(); + return mLeft; + } + + void setLeftMargin(int margin) + { + getMargins(); + mLeft = margin; + setMargins(); + } + + int rightMargin() + { + getMargins(); + return mRight; + } + + void setRightMargin(int margin) + { + getMargins(); + mRight = margin; + setMargins(); + } + + int spacing() const + { + return _layout->spacing(); + } + + void setSpacing(int spacing) + { + _layout->setSpacing(spacing); + } + + WidgetList _widgets; + QBoxLayout *_layout; + + int mTop, mLeft, mBottom, mRight; + +}; +QML_DECLARE_TYPE(QBoxLayoutObject); + +class QHBoxLayoutObject : public QBoxLayoutObject +{ +Q_OBJECT +public: + QHBoxLayoutObject(QObject *parent=0); +}; +QML_DECLARE_TYPE(QHBoxLayoutObject); + +class QVBoxLayoutObject : public QBoxLayoutObject +{ +Q_OBJECT +public: + QVBoxLayoutObject(QObject *parent=0); +}; +QML_DECLARE_TYPE(QVBoxLayoutObject); + +#endif // BASICLAYOUTS_H + + +QT_END_NAMESPACE + +QT_END_HEADER diff --git a/src/plugins/qmldesigner/components/propertyeditor/basicwidgets.cpp b/src/plugins/qmldesigner/components/propertyeditor/basicwidgets.cpp new file mode 100644 index 00000000000..7bcac7b0b09 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/basicwidgets.cpp @@ -0,0 +1,1142 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#include "basicwidgets.h" +#include <qlayoutobject.h> +#include <private/graphicswidgets_p.h> +#include <qmlcontext.h> +#include <qmlengine.h> +#include <qmlcomponent.h> +#include <QtCore/QDebug> +#include <QFile> +#include <QPixmap> +#include <QTimeLine> +#include "colorwidget.h" +#include "filewidget.h" +#include <QFileInfo> +#include <QMenu> +#include <QAction> +#include <QListView> +#include <QDebug> +#include <QApplication> +#include <QGraphicsOpacityEffect> + + +QT_BEGIN_NAMESPACE + + +class QWidgetDeclarativeUI; + +class ResizeEventFilter : public QObject +{ + Q_OBJECT +public: + ResizeEventFilter(QObject *parent) : QObject(parent), m_target(0) { } + + void setTarget(QObject *target) { m_target = target; } + void setDuiTarget(QWidgetDeclarativeUI* dui_target) {m_dui_target = dui_target;} + +protected: + bool eventFilter(QObject *obj, QEvent *event); +private: + QObject *m_target; + QWidgetDeclarativeUI* m_dui_target; +}; + +class QWidgetDeclarativeUI : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QmlList<QObject *> *children READ children) + Q_PROPERTY(QLayoutObject *layout READ layout WRITE setLayout) + Q_PROPERTY(QFont font READ font CONSTANT) + + Q_PROPERTY(QPoint pos READ pos) + Q_PROPERTY(QSize size READ size) + + Q_PROPERTY(int x READ x WRITE setX NOTIFY xChanged) + Q_PROPERTY(int y READ y WRITE setY NOTIFY yChanged) + Q_PROPERTY(bool focus READ hasFocus NOTIFY focusChanged) + Q_PROPERTY(int width READ width WRITE setWidth NOTIFY widthChanged) + Q_PROPERTY(int height READ height WRITE setHeight NOTIFY heightChanged) + Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity NOTIFY opacityChanged) + + Q_PROPERTY(QUrl styleSheetFile READ styleSheetFile WRITE setStyleSheetFile NOTIFY styleSheetFileChanged) + + Q_PROPERTY(QColor windowColor READ windowColor WRITE setWindowColor) + Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor) + Q_PROPERTY(QColor baseColor READ baseColor WRITE setBaseColor) + Q_PROPERTY(QColor textColor READ textColor WRITE setTextColor) + Q_PROPERTY(QColor windowTextColor READ windowTextColor WRITE setWindowTextColor) + Q_PROPERTY(QColor buttonTextColor READ buttonTextColor WRITE setButtonTextColor) + + Q_PROPERTY(int fixedWidth READ width WRITE setFixedWidth) + Q_PROPERTY(int fixedHeight READ height WRITE setFixedHeight) + + Q_PROPERTY(bool mouseOver READ mouseOver NOTIFY mouseOverChanged) + + Q_CLASSINFO("DefaultProperty", "children") + +signals: + void xChanged(); + void yChanged(); + void widthChanged(); + void heightChanged(); + void focusChanged(); + void mouseOverChanged(); + void opacityChanged(); + +public: + QWidgetDeclarativeUI(QObject *other) : QObject(other), _children(other), _layout(0), _graphicsOpacityEffect(0) { + + + q = qobject_cast<QWidget*>(other); + ResizeEventFilter *filter(new ResizeEventFilter(q)); + filter->setTarget(q); + filter->setDuiTarget(this); + m_mouseOver = false; + q->installEventFilter(filter); + Q_ASSERT(q); + } + virtual ~QWidgetDeclarativeUI() { + } + + class Children : public QmlConcreteList<QObject *> + { + public: + Children(QObject *widget) : q(qobject_cast<QWidget *>(widget)) {} + virtual void append(QObject *o) + { + insert(-1, o); + } + virtual void clear() + { + for (int i = 0; i < count(); ++i) + at(i)->setParent(0); + QmlConcreteList<QObject *>::clear(); + } + virtual void removeAt(int i) + { + at(i)->setParent(0); + QmlConcreteList<QObject *>::removeAt(i); + } + virtual void insert(int i, QObject *o) + { + QmlConcreteList<QObject *>::insert(i, o); + if (QWidget *w = qobject_cast<QWidget *>(o)) + w->setParent(static_cast<QWidget *>(q)); + else + o->setParent(q); + } + + private: + QWidget *q; + }; + +public: + + void setMouseOver(bool _mouseOver) + { + m_mouseOver = _mouseOver; + } + + void emitResize() + { + emit widthChanged(); + emit heightChanged(); + } + + void emitMove() + { + emit xChanged(); + emit yChanged(); + } + + void emitFocusChanged() + { + emit focusChanged(); + } + + void emitMouseOverChanged() + { + emit mouseOverChanged(); + } + + QmlList<QObject *> *children() { return &_children; } + + QLayoutObject *layout() const { return _layout; } + void setLayout(QLayoutObject *lo) + { + _layout = lo; + static_cast<QWidget *>(parent())->setLayout(lo->layout()); + } + + QFont font() const + { + return _font; + } + + void setFont(const QFont &font) + { + if (font != _font) { + _font = font; + static_cast<QWidget *>(parent())->setFont(_font); + } + } + + int x() const { + return q->x(); + } + + bool hasFocus() const { + return q->hasFocus(); + } + + bool mouseOver() const { + return m_mouseOver; + } + + void setX(int x) { + q->move(x, y()); + } + + int y() const { + return q->y(); + } + + qreal opacity() const { + if (_graphicsOpacityEffect) + return _graphicsOpacityEffect->opacity(); + else + return 1; + } + + void setOpacity(qreal newOpacity) { + if (newOpacity != opacity()) { + if (!_graphicsOpacityEffect) { + _graphicsOpacityEffect = new QGraphicsOpacityEffect(this); + q->setGraphicsEffect(_graphicsOpacityEffect); + } + _graphicsOpacityEffect->setOpacity(newOpacity); + emit opacityChanged(); + } + } + + void setY(int y) { + q->move(x(), y); + } + + int width() const { + return q->width(); + } + + void setWidth(int width) { + q->resize(width, height()); + } + + int height() const { + return q->height(); + } + + void setHeight(int height) { + q->resize(width(), height); + } + + void setFixedWidth(int width) { + q->setFixedWidth(width); + } + + void setFixedHeight(int height) { + q->setFixedHeight(height); + } + + QPoint pos() const { + return q->pos(); + } + + QSize size() const { + return q->size(); + } + + QUrl styleSheetFile() const { + return _styleSheetFile; + } + + QColor windowColor() const + { return q->palette().color(QPalette::Window); } + + void setWindowColor(const QColor &color) + { + QPalette pal = q->palette(); + pal.setColor(QPalette::Window, color); + q->setPalette(pal); + } + + QColor backgroundColor() const + { return q->palette().color(QPalette::Background); } + + void setBackgroundColor(const QColor &color) + { + QPalette pal = q->palette(); + pal.setColor(QPalette::Background, color); + q->setPalette(pal); + } + + QColor baseColor() const + { return q->palette().color(QPalette::Base); } + + void setBaseColor(const QColor &color) + { + QPalette pal = q->palette(); + pal.setColor(QPalette::Base, color); + q->setPalette(pal); + } + + QColor textColor() const + { return q->palette().color(QPalette::Text); } + + void setTextColor(const QColor &color) + { + QPalette pal = q->palette(); + pal.setColor(QPalette::Text, color); + q->setPalette(pal); + } + + QColor windowTextColor() const + { return q->palette().color(QPalette::WindowText); } + + void setWindowTextColor(const QColor &color) + { + QPalette pal = q->palette(); + pal.setColor(QPalette::WindowText, color); + q->setPalette(pal); + } + + QColor buttonTextColor() const + { return q->palette().color(QPalette::ButtonText); } + + void setButtonTextColor(const QColor &color) + { + QPalette pal = q->palette(); + pal.setColor(QPalette::ButtonText, color); + q->setPalette(pal); + } + + void setStyleSheetFile(const QUrl &url) { + _styleSheetFile = url; + _styleSheetFile.setScheme("file"); //### todo + QString fileName; + if (!QFileInfo(_styleSheetFile.toLocalFile()).exists()) + fileName = (":" + _styleSheetFile.toLocalFile().split(":").last()); //try if it is a resource + else + fileName = (_styleSheetFile.toLocalFile()); + QFile file(fileName); + file.open(QIODevice::ReadOnly); + if (file.isOpen()) { + QString styleSheet(file.readAll()); + q->setStyleSheet(styleSheet); + } else { + qWarning() << QLatin1String("setStyleSheetFile: ") << url << QLatin1String(" not found!"); + } + + } + +private: + QWidget *q; + Children _children; + QLayoutObject *_layout; + QFont _font; + QUrl _styleSheetFile; + QGraphicsOpacityEffect *_graphicsOpacityEffect; + bool m_mouseOver; +}; + +bool ResizeEventFilter::eventFilter(QObject *obj, QEvent *event) +{ + if (event->type() == QEvent::Resize) { + if (obj + && obj->isWidgetType() + && obj == m_target) { + m_dui_target->emitResize(); + return QObject::eventFilter(obj, event); + } + } else if (event->type() == QEvent::Move) { + if (obj + && obj->isWidgetType() + && obj == m_target) { + m_dui_target->emitMove(); + return QObject::eventFilter(obj, event); + } + } else if ((event->type() == QEvent::FocusIn) || (event->type() == QEvent::FocusOut)) { + if (obj + && obj->isWidgetType() + && obj == m_target) { + m_dui_target->emitFocusChanged(); + return QObject::eventFilter(obj, event); + } + } else if ((event->type() == QEvent::Enter) || (event->type() == QEvent::Leave)) { + if (obj + && obj->isWidgetType() + && obj == m_target) { + m_dui_target->setMouseOver(event->type() == QEvent::Enter); + m_dui_target->emitMouseOverChanged(); + return QObject::eventFilter(obj, event); + } + } + return QObject::eventFilter(obj, event); +} + + +class QTabObject : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QWidget *content READ content WRITE setContent) + Q_PROPERTY(QString label READ label WRITE setLabel) + Q_PROPERTY(QIcon icon READ icon WRITE setIcon) + Q_CLASSINFO("DefaultProperty", "content") +public: + QTabObject(QObject *parent = 0) : QObject(parent), _content(0) {} + + QWidget *content() const { return _content; } + void setContent(QWidget *content) + { + _content = content; + } + + QString label() const { return _label; } + void setLabel(const QString &label) + { + _label = label; + } + + QIcon icon() const { return _icon; } + void setIcon(const QIcon &icon) + { + _icon = icon; + } + +private: + QWidget *_content; + QString _label; + QIcon _icon; +}; + +class QPushButtonDeclarativeUI : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QUrl iconFromFile READ iconFromFile WRITE setIconFromFile) +public: + QPushButtonDeclarativeUI(QObject *parent = 0) : QObject(parent) + { + pb = qobject_cast<QPushButton*>(parent); + } +private: + QUrl iconFromFile() const + { + return _url; + } + + void setIconFromFile(const QUrl &url) { + _url = url; + + QString path; + if (_url.scheme() == QLatin1String("qrc")) { + path = ":" + _url.path(); + } else { + path = _url.toLocalFile(); + } + + QFile file(path); + file.open(QIODevice::ReadOnly); + if (file.exists() && file.isOpen()) { + QPixmap pixmap(path); + if (pixmap.isNull()) + qWarning() << QLatin1String("setIconFromFile: ") << url << QLatin1String(" not found!"); + pb->setIcon(pixmap); + } else { + qWarning() << QLatin1String("setIconFromFile: ") << url << QLatin1String(" not found!"); + } + + } + + QPushButton *pb; + QUrl _url; +}; + +class QMenuDeclarativeUI : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QmlList<Action *> *actions READ actions) + Q_CLASSINFO("DefaultProperty", "actions") + +public: + QMenuDeclarativeUI(QObject *parent = 0) : QObject(parent), _actions(parent) + { + menu = qobject_cast<QMenu*>(parent); + } + + QmlList<Action *> *actions() { return &_actions; } + +private: + //if not for the at() function, we could use QmlList instead + class Actions : public QmlConcreteList<Action *> + { + public: + Actions(QObject *o) : menu(qobject_cast<QMenu*>(o)) {} + virtual void append(Action *o) + { + QmlConcreteList<Action *>::append(o); + o->setParent(menu); + menu->addAction(o); + } + virtual void clear() + { + QmlConcreteList<Action *>::clear(); + menu->clear(); + } + virtual void removeAt(int i) + { + QmlConcreteList<Action *>::removeAt(i); + menu->removeAction(menu->actions().at(i)); + } + virtual void insert(int i, Action *obj) + { + QmlConcreteList<Action *>::insert(i, obj); + obj->setParent(menu); + menu->addAction(obj); + } + private: + QMenu *menu; + }; + + QMenu *menu; + Actions _actions; +}; + +class QToolButtonDeclarativeUI : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QUrl iconFromFile READ iconFromFile WRITE setIconFromFile) + Q_PROPERTY(QMenu *menu READ menu WRITE setMenu) + +public: + QToolButtonDeclarativeUI(QObject *parent = 0) : QObject(parent) + { + pb = qobject_cast<QToolButton*>(parent); + } +private: + + QMenu *menu() const { return pb->menu(); } + void setMenu(QMenu *menu) + { + pb->setMenu(menu); + } + + QUrl iconFromFile() const + { + return _url; + } + + void setIconFromFile(const QUrl &url) { + _url = url; + + QString path; + if (_url.scheme() == QLatin1String("qrc")) { + path = ":" + _url.path(); + } else { + path = _url.toLocalFile(); + } + + QFile file(path); + file.open(QIODevice::ReadOnly); + if (file.exists() && file.isOpen()) { + QPixmap pixmap(path); + if (pixmap.isNull()) + qWarning() << QLatin1String("setIconFromFile: ") << url << QLatin1String(" not found!"); + pb->setIcon(pixmap); + } else { + qWarning() << QLatin1String("setIconFromFile: ") << url << QLatin1String(" not found!"); + } + + } + + QToolButton *pb; + QUrl _url; +}; + +class QComboBoxDeclarativeUI : public QObject +{ + Q_OBJECT + Q_PROPERTY(QStringList items READ items WRITE setItems NOTIFY itemChanged) + Q_PROPERTY(QString currentText READ currentText WRITE setCurrentText NOTIFY currentTextChanged) + +public: + QComboBoxDeclarativeUI(QObject *parent = 0) : QObject(parent), m_itemsSet(false) + { + cb = qobject_cast<QComboBox*>(parent); + connect(cb, SIGNAL(currentIndexChanged(const QString &)), this, SLOT(setCurrentText(const QString &))); + } + + QString currentText() const + { + return cb->currentText(); + } + +public slots: + void setCurrentText(const QString &text) + { + if (!m_itemsSet) + return; + int i = cb->findText(text); + if (i != -1) { + cb->setCurrentIndex(i); + emit currentTextChanged(); + } + } +signals: + void currentTextChanged(); + void itemsChanged(); + +private: + void setItems(const QStringList &list) + { + _items = list; + cb->clear(); + cb->addItems(list); + m_itemsSet = true; + emit itemsChanged(); + } + + QStringList items() const + { + return _items; + } + + QComboBox *cb; + QStringList _items; + + bool m_itemsSet; +}; + +class QScrollAreaDeclarativeUI : public QObject +{ + Q_OBJECT + Q_PROPERTY(QWidget *content READ content WRITE setContent) + Q_CLASSINFO("DefaultProperty", "content") + +public slots: + void setupProperWheelBehaviour(); + +public: + QScrollAreaDeclarativeUI(QObject *parent = 0) : QObject(parent), _content(0) + { + sa = qobject_cast<QScrollArea*>(parent); + } + +private: + QWidget *content() const { return _content; } + void setContent(QWidget *content) + { + _content = content; + sa->setWidget(content); + sa->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + sa->verticalScrollBar()->show(); + setupProperWheelBehaviour(); + } + + QWidget *_content; + QScrollArea *sa; +}; + +class MouseWheelFilter : public QObject +{ + Q_OBJECT +public: + MouseWheelFilter(QObject *parent) : QObject(parent), m_target(0) { } + + void setTarget(QObject *target) { m_target = target; } + +protected: + bool eventFilter(QObject *obj, QEvent *event); +private: + QObject *m_target; +}; + +bool MouseWheelFilter::eventFilter(QObject *obj, QEvent *event) +{ + if (event->type() == QEvent::Wheel) { + if (obj + && obj->isWidgetType() + && obj != m_target) { + QApplication::sendEvent(m_target, event); + return true; + } + } + return QObject::eventFilter(obj, event); +} + +void QScrollAreaDeclarativeUI::setupProperWheelBehaviour() +{ +// We install here an eventfilter to avoid that scrolling in +// in the ScrollArea changes values in editor widgets + if (_content) { + MouseWheelFilter *forwarder(new MouseWheelFilter(this)); + forwarder->setTarget(_content); + + foreach (QWidget *childWidget, _content->findChildren<QWidget*>()) { + childWidget->installEventFilter(forwarder); + } + } +} + +class WidgetLoader : public QWidget +{ + Q_OBJECT + Q_PROPERTY(QString sourceString READ sourceString WRITE setSourceString NOTIFY sourceChanged) + Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) + Q_PROPERTY(QWidget *widget READ widget NOTIFY widgetChanged) + Q_PROPERTY(QmlComponent *component READ component NOTIFY sourceChanged) + +public: + WidgetLoader(QWidget * parent = 0) : QWidget(parent), m_source(QUrl()), m_widget(0), + m_component(0), m_layout(0) + { + m_layout = new QVBoxLayout(this); + m_layout->setContentsMargins(0,0,0,0); + } + + QUrl source() const; + void setSource(const QUrl &); + + QString sourceString() const + { return m_source.toString(); } + void setSourceString(const QString &url) + { setSource(QUrl(url)); } + + QWidget *widget() const; + QmlComponent *component() const + { return m_component; } + +signals: + void widgetChanged(); + void sourceChanged(); + +private: + QUrl m_source; + QWidget *m_widget; + QmlComponent *m_component; + QVBoxLayout *m_layout; + +}; + +QUrl WidgetLoader::source() const +{ + return m_source; +} + +void WidgetLoader::setSource(const QUrl &source) +{ + if (m_source == source) + return; + + if (m_component) { + delete m_component; + m_component = 0; + } + + QWidget *oldWidget = m_widget; + + if (m_widget) { + //m_widget->deleteLater(); + m_widget = 0; + } + + m_source = source; + if (m_source.isEmpty()) { + emit sourceChanged(); + emit widgetChanged(); + return; + } + + m_component = new QmlComponent(qmlEngine(this), m_source, this); + + if (m_component) { + emit sourceChanged(); + emit widgetChanged(); + + while (m_component->isLoading()) + QApplication::processEvents(); + + if (!m_component->isReady()) { + if (!m_component->errors().isEmpty()) + qWarning() << m_component->errors(); + emit sourceChanged(); + return; + } + + QmlContext *ctxt = new QmlContext(qmlContext(this)); + ctxt->addDefaultObject(this); + + QObject *obj = m_component->create(ctxt); + if (obj) { + QWidget *widget = qobject_cast<QWidget *>(obj); + if (widget) { + m_widget = widget; + m_layout->addWidget(m_widget); + m_widget->show(); + } + } + } + +} + +QWidget *WidgetLoader::widget() const +{ + return m_widget; +} + +class MyGroupBox : public QGroupBox +{ + Q_OBJECT + +public: + MyGroupBox(QWidget * parent = 0) : QGroupBox(parent), m_animated(false) + {} + +void setPixmap(const QPixmap &pixmap, qreal alpha = 1) +{ m_pixmap = pixmap; m_alpha = alpha;} + +void setAnimated(bool animated) +{ m_animated = animated; } + +public slots: + virtual void setVisible ( bool visible ); + +protected: + virtual void paintEvent(QPaintEvent * event); +private: + qreal m_alpha; + QPixmap m_pixmap; + bool m_animated; +}; + +void MyGroupBox::paintEvent(QPaintEvent * event) +{ + QGroupBox::paintEvent(event); + if (m_animated) { + QPainter p(this); + p.setOpacity(m_alpha); + p.drawPixmap(5, 5, m_pixmap.width(), m_pixmap.height(), m_pixmap); + } +} + +void MyGroupBox::setVisible ( bool visible ) +{ + if (parentWidget()) + QGroupBox::setVisible(visible); +} + +class QGroupBoxDeclarativeUI : public QObject +{ + Q_OBJECT + + Q_PROPERTY(bool collapsed READ isCollapsed WRITE setCollapsed) +public: + QGroupBoxDeclarativeUI(QObject *parent = 0) : QObject(parent), m_expanded(true) + { + gb = qobject_cast<MyGroupBox*>(parent); + connect(&m_timeLine, SIGNAL(frameChanged(int)), this, SLOT(animate(int))); + connect(&m_timeLine, SIGNAL(finished()), this, SLOT(finish())); + + m_timeLine.setDuration(150); + m_timeLine.setFrameRange(0, 5); + } + + bool isCollapsed() + { return ! m_expanded; } + + void setCollapsed(bool collapsed) + { + if (collapsed) + collapse(); + else + expand(); + } + +public slots: + void collapse(); + void expand(); + + void animate(int frame); + void finish(); + +private: + MyGroupBox *gb; + QTimeLine m_timeLine; + bool m_expanded; + int m_oldHeight; + int m_oldMAxHeight; + QPixmap m_contens; + + void hideChildren(); + void showChildren(); + + void reLayout(); +}; + +void QGroupBoxDeclarativeUI::reLayout() +{ + QLayout *layout = gb->parentWidget()->layout(); + if (layout) { + layout->activate(); + layout->update(); + } +} + +void QGroupBoxDeclarativeUI::finish() +{ + gb->setAnimated(false); + if (m_expanded) { + showChildren(); + + gb->setMaximumHeight(m_oldMAxHeight); + } + else { + gb->setMaximumHeight(30); + } + reLayout(); +} + +void QGroupBoxDeclarativeUI::hideChildren() +{ + if (gb->isVisible()) { + gb->setMinimumHeight(gb->height()); + foreach (QWidget *widget, gb->findChildren<QWidget*>()) { + widget->setProperty("wasVisibleGB", widget->property("visible")); + } + foreach (QWidget *widget, gb->findChildren<QWidget*>()) + if (widget->parent() == gb) + widget->hide(); + } +} + +void QGroupBoxDeclarativeUI::showChildren() +{ + foreach (QWidget *widget, gb->findChildren<QWidget*>()) { + if (widget->property("wasVisibleGB").toBool()) + widget->show(); + widget->setProperty("wasVisibleGB", QVariant()); + widget->ensurePolished(); + } + gb->show(); +} + +void QGroupBoxDeclarativeUI::collapse() +{ + m_oldMAxHeight = gb->maximumHeight(); + m_oldHeight = gb->height(); + if (!m_expanded) + return; + m_contens = QPixmap::grabWidget (gb, 5, 5, gb->width() - 5, gb->height() - 5); + gb->setPixmap(m_contens,1); + hideChildren(); + m_expanded = false; + m_timeLine.start(); +} + +void QGroupBoxDeclarativeUI::expand() +{ + if (m_expanded) + return; + m_expanded = true; + m_timeLine.start(); +} + +void QGroupBoxDeclarativeUI::animate(int frame) +{ + gb->setAnimated(true); + qreal height; + + if (m_expanded) { + height = ((qreal(frame) / 5.0) * qreal(m_oldHeight)) + (30.0 * (1.0 - qreal(frame) / 5.0)); + gb->setPixmap(m_contens, qreal(frame) / 5.0); + } + else { + height = ((qreal(frame) / 5.0) * 30.0) + (qreal(m_oldHeight) * (1.0 - qreal(frame) / 5.0)); + qreal alpha = 0.8 - qreal(frame) / 4.0; + if (alpha < 0) + alpha = 0; + gb->setPixmap(m_contens, alpha); + } + + gb->setMaximumHeight(height); + gb->setMinimumHeight(height); + reLayout(); + gb->update(); +} + +QML_DECLARE_TYPE(QTabObject); +QML_DEFINE_TYPE(Qt,4,6,QTabObject,QTabObject); //### with namespacing, this should just be 'Tab' + +class QTabWidgetDeclarativeUI : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QmlList<QTabObject *> *tabs READ tabs) + Q_CLASSINFO("DefaultProperty", "tabs") +public: + QTabWidgetDeclarativeUI(QObject *other) : QObject(other), _tabs(other) {} + + QmlList<QTabObject *> *tabs() { return &_tabs; } + +private: + //if not for the at() function, we could use QmlList instead + class Tabs : public QmlConcreteList<QTabObject *> + { + public: + Tabs(QObject *o) : tw(o) {} + virtual void append(QTabObject *o) + { + QmlConcreteList<QTabObject *>::append(o); + //XXX can we insertTab(-1, o) instead? + if (!o->icon().isNull()) + static_cast<QTabWidget *>(tw)->addTab(o->content(), o->icon(), o->label()); + else + static_cast<QTabWidget *>(tw)->addTab(o->content(), o->label()); + } + virtual void clear() + { + QmlConcreteList<QTabObject *>::clear(); + static_cast<QTabWidget *>(tw)->clear(); + } + virtual void removeAt(int i) + { + QmlConcreteList<QTabObject *>::removeAt(i); + static_cast<QTabWidget *>(tw)->removeTab(i); + } + virtual void insert(int i, QTabObject *obj) + { + QmlConcreteList<QTabObject *>::insert(i, obj); + if (!obj->icon().isNull()) + static_cast<QTabWidget *>(tw)->insertTab(i, obj->content(), obj->icon(), obj->label()); + else + static_cast<QTabWidget *>(tw)->insertTab(i, obj->content(), obj->label()); + } + private: + QObject *tw; + }; + Tabs _tabs; +}; + + +class WidgetFrame : public QFrame +{ + Q_OBJECT +public: + WidgetFrame( QWidget * parent = 0, Qt::WindowFlags f = 0 ) : QFrame(parent, f) + {} +}; + +QML_DEFINE_EXTENDED_TYPE(Qt,4,6,QWidget,QWidget,QWidgetDeclarativeUI); + +//display +QML_DEFINE_TYPE(Qt,4,6,QLabel,QLabel); +QML_DEFINE_TYPE(Qt,4,6,QProgressBar,QProgressBar); +QML_DEFINE_TYPE(Qt,4,6,QLCDNumber,QLCDNumber); + +//input +QML_DEFINE_TYPE(Qt,4,6,QLineEdit,QLineEdit); +QML_DEFINE_TYPE(Qt,4,6,QTextEdit,QTextEdit); +QML_DEFINE_TYPE(Qt,4,6,QPlainTextEdit,QPlainTextEdit); +QML_DEFINE_TYPE(Qt,4,6,QSpinBox,QSpinBox); +QML_DEFINE_TYPE(Qt,4,6,QDoubleSpinBox,QDoubleSpinBox); +QML_DEFINE_TYPE(Qt,4,6,QSlider,QSlider); +QML_DEFINE_TYPE(Qt,4,6,QDateTimeEdit,QDateTimeEdit); +QML_DEFINE_TYPE(Qt,4,6,QDateEdit,QDateEdit); +QML_DEFINE_TYPE(Qt,4,6,QTimeEdit,QTimeEdit); +QML_DEFINE_TYPE(Qt,4,6,QFontComboBox,QFontComboBox); +QML_DEFINE_TYPE(Qt,4,6,QDial,QDial); +QML_DEFINE_TYPE(Qt,4,6,QScrollBar,QScrollBar); +QML_DEFINE_TYPE(Qt,4,6,QCalendarWidget, QCalendarWidget); + + +QML_DECLARE_TYPE(MyGroupBox); +QML_DECLARE_TYPE(WidgetLoader); +QML_DECLARE_TYPE(WidgetFrame); +//QML_DEFINE_TYPE(Qt,4,6,QComboBox,QComboBox); //need a way to populate +//QML_DEFINE_EXTENDED_TYPE(QComboBox,QComboBox, QComboBox); //need a way to populate + +//buttons +//QML_DEFINE_TYPE(Qt,4,6,QPushButton,QPushButton); +QML_DEFINE_TYPE(Qt,4,6,QCheckBox,QCheckBox); +QML_DEFINE_TYPE(Qt,4,6,QAction,Action); +QML_DEFINE_TYPE(Qt,4,6,QRadioButton,QRadioButton); +QML_DEFINE_TYPE(Qt,4,6,FileWidget, FileWidget); +QML_DEFINE_TYPE(Qt,4,6,LayoutWidget, LayoutWidget); + +//containers +QML_DEFINE_TYPE(Qt,4,6,QFrame,QFrame); +QML_DEFINE_TYPE(Qt,4,6,WidgetFrame,WidgetFrame); +QML_DEFINE_TYPE(Qt,4,6,WidgetLoader,WidgetLoader); +QML_DEFINE_EXTENDED_TYPE(Qt,4,6,QExtGroupBox,MyGroupBox,QGroupBoxDeclarativeUI); +QML_DEFINE_EXTENDED_TYPE(Qt,4,6,QTabWidget,QTabWidget,QTabWidgetDeclarativeUI); +QML_DEFINE_EXTENDED_TYPE(Qt,4,6,QScrollArea,QScrollArea,QScrollAreaDeclarativeUI); +QML_DEFINE_EXTENDED_TYPE(Qt,4,6,QPushButton,QPushButton,QPushButtonDeclarativeUI); +QML_DEFINE_EXTENDED_TYPE(Qt,4,6,QToolButton,QToolButton, QToolButtonDeclarativeUI); +QML_DEFINE_EXTENDED_TYPE(Qt,4,6,QComboBox,QComboBox, QComboBoxDeclarativeUI); +QML_DEFINE_EXTENDED_TYPE(Qt,4,6,QMenu,QMenu, QMenuDeclarativeUI); +//QML_DEFINE_TYPE(Qt,4,6,QToolBox,QToolBox); +//QML_DEFINE_TYPE(Qt,4,6,QScrollArea,QScrollArea); + +//QML_DEFINE_EXTENDED_TYPE(QtColorButton,QtColorButton,QtColorButtonDeclarativeUI); + +//itemviews +//QML_DEFINE_TYPE(Qt,4,6,QListView,QListView); +//QML_DEFINE_TYPE(Qt,4,6,QTreeView,QTreeView); +//QML_DEFINE_TYPE(Qt,4,6,QTableView,QTableView); + + +#include "basicwidgets.moc" + +QT_END_NAMESPACE diff --git a/src/plugins/qmldesigner/components/propertyeditor/basicwidgets.h b/src/plugins/qmldesigner/components/propertyeditor/basicwidgets.h new file mode 100644 index 00000000000..f7a12a99611 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/basicwidgets.h @@ -0,0 +1,132 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#ifndef BASICWIDGETS_H +#define BASICWIDGETS_H + +#include <qml.h> +#include <QGraphicsScene> +#include <QGraphicsView> +#include <QLabel> +#include <QPushButton> +#include <QToolButton> +#include <QCheckBox> +#include <QRadioButton> +#include <QLineEdit> +#include <QTextEdit> +#include <QPlainTextEdit> +#include <QDoubleSpinBox> +#include <QSlider> +#include <QDateEdit> +#include <QTimeEdit> +#include <QProgressBar> +#include <QGroupBox> +#include <QDial> +#include <QLCDNumber> +#include <QFontComboBox> +#include <QScrollBar> +#include <QCalendarWidget> +#include <QTabWidget> +#include <QAction> +#include "colorwidget.h" +#include "filewidget.h" +#include "layoutwidget.h" + + +QT_BEGIN_NAMESPACE + +QML_DECLARE_TYPE(QWidget); + +//display +QML_DECLARE_TYPE(QLabel); +QML_DECLARE_TYPE(QProgressBar); +QML_DECLARE_TYPE(QLCDNumber); + +//input +QML_DECLARE_TYPE(QLineEdit); +QML_DECLARE_TYPE(QTextEdit); +QML_DECLARE_TYPE(QPlainTextEdit); +QML_DECLARE_TYPE(QSpinBox); +QML_DECLARE_TYPE(QDoubleSpinBox); +QML_DECLARE_TYPE(QSlider); +QML_DECLARE_TYPE(QDateTimeEdit); +QML_DECLARE_TYPE(QDateEdit); +QML_DECLARE_TYPE(QTimeEdit); +QML_DECLARE_TYPE(QFontComboBox); +QML_DECLARE_TYPE(QDial); +QML_DECLARE_TYPE(QScrollBar); +QML_DECLARE_TYPE(QCalendarWidget); +QML_DECLARE_TYPE(QComboBox); + +//buttons +QML_DECLARE_TYPE(QPushButton); +QML_DECLARE_TYPE(QToolButton); +QML_DECLARE_TYPE(QCheckBox); +QML_DECLARE_TYPE(QRadioButton); + +//containers +QML_DECLARE_TYPE(QGroupBox); +QML_DECLARE_TYPE(QFrame); +QML_DECLARE_TYPE(QScrollArea); +QML_DECLARE_TYPE(QTabWidget); +QML_DECLARE_TYPE(FileWidget); +QML_DECLARE_TYPE(LayoutWidget); + +class Action : public QAction { + Q_OBJECT +public: + Action(QObject *parent = 0) : QAction(parent) {} +}; + +QML_DECLARE_TYPE(QMenu); +QML_DECLARE_TYPE(Action); + +//QML_DECLARE_TYPE(QToolBox); + +//itemviews +//QML_DECLARE_TYPE(QListView); +//QML_DECLARE_TYPE(QTreeView); +//QML_DECLARE_TYPE(QTableView); + +//top-level windows? + + +QT_END_NAMESPACE +#endif // BASICWIDGETS_H diff --git a/src/plugins/qmldesigner/components/propertyeditor/behaviordialog.cpp b/src/plugins/qmldesigner/components/propertyeditor/behaviordialog.cpp new file mode 100644 index 00000000000..98609a0abc9 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/behaviordialog.cpp @@ -0,0 +1,174 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "behaviordialog.h" + +#include <abstractview.h> +#include <nodeproperty.h> +#include <variantproperty.h> +#include <bindingproperty.h> +#include <nodeproperty.h> + +#include <QLineEdit> +#include <QSpinBox> + + +QML_DEFINE_TYPE(Bauhaus,1,0,BehaviorWidget,QmlDesigner::BehaviorWidget); + +namespace QmlDesigner { + + +BehaviorWidget::BehaviorWidget() : QPushButton(), m_BehaviorDialog(new BehaviorDialog(0)) +{ + setCheckable(true); + connect(this, SIGNAL(toggled(bool)), this, SLOT(buttonPressed(bool))); + +} +BehaviorWidget::BehaviorWidget(QWidget *parent) : QPushButton(parent), m_BehaviorDialog(new BehaviorDialog(0)) +{ + setCheckable(true); + connect(this, SIGNAL(toggled(bool)), this, SLOT(buttonPressed(bool))); +} + +PropertyEditorNodeWrapper* BehaviorWidget::complexNode() const +{ + return m_complexNode; +} + +void BehaviorWidget::setComplexNode(PropertyEditorNodeWrapper* complexNode) +{ + m_complexNode = complexNode; + m_propertyName = complexNode->propertyName(); + m_modelNode = complexNode->parentModelNode(); + + if (!modelNode().isValid()) { + m_BehaviorDialog->hide(); + } + + m_BehaviorDialog->setup(modelNode(), propertyName()); +} + +void BehaviorWidget::buttonPressed(bool show) +{ + if (show) { + if (m_BehaviorDialog->isVisible()) { + m_BehaviorDialog->reject(); + } else { + m_BehaviorDialog->setup(modelNode(), propertyName()); + m_BehaviorDialog->show(); + setChecked(false); + } + } +} + +BehaviorDialog::BehaviorDialog(QWidget *parent) : QDialog(parent), m_ui(new Ui_BehaviorDialog) +{ + m_ui->setupUi(this); + setModal(true); +} + + void BehaviorDialog::setup(const ModelNode &node, const QString propertyName) +{ + m_modelNode = node; + m_ui->duration->setValue(100); + m_ui->velocity->setValue(2); + m_ui->spring->setValue(2); + m_ui->damping->setValue(2); + m_ui->stackedWidget->setCurrentIndex(0); + m_ui->curve->setCurrentIndex(0); + + if (m_modelNode.isValid()) { + m_propertyName = propertyName; + m_ui->id->setText(m_modelNode.id()); + m_ui->type->setText(m_modelNode.simplifiedTypeName()); + m_ui->propertyName->setText(propertyName); + if (m_modelNode.hasProperty(m_propertyName) && m_modelNode.property(m_propertyName).isNodeProperty()) { + NodeProperty nodeProperty(m_modelNode.nodeProperty(m_propertyName)); + if (nodeProperty.modelNode().type() == "Qt/SpringFollow") { + ModelNode springFollow = nodeProperty.modelNode(); + m_ui->curve->setCurrentIndex(1); + m_ui->stackedWidget->setCurrentIndex(1); + if (springFollow.hasProperty("velocity") && springFollow.property("velocity").isVariantProperty()) + m_ui->velocity->setValue(springFollow.variantProperty("velocity").value().toDouble()); + if (springFollow.hasProperty("spring") && springFollow.property("spring").isVariantProperty()) + m_ui->spring->setValue(springFollow.variantProperty("spring").value().toDouble()); + if (springFollow.hasProperty("damping") && springFollow.property("damping").isVariantProperty()) + m_ui->damping->setValue(springFollow.variantProperty("damping").value().toDouble()); + if (springFollow.hasProperty("source") && springFollow.property("source").isVariantProperty()) + m_ui->source->setText(springFollow.variantProperty("source").value().toString()); + } else if (nodeProperty.modelNode().type() == "Qt/Behavior") { + if (nodeProperty.modelNode().hasProperty("animation") && + nodeProperty.modelNode().property("animation").isNodeProperty() && + nodeProperty.modelNode().nodeProperty("animation").modelNode().type() == "Qt/NumberAnimation") { + m_ui->curve->setCurrentIndex(0); + ModelNode animation = nodeProperty.modelNode().nodeProperty("animation").modelNode(); + if (animation.hasProperty("duration") && animation.property("duration").isVariantProperty()) + m_ui->duration->setValue(animation.variantProperty("duration").value().toInt()); + if (animation.hasProperty("easing") && animation.property("easing").isVariantProperty()) { + QStringList easingItems; + for (int i = 0; i < m_ui->curve->count(); i++) + easingItems.append(m_ui->curve->itemText(i)); + m_ui->curve->setCurrentIndex(easingItems.indexOf(animation.variantProperty("easing").value().toString())); + } + } + } + } + } +} + +void BehaviorDialog::accept() +{ + QDialog::accept(); + if (m_modelNode.hasProperty(m_propertyName)) + m_modelNode.removeProperty(m_propertyName); + if (m_ui->comboBox->currentIndex() == 0) { + RewriterTransaction transaction(m_modelNode.view()->beginRewriterTransaction()); + ModelNode Behavior = m_modelNode.view()->createModelNode("Qt/Behavior", 4, 6); + m_modelNode.nodeProperty(m_propertyName).reparentHere(Behavior); + ModelNode animation = m_modelNode.view()->createModelNode("Qt/NumberAnimation", 4, 6); + animation.variantProperty("duration") = m_ui->duration->value(); + animation.variantProperty("easing") = m_ui->curve->currentText(); + Behavior.nodeProperty("animation").reparentHere(animation); + } else { + RewriterTransaction transaction(m_modelNode.view()->beginRewriterTransaction()); + ModelNode springFollow = m_modelNode.view()->createModelNode("Qt/SpringFollow", 4, 6); + m_modelNode.nodeProperty(m_propertyName).reparentHere(springFollow); + springFollow.variantProperty("velocity") = m_ui->velocity->value(); + springFollow.variantProperty("spring") = m_ui->spring->value(); + springFollow.variantProperty("damping") = m_ui->damping->value(); + springFollow.bindingProperty("source") = m_ui->source->text(); + } +} + +void BehaviorDialog::reject() +{ + QDialog::reject(); +} + +}; diff --git a/src/plugins/qmldesigner/components/propertyeditor/behaviordialog.h b/src/plugins/qmldesigner/components/propertyeditor/behaviordialog.h new file mode 100644 index 00000000000..dfc763b4e39 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/behaviordialog.h @@ -0,0 +1,98 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef BEHAVIORDIALOG_H +#define BEHAVIORDIALOG_H + +#include <modelnode.h> +#include <propertyeditorvalue.h> + +#include <QPushButton> +#include <QDialog> +#include <QWeakPointer> +#include <QScopedPointer> +#include <qml.h> + +#include "ui_behaviordialog.h" + +namespace QmlDesigner { + +class BehaviorDialog; + +class BehaviorWidget : public QPushButton +{ + Q_PROPERTY(PropertyEditorNodeWrapper* complexNode READ complexNode WRITE setComplexNode) + + Q_OBJECT + +public: + + BehaviorWidget(); + BehaviorWidget(QWidget* parent); + + ModelNode modelNode() const {return m_modelNode; } + QString propertyName() const {return m_propertyName; } + + PropertyEditorNodeWrapper* complexNode() const; + void setComplexNode(PropertyEditorNodeWrapper* complexNode); + +public slots: + void buttonPressed(bool); + +private: + ModelNode m_modelNode; + QString m_propertyName; + PropertyEditorNodeWrapper* m_complexNode; + QScopedPointer<BehaviorDialog> m_BehaviorDialog; +}; + +class BehaviorDialog : public QDialog +{ + Q_OBJECT +public: + BehaviorDialog(QWidget *parent); + void setup(const ModelNode &node, const QString propertyName); + +public slots: + virtual void accept(); + virtual void reject(); + + +private: + ModelNode m_modelNode; + QString m_propertyName; + QScopedPointer<Ui_BehaviorDialog> m_ui; +}; + + +}; + +QML_DECLARE_TYPE(QmlDesigner::BehaviorWidget); + +#endif// BEHAVIORDIALOG_H diff --git a/src/plugins/qmldesigner/components/propertyeditor/behaviordialog.ui b/src/plugins/qmldesigner/components/propertyeditor/behaviordialog.ui new file mode 100644 index 00000000000..d3c34b0391c --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/behaviordialog.ui @@ -0,0 +1,472 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>BehaviorDialog</class> + <widget class="QDialog" name="BehaviorDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>247</width> + <height>311</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="windowTitle"> + <string>Dialog</string> + </property> + <property name="styleSheet"> + <string notr="true">QDialog { +qlineargradient(spread:pad, x1:0.585, y1:0.0284091, x2:0.517, y2:1, stop:0 rgba(85, 85, 85, 255), stop:1 rgba(173, 173, 173, 255)) +} + +</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="minimumSize"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="text"> + <string>Type:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="type"> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_3"> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="text"> + <string>Id:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="id"> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_5"> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="text"> + <string>Property Name:</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLineEdit" name="propertyName"> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="3" column="0" colspan="2"> + <widget class="QComboBox" name="comboBox"> + <item> + <property name="text"> + <string>Animation</string> + </property> + </item> + <item> + <property name="text"> + <string>SpringFollow</string> + </property> + </item> + </widget> + </item> + <item row="4" column="0" colspan="2"> + <widget class="QGroupBox" name="groupBox"> + <property name="minimumSize"> + <size> + <width>0</width> + <height>120</height> + </size> + </property> + <property name="title"> + <string>Settings</string> + </property> + <layout class="QGridLayout" name="gridLayout_4"> + <item row="0" column="0"> + <widget class="QStackedWidget" name="stackedWidget"> + <property name="currentIndex"> + <number>1</number> + </property> + <widget class="QWidget" name="page"> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="0" column="0"> + <widget class="QLabel" name="label_7"> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="text"> + <string>Duration:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QSpinBox" name="duration"> + <property name="maximum"> + <number>10000</number> + </property> + <property name="singleStep"> + <number>10</number> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_8"> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="text"> + <string>Curve:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QComboBox" name="curve"> + <item> + <property name="text"> + <string>easeNone</string> + </property> + </item> + <item> + <property name="text"> + <string>easeInQuad</string> + </property> + </item> + <item> + <property name="text"> + <string>easeOutQuad</string> + </property> + </item> + <item> + <property name="text"> + <string>easeInOutQuad</string> + </property> + </item> + <item> + <property name="text"> + <string>easeOutInQuad</string> + </property> + </item> + <item> + <property name="text"> + <string>easeInCubic</string> + </property> + </item> + <item> + <property name="text"> + <string>easeOutCubic</string> + </property> + </item> + <item> + <property name="text"> + <string>easeInOutCubic</string> + </property> + </item> + <item> + <property name="text"> + <string>easeOutInCubic</string> + </property> + </item> + <item> + <property name="text"> + <string>easeOutInBounce</string> + </property> + </item> + <item> + <property name="text"> + <string>easeInOutBounce</string> + </property> + </item> + <item> + <property name="text"> + <string>easeOutBounce</string> + </property> + </item> + <item> + <property name="text"> + <string>easeInBounce</string> + </property> + </item> + <item> + <property name="text"> + <string>easeOutInBack</string> + </property> + </item> + <item> + <property name="text"> + <string>easeInOutBack</string> + </property> + </item> + <item> + <property name="text"> + <string>easeOutBack</string> + </property> + </item> + <item> + <property name="text"> + <string>easeInBack</string> + </property> + </item> + <item> + <property name="text"> + <string>easeOutInElastic</string> + </property> + </item> + <item> + <property name="text"> + <string>easeInOutElastic</string> + </property> + </item> + <item> + <property name="text"> + <string>easeOutElastic</string> + </property> + </item> + <item> + <property name="text"> + <string>easeInElastic</string> + </property> + </item> + <item> + <property name="text"> + <string>easeOutInCirc</string> + </property> + </item> + <item> + <property name="text"> + <string>easeInOutCirc</string> + </property> + </item> + <item> + <property name="text"> + <string>easeOutCirc</string> + </property> + </item> + <item> + <property name="text"> + <string>easeInCirc</string> + </property> + </item> + <item> + <property name="text"> + <string>easeOutInExpo</string> + </property> + </item> + <item> + <property name="text"> + <string>easeInOutExpo</string> + </property> + </item> + <item> + <property name="text"> + <string>easeOutExpo</string> + </property> + </item> + <item> + <property name="text"> + <string>easeInExpo</string> + </property> + </item> + <item> + <property name="text"> + <string>easeOutInSine</string> + </property> + </item> + <item> + <property name="text"> + <string>easeInOutSine</string> + </property> + </item> + <item> + <property name="text"> + <string>easeOutSine</string> + </property> + </item> + <item> + <property name="text"> + <string>easeInSine</string> + </property> + </item> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="page_2"> + <layout class="QGridLayout" name="gridLayout_3"> + <item row="0" column="0"> + <widget class="QLabel" name="label_2"> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="text"> + <string>Source:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="source"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_9"> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="text"> + <string>Velocity:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QDoubleSpinBox" name="velocity"/> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_10"> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="text"> + <string>Spring:</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QDoubleSpinBox" name="spring"/> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="label_11"> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="text"> + <string>Damping:</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QDoubleSpinBox" name="damping"/> + </item> + </layout> + </widget> + </widget> + </item> + </layout> + </widget> + </item> + <item row="5" column="0" colspan="2"> + <widget class="QDialogButtonBox" name="ButtonGroup"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>ButtonGroup</sender> + <signal>accepted()</signal> + <receiver>BehaviorDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>ButtonGroup</sender> + <signal>rejected()</signal> + <receiver>BehaviorDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>comboBox</sender> + <signal>currentIndexChanged(int)</signal> + <receiver>stackedWidget</receiver> + <slot>setCurrentIndex(int)</slot> + <hints> + <hint type="sourcelabel"> + <x>52</x> + <y>106</y> + </hint> + <hint type="destinationlabel"> + <x>121</x> + <y>191</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/plugins/qmldesigner/components/propertyeditor/colorwidget.cpp b/src/plugins/qmldesigner/components/propertyeditor/colorwidget.cpp new file mode 100644 index 00000000000..da1f0cd47b9 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/colorwidget.cpp @@ -0,0 +1,259 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "colorwidget.h" +#include "qtcolorline.h" +#include "qtcolorbutton.h" +#include <QHBoxLayout> +#include <QLabel> +#include <QToolButton> +#include <QGradient> +#include <qtgradientdialog.h> +#include <QtDebug> +#include <modelnode.h> +#include <abstractview.h> +#include <nodeproperty.h> +#include <nodelistproperty.h> +#include <variantproperty.h> +#include <qmlobjectnode.h> + +QML_DEFINE_TYPE(Bauhaus,1,0,ColorWidget,QmlDesigner::ColorWidget); + +namespace QmlDesigner { + +ColorWidget::ColorWidget(QWidget *parent) : + QWidget(parent), + m_label(new QLabel(this)), + m_colorButton(new QtColorButton(this)), + m_gradientButton(new QToolButton(this)) +{ + QHBoxLayout *horizonalBoxLayout = new QHBoxLayout(this); + + m_colorButton->setFixedWidth(30); + m_colorButton->setFixedHeight(20); + m_colorButton->setStyleSheet(""); + + m_gradientButton->setIcon(QIcon(":/images/gradient.png")); + m_gradientButton->setToolButtonStyle(Qt::ToolButtonIconOnly); + m_gradientButton->setFixedWidth(30); + m_gradientButton->setFixedHeight(20); + m_gradientButton->setStyleSheet(""); + + QFont f = m_label->font(); + f.setBold(true); + m_label->setFont(f); + horizonalBoxLayout->addWidget(m_label); + horizonalBoxLayout->addWidget(m_colorButton); + horizonalBoxLayout->addWidget(m_gradientButton); + horizonalBoxLayout->addStretch(); + horizonalBoxLayout->setSpacing(0); + horizonalBoxLayout->setMargin(0); + m_gradientButton->setCheckable(true); + m_gradientButton->setVisible(false); + connect(m_colorButton, SIGNAL(colorChanged(QColor)), this, SLOT(setColor(QColor))); + connect(m_gradientButton, SIGNAL(toggled(bool)), SLOT(openGradientEditor(bool))); +} + +ColorWidget::~ColorWidget() +{ +} + +QString ColorWidget::text() const +{ + if (m_label) + return m_label->text(); + else + return QString(); +} + +QColor ColorWidget::color() const +{ + return m_color; +} + +void ColorWidget::setColor(const QColor &color) +{ + if (m_color == color) + return; + bool valid = m_color.isValid(); + m_color = color; + m_colorButton->setColor(color); + if (valid) + emit colorChanged(color); +} + +QString ColorWidget::strColor() const +{ + return m_color.name(); +} + +void ColorWidget::setText(const QString &text) +{ + if (m_label) + m_label->setText(text); +} + +ModelNode ColorWidget::gradientNode() const +{ + return m_gradientNode; +} + +QGradient ColorWidget::gradient() const +{ + if (!m_gradientNode.isValid()) { + return QGradient(); + } + + if (m_gradientNode.type() != "Qt/Gradient") { + qWarning() << "ColorWidget: gradient node is of wrong type"; + return QGradient(); + } + + QLinearGradient gradient; + + QGradientStops oldStops; + + //QVariantList stopList(m_modelState.nodeState(m_gradientNode).property("stops").value().toList()); /### + foreach (const ModelNode &stopNode, m_gradientNode.nodeListProperty("stops").toModelNodeList()) { + oldStops.append(QPair<qreal, QColor>(stopNode.variantProperty("position").value().toReal(), stopNode.variantProperty("color").value().value<QColor>())); + } + gradient.setStops(oldStops); + + return gradient; +} + +void ColorWidget::setGradientNode(const ModelNode &gradient) +{ + m_gradientNode = gradient; +} + +bool ColorWidget::gradientButtonShown() const +{ + return m_gradientButton->isVisible(); +} + +void ColorWidget::setShowGradientButton(bool showButton) +{ + m_gradientButton->setVisible(showButton); + if (showButton && layout()->children().contains(m_gradientButton)) + layout()->removeWidget(m_gradientButton); + else if (showButton && !layout()->children().contains(m_gradientButton) ) + layout()->addWidget(m_gradientButton); +} + +void ColorWidget::openGradientEditor(bool show) +{ + if (show && m_gradientDialog.isNull()) { + m_gradientDialog = new QtGradientDialog(this); + m_gradientDialog->setAttribute(Qt::WA_DeleteOnClose); + m_gradientDialog->setGradient(gradient()); + m_gradientDialog->show(); + connect(m_gradientDialog.data(), SIGNAL(accepted()), SLOT(updateGradientNode())); + connect(m_gradientDialog.data(), SIGNAL(destroyed()), SLOT(resetGradientButton())); + } else { + delete m_gradientDialog.data(); + } +} + +void ColorWidget::updateGradientNode() +{ + QGradient gradient(m_gradientDialog->gradient()); + + ModelNode gradientNode; + + if (m_modelNode.hasNodeProperty("gradient")) + m_gradientNode = m_modelNode.nodeProperty("gradient").modelNode(); + + if (m_gradientNode.isValid() && m_gradientNode.type() == "Qt/Gradient") { + gradientNode = m_gradientNode; + } else { + if (m_modelNode.hasProperty("gradient")) { + m_modelNode.removeProperty("gradient"); + } + gradientNode = m_modelNode.addChildNode("Qt/Gradient", 4, 6, "gradient"); + m_gradientNode = gradientNode; + } + + if (QmlObjectNode(m_gradientNode).isInBaseState()) { + + NodeListProperty stops(gradientNode.nodeListProperty("stops")); + + foreach (ModelNode node, stops.allSubNodes()) + node.destroy(); + + if (gradientNode.hasProperty("stops")) + gradientNode.removeProperty("stops"); + + foreach(const QGradientStop &stop, gradient.stops()) { + QList<QPair<QString, QVariant> > propertyList; + + qreal p = stop.first * 100; + int ip = qRound(p); + p = qreal(ip) / 100; + + ModelNode gradientStop(gradientNode.view()->createModelNode("Qt/GradientStop", 4, 6)); + stops.reparentHere(gradientStop); + gradientStop.variantProperty("position").setValue(p); + gradientStop.variantProperty("color").setValue(stop.second.name()); + } + } else { //not in base state + //## TODO + } +} + +void ColorWidget::resetGradientButton() +{ + m_gradientButton->setChecked(false); +} + +void ColorWidget::setModelNode(const ModelNode &modelNode) +{ + if (m_modelNode != modelNode) { + m_modelNode = modelNode; + emit modelNodeChanged(); + } +} + +ModelNode ColorWidget::modelNode() const +{ + return m_modelNode; +} + +PropertyEditorNodeWrapper* ColorWidget::complexGradientNode() const +{ + return m_complexGradientNode; +} + +void ColorWidget::setComplexGradientNode(PropertyEditorNodeWrapper* complexNode) +{ + m_complexGradientNode = complexNode; + setModelNode(complexNode->parentModelNode()); +} + +} diff --git a/src/plugins/qmldesigner/components/propertyeditor/colorwidget.h b/src/plugins/qmldesigner/components/propertyeditor/colorwidget.h new file mode 100644 index 00000000000..c5080966941 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/colorwidget.h @@ -0,0 +1,108 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef COLORWIDGET_H +#define COLORWIDGET_H + +#include <QWeakPointer> +#include <QtGui/QWidget> +#include <QLabel> +#include <modelnode.h> +#include <qml.h> +#include <propertyeditorvalue.h> + + +class QtColorButton; +class QToolButton; +class QtGradientDialog; + +namespace QmlDesigner { + +class ColorWidget : public QWidget +{ + Q_OBJECT + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) + Q_PROPERTY(QColor strColor READ strColor) + Q_PROPERTY(QString text READ text WRITE setText) + Q_PROPERTY(QmlDesigner::ModelNode gradient READ gradientNode WRITE setGradientNode NOTIFY gradientNodeChanged) + Q_PROPERTY(bool showGradientButton READ gradientButtonShown WRITE setShowGradientButton) + Q_PROPERTY(QmlDesigner::ModelNode modelNode READ modelNode WRITE setModelNode NOTIFY modelNodeChanged) + Q_PROPERTY(PropertyEditorNodeWrapper* complexGradientNode READ complexGradientNode WRITE setComplexGradientNode) +public: + + ColorWidget(QWidget *parent = 0); + ~ColorWidget(); + + QColor color() const; + QString text() const; + QString strColor() const; + void setText(const QString &text); + ModelNode gradientNode() const; + bool gradientButtonShown() const; + void setShowGradientButton(bool showButton); + + void setModelNode(const ModelNode &modelNode); + ModelNode modelNode() const; + + PropertyEditorNodeWrapper* complexGradientNode() const; + void setComplexGradientNode(PropertyEditorNodeWrapper* complexNode); + +public slots: + void setColor(const QColor &color); + void setGradientNode(const QmlDesigner::ModelNode &gradient); + +signals: + void colorChanged(const QColor &color); + void gradientNodeChanged(); + void modelStateChanged(); + void modelNodeChanged(); + +protected: + QGradient gradient() const; + +private slots: + void openGradientEditor(bool show); + void updateGradientNode(); + void resetGradientButton(); +private: + QColor m_color; + ModelNode m_gradientNode; + ModelNode m_modelNode; + QWeakPointer<QtGradientDialog> m_gradientDialog; + QLabel *m_label; + QtColorButton *m_colorButton; + QToolButton *m_gradientButton; + PropertyEditorNodeWrapper* m_complexGradientNode; +}; + +} + +QML_DECLARE_TYPE(QmlDesigner::ColorWidget); + +#endif diff --git a/src/plugins/qmldesigner/components/propertyeditor/filewidget.cpp b/src/plugins/qmldesigner/components/propertyeditor/filewidget.cpp new file mode 100644 index 00000000000..a63d0889d05 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/filewidget.cpp @@ -0,0 +1,87 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "filewidget.h" +#include <QHBoxLayout> +#include <QFont> +#include <QFileDialog> +#include <QDebug> + + +QT_BEGIN_NAMESPACE + +FileWidget::FileWidget(QWidget *parent) : QWidget(parent) +{ + m_pushButton = new QPushButton(this); + m_label = new QLabel(this); + m_lineEdit = new QLineEdit(this); + QHBoxLayout *layout = new QHBoxLayout(this); + setLayout(layout); + layout->addWidget(m_label); + layout->addWidget(m_lineEdit); + layout->addWidget(m_pushButton); + m_pushButton->setText("..."); + QFont f = m_label->font(); + f.setBold(true); + m_label->setFont(f); + connect(m_lineEdit, SIGNAL(editingFinished()), this, SLOT(lineEditChanged())); + connect(m_pushButton, SIGNAL(pressed()), this, SLOT(buttonPressed())); +} + +FileWidget::~FileWidget() +{ +} + + +void FileWidget::lineEditChanged() +{ + setFileNameStr(m_lineEdit->text()); +} + +void FileWidget::buttonPressed() +{ + setFileNameStr(QFileDialog::getOpenFileName()); +} + +void FileWidget::setFileNameStr(const QString &fileName) +{ + setFileName(QUrl(fileName)); +} +void FileWidget::setFileName(const QUrl &fileName) +{ + if (fileName == m_fileName) + return; + + m_fileName = fileName; + m_lineEdit->setText(fileName.toString()); + emit fileNameChanged(fileName); +} + +QT_END_NAMESPACE + diff --git a/src/plugins/qmldesigner/components/propertyeditor/filewidget.h b/src/plugins/qmldesigner/components/propertyeditor/filewidget.h new file mode 100644 index 00000000000..318fc4772c6 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/filewidget.h @@ -0,0 +1,89 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + + +#ifndef FILEWIDGET_H +#define FILEWIDGET_H + +#include <QtGui/QWidget> +#include <QLabel> +#include <QPushButton> +#include <QLineEdit> +#include <QUrl> + +QT_BEGIN_NAMESPACE + +class FileWidget : public QWidget +{ + Q_OBJECT + + Q_PROPERTY(QString text READ text WRITE setText) + Q_PROPERTY(QString fileName READ fileName WRITE setFileNameStr NOTIFY fileNameChanged) +public: + + FileWidget(QWidget *parent = 0); + ~FileWidget(); + + QString fileName() const + { return m_fileName.toString(); } + + void setText(const QString &text) + { + m_label->setText(text); + } + + QString text() const + { + return m_label->text(); + } + + +public slots: + void setFileName(const QUrl &fileName); + void setFileNameStr(const QString &fileName); + void buttonPressed(); + void lineEditChanged(); + +signals: + void fileNameChanged(const QUrl &fileName); + +protected: + +private: + QLabel* m_label; + QPushButton *m_pushButton; + QLineEdit *m_lineEdit; + QUrl m_fileName; + +}; + +QT_END_NAMESPACE + +#endif + diff --git a/src/plugins/qmldesigner/components/propertyeditor/fontwidget.cpp b/src/plugins/qmldesigner/components/propertyeditor/fontwidget.cpp new file mode 100644 index 00000000000..8b2039dd6c6 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/fontwidget.cpp @@ -0,0 +1,172 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "fontwidget.h" +#include <QPushButton> +#include <QLabel> +#include <QHBoxLayout> +#include <QFontDialog> +#include <QApplication> +#include <QComboBox> +#include <QLabel> + +QML_DEFINE_TYPE(Bauhaus,1,0,FontWidget,QmlDesigner::FontWidget); + +namespace QmlDesigner { + +FontWidget::FontWidget(QWidget *parent) + : QWidget(parent), + m_label(new QLabel(this)), + m_fontButton(new QPushButton("ABC..", this)) +{ + QHBoxLayout *horizonalBoxLayout = new QHBoxLayout(this); + + horizonalBoxLayout->addWidget(m_label); + horizonalBoxLayout->addWidget(m_fontButton); + m_fontButton->setMinimumHeight(20); + m_fontButton->setCheckable(true); + connect(m_fontButton, SIGNAL(toggled(bool)), SLOT(openFontEditor(bool))); +} + +FontWidget::~FontWidget() +{ +} + +void FontWidget::openFontEditor(bool show) +{ + if (show && m_fontDialog.isNull()) { + m_fontDialog = new QFontDialog(); + m_fontDialog->setAttribute(Qt::WA_DeleteOnClose); + m_fontDialog->setCurrentFont(m_font); + QComboBox *comboBox = m_fontDialog->findChild<QComboBox*>(); + QList<QLabel*> labels = m_fontDialog->findChildren<QLabel*>(); + foreach (QLabel *label, labels) + if (label->buddy() == comboBox) + label->hide(); + comboBox->hide(); + m_fontDialog->show(); + connect(m_fontDialog.data(), SIGNAL(accepted()), SLOT(updateFont())); + connect(m_fontDialog.data(), SIGNAL(destroyed(QObject*)), SLOT(resetFontButton())); + } else { + delete m_fontDialog.data(); + } +} + +void FontWidget::updateFont() +{ + QFont font(m_fontDialog->currentFont()); + setFamily(font.family()); + setItalic(font.italic()); + setBold(font.bold()); + setFontSize(font.pointSizeF()); + emit dataFontChanged(); +} + +void FontWidget::resetFontButton() +{ + m_fontButton->setChecked(false); +} + +QString FontWidget::text() const +{ + return m_label->text(); +} + +void FontWidget::setText(const QString &text) +{ + m_label->setText(text); +} + +QString FontWidget::family() const +{ + return m_font.family(); +} + +void FontWidget::setFamily(const QString &fontFamily) +{ + if (fontFamily != m_font.family()) { + m_font.setFamily(fontFamily); + emit familyChanged(); + } +} + +bool FontWidget::isBold() const +{ + return m_font.bold(); +} + +void FontWidget::setBold(bool isBold) +{ + if (m_font.bold() != isBold) { + m_font.setBold(isBold); + emit boldChanged(); + } +} + +bool FontWidget::isItalic() const +{ + return m_font.italic(); +} + +void FontWidget::setItalic(bool isItalic) +{ + if (m_font.italic() != isItalic) { + m_font.setItalic(isItalic); + emit italicChanged(); + } +} + + +QFont FontWidget::font() const +{ + return m_font; +} + +void FontWidget::setFont(QFont font) +{ + if (m_font != font) { + m_font = font; + emit dataFontChanged(); + } +} + +qreal FontWidget::fontSize() const +{ + return m_font.pointSizeF(); +} + +void FontWidget::setFontSize(qreal size) +{ + if (m_font.pointSizeF() != size) { + m_font.setPointSizeF(size); + emit fontSizeChanged(); + } +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/propertyeditor/fontwidget.h b/src/plugins/qmldesigner/components/propertyeditor/fontwidget.h new file mode 100644 index 00000000000..5bc8afbf821 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/fontwidget.h @@ -0,0 +1,100 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef FONTWIDGET_H +#define FONTWIDGET_H + +#include <QWeakPointer> +#include <QWidget> +#include <qml.h> + + + +class QLabel; +class QPushButton; +class QFontDialog; + +namespace QmlDesigner { + +class FontWidget : public QWidget +{ + Q_OBJECT + Q_PROPERTY(QString text READ text WRITE setText) + Q_PROPERTY(QString family READ family WRITE setFamily NOTIFY familyChanged) + Q_PROPERTY(bool bold READ isBold WRITE setBold NOTIFY boldChanged) + Q_PROPERTY(bool italic READ isItalic WRITE setItalic NOTIFY italicChanged) + Q_PROPERTY(qreal fontSize READ fontSize WRITE setFontSize NOTIFY fontSizeChanged) + Q_PROPERTY(QFont dataFont READ font WRITE setFont NOTIFY dataFontChanged) + +public: + FontWidget(QWidget *parent = 0); + ~FontWidget(); + + QString text() const; + void setText(const QString &text); + + QString family() const; + void setFamily(const QString &fontFamily); + + bool isBold() const; + void setBold(bool isBold); + + bool isItalic() const; + void setItalic(bool isItalic); + + qreal fontSize() const; + void setFontSize(qreal size); + + QFont font() const; + void setFont(QFont size); + +signals: + void familyChanged(); + void boldChanged(); + void italicChanged(); + void fontSizeChanged(); + void dataFontChanged(); + +private slots: + void openFontEditor(bool show); + void updateFont(); + void resetFontButton(); + +private: //variables + QFont m_font; + QLabel *m_label; + QPushButton *m_fontButton; + QWeakPointer<QFontDialog> m_fontDialog; +}; + +} // namespace QmlDesigner + +QML_DECLARE_TYPE(QmlDesigner::FontWidget); + +#endif // FONTWIDGET_H diff --git a/src/plugins/qmldesigner/components/propertyeditor/genericpropertieswidget.cpp b/src/plugins/qmldesigner/components/propertyeditor/genericpropertieswidget.cpp new file mode 100644 index 00000000000..a76d73627f6 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/genericpropertieswidget.cpp @@ -0,0 +1,380 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include <QSet> +#include <QtDebug> +#include <QWidget> +#include <QApplication> + +#include "qteditorfactory.h" +#include "qtpropertymanager.h" +#include "qttreepropertybrowser.h" +#include "qtvariantproperty.h" + +#include "genericpropertieswidget.h" +#include "nodemetainfo.h" +#include "propertymetainfo.h" + +#include "modelutilities.h" + +using namespace QmlDesigner; + +GenericPropertiesWidget::GenericPropertiesWidget(QWidget* parent): + AbstractView(parent), + editor(new QtTreePropertyBrowser()), + variantManager(new QtVariantPropertyManager()), + enumManager(new QtEnumPropertyManager()) +{ +} + +QWidget* GenericPropertiesWidget::createPropertiesPage() +{ + editor->setRootIsDecorated(false); + + QtEnumEditorFactory *enumFactory = new QtEnumEditorFactory(); + editor->setFactoryForManager(enumManager, enumFactory); + + QtVariantEditorFactory *variantFactory = new QtVariantEditorFactory(); + editor->setFactoryForManager(variantManager, variantFactory); + + reconnectEditor(); + + return editor; +} + +void GenericPropertiesWidget::selectedNodesChanged(const QList<ModelNode> &selectedNodeList, + const QList<ModelNode> &lastSelectedNodeList) +{ + Q_UNUSED(lastSelectedNodeList); + + if (selectedNode.isValid() && selectedNodeList.contains(selectedNode)) + return; + + if (selectedNodeList.isEmpty()) + select(ModelNode()); + else + select(selectedNodeList.at(0)); +} + +void GenericPropertiesWidget::select(const ModelNode& node) +{ + if (!node.isValid() || (selectedNode.isValid() && node == selectedNode)) + return; + + selectedNode = node; + + disconnectEditor(); + + editor->setUpdatesEnabled(false); + editor->clear(); + buildPropertyEditorItems(); + editor->setUpdatesEnabled(true); + + reconnectEditor(); +} + +void GenericPropertiesWidget::disconnectEditor() +{ + disconnect(variantManager, SIGNAL(propertyChanged(QtProperty*)), this, SLOT(propertyChanged(QtProperty*))); + disconnect(enumManager, SIGNAL(propertyChanged(QtProperty*)), this, SLOT(propertyChanged(QtProperty*))); +} + +void GenericPropertiesWidget::reconnectEditor() +{ + connect(enumManager, SIGNAL(propertyChanged(QtProperty*)), this, SLOT(propertyChanged(QtProperty*))); + connect(variantManager, SIGNAL(propertyChanged(QtProperty*)), this, SLOT(propertyChanged(QtProperty*))); +} + +QtProperty* GenericPropertiesWidget::addVariantProperty(const PropertyMetaInfo& propertyMetaInfo, + const QHash<QString, NodeProperty>& propertiesWithValues, + const NodeInstance& instance) +{ +// qDebug() << "\t\tAdding variant property" << propertyMetaInfo.name() << "..."; + + if (variantManager->isPropertyTypeSupported(propertyMetaInfo.variantTypeId())) { + QtVariantProperty* item = variantManager->addProperty(propertyMetaInfo.variantTypeId(), propertyMetaInfo.name()); + + if (propertiesWithValues.contains(propertyMetaInfo.name())) { + NodeProperty nodeProperty(propertiesWithValues[propertyMetaInfo.name()]); + + if (nodeProperty.isValid()) { + item->setValue(nodeProperty.value()); + item->setModified(true); + } + } + + if (!item->isModified()) { + item->setValue(instance.property(propertyMetaInfo.name())); //TODO fix this + } + + return item; + } else { +// qDebug() << "\t\t" +// << "Property type not supported for property" +// << propertyMetaInfo.name() +// << ", type name:" << propertyMetaInfo.variantTypeId() +// << "(" << propertyMetaInfo.type() << ")"; + return 0; + } +} + +QtProperty* GenericPropertiesWidget::addEnumProperty(const PropertyMetaInfo& propertyMetaInfo, + const QHash<QString, NodeProperty>& propertiesWithValues, + const NodeInstance& instance) +{ +// qDebug() << "\t\tAdding enum property" << propertyMetaInfo.name() << "..."; + + QtProperty* item = enumManager->addProperty(propertyMetaInfo.name()); + + QList<QString> elementNames(propertyMetaInfo.enumerator().elementNames()); + enumManager->setEnumNames(item, elementNames); + + if (propertiesWithValues.contains(propertyMetaInfo.name())) { + NodeProperty nodeProperty(propertiesWithValues[propertyMetaInfo.name()]); + + int selectionIndex = elementNames.indexOf(nodeProperty.value().toString()); + if (selectionIndex != -1) { + enumManager->setValue(item, selectionIndex); + item->setModified(true); + } + } + + if (!item->isModified()) { + int selectionIndex = elementNames.indexOf(instance.property(propertyMetaInfo.name()).toString()); // TODO Fix this + if (selectionIndex != -1) { + enumManager->setValue(item, selectionIndex); + } + } + + return item; +} + +QtProperty* GenericPropertiesWidget::addFlagProperty(const PropertyMetaInfo &/*propertyMetaInfo*/, + const QHash<QString, NodeProperty> &/*propertiesWithValues*/, + const NodeInstance &/*instance*/) +{ +// qDebug() << "\t\tFlags are not yet supported. (Property: " << propertyMetaInfo.name() << ")"; + + return 0; +} + +QtProperty* GenericPropertiesWidget::addProperties(const NodeMetaInfo& nodeMetaInfo, + const QHash<QString, NodeProperty>& propertiesWithValues, + const NodeInstance& instance) +{ +// qDebug() << "\tAdding" << nodeMetaInfo.properties().size() +// << "properties for (super)node with type" << nodeMetaInfo.className() << "..."; + + QtProperty* groupItem = variantManager->addProperty(QtVariantPropertyManager::groupTypeId(), + nodeMetaInfo.typeName()); + + foreach (PropertyMetaInfo propMetaInfo, nodeMetaInfo.properties()) { + if (!propMetaInfo.isVisibleToPropertyEditor()) + continue; + + QtProperty* property = 0; + + if (propMetaInfo.isEnumType()) { + property = addEnumProperty(propMetaInfo, propertiesWithValues, instance); + } else if (propMetaInfo.isFlagType()) { + property = addFlagProperty(propMetaInfo, propertiesWithValues, instance); + } else { + property = addVariantProperty(propMetaInfo, propertiesWithValues, instance); + } + + if (property) { + groupItem->addSubProperty(property); + } + } + + return groupItem; +} + +void GenericPropertiesWidget::buildPropertyEditorItems() +{ + if (!selectedNode.isValid()) { + return; + } + +// qDebug() << "buildPropertyEditorItems for node" << selectedNode.name() << "..."; + + QList<NodeMetaInfo> allClasses; + allClasses.append(selectedNode.metaInfo()); + allClasses += selectedNode.metaInfo().superClasses(); +// qDebug() << "\tNode has" << allClasses.size() << "(super) classes"; + + QHash<QString, NodeProperty> propertiesWithValues; + foreach (const NodeProperty & nodeProperty, selectedNode.properties()) { + propertiesWithValues[nodeProperty.name()] = nodeProperty; + } + +// qDebug() << "\tNode has" << propertiesWithValues.size() << "properties set."; + +// NodeInstance instance = ModelUtilities::instanceForNode(selectedNode); +// +// foreach (const NodeMetaInfo &info, allClasses) { +// // FIXME: the add property is quite (too) expensive! +// editor->addProperty(addProperties(info, propertiesWithValues, instance)); +// } +} + +static bool nodeHasProperty(const ModelNode& node, const QString& name) +{ + if (node.metaInfo().hasProperty(name)) + return true; + + foreach (const NodeMetaInfo& info, node.metaInfo().superClasses()) { + if (info.hasProperty(name)) + return true; + } + + return false; +} + +void GenericPropertiesWidget::propertyChanged(QtProperty* property) +{ +// qDebug() << "property" << property->propertyName() << "changed..."; + + QtVariantProperty* variantProperty = dynamic_cast<QtVariantProperty*>(property); + + if (variantProperty) { +// qDebug() << "\tnew value: " << variantProperty->value(); + + if (selectedNode.property(property->propertyName()).value() == variantProperty->value()) { +// qDebug() << "\twhich is the same as the old one, so we'll forget about it."; + } else { + if (nodeHasProperty(selectedNode, property->propertyName())) { + selectedNode.setPropertyValue(property->propertyName(), variantProperty->value()); + + if (!property->isModified()) + property->setModified(true); + } else { +// qDebug() << "--- property " << property->propertyName() << "ignored: it doesn't exist in the metadata."; + } + } + } else { +// qDebug("\tEep: changed property is not a variant property"); + } +} + +void GenericPropertiesWidget::modelAttached(Model *model) +{ + AbstractView::modelAttached(model); +} + +void GenericPropertiesWidget::nodeCreated(const ModelNode&) +{ +} + +void GenericPropertiesWidget::nodeAboutToBeRemoved(const ModelNode &removedNode) +{ + if (selectedNode.isValid() && removedNode.isValid() && selectedNode == removedNode) { + select(selectedNode.parentNode()); + } +} + +void GenericPropertiesWidget::propertyAdded(const NodeState& state, const NodeProperty&) +{ + if (selectedNode.isValid() && state.isValid() && selectedNode == state.modelNode()) { + select(state.modelNode()); + } +} + +void GenericPropertiesWidget::propertyAboutToBeRemoved(const NodeState& /* state */, const NodeProperty&) +{ +} + +void GenericPropertiesWidget::nodeReparented(const ModelNode &node, const ModelNode &oldParent, const ModelNode &newParent) +{ + Q_UNUSED(node); + Q_UNUSED(oldParent); + Q_UNUSED(newParent); +} + +void GenericPropertiesWidget::propertyValueChanged(const NodeState& state, const NodeProperty& property, + const QVariant&, const QVariant& ) +{ + if (!selectedNode.isValid() || selectedNode != state.modelNode()) + return; + + disconnectEditor(); + + ModelNode node(state.modelNode()); + + foreach (QtProperty* qtProperty, enumManager->properties()) { + if (qtProperty->propertyName() == property.name()) { + QList<QString> elementNames = node.property(property.name()).metaInfo().enumerator().elementNames(); + int selectionIndex = elementNames.indexOf(property.value().toString()); + if (selectionIndex != -1) { + enumManager->setValue(qtProperty, selectionIndex); + qtProperty->setModified(true); + } + + reconnectEditor(); + return; + } + } + + foreach (QtProperty* qtProperty, variantManager->properties()) { + if (qtProperty->propertyName() == property.name()) { + (dynamic_cast<QtVariantProperty*>(qtProperty))->setValue(property.value()); + qtProperty->setModified(true); + + reconnectEditor(); + return; + } + } + + reconnectEditor(); +} + +void GenericPropertiesWidget::modelStateAboutToBeRemoved(const ModelState &/*modelState*/) +{ +// TODO: implement +} + +void GenericPropertiesWidget::modelStateAdded(const ModelState &/*modelState*/) +{ +// TODO: implement +} + +void GenericPropertiesWidget::nodeStatesAboutToBeRemoved(const QList<NodeState> &/*nodeStateList*/) +{ +// TODO: implement +} + +void GenericPropertiesWidget::nodeStatesAdded(const QList<NodeState> &/*nodeStateList*/) +{ +// TODO: implement +} + +void GenericPropertiesWidget::anchorsChanged(const NodeState &/*nodeState*/) +{ +// TODO: implement +} diff --git a/src/plugins/qmldesigner/components/propertyeditor/genericpropertieswidget.h b/src/plugins/qmldesigner/components/propertyeditor/genericpropertieswidget.h new file mode 100644 index 00000000000..1b577bd479e --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/genericpropertieswidget.h @@ -0,0 +1,96 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef PropertyView_h +#define PropertyView_h + +#include <QTabWidget> + +#include <modelnode.h> +#include <widgetqueryview.h> + + +class QtProperty; + +namespace QmlDesigner { + +class GenericPropertiesWidget: public AbstractView +{ + Q_OBJECT + +public: + GenericPropertiesWidget(QWidget *parent = 0); + QWidget* createPropertiesPage(); + void selectedNodesChanged(const QList<ModelNode> &selectedNodeList, + const QList<ModelNode> &lastSelectedNodeList); + + virtual void modelAttached(Model *model); + virtual void nodeCreated(const ModelNode &createdNode); + virtual void nodeAboutToBeRemoved(const ModelNode &removedNode); + virtual void propertyAdded(const NodeState& state, const NodeProperty& property); + virtual void propertyAboutToBeRemoved(const NodeState &state, const NodeProperty &property); + virtual void nodeReparented(const ModelNode &node, const ModelNode &oldParent, const ModelNode &newParent); + virtual void propertyValueChanged(const NodeState& state, const NodeProperty& property, + const QVariant& newValue, const QVariant& oldValue); + + void modelStateAboutToBeRemoved(const ModelState &modelState); + void modelStateAdded(const ModelState &modelState); + + void nodeStatesAboutToBeRemoved(const QList<NodeState> &nodeStateList); + void nodeStatesAdded(const QList<NodeState> &nodeStateList); + + void anchorsChanged(const NodeState &nodeState); + +public slots: + void select(const ModelNode& node); + +private slots: + void propertyChanged(class QtProperty* property); + +private: + void buildPropertyEditorItems(); + QtProperty* addVariantProperty(const PropertyMetaInfo& propertyMetaInfo, const QHash<QString, NodeProperty>& propertiesWithValues, const NodeInstance& instance); + QtProperty* addEnumProperty(const PropertyMetaInfo& propertyMetaInfo, const QHash<QString, NodeProperty>& propertiesWithValues, const NodeInstance& instance); + QtProperty* addFlagProperty(const PropertyMetaInfo& propertyMetaInfo, const QHash<QString, NodeProperty>& propertiesWithValues, const NodeInstance& instance); + QtProperty* addProperties(const NodeMetaInfo& nodeMetaInfo, const QHash<QString, NodeProperty>& propertiesWithValues, const NodeInstance& instance); + + void disconnectEditor(); + void reconnectEditor(); + +private: + class QtTreePropertyBrowser *editor; + class QtVariantPropertyManager* variantManager; + class QtEnumPropertyManager* enumManager; + + ModelNode selectedNode; +}; + +} + +#endif // PropertyView_h diff --git a/src/plugins/qmldesigner/components/propertyeditor/gradientdialog.cpp b/src/plugins/qmldesigner/components/propertyeditor/gradientdialog.cpp new file mode 100644 index 00000000000..a763426e271 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/gradientdialog.cpp @@ -0,0 +1,353 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "gradientdialog.h" +#include "ui_gradientdialog.h" +#include <QtGui/QPushButton> + +QT_BEGIN_NAMESPACE + +class GradientDialogPrivate +{ + GradientDialog *q_ptr; + Q_DECLARE_PUBLIC(GradientDialog) +public: + + void slotAboutToShowDetails(bool details, int extensionWidthHint); + + Ui::GradientDialog m_ui; +}; + +void GradientDialogPrivate::slotAboutToShowDetails(bool details, int extensionWidthHint) +{ + if (details) { + q_ptr->resize(q_ptr->size() + QSize(extensionWidthHint, 0)); + } else { + q_ptr->setMinimumSize(1, 1); + q_ptr->resize(q_ptr->size() - QSize(extensionWidthHint, 0)); + q_ptr->setMinimumSize(0, 0); + } +} + +/*! + \class GradientDialog + + \brief The GradientDialog class provides a dialog for specifying gradients. + + The gradient dialog's function is to allow users to edit gradients. + For example, you might use this in a drawing program to allow the user to set the brush gradient. + + \table + \row + \o \inlineimage qtgradientdialog.png + \o \inlineimage qtgradientdialogextension.png + \header + \o Details extension hidden + \o Details extension visible + \endtable + + Starting from the top of the dialog there are several buttons: + + \image qtgradientdialogtopbuttons.png + + The first three buttons allow for changing a type of the gradient (QGradient::Type), while the second three allow for + changing spread of the gradient (QGradient::Spread). The last button shows or hides the details extension of the dialog. + Conceptually the default view with hidden details provides the full functional control over gradient editing. + The additional extension with details allows to set gradient's parameters more precisely. The visibility + of extension can be controlled by detailsVisible property. Moreover, if you don't want the user to + switch on or off the visibility of extension you can set the detailsButtonVisible property to false. + + Below top buttons there is an area where edited gradient is interactively previewed. + In addition the user can edit gradient type's specific parameters directly in this area by dragging + appropriate handles. + + \table + \row + \o \inlineimage qtgradientdialoglineareditor.png + \o \inlineimage qtgradientdialogradialeditor.png + \o \inlineimage qtgradientdialogconicaleditor.png + \header + \o Editing linear type + \o Editing radial type + \o Editing conical type + \row + \o The user can change the start and final point positions by dragging the circular handles. + \o The user can change the center and focal point positions by dragging the circular handles + and can change the gradient's radius by dragging horizontal or vertical line. + \o The user can change the center point by dragging the circular handle + and can change the gradient's angle by dragging the big wheel. + \endtable + + In the middle of the dialog there is an area where the user can edit gradient stops. + + \table + \row + \o \inlineimage qtgradientdialogstops.png + \o \inlineimage qtgradientdialogstopszoomed.png + \endtable + + The top part of this area contains stop handles, and bottom part shows the preview of gradient stops path. + In order to create a new gradient stop double click inside the view over the desired position. + If you double click on existing stop handle in the top part of the view, clicked handle will be duplicated + (duplicate will contain the same color). + The stop can be activated by clicking on its handle. You can activate previous or next stop by pressing + left or right key respectively. To jump to the first or last stop press home or end key respectively. + The gradient stops editor supports multiselection. + Clicking a handle holding the shift modifier key down will select a range of stops between + the active stop and clicked one. Clicking a handle holding control modifier key down will remove from or + add to selection the clicked stop depending if it was or wasn't already selected respectively. + Multiselection can also be created using rubberband (by pressing the left mouse button outside + of any handle and dragging). + Sometimes it's hard to select a stop because its handle can be partially covered by other handle. + In that case the user can zoom in the view by spinning mouse wheel. + The selected stop handles can be moved by drag & drop. In order to remove selected stops press delete key. + For convenience context menu is provided with the following actions: + + \list + \o New Stop - creates a new gradient stop + \o Delete - removes the active and all selected stops + \o Flip All - mirrors all stops + \o Select All - selects all stops + \o Zoom In - zooms in + \o Zoom Out - zooms out + \o Zoom All - goes back to original 100% zoom + \endlist + + The bottom part of the GradientDialog contains a set of widgets allowing to control the color of + the active and selected stops. + + \table + \row + \o \inlineimage qtgradientdialogcolorhsv.png + \o \inlineimage qtgradientdialogcolorrgb.png + \endtable + + + The color button shows the color of the active gradient stop. It also allows for choosing + a color from standard color dialog and applying it to the + active stop and all selected stops. It's also possible to drag a color directly from the color button + and to drop it in gradient stops editor at desired position (it will create new stop with dragged color) + or at desired stop handle (it will change the color of that handle). + + To the right of color button there is a set of 2 radio buttons which allows to switch between + HVS and RGB color spec. + + Finally there are 4 color sliders working either in HSVA (hue saturation value alpha) or + RGBA (red green blue alpha) mode, depending on which radio button is chosen. The radio buttons + can be controlled programatically by spec() and setSpec() methods. The sliders show the + color of the active stop. By double clicking inside color slider you can set directly the desired color. + Changes of slider's are applied to stop selection in the way that the color + component being changed is applied to stops in selection only, while other components + remain unchanged in selected stops (e.g. when the user is changing the saturation, + new saturation is applied to selected stops preventing original hue, value and alpha in multiselection). + + The convenient static functions getGradient() provide modal gradient dialogs, e.g.: + + \snippet doc/src/snippets/code/tools_shared_qtgradienteditor_qtgradientdialog.cpp 0 + + In order to have more control over the properties of GradientDialog use + standard QDialog::exec() method: + + \snippet doc/src/snippets/code/tools_shared_qtgradienteditor_qtgradientdialog.cpp 1 + + \sa {Gradient View Example} +*/ + +/*! + Constructs a gradient dialog with \a parent as parent widget. +*/ + +GradientDialog::GradientDialog(QWidget *parent) + : QDialog(parent), d_ptr(new GradientDialogPrivate()) +{ +// setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + d_ptr->q_ptr = this; + d_ptr->m_ui.setupUi(this); + QPushButton *button = d_ptr->m_ui.buttonBox->button(QDialogButtonBox::Ok); + if (button) + button->setAutoDefault(false); + button = d_ptr->m_ui.buttonBox->button(QDialogButtonBox::Cancel); + if (button) + button->setAutoDefault(false); + connect(d_ptr->m_ui.gradientEditor, SIGNAL(aboutToShowDetails(bool, int)), + this, SLOT(slotAboutToShowDetails(bool, int))); +} + +/*! + Destroys the gradient dialog +*/ + +GradientDialog::~GradientDialog() +{ +} + +/*! + \property GradientDialog::gradient + \brief the gradient of the dialog +*/ +void GradientDialog::setGradient(const QGradient &gradient) +{ + d_ptr->m_ui.gradientEditor->setGradient(gradient); +} + +QGradient GradientDialog::gradient() const +{ + return d_ptr->m_ui.gradientEditor->gradient(); +} + +/*! + \property GradientDialog::backgroundCheckered + \brief whether the background of widgets able to show the colors with alpha channel is checkered. + + \table + \row + \o \inlineimage qtgradientdialogbackgroundcheckered.png + \o \inlineimage qtgradientdialogbackgroundtransparent.png + \row + \o \snippet doc/src/snippets/code/tools_shared_qtgradienteditor_qtgradientdialog.cpp 2 + \o \snippet doc/src/snippets/code/tools_shared_qtgradienteditor_qtgradientdialog.cpp 3 + \endtable + + When this property is set to true (the default) widgets inside gradient dialog like color button, + color sliders, gradient stops editor and gradient editor will show checkered background + in case of transparent colors. Otherwise the background of these widgets is transparent. +*/ + +bool GradientDialog::isBackgroundCheckered() const +{ + return d_ptr->m_ui.gradientEditor->isBackgroundCheckered(); +} + +void GradientDialog::setBackgroundCheckered(bool checkered) +{ + d_ptr->m_ui.gradientEditor->setBackgroundCheckered(checkered); +} + +/*! + \property GradientDialog::detailsVisible + \brief whether details extension is visible. + + When this property is set to true the details extension is visible. By default + this property is set to false and the details extension is hidden. + + \sa detailsButtonVisible +*/ +bool GradientDialog::detailsVisible() const +{ + return d_ptr->m_ui.gradientEditor->detailsVisible(); +} + +void GradientDialog::setDetailsVisible(bool visible) +{ + d_ptr->m_ui.gradientEditor->setDetailsVisible(visible); +} + +/*! + \property GradientDialog::detailsButtonVisible + \brief whether the details button allowing for showing and hiding details extension is visible. + + When this property is set to true (the default) the details button is visible and the user + can show and hide details extension interactively. Otherwise the button is hidden and the details + extension is always visible or hidded depending on the value of detailsVisible property. + + \sa detailsVisible +*/ +bool GradientDialog::isDetailsButtonVisible() const +{ + return d_ptr->m_ui.gradientEditor->isDetailsButtonVisible(); +} + +void GradientDialog::setDetailsButtonVisible(bool visible) +{ + d_ptr->m_ui.gradientEditor->setDetailsButtonVisible(visible); +} + +/*! + Returns the current QColor::Spec used for the color sliders in the dialog. +*/ +QColor::Spec GradientDialog::spec() const +{ + return d_ptr->m_ui.gradientEditor->spec(); +} + +/*! + Sets the current QColor::Spec to \a spec used for the color sliders in the dialog. +*/ +void GradientDialog::setSpec(QColor::Spec spec) +{ + d_ptr->m_ui.gradientEditor->setSpec(spec); +} + +/*! + Executes a modal gradient dialog, lets the user to specify a gradient, and returns that gradient. + + If the user clicks \gui OK, the gradient specified by the user is returned. If the user clicks \gui Cancel, the \a initial gradient is returned. + + The dialog is constructed with the given \a parent. \a caption is shown as the window title of the dialog and + \a initial is the initial gradient shown in the dialog. If the \a ok parameter is not-null, + the value it refers to is set to true if the user clicks \gui OK, and set to false if the user clicks \gui Cancel. +*/ +QGradient GradientDialog::getGradient(bool *ok, const QGradient &initial, QWidget *parent, const QString &caption) +{ + GradientDialog dlg(parent); + if (!caption.isEmpty()) + dlg.setWindowTitle(caption); + dlg.setGradient(initial); + const int res = dlg.exec(); + if (ok) { + *ok = (res == QDialog::Accepted) ? true : false; + } + if (res == QDialog::Accepted) + return dlg.gradient(); + return initial; +} + +/*! + This method calls getGradient(ok, QLinearGradient(), parent, caption). +*/ +QGradient GradientDialog::getGradient(bool *ok, QWidget *parent, const QString &caption) +{ + return getGradient(ok, QLinearGradient(), parent, caption); +} + +QT_END_NAMESPACE + +#include "moc_gradientdialog.cpp" diff --git a/src/plugins/qmldesigner/components/propertyeditor/gradientdialog.h b/src/plugins/qmldesigner/components/propertyeditor/gradientdialog.h new file mode 100644 index 00000000000..b8f338a5884 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/gradientdialog.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef GRADIENTDIALOG_H +#define GRADIENTDIALOG_H + +#include <QtGui/QDialog> + +QT_BEGIN_NAMESPACE + +class GradientDialogPrivate; + +class GradientDialog : public QDialog +{ + Q_OBJECT + Q_PROPERTY(QGradient gradient READ gradient WRITE setGradient) + Q_PROPERTY(bool backgroundCheckered READ isBackgroundCheckered WRITE setBackgroundCheckered) + Q_PROPERTY(bool detailsVisible READ detailsVisible WRITE setDetailsVisible) + Q_PROPERTY(bool detailsButtonVisible READ isDetailsButtonVisible WRITE setDetailsButtonVisible) +public: + GradientDialog(QWidget *parent = 0); + ~GradientDialog(); + + void setGradient(const QGradient &gradient); + QGradient gradient() const; + + bool isBackgroundCheckered() const; + void setBackgroundCheckered(bool checkered); + + bool detailsVisible() const; + void setDetailsVisible(bool visible); + + bool isDetailsButtonVisible() const; + void setDetailsButtonVisible(bool visible); + + QColor::Spec spec() const; + void setSpec(QColor::Spec spec); + + static QGradient getGradient(bool *ok, const QGradient &initial, QWidget *parent = 0, const QString &caption = QString()); + static QGradient getGradient(bool *ok, QWidget *parent = 0, const QString &caption = QString()); + +private: + QScopedPointer<GradientDialogPrivate> d_ptr; + Q_DECLARE_PRIVATE(GradientDialog) + Q_DISABLE_COPY(GradientDialog) + Q_PRIVATE_SLOT(d_func(), void slotAboutToShowDetails(bool details, int extensionWidthHint)) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/qmldesigner/components/propertyeditor/gradientdialog.ui b/src/plugins/qmldesigner/components/propertyeditor/gradientdialog.ui new file mode 100644 index 00000000000..d6a19c9dbbb --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/gradientdialog.ui @@ -0,0 +1,122 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <comment>********************************************************************* +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +*********************************************************************</comment> + <class>GradientDialog</class> + <widget class="QDialog" name="GradientDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>178</width> + <height>81</height> + </rect> + </property> + <property name="windowTitle"> + <string>Edit Gradient</string> + </property> + <layout class="QVBoxLayout"> + <item> + <widget class="GradientEditor" name="gradientEditor"> + <property name="sizePolicy"> + <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>GradientEditor</class> + <extends>QFrame</extends> + <header>gradienteditor.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>GradientDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>72</x> + <y>224</y> + </hint> + <hint type="destinationlabel"> + <x>21</x> + <y>243</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>GradientDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>168</x> + <y>233</y> + </hint> + <hint type="destinationlabel"> + <x>152</x> + <y>251</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/plugins/qmldesigner/components/propertyeditor/gradienteditor.cpp b/src/plugins/qmldesigner/components/propertyeditor/gradienteditor.cpp new file mode 100644 index 00000000000..c29a21886b8 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/gradienteditor.cpp @@ -0,0 +1,933 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "gradienteditor.h" +#include "qtgradientstopscontroller.h" +#include "ui_gradienteditor.h" + +QT_BEGIN_NAMESPACE + +class GradientEditorPrivate +{ + GradientEditor *q_ptr; + Q_DECLARE_PUBLIC(GradientEditor) +public: + GradientEditorPrivate() : m_gradient(QLinearGradient()) {} + + void slotGradientStopsChanged(const QGradientStops &stops); + void slotTypeChanged(int type); + void slotSpreadChanged(int spread); + void slotStartLinearXChanged(double value); + void slotStartLinearYChanged(double value); + void slotEndLinearXChanged(double value); + void slotEndLinearYChanged(double value); + void slotCentralRadialXChanged(double value); + void slotCentralRadialYChanged(double value); + void slotFocalRadialXChanged(double value); + void slotFocalRadialYChanged(double value); + void slotRadiusRadialChanged(double value); + void slotCentralConicalXChanged(double value); + void slotCentralConicalYChanged(double value); + void slotAngleConicalChanged(double value); + + void slotDetailsChanged(bool details); + + void startLinearChanged(const QPointF &point); + void endLinearChanged(const QPointF &point); + void centralRadialChanged(const QPointF &point); + void focalRadialChanged(const QPointF &point); + void radiusRadialChanged(qreal radius); + void centralConicalChanged(const QPointF &point); + void angleConicalChanged(qreal angle); + + void setStartLinear(const QPointF &point); + void setEndLinear(const QPointF &point); + void setCentralRadial(const QPointF &point); + void setFocalRadial(const QPointF &point); + void setRadiusRadial(qreal radius); + void setCentralConical(const QPointF &point); + void setAngleConical(qreal angle); + + void setType(QGradient::Type type); + void showDetails(bool details); + + void setSpinBox(QDoubleSpinBox *spinBox, const char *slot, double max = 1.0, double step = 0.01, int decimals = 3); + void reset(); + void setLayout(bool details); + void layoutDetails(bool details); + bool row4Visible() const; + bool row5Visible() const; + int extensionWidthHint() const; + + void setCombos(bool combos); + + QGradient gradient() const; + void updateGradient(bool emitSignal); + + Ui::GradientEditor m_ui; + QtGradientStopsController *m_gradientStopsController; + + QDoubleSpinBox *startLinearXSpinBox; + QDoubleSpinBox *startLinearYSpinBox; + QDoubleSpinBox *endLinearXSpinBox; + QDoubleSpinBox *endLinearYSpinBox; + QDoubleSpinBox *centralRadialXSpinBox; + QDoubleSpinBox *centralRadialYSpinBox; + QDoubleSpinBox *focalRadialXSpinBox; + QDoubleSpinBox *focalRadialYSpinBox; + QDoubleSpinBox *radiusRadialSpinBox; + QDoubleSpinBox *centralConicalXSpinBox; + QDoubleSpinBox *centralConicalYSpinBox; + QDoubleSpinBox *angleConicalSpinBox; + + QButtonGroup *m_typeGroup; + QButtonGroup *m_spreadGroup; + + QGradient::Type m_type; + + QGridLayout *m_gridLayout; + QWidget *m_hiddenWidget; + QGridLayout *m_hiddenLayout; + bool m_details; + bool m_detailsButtonVisible; + bool m_backgroundCheckered; + + QGradient m_gradient; + + bool m_combos; +}; + +QGradient GradientEditorPrivate::gradient() const +{ + QLinearGradient gradient; + + gradient.setStops(m_ui.gradientWidget->gradientStops()); + gradient.setSpread(m_ui.gradientWidget->gradientSpread()); + gradient.setCoordinateMode(QGradient::StretchToDeviceMode); + + return gradient; +} + +void GradientEditorPrivate::updateGradient(bool emitSignal) +{ + QGradient grad = gradient(); + if (m_gradient == grad) + return; + + m_gradient = grad; + if (emitSignal) + emit q_ptr->gradientChanged(m_gradient); +} + +void GradientEditorPrivate::setCombos(bool combos) +{ + if (m_combos == combos) + return; + + m_combos = combos; + m_ui.linearButton->setVisible(!m_combos); + m_ui.radialButton->setVisible(!m_combos); + m_ui.conicalButton->setVisible(!m_combos); + m_ui.padButton->setVisible(!m_combos); + m_ui.repeatButton->setVisible(!m_combos); + m_ui.reflectButton->setVisible(!m_combos); + m_ui.typeComboBox->setVisible(m_combos); + m_ui.spreadComboBox->setVisible(m_combos); +} + +void GradientEditorPrivate::setLayout(bool details) +{ + QHBoxLayout *hboxLayout = new QHBoxLayout(); + hboxLayout->setObjectName(QString::fromUtf8("hboxLayout")); + hboxLayout->addWidget(m_ui.typeComboBox); + hboxLayout->addWidget(m_ui.spreadComboBox); + QHBoxLayout *typeLayout = new QHBoxLayout(); + typeLayout->setSpacing(0); + typeLayout->addWidget(m_ui.linearButton); + typeLayout->addWidget(m_ui.radialButton); + typeLayout->addWidget(m_ui.conicalButton); + hboxLayout->addLayout(typeLayout); + QHBoxLayout *spreadLayout = new QHBoxLayout(); + spreadLayout->setSpacing(0); + spreadLayout->addWidget(m_ui.padButton); + spreadLayout->addWidget(m_ui.repeatButton); + spreadLayout->addWidget(m_ui.reflectButton); + hboxLayout->addLayout(spreadLayout); + hboxLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum)); + hboxLayout->addWidget(m_ui.detailsButton); + m_gridLayout->addLayout(hboxLayout, 0, 0, 1, 2); + int span = 1; + if (details) + span = 7; + m_gridLayout->addWidget(m_ui.frame, 1, 0, span, 2); + int row = 2; + if (details) { + row = 8; + span = 4; + } + m_gridLayout->addWidget(m_ui.gradientStopsWidget, row, 0, span, 2); + QHBoxLayout *hboxLayout1 = new QHBoxLayout(); + hboxLayout1->setObjectName(QString::fromUtf8("hboxLayout1")); + hboxLayout1->addWidget(m_ui.colorLabel); + hboxLayout1->addWidget(m_ui.colorButton); + hboxLayout1->addWidget(m_ui.hsvRadioButton); + hboxLayout1->addWidget(m_ui.rgbRadioButton); + hboxLayout1->addItem(new QSpacerItem(16, 23, QSizePolicy::Expanding, QSizePolicy::Minimum)); + int addRow = 0; + if (details) + addRow = 9; + m_gridLayout->addLayout(hboxLayout1, 3 + addRow, 0, 1, 2); + m_gridLayout->addWidget(m_ui.hLabel, 4 + addRow, 0, 1, 1); + m_gridLayout->addWidget(m_ui.frame_2, 4 + addRow, 1, 1, 1); + m_gridLayout->addWidget(m_ui.sLabel, 5 + addRow, 0, 1, 1); + m_gridLayout->addWidget(m_ui.frame_5, 5 + addRow, 1, 1, 1); + m_gridLayout->addWidget(m_ui.vLabel, 6 + addRow, 0, 1, 1); + m_gridLayout->addWidget(m_ui.frame_3, 6 + addRow, 1, 1, 1); + m_gridLayout->addWidget(m_ui.aLabel, 7 + addRow, 0, 1, 1); + m_gridLayout->addWidget(m_ui.frame_4, 7 + addRow, 1, 1, 1); + + if (details) { + layoutDetails(details); + } +} + +void GradientEditorPrivate::layoutDetails(bool details) +{ + QGridLayout *gridLayout = m_gridLayout; + int col = 2; + if (!details) { + col = 0; + if (!m_hiddenWidget) { + m_hiddenWidget = new QWidget(); + m_hiddenLayout = new QGridLayout(m_hiddenWidget); + m_hiddenLayout->setContentsMargins(0, 0, 0, 0); + m_hiddenLayout->setSizeConstraint(QLayout::SetFixedSize); + } + gridLayout = m_hiddenLayout; + } + gridLayout->addWidget(m_ui.label1, 1, col + 0, 1, 1); + gridLayout->addWidget(m_ui.spinBox1, 1, col + 1, 1, 1); + gridLayout->addWidget(m_ui.label2, 2, col + 0, 1, 1); + gridLayout->addWidget(m_ui.spinBox2, 2, col + 1, 1, 1); + gridLayout->addWidget(m_ui.label3, 3, col + 0, 1, 1); + gridLayout->addWidget(m_ui.spinBox3, 3, col + 1, 1, 1); + gridLayout->addWidget(m_ui.label4, 4, col + 0, 1, 1); + gridLayout->addWidget(m_ui.spinBox4, 4, col + 1, 1, 1); + gridLayout->addWidget(m_ui.label5, 5, col + 0, 1, 1); + gridLayout->addWidget(m_ui.spinBox5, 5, col + 1, 1, 1); + gridLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding), 6, col + 0, 1, 1); + gridLayout->addWidget(m_ui.line1Widget, 7, col + 0, 1, 2); + gridLayout->addWidget(m_ui.zoomLabel, 8, col + 0, 1, 1); + gridLayout->addWidget(m_ui.zoomWidget, 8, col + 1, 1, 1); + gridLayout->addWidget(m_ui.zoomButtonsWidget, 9, col + 0, 1, 1); + gridLayout->addWidget(m_ui.zoomAllButton, 9, col + 1, 1, 1); + gridLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Preferred), 10, col + 0, 1, 1); + gridLayout->addWidget(m_ui.line2Widget, 11, col + 0, 1, 2); + gridLayout->addWidget(m_ui.positionLabel, 12, col + 0, 1, 1); + gridLayout->addWidget(m_ui.positionWidget, 12, col + 1, 1, 1); + gridLayout->addWidget(m_ui.hueLabel, 13, col + 0, 1, 1); + gridLayout->addWidget(m_ui.hueWidget, 13, col + 1, 1, 1); + gridLayout->addWidget(m_ui.saturationLabel, 14, col + 0, 1, 1); + gridLayout->addWidget(m_ui.saturationWidget, 14, col + 1, 1, 1); + gridLayout->addWidget(m_ui.valueLabel, 15, col + 0, 1, 1); + gridLayout->addWidget(m_ui.valueWidget, 15, col + 1, 1, 1); + gridLayout->addWidget(m_ui.alphaLabel, 16, col + 0, 1, 1); + gridLayout->addWidget(m_ui.alphaWidget, 16, col + 1, 1, 1); + + if (details) { + if (m_hiddenLayout) { + delete m_hiddenLayout; + m_hiddenLayout = 0; + } + if (m_hiddenWidget) { + delete m_hiddenWidget; + m_hiddenWidget = 0; + } + } +} + +int GradientEditorPrivate::extensionWidthHint() const +{ + if (m_details) + return q_ptr->size().width() - m_ui.gradientStopsWidget->size().width(); + + const int space = m_ui.spinBox1->geometry().left() - m_ui.label1->geometry().right(); + + return m_hiddenLayout->minimumSize().width() + space; +} + +void GradientEditorPrivate::slotDetailsChanged(bool details) +{ + showDetails(details); +} + +bool GradientEditorPrivate::row4Visible() const +{ + if (m_type == QGradient::ConicalGradient) + return false; + return true; +} + +bool GradientEditorPrivate::row5Visible() const +{ + if (m_type == QGradient::RadialGradient) + return true; + return false; +} + +void GradientEditorPrivate::showDetails(bool details) +{ + if (m_details == details) + return; + + bool blocked = m_ui.detailsButton->signalsBlocked(); + m_ui.detailsButton->blockSignals(true); + m_ui.detailsButton->setChecked(details); + m_ui.detailsButton->blockSignals(blocked); + + bool updates = q_ptr->updatesEnabled(); + q_ptr->setUpdatesEnabled(false); + + if (m_gridLayout) { + m_gridLayout->setEnabled(false); + delete m_gridLayout; + m_gridLayout = 0; + } + + if (!details) { + layoutDetails(details); + } + + emit q_ptr->aboutToShowDetails(details, extensionWidthHint()); + m_details = details; + + m_gridLayout = new QGridLayout(q_ptr); + m_gridLayout->setEnabled(false); + m_gridLayout->setObjectName(QString::fromUtf8("gridLayout")); + m_gridLayout->setContentsMargins(0, 0, 0, 0); + + m_ui.label4->setVisible(row4Visible()); + m_ui.label5->setVisible(row5Visible()); + m_ui.spinBox4->setVisible(row4Visible()); + m_ui.spinBox5->setVisible(row5Visible()); + + setLayout(details); + m_gridLayout->setEnabled(true); + + q_ptr->setUpdatesEnabled(updates); + q_ptr->update(); +} + +void GradientEditorPrivate::setSpinBox(QDoubleSpinBox *spinBox, const char *slot, double max, double step, int decimals) +{ + bool blocked = spinBox->signalsBlocked(); + spinBox->blockSignals(true); + spinBox->setDecimals(decimals); + spinBox->setMaximum(max); + spinBox->setSingleStep(step); + spinBox->blockSignals(blocked); + QObject::connect(spinBox, SIGNAL(valueChanged(double)), q_ptr, slot); +} + +void GradientEditorPrivate::reset() +{ + startLinearXSpinBox = 0; + startLinearYSpinBox = 0; + endLinearXSpinBox = 0; + endLinearYSpinBox = 0; + centralRadialXSpinBox = 0; + centralRadialYSpinBox = 0; + focalRadialXSpinBox = 0; + focalRadialYSpinBox = 0; + radiusRadialSpinBox = 0; + centralConicalXSpinBox = 0; + centralConicalYSpinBox = 0; + angleConicalSpinBox = 0; +} + +void GradientEditorPrivate::setType(QGradient::Type type) +{ + if (m_type == type) + return; + + m_type = type; + m_ui.spinBox1->disconnect(SIGNAL(valueChanged(double))); + m_ui.spinBox2->disconnect(SIGNAL(valueChanged(double))); + m_ui.spinBox3->disconnect(SIGNAL(valueChanged(double))); + m_ui.spinBox4->disconnect(SIGNAL(valueChanged(double))); + m_ui.spinBox5->disconnect(SIGNAL(valueChanged(double))); + + reset(); + + bool ena = true; + + if (m_gridLayout) { + ena = m_gridLayout->isEnabled(); + m_gridLayout->setEnabled(false); + } + + bool spreadEnabled = true; + + if (type == QGradient::LinearGradient) { + startLinearXSpinBox = m_ui.spinBox1; + setSpinBox(startLinearXSpinBox, SLOT(slotStartLinearXChanged(double))); + m_ui.label1->setText(QApplication::translate("GradientEditor", "Start X", 0, QApplication::UnicodeUTF8)); + + startLinearYSpinBox = m_ui.spinBox2; + setSpinBox(startLinearYSpinBox, SLOT(slotStartLinearYChanged(double))); + m_ui.label2->setText(QApplication::translate("GradientEditor", "Start Y", 0, QApplication::UnicodeUTF8)); + + endLinearXSpinBox = m_ui.spinBox3; + setSpinBox(endLinearXSpinBox, SLOT(slotEndLinearXChanged(double))); + m_ui.label3->setText(QApplication::translate("GradientEditor", "Final X", 0, QApplication::UnicodeUTF8)); + + endLinearYSpinBox = m_ui.spinBox4; + setSpinBox(endLinearYSpinBox, SLOT(slotEndLinearYChanged(double))); + m_ui.label4->setText(QApplication::translate("GradientEditor", "Final Y", 0, QApplication::UnicodeUTF8)); + + setStartLinear(m_ui.gradientWidget->startLinear()); + setEndLinear(m_ui.gradientWidget->endLinear()); + } else if (type == QGradient::RadialGradient) { + centralRadialXSpinBox = m_ui.spinBox1; + setSpinBox(centralRadialXSpinBox, SLOT(slotCentralRadialXChanged(double))); + m_ui.label1->setText(QApplication::translate("GradientEditor", "Central X", 0, QApplication::UnicodeUTF8)); + + centralRadialYSpinBox = m_ui.spinBox2; + setSpinBox(centralRadialYSpinBox, SLOT(slotCentralRadialYChanged(double))); + m_ui.label2->setText(QApplication::translate("GradientEditor", "Central Y", 0, QApplication::UnicodeUTF8)); + + focalRadialXSpinBox = m_ui.spinBox3; + setSpinBox(focalRadialXSpinBox, SLOT(slotFocalRadialXChanged(double))); + m_ui.label3->setText(QApplication::translate("GradientEditor", "Focal X", 0, QApplication::UnicodeUTF8)); + + focalRadialYSpinBox = m_ui.spinBox4; + setSpinBox(focalRadialYSpinBox, SLOT(slotFocalRadialYChanged(double))); + m_ui.label4->setText(QApplication::translate("GradientEditor", "Focal Y", 0, QApplication::UnicodeUTF8)); + + radiusRadialSpinBox = m_ui.spinBox5; + setSpinBox(radiusRadialSpinBox, SLOT(slotRadiusRadialChanged(double)), 2.0); + m_ui.label5->setText(QApplication::translate("GradientEditor", "Radius", 0, QApplication::UnicodeUTF8)); + + setCentralRadial(m_ui.gradientWidget->centralRadial()); + setFocalRadial(m_ui.gradientWidget->focalRadial()); + setRadiusRadial(m_ui.gradientWidget->radiusRadial()); + } else if (type == QGradient::ConicalGradient) { + centralConicalXSpinBox = m_ui.spinBox1; + setSpinBox(centralConicalXSpinBox, SLOT(slotCentralConicalXChanged(double))); + m_ui.label1->setText(QApplication::translate("GradientEditor", "Central X", 0, QApplication::UnicodeUTF8)); + + centralConicalYSpinBox = m_ui.spinBox2; + setSpinBox(centralConicalYSpinBox, SLOT(slotCentralConicalYChanged(double))); + m_ui.label2->setText(QApplication::translate("GradientEditor", "Central Y", 0, QApplication::UnicodeUTF8)); + + angleConicalSpinBox = m_ui.spinBox3; + setSpinBox(angleConicalSpinBox, SLOT(slotAngleConicalChanged(double)), 360.0, 1.0, 1); + m_ui.label3->setText(QApplication::translate("GradientEditor", "Angle", 0, QApplication::UnicodeUTF8)); + + setCentralConical(m_ui.gradientWidget->centralConical()); + setAngleConical(m_ui.gradientWidget->angleConical()); + + spreadEnabled = false; + } + m_ui.spreadComboBox->setEnabled(spreadEnabled); + m_ui.padButton->setEnabled(spreadEnabled); + m_ui.repeatButton->setEnabled(spreadEnabled); + m_ui.reflectButton->setEnabled(spreadEnabled); + + m_ui.label4->setVisible(row4Visible()); + m_ui.spinBox4->setVisible(row4Visible()); + m_ui.label5->setVisible(row5Visible()); + m_ui.spinBox5->setVisible(row5Visible()); + + if (m_gridLayout) { + m_gridLayout->setEnabled(ena); + } +} + +void GradientEditorPrivate::slotGradientStopsChanged(const QGradientStops &stops) +{ + m_ui.gradientWidget->setGradientStops(stops); + updateGradient(true); +} + +void GradientEditorPrivate::slotTypeChanged(int idx) +{ + QGradient::Type type = QGradient::NoGradient; + if (idx == 0) + type = QGradient::LinearGradient; + else if (idx == 1) + type = QGradient::RadialGradient; + else if (idx == 2) + type = QGradient::ConicalGradient; + setType(type); + m_ui.typeComboBox->setCurrentIndex(idx); + m_typeGroup->button(idx)->setChecked(true); + m_ui.gradientWidget->setGradientType(type); + updateGradient(true); +} + +void GradientEditorPrivate::slotSpreadChanged(int spread) +{ + if (spread == 0) { + m_ui.gradientWidget->setGradientSpread(QGradient::PadSpread); + } else if (spread == 1) { + m_ui.gradientWidget->setGradientSpread(QGradient::RepeatSpread); + } else if (spread == 2) { + m_ui.gradientWidget->setGradientSpread(QGradient::ReflectSpread); + } + m_ui.spreadComboBox->setCurrentIndex(spread); + updateGradient(true); +} + +void GradientEditorPrivate::slotStartLinearXChanged(double value) +{ + QPointF point = m_ui.gradientWidget->startLinear(); + point.setX(value); + m_ui.gradientWidget->setStartLinear(point); + updateGradient(true); +} + +void GradientEditorPrivate::slotStartLinearYChanged(double value) +{ + QPointF point = m_ui.gradientWidget->startLinear(); + point.setY(value); + m_ui.gradientWidget->setStartLinear(point); + updateGradient(true); +} + +void GradientEditorPrivate::slotEndLinearXChanged(double value) +{ + QPointF point = m_ui.gradientWidget->endLinear(); + point.setX(value); + m_ui.gradientWidget->setEndLinear(point); + updateGradient(true); +} + +void GradientEditorPrivate::slotEndLinearYChanged(double value) +{ + QPointF point = m_ui.gradientWidget->endLinear(); + point.setY(value); + m_ui.gradientWidget->setEndLinear(point); + updateGradient(true); +} + +void GradientEditorPrivate::slotCentralRadialXChanged(double value) +{ + QPointF point = m_ui.gradientWidget->centralRadial(); + point.setX(value); + m_ui.gradientWidget->setCentralRadial(point); + updateGradient(true); +} + +void GradientEditorPrivate::slotCentralRadialYChanged(double value) +{ + QPointF point = m_ui.gradientWidget->centralRadial(); + point.setY(value); + m_ui.gradientWidget->setCentralRadial(point); + updateGradient(true); +} + +void GradientEditorPrivate::slotFocalRadialXChanged(double value) +{ + QPointF point = m_ui.gradientWidget->focalRadial(); + point.setX(value); + m_ui.gradientWidget->setFocalRadial(point); + updateGradient(true); +} + +void GradientEditorPrivate::slotFocalRadialYChanged(double value) +{ + QPointF point = m_ui.gradientWidget->focalRadial(); + point.setY(value); + m_ui.gradientWidget->setFocalRadial(point); + updateGradient(true); +} + +void GradientEditorPrivate::slotRadiusRadialChanged(double value) +{ + m_ui.gradientWidget->setRadiusRadial(value); + updateGradient(true); +} + +void GradientEditorPrivate::slotCentralConicalXChanged(double value) +{ + QPointF point = m_ui.gradientWidget->centralConical(); + point.setX(value); + m_ui.gradientWidget->setCentralConical(point); + updateGradient(true); +} + +void GradientEditorPrivate::slotCentralConicalYChanged(double value) +{ + QPointF point = m_ui.gradientWidget->centralConical(); + point.setY(value); + m_ui.gradientWidget->setCentralConical(point); + updateGradient(true); +} + +void GradientEditorPrivate::slotAngleConicalChanged(double value) +{ + m_ui.gradientWidget->setAngleConical(value); + updateGradient(true); +} + +void GradientEditorPrivate::startLinearChanged(const QPointF &point) +{ + setStartLinear(point); + updateGradient(true); +} + +void GradientEditorPrivate::endLinearChanged(const QPointF &point) +{ + setEndLinear(point); + updateGradient(true); +} + +void GradientEditorPrivate::centralRadialChanged(const QPointF &point) +{ + setCentralRadial(point); + updateGradient(true); +} + +void GradientEditorPrivate::focalRadialChanged(const QPointF &point) +{ + setFocalRadial(point); + updateGradient(true); +} + +void GradientEditorPrivate::radiusRadialChanged(qreal radius) +{ + setRadiusRadial(radius); + updateGradient(true); +} + +void GradientEditorPrivate::centralConicalChanged(const QPointF &point) +{ + setCentralConical(point); + updateGradient(true); +} + +void GradientEditorPrivate::angleConicalChanged(qreal angle) +{ + setAngleConical(angle); + updateGradient(true); +} + +void GradientEditorPrivate::setStartLinear(const QPointF &point) +{ + if (startLinearXSpinBox) + startLinearXSpinBox->setValue(point.x()); + if (startLinearYSpinBox) + startLinearYSpinBox->setValue(point.y()); +} + +void GradientEditorPrivate::setEndLinear(const QPointF &point) +{ + if (endLinearXSpinBox) + endLinearXSpinBox->setValue(point.x()); + if (endLinearYSpinBox) + endLinearYSpinBox->setValue(point.y()); +} + +void GradientEditorPrivate::setCentralRadial(const QPointF &point) +{ + if (centralRadialXSpinBox) + centralRadialXSpinBox->setValue(point.x()); + if (centralRadialYSpinBox) + centralRadialYSpinBox->setValue(point.y()); +} + +void GradientEditorPrivate::setFocalRadial(const QPointF &point) +{ + if (focalRadialXSpinBox) + focalRadialXSpinBox->setValue(point.x()); + if (focalRadialYSpinBox) + focalRadialYSpinBox->setValue(point.y()); +} + +void GradientEditorPrivate::setRadiusRadial(qreal radius) +{ + if (radiusRadialSpinBox) + radiusRadialSpinBox->setValue(radius); +} + +void GradientEditorPrivate::setCentralConical(const QPointF &point) +{ + if (centralConicalXSpinBox) + centralConicalXSpinBox->setValue(point.x()); + if (centralConicalYSpinBox) + centralConicalYSpinBox->setValue(point.y()); +} + +void GradientEditorPrivate::setAngleConical(qreal angle) +{ + if (angleConicalSpinBox) + angleConicalSpinBox->setValue(angle); +} + +GradientEditor::GradientEditor(QWidget *parent) + : QWidget(parent), d_ptr(new GradientEditorPrivate()) +{ + d_ptr->q_ptr = this; + d_ptr->m_type = QGradient::RadialGradient; + d_ptr->m_ui.setupUi(this); + d_ptr->m_gridLayout = 0; + d_ptr->m_hiddenLayout = 0; + d_ptr->m_hiddenWidget = 0; + bool detailsDefault = false; + d_ptr->m_details = !detailsDefault; + d_ptr->m_detailsButtonVisible = true; + bool checkeredDefault = true; + d_ptr->m_backgroundCheckered = !checkeredDefault; + d_ptr->m_gradientStopsController = new QtGradientStopsController(this); + d_ptr->m_gradientStopsController->setUi(&d_ptr->m_ui); + d_ptr->reset(); + d_ptr->setType(QGradient::LinearGradient); + d_ptr->m_combos = true; + d_ptr->setCombos(!d_ptr->m_combos); + + d_ptr->showDetails(detailsDefault); + setBackgroundCheckered(checkeredDefault); + + d_ptr->setStartLinear(QPointF(0, 0)); + d_ptr->setEndLinear(QPointF(1, 1)); + d_ptr->setCentralRadial(QPointF(0.5, 0.5)); + d_ptr->setFocalRadial(QPointF(0.5, 0.5)); + d_ptr->setRadiusRadial(0.5); + d_ptr->setCentralConical(QPointF(0.5, 0.5)); + d_ptr->setAngleConical(0); + + QIcon icon; + icon.addPixmap(style()->standardPixmap(QStyle::SP_ArrowRight), QIcon::Normal, QIcon::Off); + icon.addPixmap(style()->standardPixmap(QStyle::SP_ArrowLeft), QIcon::Normal, QIcon::On); + d_ptr->m_ui.detailsButton->setIcon(icon); + + connect(d_ptr->m_ui.detailsButton, SIGNAL(clicked(bool)), this, SLOT(slotDetailsChanged(bool))); + connect(d_ptr->m_gradientStopsController, SIGNAL(gradientStopsChanged(const QGradientStops &)), + this, SLOT(slotGradientStopsChanged(const QGradientStops &))); + + QIcon iconLinear(QLatin1String(":/trolltech/qtgradienteditor/images/typelinear.png")); + QIcon iconRadial(QLatin1String(":/trolltech/qtgradienteditor/images/typeradial.png")); + QIcon iconConical(QLatin1String(":/trolltech/qtgradienteditor/images/typeconical.png")); + + d_ptr->m_ui.typeComboBox->addItem(iconLinear, tr("Linear")); + d_ptr->m_ui.typeComboBox->addItem(iconRadial, tr("Radial")); + d_ptr->m_ui.typeComboBox->addItem(iconConical, tr("Conical")); + + d_ptr->m_ui.linearButton->setIcon(iconLinear); + d_ptr->m_ui.radialButton->setIcon(iconRadial); + d_ptr->m_ui.conicalButton->setIcon(iconConical); + + d_ptr->m_typeGroup = new QButtonGroup(this); + d_ptr->m_typeGroup->addButton(d_ptr->m_ui.linearButton, 0); + d_ptr->m_typeGroup->addButton(d_ptr->m_ui.radialButton, 1); + d_ptr->m_typeGroup->addButton(d_ptr->m_ui.conicalButton, 2); + + connect(d_ptr->m_typeGroup, SIGNAL(buttonClicked(int)), + this, SLOT(slotTypeChanged(int))); + connect(d_ptr->m_ui.typeComboBox, SIGNAL(activated(int)), + this, SLOT(slotTypeChanged(int))); + + QIcon iconPad(QLatin1String(":/trolltech/qtgradienteditor/images/spreadpad.png")); + QIcon iconRepeat(QLatin1String(":/trolltech/qtgradienteditor/images/spreadrepeat.png")); + QIcon iconReflect(QLatin1String(":/trolltech/qtgradienteditor/images/spreadreflect.png")); + + d_ptr->m_ui.spreadComboBox->addItem(iconPad, tr("Pad")); + d_ptr->m_ui.spreadComboBox->addItem(iconRepeat, tr("Repeat")); + d_ptr->m_ui.spreadComboBox->addItem(iconReflect, tr("Reflect")); + + d_ptr->m_ui.padButton->setIcon(iconPad); + d_ptr->m_ui.repeatButton->setIcon(iconRepeat); + d_ptr->m_ui.reflectButton->setIcon(iconReflect); + + d_ptr->m_spreadGroup = new QButtonGroup(this); + d_ptr->m_spreadGroup->addButton(d_ptr->m_ui.padButton, 0); + d_ptr->m_spreadGroup->addButton(d_ptr->m_ui.repeatButton, 1); + d_ptr->m_spreadGroup->addButton(d_ptr->m_ui.reflectButton, 2); + connect(d_ptr->m_spreadGroup, SIGNAL(buttonClicked(int)), + this, SLOT(slotSpreadChanged(int))); + connect(d_ptr->m_ui.spreadComboBox, SIGNAL(activated(int)), + this, SLOT(slotSpreadChanged(int))); + + connect(d_ptr->m_ui.gradientWidget, SIGNAL(startLinearChanged(const QPointF &)), + this, SLOT(startLinearChanged(const QPointF &))); + connect(d_ptr->m_ui.gradientWidget, SIGNAL(endLinearChanged(const QPointF &)), + this, SLOT(endLinearChanged(const QPointF &))); + connect(d_ptr->m_ui.gradientWidget, SIGNAL(centralRadialChanged(const QPointF &)), + this, SLOT(centralRadialChanged(const QPointF &))); + connect(d_ptr->m_ui.gradientWidget, SIGNAL(focalRadialChanged(const QPointF &)), + this, SLOT(focalRadialChanged(const QPointF &))); + connect(d_ptr->m_ui.gradientWidget, SIGNAL(radiusRadialChanged(qreal)), + this, SLOT(radiusRadialChanged(qreal))); + connect(d_ptr->m_ui.gradientWidget, SIGNAL(centralConicalChanged(const QPointF &)), + this, SLOT(centralConicalChanged(const QPointF &))); + connect(d_ptr->m_ui.gradientWidget, SIGNAL(angleConicalChanged(qreal)), + this, SLOT(angleConicalChanged(qreal))); + + QGradientStops stops = gradient().stops(); + d_ptr->m_gradientStopsController->setGradientStops(stops); + d_ptr->m_ui.gradientWidget->setGradientStops(stops); +} + +GradientEditor::~GradientEditor() +{ + if (d_ptr->m_hiddenWidget) + delete d_ptr->m_hiddenWidget; +} + +void GradientEditor::setGradient(const QGradient &grad) +{ + if (grad == gradient()) + return; + + QGradient::Type type = grad.type(); + int idx = 0; + switch (type) { + case QGradient::LinearGradient: idx = 0; break; + case QGradient::RadialGradient: idx = 1; break; + case QGradient::ConicalGradient: idx = 2; break; + default: return; + } + d_ptr->setType(type); + d_ptr->m_ui.typeComboBox->setCurrentIndex(idx); + d_ptr->m_ui.gradientWidget->setGradientType(type); + d_ptr->m_typeGroup->button(idx)->setChecked(true); + + QGradient::Spread spread = grad.spread(); + switch (spread) { + case QGradient::PadSpread: idx = 0; break; + case QGradient::RepeatSpread: idx = 1; break; + case QGradient::ReflectSpread: idx = 2; break; + default: idx = 0; break; + } + d_ptr->m_ui.spreadComboBox->setCurrentIndex(idx); + d_ptr->m_ui.gradientWidget->setGradientSpread(spread); + d_ptr->m_spreadGroup->button(idx)->setChecked(true); + + if (type == QGradient::LinearGradient) { + QLinearGradient *gr = (QLinearGradient *)(&grad); + d_ptr->setStartLinear(gr->start()); + d_ptr->setEndLinear(gr->finalStop()); + d_ptr->m_ui.gradientWidget->setStartLinear(gr->start()); + d_ptr->m_ui.gradientWidget->setEndLinear(gr->finalStop()); + } else if (type == QGradient::RadialGradient) { + QRadialGradient *gr = (QRadialGradient *)(&grad); + d_ptr->setCentralRadial(gr->center()); + d_ptr->setFocalRadial(gr->focalPoint()); + d_ptr->setRadiusRadial(gr->radius()); + d_ptr->m_ui.gradientWidget->setCentralRadial(gr->center()); + d_ptr->m_ui.gradientWidget->setFocalRadial(gr->focalPoint()); + d_ptr->m_ui.gradientWidget->setRadiusRadial(gr->radius()); + } else if (type == QGradient::ConicalGradient) { + QConicalGradient *gr = (QConicalGradient *)(&grad); + d_ptr->setCentralConical(gr->center()); + d_ptr->setAngleConical(gr->angle()); + d_ptr->m_ui.gradientWidget->setCentralConical(gr->center()); + d_ptr->m_ui.gradientWidget->setAngleConical(gr->angle()); + } + + d_ptr->m_gradientStopsController->setGradientStops(grad.stops()); + d_ptr->m_ui.gradientWidget->setGradientStops(grad.stops()); + d_ptr->updateGradient(false); +} + +QGradient GradientEditor::gradient() const +{ + return d_ptr->m_gradient; +} + +bool GradientEditor::isBackgroundCheckered() const +{ + return d_ptr->m_backgroundCheckered; +} + +void GradientEditor::setBackgroundCheckered(bool checkered) +{ + if (d_ptr->m_backgroundCheckered == checkered) + return; + + d_ptr->m_backgroundCheckered = checkered; + d_ptr->m_ui.hueColorLine->setBackgroundCheckered(checkered); + d_ptr->m_ui.saturationColorLine->setBackgroundCheckered(checkered); + d_ptr->m_ui.valueColorLine->setBackgroundCheckered(checkered); + d_ptr->m_ui.alphaColorLine->setBackgroundCheckered(checkered); + d_ptr->m_ui.gradientWidget->setBackgroundCheckered(checkered); + d_ptr->m_ui.gradientStopsWidget->setBackgroundCheckered(checkered); + d_ptr->m_ui.colorButton->setBackgroundCheckered(checkered); +} + +bool GradientEditor::detailsVisible() const +{ + return d_ptr->m_details; +} + +void GradientEditor::setDetailsVisible(bool visible) +{ + d_ptr->showDetails(visible); +} + +bool GradientEditor::isDetailsButtonVisible() const +{ + return d_ptr->m_detailsButtonVisible; +} + +void GradientEditor::setDetailsButtonVisible(bool visible) +{ + if (d_ptr->m_detailsButtonVisible == visible) + return; + + d_ptr->m_detailsButtonVisible = visible; + d_ptr->m_ui.detailsButton->setVisible(visible); +} + +QColor::Spec GradientEditor::spec() const +{ + return d_ptr->m_gradientStopsController->spec(); +} + +void GradientEditor::setSpec(QColor::Spec spec) +{ + d_ptr->m_gradientStopsController->setSpec(spec); +} + +QT_END_NAMESPACE + +#include "moc_gradienteditor.cpp" diff --git a/src/plugins/qmldesigner/components/propertyeditor/gradienteditor.h b/src/plugins/qmldesigner/components/propertyeditor/gradienteditor.h new file mode 100644 index 00000000000..b0bc2a51b92 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/gradienteditor.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef GRADIENTEDITOR_H +#define GRADIENTEDITOR_H + +#include <QtGui/QWidget> + +QT_BEGIN_NAMESPACE + +class GradientEditorPrivate; + +class GradientEditor : public QWidget +{ + Q_OBJECT + Q_PROPERTY(QGradient gradient READ gradient WRITE setGradient) + Q_PROPERTY(bool backgroundCheckered READ isBackgroundCheckered WRITE setBackgroundCheckered) + Q_PROPERTY(bool detailsVisible READ detailsVisible WRITE setDetailsVisible) + Q_PROPERTY(bool detailsButtonVisible READ isDetailsButtonVisible WRITE setDetailsButtonVisible) +public: + GradientEditor(QWidget *parent = 0); + ~GradientEditor(); + + void setGradient(const QGradient &gradient); + QGradient gradient() const; + + bool isBackgroundCheckered() const; + void setBackgroundCheckered(bool checkered); + + bool detailsVisible() const; + void setDetailsVisible(bool visible); + + bool isDetailsButtonVisible() const; + void setDetailsButtonVisible(bool visible); + + QColor::Spec spec() const; + void setSpec(QColor::Spec spec); + +signals: + + void gradientChanged(const QGradient &gradient); + void aboutToShowDetails(bool details, int extenstionWidthHint); + +private: + QScopedPointer<GradientEditorPrivate> d_ptr; + Q_DECLARE_PRIVATE(GradientEditor) + Q_DISABLE_COPY(GradientEditor) + Q_PRIVATE_SLOT(d_func(), void slotGradientStopsChanged(const QGradientStops &stops)) + Q_PRIVATE_SLOT(d_func(), void slotTypeChanged(int type)) + Q_PRIVATE_SLOT(d_func(), void slotSpreadChanged(int type)) + Q_PRIVATE_SLOT(d_func(), void slotStartLinearXChanged(double value)) + Q_PRIVATE_SLOT(d_func(), void slotStartLinearYChanged(double value)) + Q_PRIVATE_SLOT(d_func(), void slotEndLinearXChanged(double value)) + Q_PRIVATE_SLOT(d_func(), void slotEndLinearYChanged(double value)) + Q_PRIVATE_SLOT(d_func(), void slotCentralRadialXChanged(double value)) + Q_PRIVATE_SLOT(d_func(), void slotCentralRadialYChanged(double value)) + Q_PRIVATE_SLOT(d_func(), void slotFocalRadialXChanged(double value)) + Q_PRIVATE_SLOT(d_func(), void slotFocalRadialYChanged(double value)) + Q_PRIVATE_SLOT(d_func(), void slotRadiusRadialChanged(double value)) + Q_PRIVATE_SLOT(d_func(), void slotCentralConicalXChanged(double value)) + Q_PRIVATE_SLOT(d_func(), void slotCentralConicalYChanged(double value)) + Q_PRIVATE_SLOT(d_func(), void slotAngleConicalChanged(double value)) + Q_PRIVATE_SLOT(d_func(), void slotDetailsChanged(bool details)) + Q_PRIVATE_SLOT(d_func(), void startLinearChanged(const QPointF &)) + Q_PRIVATE_SLOT(d_func(), void endLinearChanged(const QPointF &)) + Q_PRIVATE_SLOT(d_func(), void centralRadialChanged(const QPointF &)) + Q_PRIVATE_SLOT(d_func(), void focalRadialChanged(const QPointF &)) + Q_PRIVATE_SLOT(d_func(), void radiusRadialChanged(qreal)) + Q_PRIVATE_SLOT(d_func(), void centralConicalChanged(const QPointF &)) + Q_PRIVATE_SLOT(d_func(), void angleConicalChanged(qreal)) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/qmldesigner/components/propertyeditor/gradienteditor.ui b/src/plugins/qmldesigner/components/propertyeditor/gradienteditor.ui new file mode 100644 index 00000000000..0860e07f062 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/gradienteditor.ui @@ -0,0 +1,1255 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <comment>********************************************************************* +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +*********************************************************************</comment> + <class>GradientEditor</class> + <widget class="QWidget" name="GradientEditor"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>364</width> + <height>233</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <widget class="QFrame" name="frame"> + <property name="geometry"> + <rect> + <x>11</x> + <y>-150</y> + <width>193</width> + <height>150</height> + </rect> + </property> + <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> + <layout class="QVBoxLayout"> + <property name="spacing"> + <number>6</number> + </property> + <property name="margin"> + <number>0</number> + </property> + <item> + <widget class="QtGradientWidget" name="gradientWidget" native="true"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string>Gradient Editor</string> + </property> + <property name="whatsThis"> + <string>This area shows a preview of the gradient being edited. It also allows you to edit parameters specific to the gradient's type such as start and final point, radius, etc. by drag & drop.</string> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QLabel" name="label1"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="geometry"> + <rect> + <x>210</x> + <y>-150</y> + <width>64</width> + <height>23</height> + </rect> + </property> + <property name="text"> + <string>1</string> + </property> + </widget> + <widget class="QDoubleSpinBox" name="spinBox1"> + <property name="geometry"> + <rect> + <x>280</x> + <y>-150</y> + <width>73</width> + <height>23</height> + </rect> + </property> + <property name="keyboardTracking"> + <bool>false</bool> + </property> + <property name="decimals"> + <number>3</number> + </property> + <property name="maximum"> + <double>1.000000000000000</double> + </property> + <property name="singleStep"> + <double>0.010000000000000</double> + </property> + </widget> + <widget class="QLabel" name="label2"> + <property name="geometry"> + <rect> + <x>210</x> + <y>-120</y> + <width>64</width> + <height>23</height> + </rect> + </property> + <property name="text"> + <string>2</string> + </property> + </widget> + <widget class="QDoubleSpinBox" name="spinBox2"> + <property name="geometry"> + <rect> + <x>280</x> + <y>-120</y> + <width>73</width> + <height>23</height> + </rect> + </property> + <property name="keyboardTracking"> + <bool>false</bool> + </property> + <property name="decimals"> + <number>3</number> + </property> + <property name="maximum"> + <double>1.000000000000000</double> + </property> + <property name="singleStep"> + <double>0.010000000000000</double> + </property> + </widget> + <widget class="QLabel" name="label3"> + <property name="geometry"> + <rect> + <x>210</x> + <y>-90</y> + <width>64</width> + <height>23</height> + </rect> + </property> + <property name="text"> + <string>3</string> + </property> + </widget> + <widget class="QDoubleSpinBox" name="spinBox3"> + <property name="geometry"> + <rect> + <x>280</x> + <y>-90</y> + <width>73</width> + <height>23</height> + </rect> + </property> + <property name="keyboardTracking"> + <bool>false</bool> + </property> + <property name="decimals"> + <number>3</number> + </property> + <property name="maximum"> + <double>1.000000000000000</double> + </property> + <property name="singleStep"> + <double>0.010000000000000</double> + </property> + </widget> + <widget class="QLabel" name="label4"> + <property name="geometry"> + <rect> + <x>210</x> + <y>-60</y> + <width>64</width> + <height>23</height> + </rect> + </property> + <property name="text"> + <string>4</string> + </property> + </widget> + <widget class="QDoubleSpinBox" name="spinBox4"> + <property name="geometry"> + <rect> + <x>280</x> + <y>-60</y> + <width>73</width> + <height>23</height> + </rect> + </property> + <property name="keyboardTracking"> + <bool>false</bool> + </property> + <property name="decimals"> + <number>3</number> + </property> + <property name="maximum"> + <double>1.000000000000000</double> + </property> + <property name="singleStep"> + <double>0.010000000000000</double> + </property> + </widget> + <widget class="QLabel" name="label5"> + <property name="geometry"> + <rect> + <x>210</x> + <y>-30</y> + <width>64</width> + <height>23</height> + </rect> + </property> + <property name="text"> + <string>5</string> + </property> + </widget> + <widget class="QDoubleSpinBox" name="spinBox5"> + <property name="geometry"> + <rect> + <x>280</x> + <y>-30</y> + <width>73</width> + <height>23</height> + </rect> + </property> + <property name="keyboardTracking"> + <bool>false</bool> + </property> + <property name="decimals"> + <number>3</number> + </property> + <property name="maximum"> + <double>1.000000000000000</double> + </property> + <property name="singleStep"> + <double>0.010000000000000</double> + </property> + </widget> + <widget class="QtGradientStopsWidget" name="gradientStopsWidget" native="true"> + <property name="geometry"> + <rect> + <x>11</x> + <y>6</y> + <width>193</width> + <height>67</height> + </rect> + </property> + <property name="toolTip"> + <string>Gradient Stops Editor</string> + </property> + <property name="whatsThis"> + <string>This area allows you to edit gradient stops. Double click on the existing stop handle to duplicate it. Double click outside of the existing stop handles to create a new stop. Drag & drop the handle to reposition it. Use right mouse button to popup context menu with extra actions.</string> + </property> + </widget> + <widget class="QLabel" name="zoomLabel"> + <property name="geometry"> + <rect> + <x>210</x> + <y>12</y> + <width>64</width> + <height>23</height> + </rect> + </property> + <property name="text"> + <string>Zoom</string> + </property> + </widget> + <widget class="QToolButton" name="zoomAllButton"> + <property name="geometry"> + <rect> + <x>280</x> + <y>41</y> + <width>72</width> + <height>26</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string>Reset Zoom</string> + </property> + <property name="text"> + <string>Reset Zoom</string> + </property> + </widget> + <widget class="QLabel" name="positionLabel"> + <property name="geometry"> + <rect> + <x>210</x> + <y>85</y> + <width>64</width> + <height>23</height> + </rect> + </property> + <property name="text"> + <string>Position</string> + </property> + </widget> + <widget class="QLabel" name="hLabel"> + <property name="geometry"> + <rect> + <x>11</x> + <y>116</y> + <width>32</width> + <height>18</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string>Hue</string> + </property> + <property name="text"> + <string>H</string> + </property> + </widget> + <widget class="QFrame" name="frame_2"> + <property name="geometry"> + <rect> + <x>49</x> + <y>114</y> + <width>155</width> + <height>23</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Ignored" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="frameShape"> + <enum>QFrame::StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> + </property> + <layout class="QHBoxLayout"> + <property name="margin"> + <number>0</number> + </property> + <item> + <widget class="QtColorLine" name="hueColorLine" native="true"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string>Hue</string> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QLabel" name="hueLabel"> + <property name="geometry"> + <rect> + <x>210</x> + <y>116</y> + <width>64</width> + <height>18</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Hue</string> + </property> + </widget> + <widget class="QLabel" name="sLabel"> + <property name="geometry"> + <rect> + <x>11</x> + <y>145</y> + <width>32</width> + <height>18</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string>Saturation</string> + </property> + <property name="text"> + <string>S</string> + </property> + </widget> + <widget class="QFrame" name="frame_5"> + <property name="geometry"> + <rect> + <x>49</x> + <y>143</y> + <width>155</width> + <height>23</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Ignored" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="frameShape"> + <enum>QFrame::StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> + </property> + <layout class="QHBoxLayout"> + <property name="margin"> + <number>0</number> + </property> + <item> + <widget class="QtColorLine" name="saturationColorLine" native="true"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string>Saturation</string> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QLabel" name="saturationLabel"> + <property name="geometry"> + <rect> + <x>210</x> + <y>145</y> + <width>64</width> + <height>18</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Sat</string> + </property> + </widget> + <widget class="QLabel" name="vLabel"> + <property name="geometry"> + <rect> + <x>11</x> + <y>174</y> + <width>32</width> + <height>18</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string>Value</string> + </property> + <property name="text"> + <string>V</string> + </property> + </widget> + <widget class="QFrame" name="frame_3"> + <property name="geometry"> + <rect> + <x>49</x> + <y>172</y> + <width>155</width> + <height>23</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Ignored" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="frameShape"> + <enum>QFrame::StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> + </property> + <layout class="QHBoxLayout"> + <property name="margin"> + <number>0</number> + </property> + <item> + <widget class="QtColorLine" name="valueColorLine" native="true"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string>Value</string> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QLabel" name="valueLabel"> + <property name="geometry"> + <rect> + <x>210</x> + <y>174</y> + <width>64</width> + <height>18</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Val</string> + </property> + </widget> + <widget class="QLabel" name="aLabel"> + <property name="geometry"> + <rect> + <x>11</x> + <y>203</y> + <width>32</width> + <height>18</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string>Alpha</string> + </property> + <property name="text"> + <string>A</string> + </property> + </widget> + <widget class="QFrame" name="frame_4"> + <property name="geometry"> + <rect> + <x>49</x> + <y>201</y> + <width>155</width> + <height>23</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Ignored" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="frameShape"> + <enum>QFrame::StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> + </property> + <layout class="QHBoxLayout"> + <property name="margin"> + <number>0</number> + </property> + <item> + <widget class="QtColorLine" name="alphaColorLine" native="true"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string>Alpha</string> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QLabel" name="alphaLabel"> + <property name="geometry"> + <rect> + <x>210</x> + <y>203</y> + <width>64</width> + <height>18</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Alpha</string> + </property> + </widget> + <widget class="QComboBox" name="typeComboBox"> + <property name="geometry"> + <rect> + <x>11</x> + <y>-179</y> + <width>79</width> + <height>22</height> + </rect> + </property> + <property name="toolTip"> + <string>Type</string> + </property> + </widget> + <widget class="QComboBox" name="spreadComboBox"> + <property name="geometry"> + <rect> + <x>97</x> + <y>-179</y> + <width>72</width> + <height>22</height> + </rect> + </property> + <property name="toolTip"> + <string>Spread</string> + </property> + </widget> + <widget class="QLabel" name="colorLabel"> + <property name="geometry"> + <rect> + <x>11</x> + <y>79</y> + <width>32</width> + <height>29</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Color</string> + </property> + </widget> + <widget class="QtColorButton" name="colorButton"> + <property name="geometry"> + <rect> + <x>49</x> + <y>81</y> + <width>26</width> + <height>25</height> + </rect> + </property> + <property name="toolTip"> + <string>Current stop's color</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + <widget class="QRadioButton" name="hsvRadioButton"> + <property name="geometry"> + <rect> + <x>81</x> + <y>82</y> + <width>49</width> + <height>23</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string>Show HSV specification</string> + </property> + <property name="text"> + <string>HSV</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + <widget class="QRadioButton" name="rgbRadioButton"> + <property name="geometry"> + <rect> + <x>136</x> + <y>82</y> + <width>49</width> + <height>23</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string>Show RGB specification</string> + </property> + <property name="text"> + <string>RGB</string> + </property> + </widget> + <widget class="QWidget" name="positionWidget" native="true"> + <property name="geometry"> + <rect> + <x>280</x> + <y>85</y> + <width>73</width> + <height>23</height> + </rect> + </property> + <layout class="QVBoxLayout"> + <property name="margin"> + <number>0</number> + </property> + <item> + <widget class="QDoubleSpinBox" name="positionSpinBox"> + <property name="toolTip"> + <string>Current stop's position</string> + </property> + <property name="keyboardTracking"> + <bool>false</bool> + </property> + <property name="decimals"> + <number>3</number> + </property> + <property name="minimum"> + <double>0.000000000000000</double> + </property> + <property name="maximum"> + <double>1.000000000000000</double> + </property> + <property name="singleStep"> + <double>0.010000000000000</double> + </property> + <property name="value"> + <double>0.000000000000000</double> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="hueWidget" native="true"> + <property name="geometry"> + <rect> + <x>280</x> + <y>114</y> + <width>73</width> + <height>23</height> + </rect> + </property> + <layout class="QVBoxLayout"> + <property name="margin"> + <number>0</number> + </property> + <item> + <widget class="QSpinBox" name="hueSpinBox"> + <property name="keyboardTracking"> + <bool>false</bool> + </property> + <property name="maximum"> + <number>359</number> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="saturationWidget" native="true"> + <property name="geometry"> + <rect> + <x>280</x> + <y>143</y> + <width>73</width> + <height>23</height> + </rect> + </property> + <layout class="QVBoxLayout"> + <property name="margin"> + <number>0</number> + </property> + <item> + <widget class="QSpinBox" name="saturationSpinBox"> + <property name="keyboardTracking"> + <bool>false</bool> + </property> + <property name="maximum"> + <number>255</number> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="valueWidget" native="true"> + <property name="geometry"> + <rect> + <x>280</x> + <y>172</y> + <width>73</width> + <height>23</height> + </rect> + </property> + <layout class="QVBoxLayout"> + <property name="margin"> + <number>0</number> + </property> + <item> + <widget class="QSpinBox" name="valueSpinBox"> + <property name="keyboardTracking"> + <bool>false</bool> + </property> + <property name="maximum"> + <number>255</number> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="alphaWidget" native="true"> + <property name="geometry"> + <rect> + <x>280</x> + <y>201</y> + <width>73</width> + <height>23</height> + </rect> + </property> + <layout class="QVBoxLayout"> + <property name="margin"> + <number>0</number> + </property> + <item> + <widget class="QSpinBox" name="alphaSpinBox"> + <property name="keyboardTracking"> + <bool>false</bool> + </property> + <property name="maximum"> + <number>255</number> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="zoomWidget" native="true"> + <property name="geometry"> + <rect> + <x>280</x> + <y>12</y> + <width>73</width> + <height>23</height> + </rect> + </property> + <layout class="QVBoxLayout"> + <property name="margin"> + <number>0</number> + </property> + <item> + <widget class="QSpinBox" name="zoomSpinBox"> + <property name="keyboardTracking"> + <bool>false</bool> + </property> + <property name="suffix"> + <string>%</string> + </property> + <property name="minimum"> + <number>100</number> + </property> + <property name="maximum"> + <number>10000</number> + </property> + <property name="singleStep"> + <number>100</number> + </property> + <property name="value"> + <number>100</number> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="line1Widget" native="true"> + <property name="geometry"> + <rect> + <x>210</x> + <y>0</y> + <width>143</width> + <height>16</height> + </rect> + </property> + <layout class="QVBoxLayout"> + <property name="margin"> + <number>0</number> + </property> + <item> + <widget class="Line" name="line1"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="line2Widget" native="true"> + <property name="geometry"> + <rect> + <x>210</x> + <y>73</y> + <width>143</width> + <height>16</height> + </rect> + </property> + <layout class="QVBoxLayout"> + <property name="margin"> + <number>0</number> + </property> + <item> + <widget class="Line" name="line2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="zoomButtonsWidget" native="true"> + <property name="geometry"> + <rect> + <x>210</x> + <y>41</y> + <width>64</width> + <height>26</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <layout class="QHBoxLayout"> + <property name="margin"> + <number>0</number> + </property> + <item> + <widget class="QToolButton" name="zoomInButton"> + <property name="toolTip"> + <string>Zoom In</string> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="zoomOutButton"> + <property name="toolTip"> + <string>Zoom Out</string> + </property> + </widget> + </item> + <item> + <spacer> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <widget class="QToolButton" name="detailsButton"> + <property name="geometry"> + <rect> + <x>177</x> + <y>-179</y> + <width>25</width> + <height>22</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Ignored"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string>Toggle details extension</string> + </property> + <property name="text"> + <string>></string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="autoRaise"> + <bool>true</bool> + </property> + </widget> + <widget class="QToolButton" name="linearButton"> + <property name="geometry"> + <rect> + <x>11</x> + <y>-209</y> + <width>30</width> + <height>26</height> + </rect> + </property> + <property name="toolTip"> + <string>Linear Type</string> + </property> + <property name="text"> + <string>...</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="autoRaise"> + <bool>true</bool> + </property> + </widget> + <widget class="QToolButton" name="radialButton"> + <property name="geometry"> + <rect> + <x>41</x> + <y>-209</y> + <width>30</width> + <height>26</height> + </rect> + </property> + <property name="toolTip"> + <string>Radial Type</string> + </property> + <property name="text"> + <string>...</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="autoRaise"> + <bool>true</bool> + </property> + </widget> + <widget class="QToolButton" name="conicalButton"> + <property name="geometry"> + <rect> + <x>71</x> + <y>-209</y> + <width>30</width> + <height>26</height> + </rect> + </property> + <property name="toolTip"> + <string>Conical Type</string> + </property> + <property name="text"> + <string>...</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="autoRaise"> + <bool>true</bool> + </property> + </widget> + <widget class="QToolButton" name="padButton"> + <property name="geometry"> + <rect> + <x>111</x> + <y>-209</y> + <width>30</width> + <height>26</height> + </rect> + </property> + <property name="toolTip"> + <string>Pad Spread</string> + </property> + <property name="text"> + <string>...</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="autoRaise"> + <bool>true</bool> + </property> + </widget> + <widget class="QToolButton" name="repeatButton"> + <property name="geometry"> + <rect> + <x>141</x> + <y>-209</y> + <width>30</width> + <height>26</height> + </rect> + </property> + <property name="toolTip"> + <string>Repeat Spread</string> + </property> + <property name="text"> + <string>...</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="autoRaise"> + <bool>true</bool> + </property> + </widget> + <widget class="QToolButton" name="reflectButton"> + <property name="geometry"> + <rect> + <x>171</x> + <y>-209</y> + <width>30</width> + <height>26</height> + </rect> + </property> + <property name="toolTip"> + <string>Reflect Spread</string> + </property> + <property name="text"> + <string>...</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="autoRaise"> + <bool>true</bool> + </property> + </widget> + </widget> + <customwidgets> + <customwidget> + <class>QtColorButton</class> + <extends>QToolButton</extends> + <header>qtcolorbutton.h</header> + </customwidget> + <customwidget> + <class>QtColorLine</class> + <extends>QWidget</extends> + <header>qtcolorline.h</header> + <container>1</container> + </customwidget> + <customwidget> + <class>QtGradientStopsWidget</class> + <extends>QWidget</extends> + <header>qtgradientstopswidget.h</header> + <container>1</container> + </customwidget> + <customwidget> + <class>QtGradientWidget</class> + <extends>QWidget</extends> + <header>qtgradientwidget.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <tabstops> + <tabstop>typeComboBox</tabstop> + <tabstop>spreadComboBox</tabstop> + <tabstop>detailsButton</tabstop> + <tabstop>spinBox1</tabstop> + <tabstop>spinBox2</tabstop> + <tabstop>spinBox3</tabstop> + <tabstop>spinBox4</tabstop> + <tabstop>spinBox5</tabstop> + <tabstop>zoomSpinBox</tabstop> + <tabstop>zoomInButton</tabstop> + <tabstop>zoomOutButton</tabstop> + <tabstop>zoomAllButton</tabstop> + <tabstop>colorButton</tabstop> + <tabstop>hsvRadioButton</tabstop> + <tabstop>rgbRadioButton</tabstop> + <tabstop>positionSpinBox</tabstop> + <tabstop>hueSpinBox</tabstop> + <tabstop>saturationSpinBox</tabstop> + <tabstop>valueSpinBox</tabstop> + <tabstop>alphaSpinBox</tabstop> + </tabstops> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/qmldesigner/components/propertyeditor/images/button2_hovered.png b/src/plugins/qmldesigner/components/propertyeditor/images/button2_hovered.png new file mode 100644 index 0000000000000000000000000000000000000000..b82f1fd0060595892b13c4a1d83889314b1fc516 GIT binary patch literal 898 zcmV-|1AY97P)<h;3K|Lk000e1NJLTq001oj001or1^@s6IvV1d0000hbVXQnLvm$d zbZKvHAXI5>WdKHUX&^TsL3L*!GB7YXATc#MFfckZFd!>1F)%Q<?X89Y000McNliru z*$4*>2^S8FIpP2S010qNS#tmY9>M?s9>M``&~-il000DMK}|sb0I`n?{9y$E00P=c zL_t(|+Ra%@j>0euO(6w=Sa1czmWy#-F2I@vD;E4a<4Q)ZaS}TT%vkD_Y5aM8aqP5h zw%e@(nNFwOVzF?u+01$G-DEOxr_;&(g*+Y)*ECI!V>}+a=kw_fhl9J_ZVin2>-Fl| zw(Wk!-S2mIKA(HT%jE(}4z7Pn0GM<Qz`_CX0W^{B7h~h$2Py+-IN;6@tOhjLYHu#3 z3u8be-~ipxX!NGysWYISJJ5(_HZ;1R1H^&Ana&7|dH91zZV+S-o!4L;{ZV|V>#0%+ zi&U@H4Dp=D|GgzhZtz@NPQ;68%YxGa&@vcH%a~M44+Im_r#Wh=I6=@>kro~OE!ALd z7t%rD)$Enisp=6D49Xd^vS?k5MW7|H79bjZNw2TdCZq|5H0eQuTVhau*-Uds+`e{t zP@JA#&;pbKY8F<=p2rT65$uS(C0m%|c6@p@jU?6DgG^Hg7RESm2}o`~pgw&FaE+Wy zE!G@=1JJk?45ddZP^fN8Gm!!+VL*&MXxy)IhT#kwa1JIGR7$JZ&4g^}HSR0PG+ry2 zQV>pz#RtC`)k3s3fzWvAjf+f66#$v85v-QZfN9D2n}9UB1EwWqYiqZrlm@7@2$P^K zEQMNe$xKA6N36Eyg0B5qf$9ZIUTG_*_Ni?uJ!`hKo^~2Om<u>`Er)HsRsgGy8P}@O z7D?^!1`Dxd?;$Z#D?OrZT-tkRfNK_Tjr16RPC5Ga+oz)D0up-$)Qoc-4OmSosb7oL zUU0egrU%>q0Q&90_ESJVmeVyBTud~1s<C|oT_Umf78+odd{#4^ssQwXLE3!f)cYuC z>)qA}ZuN%;etmmwn50K5{2cb}1|!)6K6qc<Z<y$pS@_V1Q%R?}bb3qe5iFbmuV1ZJ z&2G2r;DaNA#vpCm7BN&y02^k*dcAIXrZ<~Sv)}K#<MB9P1wQls2Z21E&*u&ZUM`pJ Y2d`OxD}s);bpQYW07*qoM6N<$f@TPYA^-pY literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/propertyeditor/images/button2_normal.png b/src/plugins/qmldesigner/components/propertyeditor/images/button2_normal.png new file mode 100644 index 0000000000000000000000000000000000000000..4fef115d3ef28a2ea839f1f3a746150fba522d8c GIT binary patch literal 898 zcmV-|1AY97P)<h;3K|Lk000e1NJLTq001oj001or1^@s6IvV1d0000hbVXQnLvm$d zbZKvHAXI5>WdKHUX&^TsL3L*!GB7YXATc#MFfckZFd!>1F)%Q<?X89Y000McNliru z*$4*>7&~u@b`byo010qNS#tmY9>D+r9>D>_X;f1H000DMK}|sb0I`n?{9y$E00P=c zL_t(|+Ra%@j>0euO(6w=Sa1czmV0tQF2I@vD;E4a<4Q)ZaS}TT%vkD_Y5aM8aqP5h zw%e@(nNFwOVzF?u+01$G-DEOxr_;&(g*+Y)*ECI!V>}+a=kw_fhl9J_ZVin2>-Fl| zw(Wk!-S2mIKA(HT%jE(}4z7Pn0GM<Qz`_CX0W^{B7h~h$2Py+-IN;6@tOhjLYHu#3 z3u8be-~ipxX!NGysWYISJJ5(_HZ;1R1H^&Ana&7|dH91zZV+S-o!4L;{ZV|V>#0%+ zi&U@H4Dp=D|GgzhZtz@NPQ;68%YxGa&@vcH%a~M44+Im_r#Wh=I6=@>kro~OE!ALd z7t%rD)$Enisp=6D49Xd^vS?k5MW7|H79bjZNw2TdCZq|5H0eQuTVhau*-Uds+`e{t zP@JA#&;pbKY8F<=p2rT65$uS(C0m%|c6@p@jU?6DgG^Hg7RESm2}o`~pgw&FaE+Wy zE!G@=1JJk?45ddZP^fN8Gm!!+VL*&MXxy)IhT#kwa1JIGR7$JZ&4g^}HSR0PG+ry2 zQV>pz#RtC`)k3s3fzWvAjf+f66#$v85v-QZfN9D2n}9UB1EwWqYiqZrlm@7@2$P^K zEQMNe$xKA6N36Eyg0B5qf$9ZIUTG_*_Ni?uJ!`hKo^~2Om<u>`Er)HsRsgGy8P}@O z7D?^!1`Dxd?;$Z#D?OrZT-tkRfNK_Tjr16RPC5Ga+oz)D0up-$)Qoc-4OmSosb7oL zUU0egrU%>q0Q&90_ESJVmeVyBTud~1s<C|oT_Umf78+odd{#4^ssQwXLE3!f)cYuC z>)qA}ZuN%;etmmwn50K5{2cb}1|!)6K6qc<Z<y$pS@_V1Q%R?}bb3qe5iFbmuV1ZJ z&2G2r;DaNA#vpCm7BN&y02^k*dcAIXrZ<~Sv)}K#<MB9P1wQls2Z21E&*u&ZUM`pJ Y2j0Yhg{uO8cmMzZ07*qoM6N<$f{qu4F#rGn literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/propertyeditor/images/button2_pressed.png b/src/plugins/qmldesigner/components/propertyeditor/images/button2_pressed.png new file mode 100644 index 0000000000000000000000000000000000000000..305530afaf6baf843bc32656ed129c0057617114 GIT binary patch literal 898 zcmV-|1AY97P)<h;3K|Lk000e1NJLTq001oj001or1^@s6IvV1d0000hbVXQnLvm$d zbZKvHAXI5>WdKHUX&^TsL3L*!GB7YXATc#MFfckZFd!>1F)%Q<?X89Y000McNliru z*$4*>2{GJ;ydMAn010qNS#tmY9>M?s9>M``&~-il000DMK}|sb0I`n?{9y$E00P=c zL_t(|+Ra%@j>0euO(6w=Sa1czmg{hGF2I@vD;E4a<4Q)ZaS}TT%vkD_Y5aM8aqP5h zw%e@(nNFwOVzF?u+01$G-DEOxr_;&(g*+Y)*ECI!V>}+a=kw_fhl9J_ZVin2>-Fl| zw(Wk!-S2mIKA(HT%jE(}4z7Pn0GM<Qz`_CX0W^{B7h~h$2Py+-IN;6@tOhjLYHu#3 z3u8be-~ipxX!NGysWYISJJ5(_HZ;1R1H^&Ana&7|dH91zZV+S-o!4L;{ZV|V>#0%+ zi&U@H4Dp=D|GgzhZtz@NPQ;68%YxGa&@vcH%a~M44+Im_r#Wh=I6=@>kro~OE!ALd z7t%rD)$Enisp=6D49Xd^vS?k5MW7|H79bjZNw2TdCZq|5H0eQuTVhau*-Uds+`e{t zP@JA#&;pbKY8F<=p2rT65$uS(C0m%|c6@p@jU?6DgG^Hg7RESm2}o`~pgw&FaE+Wy zE!G@=1JJk?45ddZP^fN8Gm!!+VL*&MXxy)IhT#kwa1JIGR7$JZ&4g^}HSR0PG+ry2 zQV>pz#RtC`)k3s3fzWvAjf+f66#$v85v-QZfN9D2n}9UB1EwWqYiqZrlm@7@2$P^K zEQMNe$xKA6N36Eyg0B5qf$9ZIUTG_*_Ni?uJ!`hKo^~2Om<u>`Er)HsRsgGy8P}@O z7D?^!1`Dxd?;$Z#D?OrZT-tkRfNK_Tjr16RPC5Ga+oz)D0up-$)Qoc-4OmSosb7oL zUU0egrU%>q0Q&90_ESJVmeVyBTud~1s<C|oT_Umf78+odd{#4^ssQwXLE3!f)cYuC z>)qA}ZuN%;etmmwn50K5{2cb}1|!)6K6qc<Z<y$pS@_V1Q%R?}bb3qe5iFbmuV1ZJ z&2G2r;DaNA#vpCm7BN&y02^k*dcAIXrZ<~Sv)}K#<MB9P1wQls2Z21E&*u&ZUM`pJ Y2Rdwj@5KL+R{#J207*qoM6N<$g0&Nf2><{9 literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/propertyeditor/images/button_hovered.png b/src/plugins/qmldesigner/components/propertyeditor/images/button_hovered.png new file mode 100644 index 0000000000000000000000000000000000000000..2dc66ec3e395026c49c40218f624113dae55df6b GIT binary patch literal 390 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1SFZ~=vx6P?Go3B66d1S#FEVXJcW?V+*Ag) zqD%#I1;^5K1tS9kO9ewCD+5z2Q)2~f149FY+XlrAKyB<Lp1!W^H#yk3#f=}i%!>mG zaTa()7Beu&9Rgv-ybGmvKtYytM_)$<hK>E)e-c@N{Cl1*jv*Ddo=$h<JFLLN;_b@% zzdlU5b9zBmwSz+t_v}gHZ{#)wEqP)Rx%tL}ATP`3L8o|^3OKF3Dj#gH;nAMiO^u?* zi*?T1u`qLcFDO^}bY@$S^2gOzewdmak1COfP<}eq%VX=O>i5$N_FR+L(|`8kQLnxJ z3uV76szemdnWmYOwesW2xW3GUf|a~$4z@MwUcKdTV5QlHKTMm7XFj%TWivi}G*jVB z<r;Z|4J)=Dn|HiLxFav=(8q88+gR3b?C>{zzAa<=w&MalS~}SZhHgvp4{~0yjhE)B goK{`GPviqX2h;z1f7bdP2l|x3)78&qol`;+06U_TQUCw| literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/propertyeditor/images/button_normal.png b/src/plugins/qmldesigner/components/propertyeditor/images/button_normal.png new file mode 100644 index 0000000000000000000000000000000000000000..ca29772319608f8ea9e269a947c2071244c7c1aa GIT binary patch literal 379 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1SFZ~=vx6P?Go3B66d1S#FEVXJcW?V+*Ag) zqD%#I1;^5K1tS9kO9ewCD+5a_BVz?^149FY(<f(%1GTZ2c>21s-{fHDmSDM?s6G!U z#981GS<Jv7cL;<T^DdOy0R>so9eo`c7&i8E|4C#8@-KV3IEGZ*dOAIjk4cfog|&+N zUA@dAH5oVF1&61V+8*q=__J~U^qJc<TcWbh?c7?Hbn=Xs=C?bp%+`5L$0d9=JU+v} zDcK?X%8rW%43CL!J!824nDCnr$1BO#-&_A{k~+Ppt?Ipr!K~LSHYW5v3>7y0Q7g>b zv!e0$<`vVEBd-`8zZY*}a4#miZ|@nC4Ubt&;}Yy=Bs`roq4ev<{?zP=JjePyzkiu{ z){yV)^0#m2a!#|H_h3rU4HxF-*0U>$b!H}SP`><HJ(`)#WR8BgodnnR-_A^Rj0#5o VZ%muckO1@^gQu&X%Q~loCIA=wka7S3 literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/propertyeditor/images/button_pressed.png b/src/plugins/qmldesigner/components/propertyeditor/images/button_pressed.png new file mode 100644 index 0000000000000000000000000000000000000000..7cf55d718d74f23397b0f3db271275b2ee812952 GIT binary patch literal 1328 zcmV-01<(44P)<h;3K|Lk000e1NJLTq001Ze001Zm0ssI21Dr290000hbVXQnLvm$d zbZKvHAXI5>WdKHUX&^TsL3L*!GB7YXATcsJFf=+bF(4~2F)%PECkJf+000McNliru z*$4*<7bE}kjyeDU010qNS#tmY9>M?s9>M``&~-il000DMK}|sb0I`n?{9y$E00f9h zL_t(|+QnB}ax6Cp1xd;HmRFOz{}m+JO_C2+91#JBN4F=Hx<7VL&5T+t0fGaE=I_6J zr@>60cnNKi^>}%#wfGZNi^%)?`+9wSeZ0J!Gmk-gpC7$8#R#`#6Ps)i(V+U*r(<Ku z@Vz?iyUq?<SF88?-fwSj>*ewC?eT5>yqcErLnrvr9p|{m!E3Jh)8qWAVip#X<Mi{j zkq&!oNCU#hSxt^FKmY7&B(}H=jMT$y13BOIw?$WUlWseDZMDlcal{uD7^FeI7^a8| z62?lsg*CoOD9s>MJanW>+%h%gn8yPra0}EKlv1Tk5cuY7D9aQ<=iX23>eWOS%O^Ix zbnhU?mLj15v^{>4I3Q$@alCAs)J5B72Vtv$>)cM=!8-uwxY|d}a3C+%IX0R0wR7fe zp(X}+l()4Ec`%;N`PRaq`}1?_hkM>ihn_u*fmhth;~ckpUrycg$Fnql+Vx*q#&#`t zIy>f$DbpVMFU=VP^1N}pPsfacg_h4+#Cq<LOu9SLV6X5zI+wfK&`E_tlTgxK=ip3Y z#uP`mY~G51BUq40>|hq;i}`u#NtZrXKP${VbJz>XGwB>>mPki<&*oz(%1jiGh-$2N z|AQX<kSQyZ^&;IaKQMQXAoZ!GH|kmUwOlF?FEc^IEM;Xp{UBXo*Ut$c<~+)0RH%zU zZog-rf{R9aBa6i|=~iA^2IIy`gH;3l<0hSMV{Fumme=W?+x~QakbZ2NGqEiReM7%a z?I>+doA<mNn2U7M8au9yz_jmOdaU_C9uY*3Y0toWUe1Jyq8b-Hx%hVD%q9)C2Et&x zJCNj=eR1L#QG-J9PnWK(nNf72&iTtQ&B!u|)b-qCJZ=^s=@wJulflq=l^d6p^rLz7 z#E5)J3RMKcKyvIr<ftZrQ7D(zqd8S%B>^)?{UY7NbhvL48Y<;MpwKu~{vnWZd9Z?b zTrkduyhdx~0svUC$jYb3#Q8+}rbw+F?6}bv8h`7i9bmTB(KtUCc^V&kX*}J}pkyb8 zYexjT5wuR-I5=20soZX-McQich(Kw+O7gM!V6Gi7!wQ9H6}wBF`ybGvXf}W|0wx#A zH+G^GtjN-mwUkL?8VdT!iXN)`Z=j33bcM+_X(<|kN@+X@X7l=V;9zb-7$mHUuq0bW zb)gb;^OmF?u6R&%#jPYECnPA2gP6HmXgC0Vl7L0l)WJypSck(~&u~ZrntSQF5UXvF z*g}^U`a;Q;?0G&$r(B}5$+f<AS*T~PnOPC*)qx_Kd47&phT0g>D%W$G*fAJ+JV1O> zJyn<=-8!N$DBm>vVf6p-rB=KV<_bC)&mg$kVud$}D3^ui2wOO*!<_rKZQ*Quh+34G zU<&t+#I!XUF5EEQX*h8eBW|jpoJ)5XMDbqKn~ZjbRf+>Ogy=~^7elG3Tq9B8rCuRL z+A2KayV$QjF*p;Ul8$GpH&?o7$k)i;oAO0>?&B8VyQ{hWu6^nCS)aMD`_OJwqxnGs m|N7hS^c5HX@#kM(qWE8|FzBLLvdB9C0000<MNUMnLSTZza(1o& literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/propertyeditor/images/frame.png b/src/plugins/qmldesigner/components/propertyeditor/images/frame.png new file mode 100644 index 0000000000000000000000000000000000000000..a9516090188a971a97725eb19b57cece8f99da89 GIT binary patch literal 4826 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 Av;Y7A literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/propertyeditor/images/gradient.png b/src/plugins/qmldesigner/components/propertyeditor/images/gradient.png new file mode 100644 index 0000000000000000000000000000000000000000..0cdbcdec0bdade4aed34f564232d24217014f85e GIT binary patch literal 604 zcmV-i0;BzjP)<h;3K|Lk000e1NJLTq0015U000yS0ssI270DWt00001b5ch_0Itp) z=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FZT01FZU(%pXi00007bV*G`2iXY~ z4;?q(zpS+Y00G=dL_t(I%XO4pZW}=igFgiew0CHLAc1q6KK1%>QlRZ6QjEktXnr(n zcd;-G%c3|OQug=9cb>#6TzGip^BX^nckvEy@Q1I#GhE==9$xu;<im6oA8_T%Z(hU& zF7U#`BcH~HxWXI!I!u8;!1%QOgOE@lL$e4V1El_*9_v6l%$?{+HgGWl0vwN7p1lG` zS=-7<qPHIAj+1o*D|KJcQBGt4fdX%YZpY^92i`VX21aN>`-C<yLxzM<*2ZLd84cn? zkkLEEEYRgp`IoTHy6Ba-^~^^vdJ&8w*c4O3@@ji;46RFFKG9h#X=!5}nsR$g*2^Ix zh@G`M>nTXl+M*a|v2K2H$TVgU4MhsQ#xkL+)=9iLL=E?pc0;!mq%~YiYnFQh;=tQF zabXtFI_tIxA|uqtZqT(gSvTLrOlS7UsBMu^Ei1*^p{>YyEaP%!Jrj4n#u$+a!n=UF z)w*WlRE#sU)<wsN%z$Q_tfBR3Nz~CjGNrxjEpK;LOs0IW_9EuyycP5TEnyZYZiBvi za|yCXG3U<q0zK&E&T~9Yl-aeujoW`&@Au%RrMlhJfh&DF(KWt$C+C~_=+TW>JLE=< qgq{mVcLOWkyJF=cQ7?1P1pfd#jZ!OTnxOLl0000<MNUMnLSTZeNDRmT literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/propertyeditor/layoutwidget.cpp b/src/plugins/qmldesigner/components/propertyeditor/layoutwidget.cpp new file mode 100644 index 00000000000..031fb9e99e4 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/layoutwidget.cpp @@ -0,0 +1,106 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "layoutwidget.h" +#include <QGridLayout> +#include <QFile> +#include <QDebug> + +QT_BEGIN_NAMESPACE + + +LayoutWidget::LayoutWidget(QWidget *parent) : QFrame(parent) +{ + QSizePolicy policy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + QGridLayout *grid = new QGridLayout(this); + setLayout(grid); + grid->setContentsMargins(0,0,0,0); + grid->setSpacing(4); + + m_topButton = new QPushButton(this); + m_topButton->setCheckable(true); + m_topButton->setSizePolicy(policy); + + grid->addWidget(m_topButton, 0, 2, 2, 1, Qt::AlignHCenter); + + m_bottomButton = new QPushButton(this); + m_bottomButton->setCheckable(true); + + m_bottomButton->setSizePolicy(policy); + + grid->addWidget(m_bottomButton, 3, 2, 2, 1, Qt::AlignHCenter); + + m_leftButton = new QPushButton(this); + m_leftButton->setCheckable(true); + m_leftButton->setSizePolicy(policy); + + + grid->addWidget(m_leftButton, 2, 0, 1, 2, Qt::AlignVCenter); + + m_rightButton = new QPushButton(this); + m_rightButton->setCheckable(true); + m_rightButton->setSizePolicy(policy); + + grid->addWidget(m_rightButton, 2, 3, 1, 2, Qt::AlignVCenter); + + + m_middleButton = new QPushButton(this); + + grid->addWidget(m_middleButton, 2, 2, 1, 1, Qt::AlignCenter); + + connect(m_topButton, SIGNAL(toggled(bool)), this, SLOT(setTopAnchor(bool))); + connect(m_bottomButton, SIGNAL(toggled(bool)), this, SLOT(setBottomAnchor(bool))); + connect(m_leftButton, SIGNAL(toggled(bool)), this, SLOT(setLeftAnchor(bool))); + connect(m_rightButton, SIGNAL(toggled(bool)), this, SLOT(setRightAnchor(bool))); + + connect(m_middleButton, SIGNAL(pressed()), this, SIGNAL(fill())); +} + +LayoutWidget::~LayoutWidget() +{ +} + + void LayoutWidget::setIcon(QPushButton *button, QUrl url) + { + if (url.scheme() == QLatin1String("file")) { + QFile file(url.toLocalFile()); + file.open(QIODevice::ReadOnly); + if (file.isOpen()) { + QPixmap pixmap(url.toLocalFile()); + button->setIcon(pixmap); + } else { + qWarning() << QLatin1String("setIconFromFile: ") << url << QLatin1String(" not found!"); + } + } + } + +QT_END_NAMESPACE + + diff --git a/src/plugins/qmldesigner/components/propertyeditor/layoutwidget.h b/src/plugins/qmldesigner/components/propertyeditor/layoutwidget.h new file mode 100644 index 00000000000..82f7a4a2691 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/layoutwidget.h @@ -0,0 +1,137 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + + +#ifndef LAYOUTWIDGET_H +#define LAYOUTWIDGET_H + +#include <QtGui/QFrame> +#include <QLabel> +#include <QPushButton> +#include <QUrl> + +QT_BEGIN_NAMESPACE + +class LayoutWidget : public QFrame +{ + Q_OBJECT + + Q_PROPERTY(bool leftAnchor READ leftAnchor WRITE setLeftAnchor NOTIFY leftAnchorChanged) + Q_PROPERTY(bool rightAnchor READ rightAnchor WRITE setRightAnchor NOTIFY rightAnchorChanged) + Q_PROPERTY(bool bottomAnchor READ bottomAnchor WRITE setBottomAnchor NOTIFY bottomAnchorChanged) + Q_PROPERTY(bool topAnchor READ topAnchor WRITE setTopAnchor NOTIFY topAnchorChanged) + + Q_PROPERTY(QUrl leftButtonIcon READ icon WRITE setLeftButtonIcon) + Q_PROPERTY(QUrl rightButtonIcon READ icon WRITE setRightButtonIcon) + Q_PROPERTY(QUrl topButtonIcon READ icon WRITE setTopButtonIcon) + Q_PROPERTY(QUrl bottomButtonIcon READ icon WRITE setBottomButtonIcon) + +public: + + void setLeftButtonIcon(const QUrl &url) + { setIcon(m_leftButton, url); } + + void setRightButtonIcon(const QUrl &url) + { setIcon(m_rightButton, url); } + + void setTopButtonIcon(const QUrl &url) + { setIcon(m_topButton, url); } + + void setBottomButtonIcon(const QUrl &url) + { setIcon(m_bottomButton, url); } + + QUrl icon() const {return QUrl(); } + + LayoutWidget(QWidget *parent = 0); + ~LayoutWidget(); + + bool leftAnchor() const { return m_leftAnchor; } + bool rightAnchor() const { return m_rightAnchor; } + bool topAnchor() const { return m_topAnchor; } + bool bottomAnchor() const { return m_bottomAnchor; } + +public slots: + void setLeftAnchor(bool anchor) + { + if (anchor == m_leftAnchor) + return; + m_leftAnchor = anchor; + m_leftButton->setChecked(anchor); + emit leftAnchorChanged(); + } + + void setRightAnchor(bool anchor) + { + if (anchor == m_rightAnchor) + return; + m_rightAnchor = anchor; + m_rightButton->setChecked(anchor); + emit rightAnchorChanged(); + } + + void setTopAnchor(bool anchor) + { + if (anchor == m_topAnchor) + return; + m_topAnchor = anchor; + m_topButton->setChecked(anchor); + emit topAnchorChanged(); + } + + void setBottomAnchor(bool anchor) + { + if (anchor == m_bottomAnchor) + return; + m_bottomAnchor = anchor; + m_bottomButton->setChecked(anchor); + emit bottomAnchorChanged(); + } + +signals: + //void colorChanged(const QColor &color); + void fill(); + void topAnchorChanged(); + void bottomAnchorChanged(); + void leftAnchorChanged(); + void rightAnchorChanged(); + +private: + void setIcon(QPushButton *button, QUrl url); + bool m_leftAnchor, m_rightAnchor, m_topAnchor, m_bottomAnchor; + QPushButton *m_leftButton; + QPushButton *m_rightButton; + QPushButton *m_topButton; + QPushButton *m_bottomButton; + QPushButton *m_middleButton; +}; + +QT_END_NAMESPACE + +#endif + diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditor.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditor.cpp new file mode 100644 index 00000000000..997975ffc23 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditor.cpp @@ -0,0 +1,583 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "propertyeditor.h" + +#include <nodemetainfo.h> + +#include <propertymetainfo.h> + +#include <invalididexception.h> +#include <invalidnodestateexception.h> +#include <variantproperty.h> + +#include "propertyeditorvalue.h" + +#include <QtCore/QCoreApplication> +#include <QtCore/QDir> +#include <QtCore/QFileSystemWatcher> +#include <QtCore/QFileInfo> +#include <QtCore/QDebug> +#include <QtDeclarative/QmlView> +#include <QtDeclarative/QmlContext> +#include <QtGui/QVBoxLayout> +#include <QtGui/QShortcut> +#include <QtGui/QStackedWidget> +#include <QmlEngine> +#include <QmlMetaType> + +enum { + debug = false +}; + +namespace QmlDesigner { + +PropertyEditor::NodeType::NodeType(const QUrl &qmlFile, PropertyEditor *propertyEditor) : + m_view(new QmlView) +{ + Q_ASSERT(QFileInfo(":/images/button_normal.png").exists()); + + m_view->setContentResizable(true); + m_view->setUrl(qmlFile); + + connect(&m_backendValuesPropertyMap, SIGNAL(valueChanged(const QString&)), propertyEditor, SLOT(changeValue(const QString&))); +} + +PropertyEditor::NodeType::~NodeType() +{ +} + +void createPropertyEditorValue(const QmlObjectNode &fxObjectNode, const QString &name, const QVariant &value, QmlPropertyMap *propertyMap) +{ + QString propertyName(name); + propertyName.replace(".", "_"); + PropertyEditorValue *valueObject = new PropertyEditorValue(propertyMap); + valueObject->setName(propertyName); + valueObject->setIsInModel(fxObjectNode.modelNode().hasProperty(name)); + valueObject->setIsInSubState(fxObjectNode.propertyAffectedByCurrentState(name)); + valueObject->setModelNode(fxObjectNode.modelNode()); + + if (fxObjectNode.propertyAffectedByCurrentState(name) && !(fxObjectNode.modelNode().property(propertyName).isBindingProperty())) { + valueObject->setValue(fxObjectNode.modelValue(name)); + + } else { + valueObject->setValue(value); + } + + if (propertyName != QLatin1String("id") && + fxObjectNode.currentState().isBaseState() && + fxObjectNode.modelNode().property(propertyName).isBindingProperty()) { + valueObject->setExpression(fxObjectNode.modelNode().bindingProperty(propertyName).expression()); + } else { + valueObject->setExpression(fxObjectNode.instanceValue(name).toString()); + } + + QObject::connect(valueObject, SIGNAL(valueChanged(QString)), propertyMap, SIGNAL(valueChanged(QString))); + propertyMap->insert(propertyName, QmlMetaType::qmlType(valueObject->metaObject())->fromObject(valueObject)); +} + +void PropertyEditor::NodeType::setValue(const QmlObjectNode &fxObjectNode, const QString &name, const QVariant &value) +{ + createPropertyEditorValue(fxObjectNode, name, value, &m_backendValuesPropertyMap); +} + +void PropertyEditor::NodeType::setup(const QmlObjectNode &fxObjectNode, const QString &stateName, const QUrl &qmlSpecificsFile) +{ + if (!fxObjectNode.isValid()) + return; + + QmlContext *ctxt = m_view->rootContext(); + + // First remove complex objects from backend, so that we don't trigger a flood of updates + ctxt->setContextProperty("anchorBackend", 0); + ctxt->setContextProperty("backendValues", 0); + + foreach (const QString &propertyName, m_backendValuesPropertyMap.keys()) + m_backendValuesPropertyMap.clear(propertyName); + qDeleteAll(m_backendValuesPropertyMap.children()); + + if (fxObjectNode.isValid()) { + foreach (const QString &propertyName, fxObjectNode.modelNode().metaInfo().properties(true).keys()) + createPropertyEditorValue(fxObjectNode, propertyName, fxObjectNode.instanceValue(propertyName), &m_backendValuesPropertyMap); + + // className + PropertyEditorValue *valueObject = new PropertyEditorValue(&m_backendValuesPropertyMap); + valueObject->setName("className"); + valueObject->setModelNode(fxObjectNode.modelNode()); + valueObject->setValue(fxObjectNode.modelNode().simplifiedTypeName()); + QObject::connect(valueObject, SIGNAL(valueChanged(QString)), &m_backendValuesPropertyMap, SIGNAL(valueChanged(QString))); + m_backendValuesPropertyMap.insert("className", QmlMetaType::qmlType(valueObject->metaObject())->fromObject(valueObject)); + + // id + valueObject = new PropertyEditorValue(&m_backendValuesPropertyMap); + valueObject->setName("id"); + valueObject->setIsInModel(!fxObjectNode.modelNode().id().isEmpty()); + valueObject->setModelNode(fxObjectNode.modelNode()); + valueObject->setValue(fxObjectNode.modelNode().id()); + QObject::connect(valueObject, SIGNAL(valueChanged(QString)), &m_backendValuesPropertyMap, SIGNAL(valueChanged(QString))); + m_backendValuesPropertyMap.insert("id", QmlMetaType::qmlType(valueObject->metaObject())->fromObject(valueObject)); + + // anchors + m_backendAnchorBinding.setup(QmlItemNode(fxObjectNode.modelNode())); + + ctxt->setContextProperty("anchorBackend", &m_backendAnchorBinding); + ctxt->setContextProperty("backendValues", &m_backendValuesPropertyMap); + + ctxt->setContextProperty("specificsUrl", QVariant(qmlSpecificsFile)); + ctxt->setContextProperty("stateName", QVariant(stateName)); + ctxt->setContextProperty("propertyCount", QVariant(fxObjectNode.modelNode().properties().count())); + ctxt->setContextProperty("isBaseState", QVariant(fxObjectNode.isInBaseState())); + } else { + qWarning() << "PropertyEditor: invalid node for setup"; + } +} + +PropertyEditor::PropertyEditor(QWidget *parent) : + QmlModelView(parent), + m_parent(parent), + m_updateShortcut(0), + m_timerId(0), + m_stackedWidget(new QStackedWidget(parent)), + m_currentType(0) +{ + m_updateShortcut = new QShortcut(QKeySequence("F5"), m_stackedWidget); + connect(m_updateShortcut, SIGNAL(activated()), this, SLOT(reloadQml())); + + QFile file(":/qmldesigner/stylesheet.css"); + file.open(QFile::ReadOnly); + QString styleSheet = QLatin1String(file.readAll()); + m_stackedWidget->setStyleSheet(styleSheet); + m_stackedWidget->setMinimumWidth(320); +} + +PropertyEditor::~PropertyEditor() +{ + delete m_stackedWidget; + qDeleteAll(m_typeHash); +} + +void PropertyEditor::changeValue(const QString &name) +{ + if (name == "type") + return; + + if (name == "id") { + PropertyEditorValue *value = qobject_cast<PropertyEditorValue*>(QmlMetaType::toQObject(m_currentType->m_backendValuesPropertyMap.value(name))); + const QString newId = value->value().toString(); + + try { + if (ModelNode::isValidId(newId)) + m_selectedNode.setId(newId); + else + value->setValue(m_selectedNode.id()); + } catch (InvalidIdException &) { + value->setValue(m_selectedNode.id()); + } + + return; + } + + QString propertyName(name); + propertyName.replace("_", "."); + + PropertyEditorValue *value = qobject_cast<PropertyEditorValue*>(QmlMetaType::toQObject(m_currentType->m_backendValuesPropertyMap.value(name))); + + + if (value ==0) { + qWarning() << "PropertyEditor:" <<name << " - value is null"; + return; + } + + QmlObjectNode fxObjectNode(m_selectedNode); + + QVariant castedValue; + qreal castedExpressionValue; + bool converted = false; + + + if (fxObjectNode.modelNode().metaInfo().isValid() && fxObjectNode.modelNode().metaInfo().property(propertyName, true).isValid()) { + castedValue = fxObjectNode.modelNode().metaInfo().property(propertyName, true).castedValue(value->value()); + castedExpressionValue = value->expression().toDouble(&converted); + } else { + qWarning() << "PropertyEditor:" <<name << "cannot be casted (metainfo)"; + return ; + } + + if (value->value().isValid() && !castedValue.isValid()) { + qWarning() << "PropertyEditor:" <<name << "not properly casted (metainfo)"; + return ; + } + + if (fxObjectNode.modelNode().metaInfo().isValid() && fxObjectNode.modelNode().metaInfo().property(propertyName).isValid()) + if (fxObjectNode.modelNode().metaInfo().property(propertyName).type() == QLatin1String("QUrl")) { //turn absolute local file paths into relative paths + QString filePath = castedValue.toUrl().toString(); + if (QFileInfo(filePath).exists() && QFileInfo(filePath).isAbsolute()) { + QDir fileDir(QFileInfo(model()->fileUrl().toLocalFile()).absolutePath()); + castedValue = QUrl(fileDir.relativeFilePath(filePath)); + } + } + + + if (value->expression() == value->value().toString() || !value->value().isValid() || converted) { + if (converted) + castedValue = castedExpressionValue; + + if (!value->value().isValid()) { + fxObjectNode.removeVariantProperty(propertyName); + } else { + if (value->value().canConvert<ModelNode>()) { + ; //fxObjectNode.setVariantProperty(propertyName, value->value().value<NodeState>().modelNode().toVariant()); ### hmmm only in basestate blah blah + } else { + if (castedValue.isValid() && !castedValue.isNull()) + fxObjectNode.setVariantProperty(propertyName, castedValue); + } + } + } else { //expression + changeExpression(name); + } +} + +void PropertyEditor::changeExpression(const QString &name) +{ + QmlObjectNode fxObjectNode(m_selectedNode); + PropertyEditorValue *value = qobject_cast<PropertyEditorValue*>(QmlMetaType::toQObject(m_currentType->m_backendValuesPropertyMap.value(name))); + if (fxObjectNode.currentState().isBaseState()) { + fxObjectNode.modelNode().bindingProperty(name).setExpression(value->expression()); + } +} + +void PropertyEditor::otherPropertyChanged(const QmlObjectNode &fxObjectNode) +{ + if (fxObjectNode.isValid() && m_currentType && fxObjectNode == m_selectedNode && fxObjectNode.currentState().isValid()) { + foreach (const QString &propertyName, fxObjectNode.modelNode().metaInfo().properties(true).keys()) { + if ( propertyName != "id" && propertyName != "objectName") { + QString name(propertyName); + name.replace(".", "_"); + QVariant backendValue(m_currentType->m_backendValuesPropertyMap.value(propertyName)); + PropertyEditorValue *valueObject = 0; + if (backendValue.isValid()) + valueObject = qobject_cast<PropertyEditorValue*>(QmlMetaType::toQObject(backendValue)); + else + valueObject = new PropertyEditorValue(&m_currentType->m_backendValuesPropertyMap); + + if (valueObject == 0) { + qWarning() << "PropertyEditor: you propably assigned a wrong value to backendValues"; + return; + } + + valueObject->setName(propertyName); + valueObject->setIsInModel(fxObjectNode.hasProperty(propertyName)); + valueObject->setIsInSubState(fxObjectNode.propertyAffectedByCurrentState(propertyName)); + valueObject->setModelNode(fxObjectNode.modelNode()); + + if (fxObjectNode.modelNode().property(propertyName).isBindingProperty()) + valueObject->setValue(fxObjectNode.instanceValue(propertyName)); + else + valueObject->setValue(fxObjectNode.modelValue(propertyName)); + + connect(valueObject, SIGNAL(valueChanged(QString)), &m_currentType->m_backendValuesPropertyMap, SIGNAL(valueChanged(QString))); + m_currentType->m_backendValuesPropertyMap.insert(propertyName, QmlMetaType::qmlType(valueObject->metaObject())->fromObject(valueObject)); + } + } + } +} + +void PropertyEditor::setQmlDir(const QString &qmlDir) +{ + m_qmlDir = qmlDir; + + QFileSystemWatcher *watcher = new QFileSystemWatcher(this); + watcher->addPath(m_qmlDir); + connect(watcher, SIGNAL(directoryChanged(QString)), this, SLOT(reloadQml())); +} + +void PropertyEditor::delayedResetView() +{ + if (m_timerId == 0) + m_timerId = startTimer(50); +} + +void PropertyEditor::timerEvent(QTimerEvent *timerEvent) +{ + if (m_timerId == timerEvent->timerId()) { + resetView(); + } +} + +void PropertyEditor::resetView() +{ + if (model() == 0) + return; + + if (debug) + qDebug() << "________________ RELOADING PROPERTY EDITOR QML _______________________"; + + if (m_timerId) + killTimer(m_timerId); + + if (m_selectedNode.isValid() && model() != m_selectedNode.model()) + m_selectedNode = ModelNode(); + + QUrl qmlFile(qmlForNode(m_selectedNode)); + QUrl qmlSpecificsFile; + if (m_selectedNode.isValid()) + qmlSpecificsFile = fileToUrl(locateQmlFile(m_selectedNode.type() + "Specifics.qml")); + + NodeType *type = m_typeHash.value(qmlFile.toString()); + if (!type) { + type = new NodeType(qmlFile, this); + + m_stackedWidget->addWidget(type->m_view); + m_typeHash.insert(qmlFile.toString(), type); + + QmlObjectNode fxObjectNode; + if (m_selectedNode.isValid()) { + fxObjectNode = QmlObjectNode(m_selectedNode); + Q_ASSERT(fxObjectNode.isValid()); + } + type->setup(fxObjectNode, currentState().name(), qmlSpecificsFile); + + QmlContext *ctxt = type->m_view->rootContext(); + ctxt->setContextProperty("finishedNotify", QVariant(false)); + type->m_view->execute(); + ctxt->setContextProperty("finishedNotify", QVariant(true)); + } else { + QmlObjectNode fxObjectNode; + if (m_selectedNode.isValid()) { + fxObjectNode = QmlObjectNode(m_selectedNode); + } + type->setup(fxObjectNode, currentState().name(), qmlSpecificsFile); + } + + m_stackedWidget->setCurrentWidget(type->m_view); + m_currentType = type; + + if (m_timerId) + m_timerId = 0; +} + +void PropertyEditor::selectedNodesChanged(const QList<ModelNode> &selectedNodeList, + const QList<ModelNode> &lastSelectedNodeList) +{ + Q_UNUSED(lastSelectedNodeList); + + if (m_selectedNode.isValid() && selectedNodeList.contains(m_selectedNode)) + return; + + if (selectedNodeList.isEmpty()) + select(ModelNode()); + else + select(selectedNodeList.first()); +} + +void PropertyEditor::nodeAboutToBeRemoved(const ModelNode &removedNode) +{ + QmlModelView::nodeAboutToBeRemoved(removedNode); + if (m_selectedNode.isValid() && removedNode.isValid() && m_selectedNode == removedNode) + select(m_selectedNode.parentProperty().parentModelNode()); +} + +void PropertyEditor::modelAttached(Model *model) +{ + QmlModelView::modelAttached(model); + + if (debug) + qDebug() << Q_FUNC_INFO; + + resetView(); +} + +void PropertyEditor::modelAboutToBeDetached(Model *model) +{ + QmlModelView::modelAboutToBeDetached(model); + + resetView(); +} + + +void PropertyEditor::propertiesAboutToBeRemoved(const QList<AbstractProperty>& propertyList) +{ + QmlModelView::propertiesAboutToBeRemoved(propertyList); + + if (!m_selectedNode.isValid()) + return; + + foreach (const AbstractProperty &property, propertyList) { + if (property.isVariantProperty() || property.isBindingProperty()) { + ModelNode node(property.parentModelNode()); + m_currentType->setValue(node, property.name(), QmlObjectNode(node).instanceValue(property.name())); + } + } +} + + +void PropertyEditor::variantPropertiesChanged(const QList<VariantProperty>& propertyList, PropertyChangeFlags propertyChange) +{ + QmlModelView::variantPropertiesChanged(propertyList, propertyChange); + + if (!m_selectedNode.isValid()) + return; + + foreach (const VariantProperty &property, propertyList) { + ModelNode node(property.parentModelNode()); + + if (node == m_selectedNode || QmlObjectNode(m_selectedNode).propertyChangeForCurrentState() == node) { + if ( QmlObjectNode(m_selectedNode).modelNode().property(property.name()).isBindingProperty()) + m_currentType->setValue(m_selectedNode, property.name(), QmlObjectNode(m_selectedNode).instanceValue(property.name())); + else + m_currentType->setValue(m_selectedNode, property.name(), QmlObjectNode(m_selectedNode).modelValue(property.name())); + } + } +} + +void PropertyEditor::bindingPropertiesChanged(const QList<BindingProperty>& propertyList, PropertyChangeFlags propertyChange) +{ + QmlModelView::bindingPropertiesChanged(propertyList, propertyChange); + + if (!m_selectedNode.isValid()) + return; + + foreach (const BindingProperty &property, propertyList) { + ModelNode node(property.parentModelNode()); + + if (node == m_selectedNode || QmlObjectNode(m_selectedNode).propertyChangeForCurrentState() == node) { + if ( QmlObjectNode(m_selectedNode).modelNode().property(property.name()).isBindingProperty()) + m_currentType->setValue(m_selectedNode, property.name(), QmlObjectNode(m_selectedNode).instanceValue(property.name())); + else + m_currentType->setValue(m_selectedNode, property.name(), QmlObjectNode(m_selectedNode).modelValue(property.name())); + } + } +} + +void PropertyEditor::nodeIdChanged(const ModelNode& node, const QString& newId, const QString& oldId) +{ + QmlModelView::nodeIdChanged(node, newId, oldId); + + if (!m_selectedNode.isValid()) + return; + + if (node == m_selectedNode) { + + if (m_currentType) { + m_currentType->setValue(node, "id", newId); + } + } +} + +void PropertyEditor::select(const ModelNode &node) +{ + if (node.isValid()) + m_selectedNode = node; + else + m_selectedNode = ModelNode(); + + delayedResetView(); +} + +QWidget *PropertyEditor::createPropertiesPage() +{ + delayedResetView(); + return m_stackedWidget; +} + +void PropertyEditor::stateChanged(const QmlModelState &newQmlModelState, const QmlModelState &oldQmlModelState) +{ + QmlModelView::stateChanged(newQmlModelState, oldQmlModelState); + Q_ASSERT(newQmlModelState.isValid()); + if (debug) + qDebug() << Q_FUNC_INFO << newQmlModelState.name(); + delayedResetView(); +} + +void PropertyEditor::reloadQml() +{ + m_typeHash.clear(); + while (QWidget *widget = m_stackedWidget->widget(0)) { + m_stackedWidget->removeWidget(widget); + delete widget; + } + m_currentType = 0; + + delayedResetView(); +} + +QString PropertyEditor::qmlFileName(const NodeMetaInfo &nodeInfo) const +{ + return nodeInfo.typeName() + QLatin1String("Pane.qml"); +} + +QUrl PropertyEditor::fileToUrl(const QString &filePath) const { + QUrl fileUrl; + + if (filePath.isEmpty()) + return fileUrl; + + if (filePath.startsWith(":")) { + fileUrl.setScheme("qrc"); + QString path = filePath; + path.remove(0, 1); // remove trailing ':' + fileUrl.setPath(path); + } else { + fileUrl = QUrl::fromLocalFile(filePath); + } + + return fileUrl; +} + +QUrl PropertyEditor::qmlForNode(const ModelNode &modelNode) const +{ + if (modelNode.isValid()) { + QList<NodeMetaInfo> hierarchy; + hierarchy << modelNode.metaInfo(); + hierarchy << modelNode.metaInfo().superClasses(); + + foreach (const NodeMetaInfo &info, hierarchy) { + QUrl fileUrl = fileToUrl(locateQmlFile(qmlFileName(info))); + if (fileUrl.isValid()) + return fileUrl; + } + } + return fileToUrl(QDir(m_qmlDir).filePath("Qt/emptyPane.qml")); +} + +QString PropertyEditor::locateQmlFile(const QString &relativePath) const +{ + QDir fileSystemDir(m_qmlDir); + static QDir resourcesDir(":/propertyeditor"); + + if (fileSystemDir.exists(relativePath)) + return fileSystemDir.absoluteFilePath(relativePath); + if (resourcesDir.exists(relativePath)) + return resourcesDir.absoluteFilePath(relativePath); + + return QString(); +} + +} //QmlDesigner + diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditor.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditor.h new file mode 100644 index 00000000000..6f3672ed8f8 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditor.h @@ -0,0 +1,126 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef QmlPropertyView_h +#define QmlPropertyView_h + +#include <qmlmodelview.h> +#include <QmlView> +#include <QHash> +#include <QmlPropertyMap> + +#include "qmlanchorbindingproxy.h" + +class QShortcut; +class QStackedWidget; + +namespace QmlDesigner { + +class PropertyEditor: public QmlModelView +{ + Q_OBJECT + + class NodeType { + public: + NodeType(const QUrl &qmlFile, PropertyEditor *propertyEditor); + ~NodeType(); + + void setup(const QmlObjectNode &fxObjectNode, const QString &stateName, const QUrl &qmlSpecificsFile); + void setValue(const QmlObjectNode &fxObjectNode, const QString &name, const QVariant &value); + + QmlView *m_view; + Internal::QmlAnchorBindingProxy m_backendAnchorBinding; + QmlPropertyMap m_backendValuesPropertyMap; + }; + +public: + PropertyEditor(QWidget *parent = 0); + ~PropertyEditor(); + + void setQmlDir(const QString &qmlDirPath); + + QWidget* createPropertiesPage(); + + void selectedNodesChanged(const QList<ModelNode> &selectedNodeList, + const QList<ModelNode> &lastSelectedNodeList); + void nodeAboutToBeRemoved(const ModelNode &removedNode); + + void propertiesAdded(const NodeState &state, const QList<NodeProperty>& propertyList); + void propertiesAboutToBeRemoved(const NodeState &state, const QList<NodeProperty>& propertyList); + void propertyValuesChanged(const NodeState &state, const QList<NodeProperty>& propertyList); + + void modelAttached(Model *model); + + void modelAboutToBeDetached(Model *model); + + ModelState modelState() const; + + void otherPropertyChanged(const QmlObjectNode &); + + void stateChanged(const QmlModelState &newQmlModelState, const QmlModelState &oldQmlModelState); + + void propertiesAboutToBeRemoved(const QList<AbstractProperty>& propertyList); + void variantPropertiesChanged(const QList<VariantProperty>& propertyList, PropertyChangeFlags propertyChange); + void bindingPropertiesChanged(const QList<BindingProperty>& propertyList, PropertyChangeFlags propertyChange); + + void nodeIdChanged(const ModelNode& node, const QString& newId, const QString& oldId); + +protected: + void timerEvent(QTimerEvent *event); + +private slots: + void reloadQml(); + void changeValue(const QString &name); + void changeExpression(const QString &name); + +private: //functions + QString qmlFileName(const NodeMetaInfo &nodeInfo) const; + QUrl fileToUrl(const QString &filePath) const; + QUrl qmlForNode(const ModelNode &modelNode) const; + QString locateQmlFile(const QString &relativePath) const; + void select(const ModelNode& node); + + void resetView(); + void delayedResetView(); + + +private: //variables + ModelNode m_selectedNode; + QWidget *m_parent; + QShortcut *m_updateShortcut; + int m_timerId; + QStackedWidget* m_stackedWidget; + QString m_qmlDir; + QHash<QString, NodeType *> m_typeHash; + NodeType *m_currentType; +}; + +} + +#endif // QmlPropertyView_h diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditor.pri b/src/plugins/qmldesigner/components/propertyeditor/propertyeditor.pri new file mode 100644 index 00000000000..4d0fc95a6f6 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditor.pri @@ -0,0 +1,34 @@ +VPATH += $$PWD +INCLUDEPATH += $$PWD +include($$PWD/qtgradienteditor/qtgradienteditor.pri) +SOURCES += propertyeditor.cpp \ + qmlanchorbindingproxy.cpp \ + allpropertiesbox.cpp \ + resetwidget.cpp \ + qlayoutobject.cpp \ + basiclayouts.cpp \ + basicwidgets.cpp \ + colorwidget.cpp \ + behaviordialog.cpp \ + qproxylayoutitem.cpp \ + layoutwidget.cpp \ + filewidget.cpp \ + propertyeditorvalue.cpp \ + fontwidget.cpp +HEADERS += propertyeditor.h \ + qmlanchorbindingproxy.h \ + allpropertiesbox.h \ + resetwidget.h \ + qlayoutobject.h \ + basiclayouts.h \ + basicwidgets.h \ + colorwidget.h \ + behaviordialog.h \ + qproxylayoutitem.h \ + layoutwidget.h \ + filewidget.h \ + propertyeditorvalue.h \ + fontwidget.h +QT += declarative +RESOURCES += propertyeditor.qrc +FORMS += behaviordialog.ui diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditor.qrc b/src/plugins/qmldesigner/components/propertyeditor/propertyeditor.qrc new file mode 100644 index 00000000000..61d22611ba8 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditor.qrc @@ -0,0 +1,12 @@ +<RCC> + <qresource prefix="/"> + <file>images/button_hovered.png</file> + <file>images/button_normal.png</file> + <file>images/button_pressed.png</file> + <file>images/button2_hovered.png</file> + <file>images/button2_normal.png</file> + <file>images/button2_pressed.png</file> + <file>images/frame.png</file> + <file>images/gradient.png</file> + </qresource> +</RCC> diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp new file mode 100644 index 00000000000..c08b535a2a1 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp @@ -0,0 +1,343 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "propertyeditorvalue.h" +#include <abstractview.h> +#include <nodeabstractproperty.h> +#include <nodeproperty.h> +#include <model.h> +#include <nodemetainfo.h> +#include <metainfo.h> +#include <propertymetainfo.h> +#include <nodeproperty.h> +#include <qmlobjectnode.h> + +QML_DEFINE_TYPE(Bauhaus,1,0,PropertyEditorValue,PropertyEditorValue) +QML_DEFINE_TYPE(Bauhaus,1,0,PropertyEditorNodeWrapper,PropertyEditorNodeWrapper) +QML_DEFINE_TYPE(Bauhaus,1,0,QmlPropertyMap,QmlPropertyMap) + + +//using namespace QmlDesigner; + +PropertyEditorValue::PropertyEditorValue(QObject *parent) + : QObject(parent), + m_isInSubState(false), + m_isInModel(false), + m_isBound(false), + m_isValid(false), + m_complexNode(new PropertyEditorNodeWrapper(this)) +{ +} + +QVariant PropertyEditorValue::value() const +{ + QVariant returnValue = m_value; + if (modelNode().metaInfo().isValid() && modelNode().metaInfo().property(name()).isValid()) + if (modelNode().metaInfo().property(name()).type() == QLatin1String("QUrl")) { + returnValue = returnValue.toUrl().toString(); + } + return returnValue; +} + +static bool cleverDoubleCompare(QVariant value1, QVariant value2) +{ //we ignore slight changes on doubles + if ((value1.type() == QVariant::Double) && (value2.type() == QVariant::Double)) { + int a = value1.toDouble() * 100; + int b = value2.toDouble() * 100; + + if (qFuzzyCompare((qreal(a) / 100), (qreal(b) / 100))) { + return true; + } + } + return false; +} + +void PropertyEditorValue::setValueWithEmit(const QVariant &value) +{ + if ( m_value != value) { + QVariant newValue = value; + if (modelNode().metaInfo().isValid() && modelNode().metaInfo().property(name()).isValid()) + if (modelNode().metaInfo().property(name()).type() == QLatin1String("QUrl")) { + newValue = QUrl(newValue.toString()); + } + + if (cleverDoubleCompare(newValue, m_value)) + return; + setValue(newValue); + setExpressionWithEmit(value.toString()); + m_isBound = false; + emit valueChanged(name()); + emit isBoundChanged(); + } +} + +void PropertyEditorValue::setValue(const QVariant &value) +{ + if ( m_value != value) { + m_value = value; + emit valueChanged(); + } + emit isBoundChanged(); +} + +QString PropertyEditorValue::expression() const +{ + + return m_expression; +} + +void PropertyEditorValue::setExpressionWithEmit(const QString &expression) +{ + if ( m_expression != expression) { + setExpression(expression); + m_isBound = true; + emit expressionChanged(name()); + emit valueChanged(name()); + } +} + +void PropertyEditorValue::setExpression(const QString &expression) +{ + if ( m_expression != expression) { + m_expression = expression; + emit expressionChanged(); + } +} + +bool PropertyEditorValue::isInSubState() const +{ + return m_isInSubState; +} + +bool PropertyEditorValue::isBound() const +{ + return modelNode().property(name()).isBindingProperty(); +} + +void PropertyEditorValue::setIsInSubState(bool isInSubState) +{ + m_isInSubState = isInSubState; +} + +bool PropertyEditorValue::isInModel() const +{ + return m_isInModel; +} + +void PropertyEditorValue::setIsInModel(bool isInModel) +{ + m_isInModel = isInModel; +} + + +QString PropertyEditorValue::name() const +{ + return m_name; +} + +void PropertyEditorValue::setName(const QString &name) +{ + m_name = name; +} + + +bool PropertyEditorValue::isValid() const +{ + return m_isValid; +} + +void PropertyEditorValue::setIsValid(bool valid) +{ + m_isValid = valid; +} + +ModelNode PropertyEditorValue::modelNode() const +{ + return m_modelNode; +} + +void PropertyEditorValue::setModelNode(const ModelNode &modelNode) +{ + if (modelNode != m_modelNode) { + m_modelNode = modelNode; + m_complexNode->update(); + emit modelNodeChanged(); + } +} + +PropertyEditorNodeWrapper* PropertyEditorValue::complexNode() +{ + return m_complexNode; +} + +void PropertyEditorValue::resetValue() +{ + if (m_value.isValid()) { + setValue(QVariant()); + m_isBound = false; + emit valueChanged(name()); + } +} + +PropertyEditorNodeWrapper::PropertyEditorNodeWrapper(PropertyEditorValue* parent) : m_valuesPropertyMap(this) +{ + m_editorValue = parent; + connect(m_editorValue, SIGNAL(modelNodeChanged()), this, SLOT(update())); +} + +PropertyEditorNodeWrapper::PropertyEditorNodeWrapper(QObject *parent) : QObject(parent) +{ +} + +bool PropertyEditorNodeWrapper::exists() +{ + if (!(m_editorValue && m_editorValue->modelNode().isValid())) + return false; + + return m_modelNode.isValid(); +} + +QString PropertyEditorNodeWrapper::type() +{ + if (!(m_modelNode.isValid())) + return QString(""); + + return m_modelNode.simplifiedTypeName(); + +} + +ModelNode PropertyEditorNodeWrapper::parentModelNode() const +{ + return m_editorValue->modelNode(); +} + +QString PropertyEditorNodeWrapper::propertyName() const +{ + return m_editorValue->name(); +} + +QmlPropertyMap* PropertyEditorNodeWrapper::properties() +{ + return &m_valuesPropertyMap; +} + +void PropertyEditorNodeWrapper::add(const QString &type) +{ + + QString propertyType = type; + + if ((m_editorValue && m_editorValue->modelNode().isValid())) { + if (propertyType == "") + propertyType = m_editorValue->modelNode().metaInfo().property(m_editorValue->name()).type(); + while (propertyType.contains('*')) //strip star + propertyType.chop(1); + m_modelNode = m_editorValue->modelNode().view()->createModelNode(propertyType, 4, 6); + m_editorValue->modelNode().nodeAbstractProperty(m_editorValue->name()).reparentHere(m_modelNode); + if (!m_modelNode.isValid()) { + qWarning("PropertyEditorNodeWrapper::add failed"); + } + } else { + qWarning("PropertyEditorNodeWrapper::add failed - node invalid"); + } + setup(); +} + +void PropertyEditorNodeWrapper::remove() +{ + if ((m_editorValue && m_editorValue->modelNode().isValid())) { + if (QmlDesigner::QmlObjectNode(m_modelNode).isValid()) + QmlDesigner::QmlObjectNode(m_modelNode).destroy(); + m_editorValue->modelNode().removeProperty(m_editorValue->name()); + } else { + qWarning("PropertyEditorNodeWrapper::remove failed - node invalid"); + } + m_modelNode = QmlDesigner::ModelNode(); + + foreach (const QString &propertyName, m_valuesPropertyMap.keys()) + m_valuesPropertyMap.clear(propertyName); + foreach (QObject *object, m_valuesPropertyMap.children()) + delete object; + emit propertiesChanged(); + emit existsChanged(); +} + +void PropertyEditorNodeWrapper::changeValue(const QString &name) +{ + if (m_modelNode.isValid()) { + QmlDesigner::QmlObjectNode fxObjectNode(m_modelNode); + + PropertyEditorValue *valueObject = qvariant_cast<PropertyEditorValue *>(m_valuesPropertyMap.value(name)); + + if (valueObject->value().isValid()) + fxObjectNode.setVariantProperty(name, valueObject->value()); + else + fxObjectNode.removeVariantProperty(name); + } +} + +void PropertyEditorNodeWrapper::setup() +{ + Q_ASSERT(m_editorValue); + Q_ASSERT(m_editorValue->modelNode().isValid()); + if ((m_editorValue->modelNode().isValid() && m_modelNode.isValid())) { + QmlDesigner::QmlObjectNode fxObjectNode(m_modelNode); + foreach ( const QString &propertyName, m_valuesPropertyMap.keys()) + m_valuesPropertyMap.clear(propertyName); + foreach (QObject *object, m_valuesPropertyMap.children()) + delete object; + + foreach (const QString &propertyName, m_modelNode.metaInfo().properties().keys()) { + PropertyEditorValue *valueObject = new PropertyEditorValue(&m_valuesPropertyMap); + valueObject->setName(propertyName); + valueObject->setIsInModel(fxObjectNode.hasProperty(propertyName)); + valueObject->setIsInSubState(fxObjectNode.propertyAffectedByCurrentState(propertyName)); + valueObject->setModelNode(fxObjectNode.modelNode()); + valueObject->setValue(fxObjectNode.instanceValue(propertyName)); + + connect(valueObject, SIGNAL(valueChanged(QString)), &m_valuesPropertyMap, SIGNAL(valueChanged(QString))); + m_valuesPropertyMap.insert(propertyName, QmlMetaType::qmlType(valueObject->metaObject())->fromObject(valueObject)); + } + } + connect(&m_valuesPropertyMap, SIGNAL(valueChanged(const QString &)), this, SLOT(changeValue(const QString&))); + + emit propertiesChanged(); + emit existsChanged(); +} + +void PropertyEditorNodeWrapper::update() +{ + if (m_editorValue && m_editorValue->modelNode().isValid()) { + if (m_editorValue->modelNode().hasProperty(m_editorValue->name()) && m_editorValue->modelNode().property(m_editorValue->name()).isNodeProperty()) { + m_modelNode = m_editorValue->modelNode().nodeProperty(m_editorValue->name()).modelNode(); + } + setup(); + emit existsChanged(); + emit typeChanged(); + } +} diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h new file mode 100644 index 00000000000..d07ec846cb7 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h @@ -0,0 +1,155 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef PROPERTYEDITORVALUE_H +#define PROPERTYEDITORVALUE_H + +#include <QObject> +#include <QmlPropertyMap> +#include <qml.h> +#include <modelnode.h> + +class PropertyEditorValue; + +typedef QmlDesigner::ModelNode ModelNode; + + +class PropertyEditorNodeWrapper : public QObject { + Q_OBJECT + + Q_PROPERTY(bool exists READ exists NOTIFY existsChanged) + Q_PROPERTY(QmlPropertyMap* properties READ properties NOTIFY propertiesChanged) + Q_PROPERTY(QString type READ type NOTIFY typeChanged) + +public: + PropertyEditorNodeWrapper(QObject *parent=0); + PropertyEditorNodeWrapper(PropertyEditorValue* parent); + bool exists(); + QString type(); + QmlPropertyMap* properties(); + ModelNode parentModelNode() const; + QString propertyName() const; + +public slots: + void add(const QString &type = ""); + void remove(); + void changeValue(const QString &name); + void update(); + +signals: + void existsChanged(); + void propertiesChanged(); + void typeChanged(); + +private: + void setup(); + + QmlDesigner::ModelNode m_modelNode; + QmlPropertyMap m_valuesPropertyMap; + PropertyEditorValue* m_editorValue; +}; + + +class PropertyEditorValue : public QObject +{ + Q_OBJECT + Q_PROPERTY(QVariant value READ value WRITE setValueWithEmit NOTIFY valueChanged FINAL) + Q_PROPERTY(QString expression READ expression WRITE setExpressionWithEmit NOTIFY expressionChanged FINAL) + Q_PROPERTY(bool isInModel READ isInModel FINAL) + Q_PROPERTY(bool isInSubState READ isInSubState FINAL) + Q_PROPERTY(bool isBound READ isBound NOTIFY isBoundChanged FINAL) + Q_PROPERTY(bool isValid READ isValid NOTIFY isValid FINAL) + Q_PROPERTY(QString name READ name FINAL) + Q_PROPERTY(PropertyEditorNodeWrapper* complexNode READ complexNode NOTIFY complexNodeChanged FINAL) + +public: + PropertyEditorValue(QObject *parent=0); + + QVariant value() const; + void setValueWithEmit(const QVariant &value); + void setValue(const QVariant &value); + + QString expression() const; + void setExpressionWithEmit(const QString &expression); + void setExpression(const QString &expression); + + bool isInSubState() const; + void setIsInSubState(bool isInSubState); + + bool isInModel() const; + + bool isBound() const; + bool isValid() const; + + void setIsValid(bool valid); + + void setIsInModel(bool isInModel); + + QString name() const; + void setName(const QString &name); + + ModelNode modelNode() const; + void setModelNode(const ModelNode &modelNode); + + PropertyEditorNodeWrapper* complexNode(); + +public slots: + void resetValue(); + +signals: + void valueChanged(const QString &name); + void valueChanged(); + + void expressionChanged(const QString &name); + void expressionChanged(); + + void modelStateChanged(); + void modelNodeChanged(); + void complexNodeChanged(); + void isBoundChanged(); + void isValidChanged(); + +private: //variables + ModelNode m_modelNode; + QVariant m_value; + QString m_expression; + QString m_name; + bool m_isInSubState; + bool m_isInModel; + bool m_isBound; + bool m_isValid; //if the property value belongs to an non existant complexProperty it is invalid + PropertyEditorNodeWrapper *m_complexNode; +}; + +QML_DECLARE_TYPE(PropertyEditorValue); +QML_DECLARE_TYPE(PropertyEditorNodeWrapper); +QML_DECLARE_TYPE(QmlPropertyMap); + + +#endif // PROPERTYEDITORVALUE_H diff --git a/src/plugins/qmldesigner/components/propertyeditor/qlayoutobject.cpp b/src/plugins/qmldesigner/components/propertyeditor/qlayoutobject.cpp new file mode 100644 index 00000000000..fede3912b09 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/qlayoutobject.cpp @@ -0,0 +1,57 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#include "qlayoutobject.h" + + +QT_BEGIN_NAMESPACE +QML_DEFINE_TYPE(Qt,4,6,QLayoutObject,QLayoutObject); + +QLayoutObject::QLayoutObject(QObject *parent) +: QObject(parent) +{ +} + +QLayout *QLayoutObject::layout() const +{ + return 0; +} + +QT_END_NAMESPACE diff --git a/src/plugins/qmldesigner/components/propertyeditor/qlayoutobject.h b/src/plugins/qmldesigner/components/propertyeditor/qlayoutobject.h new file mode 100644 index 00000000000..e5d23c64fbc --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/qlayoutobject.h @@ -0,0 +1,69 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#ifndef QLAYOUTOBJECT_H +#define QLAYOUTOBJECT_H + +#include <QObject> +#include <QLayout> +#include <qml.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QLayoutObject : public QObject +{ +Q_OBJECT +public: + QLayoutObject(QObject *parent = 0); + + virtual QLayout *layout() const; +}; +QML_DECLARE_TYPE(QLayoutObject); + +#endif // QLAYOUTOBJECT_H + + +QT_END_NAMESPACE + +QT_END_HEADER diff --git a/src/plugins/qmldesigner/components/propertyeditor/qmlanchorbindingproxy.cpp b/src/plugins/qmldesigner/components/propertyeditor/qmlanchorbindingproxy.cpp new file mode 100644 index 00000000000..805df99070f --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/qmlanchorbindingproxy.cpp @@ -0,0 +1,287 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "qmlanchorbindingproxy.h" +#include <qmlanchors.h> +#include <nodeabstractproperty.h> +#include <nodeinstance.h> +#include <QDebug> + +namespace QmlDesigner { + +class ModelNode; +class NodeState; + +namespace Internal { + +QmlAnchorBindingProxy::QmlAnchorBindingProxy(QObject *parent) : + QObject(parent) +{ +} + +QmlAnchorBindingProxy::~QmlAnchorBindingProxy() +{ +} + +void QmlAnchorBindingProxy::setup(const QmlItemNode &fxItemNode) +{ + m_fxItemNode = fxItemNode; + emit parentChanged(); + emit topAnchorChanged(); + emit bottomAnchorChanged(); + emit leftAnchorChanged(); + emit rightAnchorChanged(); + emit centeredHChanged(); + emit centeredVChanged(); + emit anchorsChanged(); +} + +bool QmlAnchorBindingProxy::hasParent() +{ + return m_fxItemNode.hasNodeParent(); +} + +bool QmlAnchorBindingProxy::topAnchored() +{ + return m_fxItemNode.anchors().instanceHasAnchor(AnchorLine::Top); +} + +bool QmlAnchorBindingProxy::bottomAnchored() +{ + return m_fxItemNode.anchors().instanceHasAnchor(AnchorLine::Bottom); +} + +bool QmlAnchorBindingProxy::leftAnchored() +{ + return m_fxItemNode.anchors().instanceHasAnchor(AnchorLine::Left); +} + +bool QmlAnchorBindingProxy::rightAnchored() +{ + return m_fxItemNode.anchors().instanceHasAnchor(AnchorLine::Right); +} + +bool QmlAnchorBindingProxy::hasAnchors() +{ + return m_fxItemNode.anchors().instanceHasAnchors(); +} + + +void QmlAnchorBindingProxy::resetLayout() { + m_fxItemNode.anchors().removeAnchors(); + m_fxItemNode.anchors().removeMargins(); + + emit topAnchorChanged(); + emit bottomAnchorChanged(); + emit leftAnchorChanged(); + emit rightAnchorChanged(); + emit anchorsChanged(); + } + +void QmlAnchorBindingProxy::setBottomAnchor(bool anchor) +{ + if (!m_fxItemNode.hasNodeParent()) + return; + + if (bottomAnchored() == anchor) + return; + + if (!anchor) { + removeBottomAnchor(); + } else { + + qreal bottomMargin = parentBoundingBox().bottom() - transformedBoundingBox().bottom(); + + m_fxItemNode.anchors().setAnchor(AnchorLine::Bottom, m_fxItemNode.modelNode().parentProperty().parentModelNode(), AnchorLine::Bottom); + m_fxItemNode.anchors().setMargin(AnchorLine::Bottom, bottomMargin); + } + emit bottomAnchorChanged(); + + if (hasAnchors() != anchor) + emit anchorsChanged(); +} + +void QmlAnchorBindingProxy::setLeftAnchor(bool anchor) +{ + if (!m_fxItemNode.hasNodeParent()) + return; + + if (leftAnchored() == anchor) + return; + + if (!anchor) { + removeLeftAnchor(); + } else { + qreal leftMargin = transformedBoundingBox().left() - parentBoundingBox().left(); + + m_fxItemNode.anchors().setAnchor(AnchorLine::Left, m_fxItemNode.modelNode().parentProperty().parentModelNode(), AnchorLine::Left); + m_fxItemNode.anchors().setMargin(AnchorLine::Left, leftMargin); + } + emit leftAnchorChanged(); + if (hasAnchors() != anchor) + emit anchorsChanged(); +} + +void QmlAnchorBindingProxy::setRightAnchor(bool anchor) +{ + if (!m_fxItemNode.hasNodeParent()) + return; + + if (rightAnchored() == anchor) + return; + + if (!anchor) { + removeRightAnchor(); + } else { + + qreal rightMargin = parentBoundingBox().right() - transformedBoundingBox().right(); + + m_fxItemNode.anchors().setAnchor(AnchorLine::Right, m_fxItemNode.modelNode().parentProperty().parentModelNode(), AnchorLine::Right); + m_fxItemNode.anchors().setMargin(AnchorLine::Right, rightMargin); + } + emit rightAnchorChanged(); + if (hasAnchors() != anchor) + emit anchorsChanged(); +} + QRectF QmlAnchorBindingProxy::parentBoundingBox() +{ + if (m_fxItemNode.hasInstanceParent()) + return m_fxItemNode.instanceParent().toQmlItemNode().instanceBoundingRect(); + + return QRect(); +} + +QRectF QmlAnchorBindingProxy::transformedBoundingBox() +{ + return m_fxItemNode.instanceTransform().mapRect(m_fxItemNode.instanceBoundingRect()); +} + +void QmlAnchorBindingProxy::setTopAnchor(bool anchor) +{ + if (!m_fxItemNode.hasNodeParent()) + return ; + + if (topAnchored() == anchor) + return; + + if (!anchor) { + removeTopAnchor(); + } else { + + qreal topMargin = transformedBoundingBox().top() - parentBoundingBox().top(); + + m_fxItemNode.anchors().setAnchor(AnchorLine::Top, m_fxItemNode.modelNode().parentProperty().parentModelNode(), AnchorLine::Top); + m_fxItemNode.anchors().setMargin( AnchorLine::Top, topMargin); + } + emit topAnchorChanged(); + if (hasAnchors() != anchor) + emit anchorsChanged(); +} + +void QmlAnchorBindingProxy::removeTopAnchor() { + m_fxItemNode.anchors().removeAnchor(AnchorLine::Top); + m_fxItemNode.anchors().removeMargin(AnchorLine::Top); +} + +void QmlAnchorBindingProxy::removeBottomAnchor() { + m_fxItemNode.anchors().removeAnchor(AnchorLine::Bottom); + m_fxItemNode.anchors().removeMargin(AnchorLine::Bottom); +} + +void QmlAnchorBindingProxy::removeLeftAnchor() { + m_fxItemNode.anchors().removeAnchor(AnchorLine::Left); + m_fxItemNode.anchors().removeMargin(AnchorLine::Left); +} + +void QmlAnchorBindingProxy::removeRightAnchor() { + m_fxItemNode.anchors().removeAnchor(AnchorLine::Right); + m_fxItemNode.anchors().removeMargin(AnchorLine::Right); +} + +void QmlAnchorBindingProxy::setVerticalCentered(bool centered) +{ + if (!m_fxItemNode.hasNodeParent()) + return ; + + if (verticalCentered() == centered) + return; + + if (!centered) { + m_fxItemNode.anchors().removeAnchor(AnchorLine::VerticalCenter); + } else { + m_fxItemNode.anchors().setAnchor(AnchorLine::VerticalCenter, m_fxItemNode.modelNode().parentProperty().parentModelNode(), AnchorLine::VerticalCenter); + } + emit centeredVChanged(); +} + +void QmlAnchorBindingProxy::setHorizontalCentered(bool centered) +{ + if (!m_fxItemNode.hasNodeParent()) + return ; + + if (horizontalCentered() == centered) + return; + + if (!centered) { + m_fxItemNode.anchors().removeAnchor(AnchorLine::HorizontalCenter); + } else { + m_fxItemNode.anchors().setAnchor(AnchorLine::HorizontalCenter, m_fxItemNode.modelNode().parentProperty().parentModelNode(), AnchorLine::HorizontalCenter); + } + emit centeredHChanged(); +} + +bool QmlAnchorBindingProxy::verticalCentered() +{ + return m_fxItemNode.anchors().instanceHasAnchor(AnchorLine::VerticalCenter); +} + +bool QmlAnchorBindingProxy::horizontalCentered() +{ + return m_fxItemNode.anchors().instanceHasAnchor(AnchorLine::HorizontalCenter); +} + +void QmlAnchorBindingProxy::fill() +{ + m_fxItemNode.anchors().fill(); + + m_fxItemNode.anchors().setMargin(AnchorLine::Right, 0); + m_fxItemNode.anchors().setMargin(AnchorLine::Left, 0); + m_fxItemNode.anchors().setMargin(AnchorLine::Top, 0); + m_fxItemNode.anchors().setMargin(AnchorLine::Bottom, 0); + + emit topAnchorChanged(); + emit bottomAnchorChanged(); + emit leftAnchorChanged(); + emit rightAnchorChanged(); + emit anchorsChanged(); +} + +} // namespace Internal +} // namespace QmlDesigner + diff --git a/src/plugins/qmldesigner/components/propertyeditor/qmlanchorbindingproxy.h b/src/plugins/qmldesigner/components/propertyeditor/qmlanchorbindingproxy.h new file mode 100644 index 00000000000..f0144012289 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/qmlanchorbindingproxy.h @@ -0,0 +1,117 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef QmlAnchorBindingProxy_h +#define QmlAnchorBindingProxy_h + +#include <QObject> +#include <modelnode.h> +#include <nodeinstanceview.h> +#include <QRectF> +#include <qmlitemnode.h> + +namespace QmlDesigner { + +class NodeInstanceView; + +namespace Internal { + +class QmlAnchorBindingProxy : public QObject +{ + Q_OBJECT + + Q_PROPERTY(bool topAnchored READ topAnchored WRITE setTopAnchor NOTIFY topAnchorChanged) + Q_PROPERTY(bool bottomAnchored READ bottomAnchored WRITE setBottomAnchor NOTIFY bottomAnchorChanged) + Q_PROPERTY(bool leftAnchored READ leftAnchored WRITE setLeftAnchor NOTIFY leftAnchorChanged) + Q_PROPERTY(bool rightAnchored READ rightAnchored WRITE setRightAnchor NOTIFY rightAnchorChanged) + Q_PROPERTY(bool hasParent READ hasParent NOTIFY parentChanged); + + Q_PROPERTY(bool hasAnchors READ hasAnchors NOTIFY anchorsChanged) + + Q_PROPERTY(bool verticalCentered READ verticalCentered WRITE setVerticalCentered NOTIFY centeredVChanged) + Q_PROPERTY(bool horizontalCentered READ horizontalCentered WRITE setHorizontalCentered NOTIFY centeredHChanged) + +public: + //only enable if node has parent + + QmlAnchorBindingProxy(QObject *parent = 0); + ~QmlAnchorBindingProxy(); + + void setup(const QmlItemNode &itemNode); + + bool topAnchored(); + bool bottomAnchored(); + bool leftAnchored(); + bool rightAnchored(); + + bool hasParent(); + + void removeTopAnchor(); + void removeBottomAnchor(); + void removeLeftAnchor(); + void removeRightAnchor(); + bool hasAnchors(); + + bool verticalCentered(); + bool horizontalCentered(); + +public slots: + void resetLayout(); + void fill(); + void setTopAnchor(bool anchor =true); + void setBottomAnchor(bool anchor = true); + void setLeftAnchor(bool anchor = true); + void setRightAnchor(bool anchor = true); + + void setVerticalCentered(bool centered = true); + void setHorizontalCentered(bool centered = true); + +signals: + void parentChanged(); + + void topAnchorChanged(); + void bottomAnchorChanged(); + void leftAnchorChanged(); + void rightAnchorChanged(); + void centeredVChanged(); + void centeredHChanged(); + void anchorsChanged(); +private: + QmlItemNode m_fxItemNode; + + QRectF parentBoundingBox(); + QRectF transformedBoundingBox(); +}; + +} // namespace Internal +} // namespace QmlDesigner + + +#endif //QmlAnchorBindingProxy + diff --git a/src/plugins/qmldesigner/components/propertyeditor/qproxylayoutitem.cpp b/src/plugins/qmldesigner/components/propertyeditor/qproxylayoutitem.cpp new file mode 100644 index 00000000000..fd997532c0c --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/qproxylayoutitem.cpp @@ -0,0 +1,176 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#include "qproxylayoutitem.h" +#include <QGraphicsWidget> + +QT_BEGIN_NAMESPACE + +QML_DEFINE_TYPE(Qt,4,6,LayoutItem,QProxyLayoutItem); + +QProxyLayoutItem::QProxyLayoutItem(QGraphicsLayoutItem *i) +: enabled(true), other(i) +{ +} + +void QProxyLayoutItem::setGeometry(const QRectF &r) +{ + geometry = r; + if (enabled && other) + other->setGeometry(r); +} + +QGraphicsLayoutItem *QProxyLayoutItem::item() const +{ + return other; +} + +void QProxyLayoutItem::setItem(QGraphicsLayoutItem *o) +{ + if (other == o) + return; + other = o; + if (enabled && other) + other->setGeometry(geometry); + + updateGeometry(); + if (parentLayoutItem()) + parentLayoutItem()->updateGeometry(); +} + +void QProxyLayoutItem::setEnabled(bool e) +{ + if (e == enabled) + return; + + enabled = e; + if (e && other) + other->setGeometry(geometry); +} + +QSizeF QProxyLayoutItem::sizeHint(Qt::SizeHint which, const QSizeF &c) const +{ + struct Accessor : public QGraphicsLayoutItem + { + QSizeF getSizeHint(Qt::SizeHint which, const QSizeF &c) const + { + return sizeHint(which, c); + } + }; + + QSizeF rv; + if (other) + rv = static_cast<Accessor *>(other)->getSizeHint(which, c); + return rv; +} + +QML_DEFINE_TYPE(Qt,4,6,ProxyLayout,QProxyLayout); + +QProxyLayout::QProxyLayout(QObject *parent) +: QObject(parent), proxy(0) +{ +} + +void QProxyLayout::setLayout(QGraphicsLayout *l) +{ + proxy = l; + updateGeometry(); + if (parentLayoutItem()) + parentLayoutItem()->updateGeometry(); +} + +QGraphicsLayout *QProxyLayout::layout() const +{ + return proxy; +} + +void QProxyLayout::updateGeometry() +{ + QGraphicsLayout::updateGeometry(); +} + +void QProxyLayout::setGeometry(const QRectF &g) +{ + geometry = g; + if (proxy) + proxy->setGeometry(g); + +} + +int QProxyLayout::count() const +{ + if (proxy) + return proxy->count(); + else + return 0; +} + +QGraphicsLayoutItem *QProxyLayout::itemAt(int idx) const +{ + if (proxy) + return proxy->itemAt(idx); + else + return 0; +} + +void QProxyLayout::removeAt(int idx) +{ + if (proxy) + proxy->removeAt(idx); +} + +QSizeF QProxyLayout::sizeHint(Qt::SizeHint which, + const QSizeF &constraint) const +{ + struct Accessor : public QGraphicsLayout + { + QSizeF getSizeHint(Qt::SizeHint which, const QSizeF &c) const + { + return sizeHint(which, c); + } + }; + + if (proxy) + return static_cast<Accessor *>(proxy)->getSizeHint(which, constraint); + else + return QSizeF(); +} + +QT_END_NAMESPACE diff --git a/src/plugins/qmldesigner/components/propertyeditor/qproxylayoutitem.h b/src/plugins/qmldesigner/components/propertyeditor/qproxylayoutitem.h new file mode 100644 index 00000000000..d0e33739426 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/qproxylayoutitem.h @@ -0,0 +1,107 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#ifndef QPROXYLAYOUTITEM_H +#define QPROXYLAYOUTITEM_H + +#include <qml.h> +#include <QGraphicsLayout> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QProxyLayout : public QObject, public QGraphicsLayout +{ + Q_OBJECT + Q_INTERFACES(QGraphicsLayout QGraphicsLayoutItem) + Q_PROPERTY(QGraphicsLayout *layout READ layout WRITE setLayout) +public: + QProxyLayout(QObject *parent=0); + + void setLayout(QGraphicsLayout *); + QGraphicsLayout *layout() const; + + virtual void setGeometry(const QRectF &); + virtual int count() const; + virtual QGraphicsLayoutItem *itemAt(int) const; + virtual void removeAt(int); + virtual void updateGeometry(); + +protected: + virtual QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint) const; + +private: + QRectF geometry; + QGraphicsLayout *proxy; +}; +QML_DECLARE_TYPE(QProxyLayout); + +class QProxyLayoutItem : public QObject, public QGraphicsLayoutItem +{ + Q_OBJECT + Q_INTERFACES(QGraphicsLayoutItem) + Q_PROPERTY(QGraphicsLayoutItem *widget READ item WRITE setItem) +public: + QProxyLayoutItem(QGraphicsLayoutItem * = 0); + + virtual void setGeometry(const QRectF &); + + QGraphicsLayoutItem *item() const; + void setItem(QGraphicsLayoutItem *); + + void setEnabled(bool); +protected: + virtual QSizeF sizeHint(Qt::SizeHint, const QSizeF &) const; + +private: + bool enabled; + QRectF geometry; + QGraphicsLayoutItem *other; +}; +QML_DECLARE_TYPE(QProxyLayoutItem); + +#endif // QPROXYLAYOUTITEM_H + +QT_END_NAMESPACE + +QT_END_HEADER diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/images/down.png b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/images/down.png new file mode 100644 index 0000000000000000000000000000000000000000..29d1d4439a139c662aecca94b6f43a465cfb9cc6 GIT binary patch literal 594 zcmV-Y0<HatP)<h;3K|Lk000e1NJLTq000&M000&U1^@s6#I$TX00004XF*Lt007q5 z)K6G40000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz<Vi$9R5*>j z)Xz`TU>wKswOeUBH_Vo3LZ*V4p&U4v;LVFDq!ObUNJt<WZW<Ql#KjmwBRZnS7-Pnd zIR;d-@<2Q(mvZqh`|y2gOX{{~Jdog%ypMhL$<uG27=YMWbSCKkhiR25NVG;JC`A@V z{m%<*cu$2b)o+D(n&Z;&ruPKPY*5%U`+5j>QHC_U<xSx|yD99soe$w&>YOy}c$4_Z z287Mpy&>Gkk3$;%;XTGD)-SARcb^V+y#l_lys$a@k{nD+qgKLE+C6xLudGK{sd70w zcE71nDjtqr6rQslcH!s21HbzIZLG4Ku(F%O+U^xp_O4>4nBl-LJ{^?W2<z1PkY!@k zy@++zzR$G_m|x6bw2(w9t#V*GnCW;UtWYVccGogcakHqh=DzJ@vEpQ~u-u6|BL*~0 z;SU$79F=U^ae(vRGfO%5tPX6rX>788E7ww3c$dW3qz>Ki(HSZqJlD~5#;x#SD}gQ7 zgv0(;bxhbL9Yezjn5K`uZiTiRw<CPSQt^F9Lv>q2=|ckJ6DkxX7Tsy45p8>IMse%D zf;Vqf6vh<#P(J!fv{R}3IKcTOvuzkL=(>--JPth;j^KP+u2DCF7oBg1O2Gjh4<I9$ g_m-FYiQ&_92v!gZ3$d!;LI3~&07*qoM6N<$f*Xwy2LJ#7 literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/images/edit.png b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/images/edit.png new file mode 100644 index 0000000000000000000000000000000000000000..4231bd9a90439ece81e21c5622ea16298e88ca75 GIT binary patch literal 503 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H3?x5i&EW)6w*!1aT>t<74`k8@{Qu7I|BmjB ziwqBMGCVw_zGWT5`Ay23*D^dmuJP|1!;`xV8`m)0y3BBRFT?N83~QD%TsX;aa5qC! zzQ~rf43F<H+`Y!IW+}twbqtr!G8E)6JU*;`X^Zlw3z{im{P(Ugyg#eCs7GeodWNm* z7<O%8`0|E9*?7khpvRO;g8YIR7-cPdrP-KeBuvZ_1TA?T6y4-Ky}X0XgJL8-#N1hH z^|^vRb!P#Uukdtn45^6AJ?mQLY$(EVu|I8@!L`XVXCB(|U)1&ZVw;<$dz$m!ySV<p zVRRt!(p8z*%j)kl#CX`;ou6$vYrO7VDx-@Rw~1@Y@tv21l2_YLnHpBH{g-QKW^eD+ z{HFgeu4hKZTHZ>~HF_bS-OG1Nd1;xxMAwOqh?lC9SDxt6UdlP`!*12LE7uvespoA` zt1w)j|IN4j^l|l;eb=ntDSo&kw;}$xs{CHzzl@u5L_e=^4BrWgJ5N_Xmvv4FO#m0% Bsj2_~ literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/images/editdelete.png b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/images/editdelete.png new file mode 100644 index 0000000000000000000000000000000000000000..df2a147d246ef62d628d73db36b0b24af98a2ab9 GIT binary patch literal 831 zcmV-F1Hk-=P)<h;3K|Lk000e1NJLTq000&M000&U1^@s6#I$TX00004XF*Lt007q5 z)K6G40000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!(Md!>R5;6h zl}l(-K@^6Q=FtdAC<I#<1vTzkyHK*R+J%o@wZ0a1DJ?=tSMAP?3Zme;s}zKaD^Wy6 z7Yd41@c|mJRwJ#V#kP`amA*91!{dK$&Sd6J(iT4E-p)UNX3k@-hlo5T`11jNs0RU1 zXKM^?ouLi#+RB_E@`@2re}7&UYy@H8uVLzUv;zZ5+Xy1!;WdSh>A#^NDs^{Lp)6)L zgDB5eX;UdG_4H6F7*yIgXmIAu<CkDs?CqsOOAF=j{#~1IRp!dHG(1ejWRePPZM2N{ zajlIzS}~^8WHP^so#t9w=`Y^@fCVt^UJ&Ew0!*Q=XeYsCaMt4~ind7^hxi{$U{OZJ zFSj}6VVZ;K3QU*8bOapNns(5vjDr$<`V8j8@VAW2Vj@8+gM+l(*GCI5<++UP^q4m7 z0y`WpBgSD#6nz9UAS*w@{D{S99`XDck2~-0Knfhurr5M+mw1Hj@`*~K9CHrAYz^Lv z>0!5NyOCtSU7G=!;6%|3j{gliox-!pOK?G(o&X({YLK$5)lC7F{VZbo703UCXA=&? zO>Nu>w#%A8Rp;5oKacEBBT*BGX+{#I_yE%2i8f!~SeeejbP6SvLH5VQ-~o6A-hwe1 z-+)oOG3-#N-p|7H3rxph%DcJ`E`ihFDtIo2X&L#)9#wa!-__Ey=>18UreVqnx(m*O z14e_~1JA$<Ng|Jr!8P~Y60WrL1a7GeE|k|u7B9RF&Vl{z>~|`2HhU^Ra!WRl)GgiU zYU$BXh#q3R4$gpm?mF#|Br$YH!gK>%1c$VCx82fBJaI+hxwpFb)g=^Dbv{zQc<7+k z9t8>W67osVx3S=)K2n#oseNr$I`ov*vgsc2h}xyrpv>h+JHeJUF8Zj<DQ!a5>Wkj` zH1UE>PMpf&iLCQ!iioCJ)~Hi?YjvJaK8_pg59Au!plIt&?SDO~mzDzYD;xj-002ov JPDHLkV1h`%ho%4k literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/images/minus.png b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/images/minus.png new file mode 100644 index 0000000000000000000000000000000000000000..d6f233d7399c4c07c6c66775f7806acac84b1870 GIT binary patch literal 250 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H3?x5i&EW)6P60k4u0Wb3;FYqWamwl0TmSYg zzPEV)f9L4=uDMg50*&A=3GxeOV9U{Bm1EIh)b!)dNY8G|=4l2>MR>Y6hE&9zJ@3ia zpuppBkxMsqLg3#2p{{ow4y^sCTR&@AtNcZ-!c0XiUIF(fS@*p4-Mvkxubuwr)McGB zHm7G5zmh%w<-tAQYwQ=f{_Q#<D1Fn&=iS@W@8{NAa@EcF`+gb72cE8eF6*2UngAl# BOuhgB literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/images/plus.png b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/images/plus.png new file mode 100644 index 0000000000000000000000000000000000000000..40df1134f8472f399adfa5c8c66c50a98d3bacc0 GIT binary patch literal 462 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H3?x5i&EW)6y#YQUu0Wa$(6{&=P?uNAhRG|R zPhI<Q_SV0>i|$63?4Gdn(WDhGy5?S*zvKV>UH^L*-t<b{<dM2=%BojWSHI3|I5>UX z$Jv{I&ffC7bM{5Qj4e}Ezbj}vVjnSc;_|19_y1onFfAQuu24ykUoZnB3o9G9teS?7 zt9NpGMrLu#@0UVzfU@^IT^vIy;%X<}%xf{=arHKHYQL~@ZfotgwYUG*$MH^!I&%2V z@)sJL`rl4pesV%`$+ja*^=&3!b{Hmgs`72G$lh?~sgSmhV3T|L)#5pvnp5W$R3F&x zq^-&N)%9FO<F7x@ooC1dNge0-T3hb>2FOm@bA*rKR&a{^p(h(SexKRzobg)f!S5Tb z0(ReeejodN_$Ob%pPGvI-{X5McZ=;}w|{;sE!Zmj;a8K;+WTSWrcUlO+qsq1zw`F> zL$~g{I69@}|MDjqpAy@zuKLv`b1gzCeQUnq?;S;@b8|k;l-+S#KOj|nW)fHH0-#SB NJYD@<);T3K0RYVY#HauO literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/images/spreadpad.png b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/images/spreadpad.png new file mode 100644 index 0000000000000000000000000000000000000000..104c0a23d29edbd506b873b7a320c81b23ebfd49 GIT binary patch literal 151 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5DCp_w z;usRq`u3b57lQ&1%farsT#w%W`R^^VMR3ce&ZBxPeIdz{cFAn!>~K9*8oTQGzQ5o8 iMsWi*B7+0^49D5h1Y#KP&EMz^lJIo(b6Mw<&;$U2FDpp^ literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/images/spreadreflect.png b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/images/spreadreflect.png new file mode 100644 index 0000000000000000000000000000000000000000..17b82b711a4937944d407621fa4564f4ec21e707 GIT binary patch literal 165 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5C>ZYP z;usR){`Q<9*8v3qmy7u`S(?7@pIa&weB_#l630gKqw*1DGhVSS*kr)CB1c_j<6XJ^ zwcC34-uaNCvGec7^V{yNS8-@yU}R$95Kw?H{;+N^(2uZKqW88T!Wd`;gQu&X%Q~lo FCIHz3G)e#f literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/images/spreadrepeat.png b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/images/spreadrepeat.png new file mode 100644 index 0000000000000000000000000000000000000000..7aea898b1de2945fb04613af513ab27dbd5eccf3 GIT binary patch literal 156 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5DCp<u z;usQf`0crkoD2#A&KDzADVR4;wru#lo$D4y)Xj{K%<PH<ALcAs)?Uc`V6sSV+=jjL ow`~>Q{d>J6P&YF8!TP~KmV5RL#&z1)K7%AYUHx3vIVCg!0Eh`L4*&oF literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/images/typeconical.png b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/images/typeconical.png new file mode 100644 index 0000000000000000000000000000000000000000..5479811df61c927c87944c536abbfc00352ebc64 GIT binary patch literal 937 zcmV;a16KTrP)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F800009a7bBm000ie z000ie0hKEb8vp<UTS-JgR9J<@muYVkF%X8`Uka3~;#38yKp^0kp_D>ZC{;OBRpsnO zdL*H_H_0Bsvz}KwJF%C-mqv1CceU&H@r*ql&lzK;<^q3nIl#+H#-x@b&74g#cO7T$ znp&K?d$JS0p5R(Mi<M18OJP~-)gA*ZUn#^>^-+?Ag>?&oBtjS<bp2+Z0bb9~dLT4% z-Nvwqf)V0zzyoWC46t^sJbFNAcS@l#UuzqJNR&9)^<NCIej^+L2*)1L5jVQeZBlpy z-$)1@g3Tia*j)HKz(k0Tgm@I&CSe3b6+mXMKb<hZ=Uc@?UkD@sgoZ4fTfQa>K<R~0 z2(}vxuzkmgYcJ@@C&keQ)FuTGATS?%OEK57?Q4qxzTGPx>XiThbSj1WR=78Iwl5$M z3Jrk1Gj2c67+`O)@Blz0wRAtkNw{lUhb=XD0uxgvBk5fL9CjI?{-AiMJ%LUn0FAai zGUm*--;uSE10g5?AZ#-Ba*(tEob(x>v1I?J)ex8iB+#2U;l{vrV%zD!L39;>o_Oe+ z#7WQg;Q-o?iic!NAZj_%BqO2bFn6KZZ97Z%1cph7*^j~G!V188(g6&g6nmnCm@1DT z8BwfB@Jq{>_jPCKp5_LHN{?C&4wioQT?Y_9EANq0e!~ZqF$lSugkRL0rF#hb#L%be z*8!x@D_<&ghLHFGVi{+TxUM{GUk)I8>K8)@WO6!B2;yvZ()pl92toI8Sr8w{^o*rQ ziKZ8rgfuy$q@03f;*k(!?fVX(xl{(wqee=`G9)3&0kr(=S~iWLTb4Qxz!Q2IVvwyN z0~|dpyIU?lWI~+>Iw}!SV(JGnVF=<FiD-Y(pMd0!s8cc0FM*IHD4mLBGSV9n*@F%P ze7|2#JfY19`#;igz8SG4@j+a@8){Gdz-F{_H@p$)-7pdmaw|r|mhCIcq<2Ycr^x_Y zx6fA*VVebnEJ&oduPl>ADF(WA$^ai0%A?n_UlydYB-7<ss4fguL?4eCVB_ZOp~MHE zA#`CX3szaoh5$C|4Dj}PbqMsCpsFPI6{Mka!8?@Yt5?J4f+`5#fkaO}Urh+D)dL1t znfHf4hv9#YXZn~YL$HFgVePWt1HR+gzZ_8I_xeymwzkUv7x?=F%PQYnmjb?p00000 LNkvXXu0mjf2%49w literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/images/typelinear.png b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/images/typelinear.png new file mode 100644 index 0000000000000000000000000000000000000000..dbd8a1f5a18c8001abe34f2271985ba1040cfac2 GIT binary patch literal 145 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5DCq3z z;usRq`u6%p-Ub5(=7ZcxIcdpyT^bu$Gw)5xj(^>F{n(rbJ7>K9AGe*k`q=X(21X_p k4gm#+1_<N8hM)85uPpjzIw2$LGSD~%Pgg&ebxsLQ0M(5xq5uE@ literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/images/typeradial.png b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/images/typeradial.png new file mode 100644 index 0000000000000000000000000000000000000000..dc5888dca7c7515bd3e0cdb9b8a2fa93f09aaa1b GIT binary patch literal 583 zcmV-N0=WH&P)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F800009a7bBm000ie z000ie0hKEb8vp<S`AI}UR9M5!mkV!#KoEqFhax`ECQbYQe_op=MXds&(i?YI77Eoi zZH`MO2pZ<wodv-&##F}P^6mog?9avlDfEpA?7r{OajeRo;%s%YI-ZR=oJdvm2LSQN zm?&rhKwSVZ*8qi8R{jzICR1xZ2}w~%0HYfKB?A;kV{+EMr0?zqfYEGB0C8ADTmV!I z8bI1#a`j@KF~IIX$8Q3_WInC|G=y~FI{;n1m<zziSBNG6PP~W+-~u543;}>h&4C7x z{0jzvc_!v1nM2#65l0gsiE4lacR?Uz9sn`w0YLtR%3aJ+{Z;{NiVgr&2~&Ysj8!Mp zJm?sF1~|9cuXDdvuFUb7uMPr0CCiA+MFJrm(eog~r~zd7bna5IHW#gnOXmmxRRLf* z=?2j=5pxuXA@B*{#O=>H_jcxO<8@A3+bsfsi3Y$niE0=fi3}x%Q3J?L=UTZkU&}Uc z0@&{f;2|cn)r8Df9UvM4Gl4FI3~=P?cdfkJ#%WHUQ{I2i7~pYArfmS(*D#5i3Csc( zvX{y?9o42C0MOf>0iI?xKwDiv33mfsz(V!gLv7q=*w*Wg0iNgm0KH(`4J^qy&3~qs zkD&lKkHGm5I=>8^--w*wik<%%IsZEz!~@A79?6_Pln&ytd=M`tgLpL?%**LNuTQUR VBrZCuWhDRr002ovPDHLkV1l~X@5%rG literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/images/up.png b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/images/up.png new file mode 100644 index 0000000000000000000000000000000000000000..e4373122171599c88b78c884b927c6a8b4a90c6a GIT binary patch literal 692 zcmV;l0!#ggP)<h;3K|Lk000e1NJLTq000&M000&U1^@s6#I$TX00004XF*Lt007q5 z)K6G40000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!MoC0LR5;7E z)W1&@K@`C8@0(c;K@2%9jfnwKjKRXjhMK6vA0b2yoW??kN+B96{{Sm(jlq(_LJ6fI z5)H<f5G58CR7k9h;>p2raNh0iv$(l~TMx4kdC6q9nEA|`**D{<DmxJ#I~1{D5!nNX zsxJRGSVT_M@3JouMMOq``#?rjgKfhia-lhy7{5Gll-6U)*y!xw^kgvs+!c|ms`^`o zMdY|KGJbWag9B|v7PIqob_Wy+a_!SwwZO25EZ4%lf1o|Po9f2S_Z_08U0KM?5rnJM zS?KBoBV3u8LV(+~aBs6r8ydZ_fjb@SZ}C~m&J%{KSOGC$1WsQRjM3DGH&6@rYM@<- zdNa|}-^RhiA-|U92*Wizhl;pnq5G0xL{rnR|Hi#2+MG;G^j~kM<%qDd@)g~Xsg!#I zh7x|RPq0Q)?<cGAXvR0s^w!a|_cQ55NYqoEsR3g^DD^+TLoNp=P6~y*rar!77I-8g zK@@)S>}k#dX8|6LB>7#;)I^Ped=4Hzs5}YJfl=IMqVOwV3TOn<`<h$%k)ByU*A1wV zlHj5f_y#Ni0iaP>fg+FtutHTOl+p4ItW@S@UCRT$s#e2Vdg=lo5D}~>p3$197_jRp z=YhPc7Gm8z<Fcy2t}marC*n4_#Vc(98(Ba$EMkpr?{!Nr%rO6D{fx_HSBBSCD?@!C zj0=z4y1`(KSM>$3=Kf7AcnG)$Gyx5pjP)J5;=W_SftyqWmZ>V+N`!8lA3I}LdVVyM axbX+reAIe(fQ}9T0000<MNUMnLSTaA2sYsW literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/images/zoomin.png b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/images/zoomin.png new file mode 100644 index 0000000000000000000000000000000000000000..2e586fc7bfb6216aec1be411603315d1d0e129d7 GIT binary patch literal 1208 zcmV;p1V{UcP)<h;3K|Lk000e1NJLTq000;O000;W1^@s6;CDUv00004XF*Lt007q5 z)K6G40000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$N=ZaPR7l6g zR$)wAWfc8vGSg`w4rL@uH&$ATv(iqbLUBXDwUiL%0zwn2W20kZ*%(>3rU9b}h+zvU zvdt-Gx`Bk+iiy+!DMK?HAf-^(YFA1l#B5Pnlrg*b!*09YySzoz6%sSG$;nG!-@WJi z&OPUSWdOkbO|bus06#GZg-WF|C=`k#JRa|5{JtMij8G!t5WIgxfS-+0skE}LuI}Uh z{{BlIkLN}(7`!z;K7Q3^v-wOWlU*W_7&)T*mm<1b^YpT^vUeR0hd&SqfYa%Owzf7f zo6XSC(E$?^6X5lF!D6ul3kwT7@%bFY%19)CTRAl!86ExJ?RJCdmD=C(_ZzPjG#m?S z-?qZmx;I0+8_hp=T0aWoZoq1@UD0SX7OcyRMDi!iNfuf;H7`9}bM@&%uTP~kcLrHZ z8nD>Sz*c91tl2X2!ilcw_P(<VlarHBUtd2X5D4n9UQBW=MW7(fNfuf;HJ2a#z^Ojf z=XrKpB|KkN3(pi7AoI-N%%g1sUzUG();Byp5$^8phTPoTL9EY5ME!LaiwO$scDt7} z*Hj$*QPlAMXmO|GE8{tz_r-ljV7s9eN=+x>z{t!+d5{0IWUG7N)Vc8=NUD;Ok|5Ss zAz~3LA`$FGY36C5N<;lLi&4VHta5m+s2aBDUW2Tc8X>2=88UPYz~T#mt;vJK18%R+ z=YxuhifdT^210<~M<Otx6>pMT<h5_PS!@QZOe%($l-;mC+W?{*6Rg`}geb*cU`gA7 zCF)>n@7L$2rlyGCCe|NE2oVoNBG~70x#r1=s#nd^G*FgM1Z=}jg#HgahLF;AC)A7t zFZlg_&}y}`gQJK9#DkFtiYY|o^NOa{xujz~L&EpI857!m8e<|{u>^@EdJx+~!-}zA zdY&4Z`_$_B_A)sM(@ry}UTz0UN+^Yhd|rIgdNKZl%eD6O<w>>%H5ZaBdJugQ8ci9Q zw;RW1-JYpHxTU29WHQ+h*6&zu25}UBN+>y_y>D<q-}SjavHijqEG`e&dfn|^Bs=CD z)V$a2`*nDD7}RR@LUeTW8LZ#DoQrt03yMD_l(tSjCo7U$XrQDy$>Q+`!qi;9YuByx z^z`dk*NdRA$s^`~BcT^p4#l4mN+F`%kQK=-G*BWYS@4+!rBWG6O-+UTG~xAw+csRm z+5?p6r3iT3J0+9iPYI<Ek<ZDB<Q5tzX^yg-=JWZ^tjFVT)Z`_?J9|=~RwuheZd^=q zx3}pX5-}J5DlSCFIV)Ck1I@UPvC&OySA{A!t%If_DRfk&LF0}mW*3u0IuCf9Tv6QM z1e^sExT}D>FGrH|*`h~6#%v*&OOm1MP&zc}pS(@7?2*Y4tsv`>zbQoAmCxO$-^EDM zuM)2Mu39aE)>1j_(TII`UwhBaWR9d;BYt=`FL^aJFGoCdPY-==W~9CLPV@idU&Zf9 WSJt-!dAjKU0000<MNUMnLSTYraXSqF literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/images/zoomout.png b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/images/zoomout.png new file mode 100644 index 0000000000000000000000000000000000000000..a736d3934354d733ec6406bbe183fcbeae6da4f4 GIT binary patch literal 1226 zcmV;*1U37KP)<h;3K|Lk000e1NJLTq000;O000;W1^@s6;CDUv00004XF*Lt007q5 z)K6G40000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$TuDShR7l6g zRtrp91r)7KW|#)z=4UaMvC@K@2pvjgZQO)8O34CQK&YuRTiW3;j<Jn32*#0uL!1-b zPsQv<MW7WjsRAiO1EcMrrEj#ZE2SB1vZyS=iY3O_jqAPUEuyZFn5j)pUi$j(J@1}- z&U@tm0O#KX=YJ!>&r*aqEiG-2SS)^r%jLd--*+JL5E8^P1os~i;AeGQTwI}6t8MM< z>>PJGoi}|x->l7MyJ0XG+$AL?#@N_c4NG+Id_;F@E>)>iADc`jkJsykfq?<2tE+>W zni|mQbTBwL2ricknwpw?IXO8EczrrzaVV0%-8nV4SgqeN3<D)^mj9Z$Lo=OSc{r%} zP!Cz!_XDr&ubFJne-^}PKyNTym&@f%SeFut<WHKDEOh78e9PXl8_yKKHJo&?!MCa2 z^vmW}Gbmdvvk3>AMqfDEI8xtn*6(mQprWE;l*i*~v0g}W%|{?6%}Exzb80R+@X3Je zc!yJCa!gd7yEs$W>6lJA-95UpuIqe$^I5mqHW+MaX@QK4jBc#YM1=o!in#>YMx)V1 zn#&7z{~)M5Y0YaeUC{KnU2ikvo+{?s%T<o6S9V%PFNxYbU&ZU0uH!wn?@219Qt89` zBE-XpMWF~@MQNsJp^`lnBQftb_pN?T1<&V}!iE?3L27Opq$|r|UH0q1c`OSOO;<Yh zb}=ru+YJQ;1=Cnxh2SB=LJ^dp6>pJSMCHer#}BvlE?$udOA}SFB6T+iHWb71lwA;> zm=B!Tm*MH&i7&F+zv&wu9wvfYSbqe;N8A^R;5ED5K1Ehc+gCHP^4OO>oJZxr5pD*~ zD)k?D0uiT%B=e-P%;FpJcs!s`C}@HMh-k$9p$PIQMC9{=>O&JW0sq9se!k(ye!-c5 zRnQi&M4t(q7a9X*asN+kG=biE`5HM1(@r}mU1$OcC6q!$KF|9=e<|vy-5z=BnuGVL ze~8yS!yqmQJ`Y%vEK^2J{}|&O_6BQfYat;ap%?47F0_MX6n{!6IitR#+pli?+7nYh zbS_FaZi{S~bO>6ftnsD^6E$yV+&`PmW{}Bb{)mW((^$WDAs2CJ3W`4^ltw3?lNHG= zv{2HVWN~`DL29nvwry4_mCj&YJA%R{3Yi0zgdSY!6n{!6g@~phE0SAip+row;5B}U zL=s3&PKL}i{Fz;wR$a&1os{VL2)OJsC57To38fH`&&i797FsB2j<TEz3kw@aeKP81 z*~S?7XnPWrzmzaeZk$VUr>E&{5+NJ^A~r-vSSyyY3te{)hhtqExg=2d>~g5ijRRfL z8rZ)zacnM0sP_Svl`EWG9FMhNG<)Q+=S4`;w?6uzfMz`(-c!ayW3d#f)ldCSvfwL| zC0az*BY#tf*kc%bo_-f1NoN{=$<<Ps01j;t!FIXOjpr42?VZe$w8(`Ij%|#8gqr6g o9=NM7eRgN0z2bKB|KwlAZzfId9q_{lbpQYW07*qoM6N<$f}q_qumAu6 literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtcolorbutton.cpp b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtcolorbutton.cpp new file mode 100644 index 00000000000..5c38b524ade --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtcolorbutton.cpp @@ -0,0 +1,272 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtcolorbutton.h" +#include <QtGui/QColorDialog> +#include <QtGui/QPainter> +#include <QtCore/QMimeData> +#include <QtGui/QDragEnterEvent> +#include <QtGui/QApplication> + +QT_BEGIN_NAMESPACE + +class QtColorButtonPrivate +{ + QtColorButton *q_ptr; + Q_DECLARE_PUBLIC(QtColorButton) +public: + QColor m_color; +#ifndef QT_NO_DRAGANDDROP + QColor m_dragColor; + QPoint m_dragStart; + bool m_dragging; +#endif + bool m_backgroundCheckered; + + void slotEditColor(); + QColor shownColor() const; + QPixmap generatePixmap() const; +}; + +void QtColorButtonPrivate::slotEditColor() +{ + const QColor newColor = QColorDialog::getColor(m_color, q_ptr, QString(), QColorDialog::ShowAlphaChannel); + if (!newColor.isValid() || newColor == q_ptr->color()) + return; + q_ptr->setColor(newColor); + emit q_ptr->colorChanged(m_color); +} + +QColor QtColorButtonPrivate::shownColor() const +{ +#ifndef QT_NO_DRAGANDDROP + if (m_dragging) + return m_dragColor; +#endif + return m_color; +} + +QPixmap QtColorButtonPrivate::generatePixmap() const +{ + QPixmap pix(24, 24); + + int pixSize = 20; + QBrush br(shownColor()); + + QPixmap pm(2 * pixSize, 2 * pixSize); + QPainter pmp(&pm); + pmp.fillRect(0, 0, pixSize, pixSize, Qt::lightGray); + pmp.fillRect(pixSize, pixSize, pixSize, pixSize, Qt::lightGray); + pmp.fillRect(0, pixSize, pixSize, pixSize, Qt::darkGray); + pmp.fillRect(pixSize, 0, pixSize, pixSize, Qt::darkGray); + pmp.fillRect(0, 0, 2 * pixSize, 2 * pixSize, shownColor()); + br = QBrush(pm); + + QPainter p(&pix); + int corr = 1; + QRect r = pix.rect().adjusted(corr, corr, -corr, -corr); + p.setBrushOrigin((r.width() % pixSize + pixSize) / 2 + corr, (r.height() % pixSize + pixSize) / 2 + corr); + p.fillRect(r, br); + + p.fillRect(r.width() / 4 + corr, r.height() / 4 + corr, + r.width() / 2, r.height() / 2, + QColor(shownColor().rgb())); + p.drawRect(pix.rect().adjusted(0, 0, -1, -1)); + + return pix; +} + +/////////////// + +QtColorButton::QtColorButton(QWidget *parent) + : QToolButton(parent), d_ptr(new QtColorButtonPrivate) +{ + d_ptr->q_ptr = this; + d_ptr->m_dragging = false; + d_ptr->m_backgroundCheckered = true; + + setAcceptDrops(true); + + connect(this, SIGNAL(clicked()), this, SLOT(slotEditColor())); + setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred)); +} + +QtColorButton::~QtColorButton() +{ +} + +void QtColorButton::setColor(const QColor &color) +{ + if (d_ptr->m_color == color) + return; + d_ptr->m_color = color; + update(); +} + +QColor QtColorButton::color() const +{ + return d_ptr->m_color; +} + +void QtColorButton::setBackgroundCheckered(bool checkered) +{ + if (d_ptr->m_backgroundCheckered == checkered) + return; + d_ptr->m_backgroundCheckered = checkered; + update(); +} + +bool QtColorButton::isBackgroundCheckered() const +{ + return d_ptr->m_backgroundCheckered; +} + +void QtColorButton::paintEvent(QPaintEvent *event) +{ + QToolButton::paintEvent(event); + if (!isEnabled()) + return; + + const int pixSize = 10; + QBrush br(d_ptr->shownColor()); + if (d_ptr->m_backgroundCheckered) { + QPixmap pm(2 * pixSize, 2 * pixSize); + QPainter pmp(&pm); + pmp.fillRect(0, 0, pixSize, pixSize, Qt::white); + pmp.fillRect(pixSize, pixSize, pixSize, pixSize, Qt::white); + pmp.fillRect(0, pixSize, pixSize, pixSize, Qt::black); + pmp.fillRect(pixSize, 0, pixSize, pixSize, Qt::black); + pmp.fillRect(0, 0, 2 * pixSize, 2 * pixSize, d_ptr->shownColor()); + br = QBrush(pm); + } + + QPainter p(this); + const int corr = 4; + QRect r = rect().adjusted(corr, corr, -corr, -corr); + p.setBrushOrigin((r.width() % pixSize + pixSize) / 2 + corr, (r.height() % pixSize + pixSize) / 2 + corr); + p.fillRect(r, br); + + //const int adjX = qRound(r.width() / 4.0); + //const int adjY = qRound(r.height() / 4.0); + //p.fillRect(r.adjusted(adjX, adjY, -adjX, -adjY), + // QColor(d_ptr->shownColor().rgb())); + /* + p.fillRect(r.adjusted(0, r.height() * 3 / 4, 0, 0), + QColor(d_ptr->shownColor().rgb())); + p.fillRect(r.adjusted(0, 0, 0, -r.height() * 3 / 4), + QColor(d_ptr->shownColor().rgb())); + */ + /* + const QColor frameColor0(0, 0, 0, qRound(0.2 * (0xFF - d_ptr->shownColor().alpha()))); + p.setPen(frameColor0); + p.drawRect(r.adjusted(adjX, adjY, -adjX - 1, -adjY - 1)); + */ + + const QColor frameColor1(0, 0, 0, 26); + p.setPen(frameColor1); + p.drawRect(r.adjusted(1, 1, -2, -2)); + const QColor frameColor2(0, 0, 0, 51); + p.setPen(frameColor2); + p.drawRect(r.adjusted(0, 0, -1, -1)); +} + +void QtColorButton::mousePressEvent(QMouseEvent *event) +{ +#ifndef QT_NO_DRAGANDDROP + if (event->button() == Qt::LeftButton) + d_ptr->m_dragStart = event->pos(); +#endif + QToolButton::mousePressEvent(event); +} + +void QtColorButton::mouseMoveEvent(QMouseEvent *event) +{ +#ifndef QT_NO_DRAGANDDROP + if (event->buttons() & Qt::LeftButton && + (d_ptr->m_dragStart - event->pos()).manhattanLength() > QApplication::startDragDistance()) { + QMimeData *mime = new QMimeData; + mime->setColorData(color()); + QDrag *drg = new QDrag(this); + drg->setMimeData(mime); + drg->setPixmap(d_ptr->generatePixmap()); + setDown(false); + event->accept(); + drg->start(); + return; + } +#endif + QToolButton::mouseMoveEvent(event); +} + +#ifndef QT_NO_DRAGANDDROP +void QtColorButton::dragEnterEvent(QDragEnterEvent *event) +{ + const QMimeData *mime = event->mimeData(); + if (!mime->hasColor()) + return; + + event->accept(); + d_ptr->m_dragColor = qvariant_cast<QColor>(mime->colorData()); + d_ptr->m_dragging = true; + update(); +} + +void QtColorButton::dragLeaveEvent(QDragLeaveEvent *event) +{ + event->accept(); + d_ptr->m_dragging = false; + update(); +} + +void QtColorButton::dropEvent(QDropEvent *event) +{ + event->accept(); + d_ptr->m_dragging = false; + if (d_ptr->m_dragColor == color()) + return; + setColor(d_ptr->m_dragColor); + emit colorChanged(color()); +} +#endif + +QT_END_NAMESPACE + +#include "moc_qtcolorbutton.cpp" diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtcolorbutton.h b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtcolorbutton.h new file mode 100644 index 00000000000..1a8b592d89e --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtcolorbutton.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTCOLORBUTTON_H +#define QTCOLORBUTTON_H + +#include <QtGui/QToolButton> + +QT_BEGIN_NAMESPACE + +class QtColorButton : public QToolButton +{ + Q_OBJECT + Q_PROPERTY(bool backgroundCheckered READ isBackgroundCheckered WRITE setBackgroundCheckered) +public: + QtColorButton(QWidget *parent = 0); + ~QtColorButton(); + + bool isBackgroundCheckered() const; + void setBackgroundCheckered(bool checkered); + + QColor color() const; + +public slots: + + void setColor(const QColor &color); + +signals: + void colorChanged(const QColor &color); +protected: + void paintEvent(QPaintEvent *event); + void mousePressEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); +#ifndef QT_NO_DRAGANDDROP + void dragEnterEvent(QDragEnterEvent *event); + void dragLeaveEvent(QDragLeaveEvent *event); + void dropEvent(QDropEvent *event); +#endif +private: + QScopedPointer<class QtColorButtonPrivate> d_ptr; + Q_DECLARE_PRIVATE(QtColorButton) + Q_DISABLE_COPY(QtColorButton) + Q_PRIVATE_SLOT(d_func(), void slotEditColor()) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtcolorbutton.pri b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtcolorbutton.pri new file mode 100644 index 00000000000..0e41068f8a4 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtcolorbutton.pri @@ -0,0 +1,4 @@ +INCLUDEPATH += $$PWD +DEPENDPATH += $$PWD +SOURCES += $$PWD/qtcolorbutton.cpp +HEADERS += $$PWD/qtcolorbutton.h diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtcolorline.cpp b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtcolorline.cpp new file mode 100644 index 00000000000..047d0a6cc08 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtcolorline.cpp @@ -0,0 +1,1122 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtcolorline.h" +#include "qdrawutil.h" + +#include <QtGui/QPainter> +#include <QtGui/QPaintEvent> +#include <QtGui/QStyleOption> + +QT_BEGIN_NAMESPACE + +class QtColorLinePrivate +{ + QtColorLine *q_ptr; + Q_DECLARE_PUBLIC(QtColorLine) +public: + QtColorLinePrivate(); + + QColor color() const; + void setColor(const QColor &color); + + QtColorLine::ColorComponent colorComponent() const; + void setColorComponent(QtColorLine::ColorComponent component); + + void setIndicatorSize(int size); + int indicatorSize() const; + + void setIndicatorSpace(int space); + int indicatorSpace() const; + + void setFlip(bool flip); + bool flip() const; + + void setBackgroundCheckered(bool checkered); + bool isBackgroundCheckered() const; + + void setOrientation(Qt::Orientation orientation); + Qt::Orientation orientation() const; + + void resizeEvent(QResizeEvent *event); + void paintEvent(QPaintEvent *event); + void mousePressEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void mouseDoubleClickEvent(QMouseEvent *event); +private: + void checkColor(); + bool isMainPixmapValid() const; + void validate(); + void recreateMainPixmap(); + QSize pixmapSizeFromGeometrySize(const QSize &geometrySize) const; + QPixmap gradientPixmap(int size, Qt::Orientation orientation, const QColor &begin, const QColor &end, bool flipped = false) const; + QPixmap gradientPixmap(Qt::Orientation orientation, const QColor &begin, const QColor &end, bool flipped = false) const; + QPixmap hueGradientPixmap(int size, Qt::Orientation orientation, bool flipped = false, + int saturation = 0xFF, int value = 0xFF, int alpha = 0xFF) const; + QPixmap hueGradientPixmap(Qt::Orientation orientation, bool flipped = false, + int saturation = 0xFF, int value = 0xFF, int alpha = 0xFF) const; + + QVector<QRect> rects(const QPointF &point) const; + + QColor colorFromPoint(const QPointF &point) const; + QPointF pointFromColor(const QColor &color) const; + + QColor m_color; + QtColorLine::ColorComponent m_component; + bool m_flipped; + bool m_backgroundCheckered; + Qt::Orientation m_orientation; + bool m_dragging; + bool m_combiningAlpha; + int m_indicatorSize; + int m_indicatorSpace; + QPointF m_point; + QPoint m_clickOffset; + + QPixmap m_mainPixmap; + QPixmap m_alphalessPixmap; + QPixmap m_semiAlphaPixmap; + QSize m_pixmapSize; + + struct PixData { + QSize size; + QColor color; + QtColorLine::ColorComponent component; + bool flipped; + Qt::Orientation orientation; + }; + + PixData m_lastValidMainPixmapData; +}; + +QtColorLinePrivate::QtColorLinePrivate() + : m_color(Qt::black), m_component(QtColorLine::Value), + m_flipped(false), m_backgroundCheckered(true), m_orientation(Qt::Horizontal), m_dragging(false), m_combiningAlpha(false) +{ + m_indicatorSize = 22; + m_indicatorSpace = 0; + m_pixmapSize = QSize(0, 0); + m_point = pointFromColor(m_color); +} + +void QtColorLinePrivate::setColor(const QColor &color) +{ + if (m_color == color) + return; + if (!color.isValid()) + return; + if (m_dragging) // Warning perhaps here, recursive call + return; + m_color = color; + checkColor(); + QColor c = colorFromPoint(m_point); + m_point = pointFromColor(m_color); + q_ptr->update(); +} + +QColor QtColorLinePrivate::color() const +{ + return m_color; +} + +void QtColorLinePrivate::setColorComponent(QtColorLine::ColorComponent component) +{ + if (m_component == component) + return; + if (m_dragging) // Warning perhaps here, recursive call + return; + m_component = component; + checkColor(); + m_point = pointFromColor(m_color); + q_ptr->update(); +} + +QtColorLine::ColorComponent QtColorLinePrivate::colorComponent() const +{ + return m_component; +} + +void QtColorLinePrivate::setIndicatorSize(int size) +{ + if (size <= 0) + return; + if (m_dragging) // Warning perhaps here, recursive call + return; + if (m_indicatorSize == size) + return; + m_indicatorSize = size; + m_pixmapSize = pixmapSizeFromGeometrySize(q_ptr->contentsRect().size()); + q_ptr->update(); + q_ptr->updateGeometry(); +} + +int QtColorLinePrivate::indicatorSize() const +{ + return m_indicatorSize; +} + +void QtColorLinePrivate::setIndicatorSpace(int space) +{ + if (space < 0) + return; + if (m_dragging) // Warning perhaps here, recursive call + return; + if (m_indicatorSpace == space) + return; + m_indicatorSpace = space; + m_pixmapSize = pixmapSizeFromGeometrySize(q_ptr->contentsRect().size()); + q_ptr->update(); +} + +int QtColorLinePrivate::indicatorSpace() const +{ + return m_indicatorSpace; +} + +void QtColorLinePrivate::setFlip(bool flip) +{ + if (m_dragging) // Warning perhaps here, recursive call + return; + if (m_flipped == flip) + return; + m_flipped = flip; + m_point = pointFromColor(m_color); + q_ptr->update(); +} + +bool QtColorLinePrivate::flip() const +{ + return m_flipped; +} + +void QtColorLinePrivate::setBackgroundCheckered(bool checkered) +{ + if (m_backgroundCheckered == checkered) + return; + m_backgroundCheckered = checkered; + q_ptr->update(); +} + +bool QtColorLinePrivate::isBackgroundCheckered() const +{ + return m_backgroundCheckered; +} + +void QtColorLinePrivate::setOrientation(Qt::Orientation orientation) +{ + if (m_dragging) // Warning perhaps here, recursive call + return; + if (m_orientation == orientation) + return; + + m_orientation = orientation; + if (!q_ptr->testAttribute(Qt::WA_WState_OwnSizePolicy)) { + QSizePolicy sp = q_ptr->sizePolicy(); + sp.transpose(); + q_ptr->setSizePolicy(sp); + q_ptr->setAttribute(Qt::WA_WState_OwnSizePolicy, false); + } + m_point = pointFromColor(m_color); + q_ptr->update(); + q_ptr->updateGeometry(); +} + +Qt::Orientation QtColorLinePrivate::orientation() const +{ + return m_orientation; +} + +void QtColorLinePrivate::checkColor() +{ + switch (m_component) { + case QtColorLine::Red: + case QtColorLine::Green: + case QtColorLine::Blue: + if (m_color.spec() != QColor::Rgb) + m_color = m_color.toRgb(); + break; + case QtColorLine::Hue: + case QtColorLine::Saturation: + case QtColorLine::Value: + if (m_color.spec() != QColor::Hsv) + m_color = m_color.toHsv(); + break; + default: + break; + } + if (m_color.spec() == QColor::Hsv) { + if (m_color.hue() == 360 || m_color.hue() == -1) { + m_color.setHsvF(0.0, m_color.saturationF(), m_color.valueF(), m_color.alphaF()); + } + } +} + +bool QtColorLinePrivate::isMainPixmapValid() const +{ + if (m_mainPixmap.isNull()) { + if (m_pixmapSize.isEmpty()) + return true; + else + return false; + } + if (m_lastValidMainPixmapData.component != m_component) + return false; + if (m_lastValidMainPixmapData.size != m_pixmapSize) + return false; + if (m_lastValidMainPixmapData.flipped != m_flipped) + return false; + if (m_lastValidMainPixmapData.orientation != m_orientation) + return false; + if (m_lastValidMainPixmapData.color == m_color) + return true; + switch (m_component) { + case QtColorLine::Red: + if (m_color.green() == m_lastValidMainPixmapData.color.green() && + m_color.blue() == m_lastValidMainPixmapData.color.blue() && + (!m_combiningAlpha || m_color.alpha() == m_lastValidMainPixmapData.color.alpha())) + return true; + break; + case QtColorLine::Green: + if (m_color.red() == m_lastValidMainPixmapData.color.red() && + m_color.blue() == m_lastValidMainPixmapData.color.blue() && + (!m_combiningAlpha || m_color.alpha() == m_lastValidMainPixmapData.color.alpha())) + return true; + break; + case QtColorLine::Blue: + if (m_color.red() == m_lastValidMainPixmapData.color.red() && + m_color.green() == m_lastValidMainPixmapData.color.green() && + (!m_combiningAlpha || m_color.alpha() == m_lastValidMainPixmapData.color.alpha())) + return true; + break; + case QtColorLine::Hue: + if (m_color.saturation() == m_lastValidMainPixmapData.color.saturation() && + m_color.value() == m_lastValidMainPixmapData.color.value() && + (!m_combiningAlpha || m_color.alpha() == m_lastValidMainPixmapData.color.alpha())) + return true; + break; + case QtColorLine::Saturation: + if (m_color.hue() == m_lastValidMainPixmapData.color.hue() && + m_color.value() == m_lastValidMainPixmapData.color.value() && + (!m_combiningAlpha || m_color.alpha() == m_lastValidMainPixmapData.color.alpha())) + return true; + break; + case QtColorLine::Value: + if (m_color.hue() == m_lastValidMainPixmapData.color.hue() && + m_color.saturation() == m_lastValidMainPixmapData.color.saturation() && + (!m_combiningAlpha || m_color.alpha() == m_lastValidMainPixmapData.color.alpha())) + return true; + break; + case QtColorLine::Alpha: + if (m_color.hue() == m_lastValidMainPixmapData.color.hue() && + m_color.saturation() == m_lastValidMainPixmapData.color.saturation() && + m_color.value() == m_lastValidMainPixmapData.color.value()) + return true; + } + return false; +} + +void QtColorLinePrivate::validate() +{ + if (isMainPixmapValid()) + return; + + recreateMainPixmap(); +} + +QPixmap QtColorLinePrivate::gradientPixmap(Qt::Orientation orientation, const QColor &begin, const QColor &end, bool flipped) const +{ + int size = m_pixmapSize.width(); + if (orientation == Qt::Vertical) + size = m_pixmapSize.height(); + return gradientPixmap(size, orientation, begin, end, flipped); +} + +QPixmap QtColorLinePrivate::gradientPixmap(int size, Qt::Orientation orientation, + const QColor &begin, const QColor &end, bool flipped) const +{ + int gradW = size; + int gradH = size; + int w = size; + int h = size; + if (orientation == Qt::Horizontal) { + gradH = 0; + h = 1; + } else { + gradW = 0; + w = 1; + } + QColor c1 = begin; + QColor c2 = end; + if (flipped) { + c1 = end; + c2 = begin; + } + QLinearGradient lg(0, 0, gradW, gradH); + lg.setColorAt(0, c1); + lg.setColorAt(1, c2); + QImage img(w, h, QImage::Format_ARGB32); + QPainter p(&img); + p.setCompositionMode(QPainter::CompositionMode_Source); + p.fillRect(QRect(0, 0, w, h), lg); + return QPixmap::fromImage(img); +} + +QPixmap QtColorLinePrivate::hueGradientPixmap(Qt::Orientation orientation, bool flipped, + int saturation, int value, int alpha) const +{ + int size = m_pixmapSize.width(); + if (orientation == Qt::Vertical) + size = m_pixmapSize.height(); + return hueGradientPixmap(size, orientation, flipped, saturation, value, alpha); +} + +QPixmap QtColorLinePrivate::hueGradientPixmap(int size, Qt::Orientation orientation, bool flipped, + int saturation, int value, int alpha) const +{ + int gradW = size + 1; + int gradH = size + 1; + int w = size; + int h = size; + if (orientation == Qt::Horizontal) { + gradH = 0; + h = 1; + } else { + gradW = 0; + w = 1; + } + QList<QColor> colorList; + colorList << QColor::fromHsv(0, saturation, value, alpha); + colorList << QColor::fromHsv(60, saturation, value, alpha); + colorList << QColor::fromHsv(120, saturation, value, alpha); + colorList << QColor::fromHsv(180, saturation, value, alpha); + colorList << QColor::fromHsv(240, saturation, value, alpha); + colorList << QColor::fromHsv(300, saturation, value, alpha); + colorList << QColor::fromHsv(0, saturation, value, alpha); + QLinearGradient lg(0, 0, gradW, gradH); + for (int i = 0; i <= 6; i++) + lg.setColorAt((double)i / 6.0, flipped ? colorList.at(6 - i) : colorList.at(i)); + QImage img(w, h, QImage::Format_ARGB32); + QPainter p(&img); + p.setCompositionMode(QPainter::CompositionMode_Source); + p.fillRect(QRect(0, 0, w, h), lg); + return QPixmap::fromImage(img); +} + +void QtColorLinePrivate::recreateMainPixmap() +{ + m_lastValidMainPixmapData.size = m_pixmapSize; + m_lastValidMainPixmapData.component = m_component; + m_lastValidMainPixmapData.color = m_color; + m_lastValidMainPixmapData.flipped = m_flipped; + m_lastValidMainPixmapData.orientation = m_orientation; + + if (m_pixmapSize.isEmpty()) { + m_mainPixmap = QPixmap(); + m_alphalessPixmap = QPixmap(); + m_semiAlphaPixmap = QPixmap(); + return; + } + + if (m_mainPixmap.size() != m_pixmapSize) { + m_mainPixmap = QPixmap(m_pixmapSize); + m_alphalessPixmap = QPixmap(m_pixmapSize); + m_semiAlphaPixmap = QPixmap(m_pixmapSize); + } + + Qt::Orientation orient = m_orientation; + const bool flip = m_flipped; + + const int r = m_color.red(); + const int g = m_color.green(); + const int b = m_color.blue(); + const int h = m_color.hue(); + const int s = m_color.saturation(); + const int v = m_color.value(); + const int a = m_color.alpha(); + const double coef = 0.5; + const int semi = qRound(a * coef + 0xFF * (1.0 - coef)); + + if (m_component == QtColorLine::Hue) { + m_alphalessPixmap = hueGradientPixmap(orient, flip, s, v, 0xFF); + if (m_combiningAlpha) { + m_mainPixmap = hueGradientPixmap(orient, flip, s, v, a); + m_semiAlphaPixmap = hueGradientPixmap(orient, flip, s, v, semi); + } + } else if (m_component == QtColorLine::Saturation) { + m_alphalessPixmap = gradientPixmap(orient, QColor::fromHsv(h, 0, v, 0xFF), QColor::fromHsv(h, 0xFF, v, 0xFF), flip); + if (m_combiningAlpha) { + m_mainPixmap = gradientPixmap(orient, QColor::fromHsv(h, 0, v, a), QColor::fromHsv(h, 0xFF, v, a), flip); + m_semiAlphaPixmap = gradientPixmap(orient, QColor::fromHsv(h, 0, v, semi), QColor::fromHsv(h, 0xFF, v, semi), flip); + } + } else if (m_component == QtColorLine::Value) { + m_alphalessPixmap = gradientPixmap(orient, QColor::fromRgb(0, 0, 0, 0xFF), QColor::fromHsv(h, s, 0xFF, 0xFF), flip); + if (m_combiningAlpha) { + m_mainPixmap = gradientPixmap(orient, QColor::fromRgb(0, 0, 0, a), QColor::fromHsv(h, s, 0xFF, a), flip); + m_semiAlphaPixmap = gradientPixmap(orient, QColor::fromRgb(0, 0, 0, semi), QColor::fromHsv(h, s, 0xFF, semi), flip); + } + } else if (m_component == QtColorLine::Red) { + m_alphalessPixmap = gradientPixmap(orient, QColor::fromRgb(0, g, b, 0xFF), QColor::fromRgb(0xFF, g, b, 0xFF), flip); + if (m_combiningAlpha) { + m_mainPixmap = gradientPixmap(orient, QColor::fromRgb(0, g, b, a), QColor::fromRgb(0xFF, g, b, a), flip); + m_semiAlphaPixmap = gradientPixmap(orient, QColor::fromRgb(0, g, b, semi), QColor::fromRgb(0xFF, g, b, semi), flip); + } + } else if (m_component == QtColorLine::Green) { + m_alphalessPixmap = gradientPixmap(orient, QColor::fromRgb(r, 0, b, 0xFF), QColor::fromRgb(r, 0xFF, b, 0xFF), flip); + if (m_combiningAlpha) { + m_mainPixmap = gradientPixmap(orient, QColor::fromRgb(r, 0, b, a), QColor::fromRgb(r, 0xFF, b, a), flip); + m_semiAlphaPixmap = gradientPixmap(orient, QColor::fromRgb(r, 0, b, semi), QColor::fromRgb(r, 0xFF, b, semi), flip); + } + } else if (m_component == QtColorLine::Blue) { + m_alphalessPixmap = gradientPixmap(orient, QColor::fromRgb(r, g, 0, 0xFF), QColor::fromRgb(r, g, 0xFF, 0xFF), flip); + if (m_combiningAlpha) { + m_mainPixmap = gradientPixmap(orient, QColor::fromRgb(r, g, 0, a), QColor::fromRgb(r, g, 0xFF, a), flip); + m_semiAlphaPixmap = gradientPixmap(orient, QColor::fromRgb(r, g, 0, semi), QColor::fromRgb(r, g, 0xFF, semi), flip); + } + } else if (m_component == QtColorLine::Alpha) { + m_mainPixmap = gradientPixmap(orient, QColor::fromRgb(r, g, b, 0), QColor::fromRgb(r, g, b, 0xFF), flip); + +// m_alphalessPixmap = gradientPixmap(orient, QColor::fromRgb(r, g, b, 0xFF), QColor::fromRgb(r, g, b, 0xFF), flip); +// m_semiAlphaPixmap = gradientPixmap(orient, QColor::fromRgb(r, g, b, semi), QColor::fromRgb(r, g, b, semi), flip); + } + if (!m_combiningAlpha && m_component != QtColorLine::Alpha) + m_mainPixmap = m_alphalessPixmap; +} + +QSize QtColorLinePrivate::pixmapSizeFromGeometrySize( + const QSize &geometrySize) const +{ + QSize size(m_indicatorSize + 2 * m_indicatorSpace - 1, + m_indicatorSize + 2 * m_indicatorSpace - 1); + if (m_orientation == Qt::Horizontal) + size.setHeight(0); + else + size.setWidth(0); + return geometrySize - size; +} + +QColor QtColorLinePrivate::colorFromPoint(const QPointF &point) const +{ + QPointF p = point; + if (p.x() < 0) + p.setX(0.0); + else if (p.x() > 1) + p.setX(1.0); + if (p.y() < 0) + p.setY(0.0); + else if (p.y() > 1) + p.setY(1.0); + + double pos = p.x(); + if (m_orientation == Qt::Vertical) + pos = p.y(); + if (m_flipped) + pos = 1.0 - pos; + QColor c; + qreal hue; + switch (m_component) { + case QtColorLine::Red: + c.setRgbF(pos, m_color.greenF(), m_color.blueF(), m_color.alphaF()); + break; + case QtColorLine::Green: + c.setRgbF(m_color.redF(), pos, m_color.blueF(), m_color.alphaF()); + break; + case QtColorLine::Blue: + c.setRgbF(m_color.redF(), m_color.greenF(), pos, m_color.alphaF()); + break; + case QtColorLine::Hue: + hue = pos; + hue *= 35999.0 / 36000.0; + c.setHsvF(hue, m_color.saturationF(), m_color.valueF(), m_color.alphaF()); + break; + case QtColorLine::Saturation: + c.setHsvF(m_color.hueF(), pos, m_color.valueF(), m_color.alphaF()); + break; + case QtColorLine::Value: + c.setHsvF(m_color.hueF(), m_color.saturationF(), pos, m_color.alphaF()); + break; + case QtColorLine::Alpha: + c.setHsvF(m_color.hueF(), m_color.saturationF(), m_color.valueF(), pos); + break; + } + return c; +} + +QPointF QtColorLinePrivate::pointFromColor(const QColor &color) const +{ + qreal hue = color.hueF(); + if (color.hue() == 360) + hue = 0.0; + else + hue *= 36000.0 / 35999.0; + + double pos = 0.0; + switch (m_component) { + case QtColorLine::Red: + pos = color.redF(); + break; + case QtColorLine::Green: + pos = color.greenF(); + break; + case QtColorLine::Blue: + pos = color.blueF(); + break; + case QtColorLine::Hue: + pos = hue; + break; + case QtColorLine::Saturation: + pos = color.saturationF(); + break; + case QtColorLine::Value: + pos = color.valueF(); + break; + case QtColorLine::Alpha: + pos = color.alphaF(); + break; + } + if (m_flipped) + pos = 1.0 - pos; + QPointF p(pos, pos); + if (m_orientation == Qt::Horizontal) + p.setY(0); + else + p.setX(0); + return p; +} + +QVector<QRect> QtColorLinePrivate::rects(const QPointF &point) const +{ + QRect r = q_ptr->geometry(); + r.moveTo(0, 0); + + int x1 = (int)((r.width() - m_indicatorSize - 2 * m_indicatorSpace) * point.x() + 0.5); + int x2 = x1 + m_indicatorSize + 2 * m_indicatorSpace; + int y1 = (int)((r.height() - m_indicatorSize - 2 * m_indicatorSpace) * point.y() + 0.5); + int y2 = y1 + m_indicatorSize + 2 * m_indicatorSpace; + + QVector<QRect> rects; + if (m_orientation == Qt::Horizontal) { + // r0 r1 r2 + QRect r0(0, 0, x1, r.height()); + QRect r1(x1 + m_indicatorSpace, 0, m_indicatorSize, r.height()); + QRect r2(x2, 0, r.width() - x2, r.height()); + + rects << r0 << r1 << r2; + } else { + // r0 + // r1 + // r2 + QRect r0(0, 0, r.width(), y1); + QRect r1(0, y1 + m_indicatorSpace, r.width(), m_indicatorSize); + QRect r2(0, y2, r.width(), r.height() - y2); + + rects << r0 << r1 << r2; + } + return rects; +} + +void QtColorLinePrivate::resizeEvent(QResizeEvent *event) +{ + m_pixmapSize = pixmapSizeFromGeometrySize(event->size()); +} + +void QtColorLinePrivate::paintEvent(QPaintEvent *) +{ + QRect rect = q_ptr->rect(); + + QVector<QRect> r = rects(m_point); + + QColor cBack = q_ptr->palette().color(QPalette::Active, QPalette::Window); + QColor c = colorFromPoint(m_point); + if (!m_combiningAlpha && m_component != QtColorLine::Alpha) + c.setAlpha(0xFF); + + QPainter p(q_ptr); + if (q_ptr->isEnabled()) { + if (m_backgroundCheckered) { + int pixSize = 20; + QPixmap pm(2 * pixSize, 2 * pixSize); + QPainter pmp(&pm); + pmp.fillRect(0, 0, pixSize, pixSize, Qt::white); + pmp.fillRect(pixSize, pixSize, pixSize, pixSize, Qt::white); + pmp.fillRect(0, pixSize, pixSize, pixSize, Qt::black); + pmp.fillRect(pixSize, 0, pixSize, pixSize, Qt::black); + pmp.end(); + + p.setBrushOrigin((rect.width() % pixSize + pixSize) / 2, (rect.height() % pixSize + pixSize) / 2); + p.setClipRect(r[1].adjusted(4, 4, -4, -4)); + p.setClipRect(QRect(rect.topLeft(), QPoint(r[1].left() + 0, rect.bottom())), Qt::UniteClip); + p.setClipRect(QRect(QPoint(r[1].right() - 0, rect.top()), rect.bottomRight()), Qt::UniteClip); + p.setClipRect(QRect(rect.topLeft(), QPoint(rect.right(), r[1].top() + 0)), Qt::UniteClip); + p.setClipRect(QRect(QPoint(rect.left(), r[1].bottom() - 0), rect.bottomRight()), Qt::UniteClip); + /* + p.setClipRect(r[1].adjusted(3, 3, -3, -3)); + p.setClipRect(QRect(rect.topLeft(), QPoint(r[1].left() + 1, rect.bottom())), Qt::UniteClip); + p.setClipRect(QRect(QPoint(r[1].right() - 1, rect.top()), rect.bottomRight()), Qt::UniteClip); + p.setClipRect(QRect(rect.topLeft(), QPoint(rect.right(), r[1].top() + 1)), Qt::UniteClip); + p.setClipRect(QRect(QPoint(rect.left(), r[1].bottom() - 1), rect.bottomRight()), Qt::UniteClip); + */ + p.fillRect(rect, pm); + p.setBrushOrigin(0, 0); + p.setClipping(false); + } + + validate(); + + QSize fieldSize = pixmapSizeFromGeometrySize(q_ptr->geometry().size()); + + QPoint posOnField = r[1].topLeft() - QPoint(m_indicatorSpace, m_indicatorSpace); + int x = posOnField.x(); + int y = posOnField.y(); + int w = fieldSize.width(); + int h = fieldSize.height(); + + QRect r0, r2; + if (m_orientation == Qt::Horizontal) { + r0 = QRect(0, 0, x, m_pixmapSize.height()); + r2 = QRect(x + 1, 0, w - x - 1, m_pixmapSize.height()); + } else { + r0 = QRect(0, 0, m_pixmapSize.width(), y); + r2 = QRect(0, y + 1, m_pixmapSize.width(), h - y - 1); + } + + p.setBrush(m_mainPixmap); + p.setPen(Qt::NoPen); + if (r[0].isValid()) { + p.drawRect(r[0]); + } + if (r[2].isValid()) { + p.setBrushOrigin(r[2].topLeft() - r2.topLeft()); + p.drawRect(r[2]); + } + if (m_indicatorSpace) { + p.setBrush(c); + if (m_orientation == Qt::Horizontal) { + p.drawRect(r[1].adjusted(-m_indicatorSpace, 0, -r[1].width(), 0)); + p.drawRect(r[1].adjusted(r[1].width(), 0, m_indicatorSpace, 0)); + } else { + p.drawRect(r[1].adjusted(0, -m_indicatorSpace, 0, -r[1].height())); + p.drawRect(r[1].adjusted(0, r[1].height(), 0, m_indicatorSpace)); + } + } + + QPen pen(c); + p.setPen(pen); + p.setBrush(Qt::NoBrush); + if (r[1].isValid()) { + p.drawRect(r[1].adjusted(0, 0, -1, -1)); + // p.drawRect(r[1].adjusted(1, 1, -2, -2)); + } + double coef = 9.0 / 10; + p.setPen(Qt::NoPen); + if (m_component != QtColorLine::Alpha && m_combiningAlpha) { + p.setBrush(m_alphalessPixmap); + if (r[0].isValid()) { + p.setBrushOrigin(QPoint(0, 0)); + QRect thinRect1 = r[0]; + QRect thinRect2 = r[0]; + QRect thinRect = r[0]; + if (m_orientation == Qt::Horizontal) { + thinRect1.adjust(0, qRound(thinRect1.height() * coef), 0, 0); + thinRect2.adjust(0, 0, 0, -qRound(thinRect2.height() * coef)); + thinRect.adjust(0, qRound(thinRect.height() * coef), 0, -qRound(thinRect.height() * coef)); + } else { + thinRect1.adjust(qRound(thinRect1.width() * coef), 0, 0, 0); + thinRect2.adjust(0, 0, -qRound(thinRect2.width() * coef), 0); + thinRect.adjust(qRound(thinRect.width() * coef), 0, -qRound(thinRect.width() * coef), 0); + } + p.drawRect(thinRect1); + p.drawRect(thinRect2); + //p.drawRect(thinRect); + } + if (r[2].isValid()) { + p.setBrushOrigin(r[2].topLeft() - r2.topLeft()); + QRect thinRect1 = r[2]; + QRect thinRect2 = r[2]; + QRect thinRect = r[2]; + if (m_orientation == Qt::Horizontal) { + thinRect1.adjust(0, qRound(thinRect1.height() * coef), 0, 0); + thinRect2.adjust(0, 0, 0, -qRound(thinRect2.height() * coef)); + thinRect.adjust(0, qRound(thinRect.height() * coef), 0, -qRound(thinRect.height() * coef)); + } else { + thinRect1.adjust(qRound(thinRect1.width() * coef), 0, 0, 0); + thinRect2.adjust(0, 0, -qRound(thinRect2.width() * coef), 0); + thinRect.adjust(qRound(thinRect.width() * coef), 0, -qRound(thinRect.width() * coef), 0); + } + p.drawRect(thinRect1); + p.drawRect(thinRect2); + //p.drawRect(thinRect); + } + /* + +*/ + + + + + + p.setPen(Qt::NoPen); + + p.setBrush(m_semiAlphaPixmap); + if (r[0].isValid()) { + p.setBrushOrigin(QPoint(0, 0)); + QRect thinRect1 = r[0]; + QRect thinRect2 = r[0]; + QRect thinRect = r[0]; + if (m_orientation == Qt::Horizontal) { + thinRect1.adjust(0, qRound(thinRect1.height() * coef) - 1, 0, 0); + thinRect1.setBottom(thinRect1.top()); + thinRect2.adjust(0, 0, 0, -qRound(thinRect2.height() * coef) + 1); + thinRect2.setTop(thinRect2.bottom()); + thinRect.adjust(0, qRound(thinRect.height() * coef), 0, -qRound(thinRect.height() * coef)); + } else { + thinRect1.adjust(qRound(thinRect1.width() * coef) - 1, 0, 0, 0); + thinRect1.setRight(thinRect1.left()); + thinRect2.adjust(0, 0, -qRound(thinRect2.width() * coef) + 1, 0); + thinRect2.setLeft(thinRect2.right()); + thinRect.adjust(qRound(thinRect.width() * coef), 0, -qRound(thinRect.width() * coef), 0); + } + p.drawRect(thinRect1); + p.drawRect(thinRect2); + //p.drawRect(thinRect); + } + if (r[2].isValid()) { + p.setBrushOrigin(r[2].topLeft() - r2.topLeft()); + QRect thinRect1 = r[2]; + QRect thinRect2 = r[2]; + QRect thinRect = r[2]; + if (m_orientation == Qt::Horizontal) { + thinRect1.adjust(0, qRound(thinRect1.height() * coef) - 1, 0, 0); + thinRect1.setBottom(thinRect1.top()); + thinRect2.adjust(0, 0, 0, -qRound(thinRect2.height() * coef) + 1); + thinRect2.setTop(thinRect2.bottom()); + thinRect.adjust(0, qRound(thinRect.height() * coef), 0, -qRound(thinRect.height() * coef)); + } else { + thinRect1.adjust(qRound(thinRect1.width() * coef) - 1, 0, 0, 0); + thinRect1.setRight(thinRect1.left()); + thinRect2.adjust(0, 0, -qRound(thinRect2.width() * coef) + 1, 0); + thinRect2.setLeft(thinRect2.right()); + thinRect.adjust(qRound(thinRect.width() * coef), 0, -qRound(thinRect.width() * coef), 0); + } + p.drawRect(thinRect1); + p.drawRect(thinRect2); + //p.drawRect(thinRect); + } + p.setBrush(m_alphalessPixmap); + if (m_orientation == Qt::Horizontal) { + p.setClipRect(r[1].adjusted(0, qRound(r[1].height() * coef), 0, 0)); + p.setClipRect(r[1].adjusted(0, 0, 0, -qRound(r[1].height() * coef)), Qt::UniteClip); + } else { + p.setClipRect(r[1].adjusted(qRound(r[1].width() * coef), 0, 0, 0)); + p.setClipRect(r[1].adjusted(0, 0, -qRound(r[1].width() * coef), 0), Qt::UniteClip); + } + p.setBrush(Qt::NoBrush); + p.setPen(QPen(QColor(c.rgb()))); + + p.drawRect(r[1].adjusted(0, 0, -1, -1)); + // p.drawRect(r[1].adjusted(1, 1, -2, -2)); +/* + p.setBrush(m_semiAlphaPixmap); + if (m_orientation == Qt::Horizontal) { + QRect top = r[1].adjusted(0, 0, 0, -qRound(r[1].height() * coef) + 1); + top.setTop(top.bottom()); + QRect bottom = r[1].adjusted(0, qRound(r[1].height() * coef) - 1, 0, 0); + top.setBottom(bottom.top()); + p.setClipRect(top); + p.setClipRect(bottom, Qt::UniteClip); + } else { + + } + QColor semiColor(c.rgb()); + semiColor.setAlpha((c.alpha() + 0xFF) / 2); + p.setPen(QPen(semiColor)); + p.drawRect(r[1].adjusted(0, 0, -1, -1)); + // p.drawRect(r[1].adjusted(1, 1, -2, -2)); +*/ + p.setClipping(false); + } + } + + p.setBrush(Qt::NoBrush); + int lw = 4; + //int br = 1; + int br = 0; + r[1].adjust(br, br, -br, -br); + if (r[1].adjusted(lw, lw, -lw, -lw).isValid()) { + QStyleOptionFrame opt; + opt.init(q_ptr); + opt.rect = r[1]; + opt.lineWidth = 2; + opt.midLineWidth = 1; + if (m_dragging) + opt.state |= QStyle::State_Sunken; + else + opt.state |= QStyle::State_Raised; + q_ptr->style()->drawPrimitive(QStyle::PE_Frame, &opt, &p, q_ptr); + QRect colorRect = r[1].adjusted(lw, lw, -lw, -lw); + if (q_ptr->isEnabled()) { + p.fillRect(colorRect, c); + const QColor frameColor(0, 0, 0, 38); + p.setPen(frameColor); + p.drawRect(colorRect.adjusted(0, 0, -1, -1)); + /* + p.fillRect(colorRect.width() / 4 + colorRect.left(), + colorRect.height() / 4 + colorRect.top(), + colorRect.width() / 2, + colorRect.height() / 2, + QColor(c.rgb())); + */ + /* + if (m_component != QtColorLine::Alpha) { + p.fillRect(colorRect.adjusted(0, colorRect.height() * 4 / 5, 0, 0), QColor(c.rgb())); + p.fillRect(colorRect.adjusted(0, 0, 0, -colorRect.height() * 4 / 5), QColor(c.rgb())); + } + */ + } + } +} + +void QtColorLinePrivate::mousePressEvent(QMouseEvent *event) +{ + if (event->button() != Qt::LeftButton) + return; + + QVector<QRect> r = rects(m_point); + QPoint clickPos = event->pos(); + + QSize fieldSize = q_ptr->geometry().size() - + QSize(m_indicatorSize + 2 * m_indicatorSpace - 1, m_indicatorSize + 2 * m_indicatorSpace - 1); + QPoint posOnField = r[1].topLeft() - QPoint(m_indicatorSpace, m_indicatorSpace); + m_clickOffset = posOnField - clickPos; + + if (!r[1].contains(clickPos)) + return; + m_dragging = true; + q_ptr->update(); +} + +void QtColorLinePrivate::mouseMoveEvent(QMouseEvent *event) +{ + if (!m_dragging) + return; + QPoint newPos = event->pos(); + + QSize fieldSize = q_ptr->geometry().size() - + QSize(m_indicatorSize + 2 * m_indicatorSpace - 1, m_indicatorSize + 2 * m_indicatorSpace - 1); + QPoint newPosOnField = newPos + m_clickOffset; + if (newPosOnField.x() < 0) + newPosOnField.setX(0); + else if (newPosOnField.x() > fieldSize.width()) + newPosOnField.setX(fieldSize.width()); + if (newPosOnField.y() < 0) + newPosOnField.setY(0); + else if (newPosOnField.y() > fieldSize.height()) + newPosOnField.setY(fieldSize.height()); + + double x = (double)newPosOnField.x() / fieldSize.width(); + double y = (double)newPosOnField.y() / fieldSize.height(); + m_point = QPointF(x, y); + QColor color = colorFromPoint(m_point); + if (m_color == color) + return; + m_color = color; + emit q_ptr->colorChanged(color); // maybe before internal set, 1 line above + q_ptr->update(); +} + +void QtColorLinePrivate::mouseReleaseEvent(QMouseEvent *event) +{ + if (event->button() != Qt::LeftButton) + return; + m_dragging = false; + q_ptr->update(); +} + +void QtColorLinePrivate::mouseDoubleClickEvent(QMouseEvent *event) +{ + if (event->button() != Qt::LeftButton) + return; + + QVector<QRect> r = rects(m_point); + QPoint clickPos = event->pos(); + if (!r[0].contains(clickPos) && !r[2].contains(clickPos)) + return; + QPoint newPosOnField = clickPos; + if (r[2].contains(clickPos)) + newPosOnField -= QPoint(m_indicatorSize + 2 * m_indicatorSpace - 2, m_indicatorSize + 2 * m_indicatorSpace - 2); + QSize fieldSize = q_ptr->geometry().size() - + QSize(m_indicatorSize + 2 * m_indicatorSpace - 1, m_indicatorSize + 2 * m_indicatorSpace - 1); + + double x = (double)newPosOnField.x() / fieldSize.width(); + double y = (double)newPosOnField.y() / fieldSize.height(); + m_point = QPointF(x, y); + QColor color = colorFromPoint(m_point); + if (m_color == color) + return; + m_color = color; + emit q_ptr->colorChanged(color); // maybe before internal set, 1 line above + q_ptr->update(); +} + +//////////////////////////////////////////////////// + +QtColorLine::QtColorLine(QWidget *parent) + : QWidget(parent), d_ptr(new QtColorLinePrivate) +{ + d_ptr->q_ptr = this; + + setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed)); +} + +QtColorLine::~QtColorLine() +{ +} + +QSize QtColorLine::minimumSizeHint() const +{ + return QSize(d_ptr->m_indicatorSize, d_ptr->m_indicatorSize); +} + +QSize QtColorLine::sizeHint() const +{ + return QSize(d_ptr->m_indicatorSize, d_ptr->m_indicatorSize); +} + +void QtColorLine::setColor(const QColor &color) +{ + d_ptr->setColor(color); +} + +QColor QtColorLine::color() const +{ + return d_ptr->color(); +} + +void QtColorLine::setColorComponent(QtColorLine::ColorComponent component) +{ + d_ptr->setColorComponent(component); +} + +QtColorLine::ColorComponent QtColorLine::colorComponent() const +{ + return d_ptr->colorComponent(); +} + +void QtColorLine::setIndicatorSize(int size) +{ + d_ptr->setIndicatorSize(size); +} + +int QtColorLine::indicatorSize() const +{ + return d_ptr->indicatorSize(); +} + +void QtColorLine::setIndicatorSpace(int space) +{ + d_ptr->setIndicatorSpace(space); +} + +int QtColorLine::indicatorSpace() const +{ + return d_ptr->indicatorSpace(); +} + +void QtColorLine::setFlip(bool flip) +{ + d_ptr->setFlip(flip); +} + +bool QtColorLine::flip() const +{ + return d_ptr->flip(); +} + +void QtColorLine::setBackgroundCheckered(bool checkered) +{ + d_ptr->setBackgroundCheckered(checkered); +} + +bool QtColorLine::isBackgroundCheckered() const +{ + return d_ptr->isBackgroundCheckered(); +} + +void QtColorLine::setOrientation(Qt::Orientation orientation) +{ + d_ptr->setOrientation(orientation); +} + +Qt::Orientation QtColorLine::orientation() const +{ + return d_ptr->orientation(); +} +void QtColorLine::resizeEvent(QResizeEvent *event) +{ + d_ptr->resizeEvent(event); +} + +void QtColorLine::paintEvent(QPaintEvent *event) +{ + d_ptr->paintEvent(event); +} + +void QtColorLine::mousePressEvent(QMouseEvent *event) +{ + d_ptr->mousePressEvent(event); +} + +void QtColorLine::mouseMoveEvent(QMouseEvent *event) +{ + d_ptr->mouseMoveEvent(event); +} + +void QtColorLine::mouseReleaseEvent(QMouseEvent *event) +{ + d_ptr->mouseReleaseEvent(event); +} + +void QtColorLine::mouseDoubleClickEvent(QMouseEvent *event) +{ + d_ptr->mouseDoubleClickEvent(event); +} + +QT_END_NAMESPACE diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtcolorline.h b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtcolorline.h new file mode 100644 index 00000000000..5171fed4b09 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtcolorline.h @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTCOLORLINE_H +#define QTCOLORLINE_H + +#include <QtGui/QWidget> + +QT_BEGIN_NAMESPACE + +class QtColorLine : public QWidget +{ + Q_OBJECT + Q_PROPERTY(QColor color READ color WRITE setColor) + Q_PROPERTY(int indicatorSpace READ indicatorSpace WRITE setIndicatorSpace) + Q_PROPERTY(int indicatorSize READ indicatorSize WRITE setIndicatorSize) + Q_PROPERTY(bool flip READ flip WRITE setFlip) + Q_PROPERTY(bool backgroundCheckered READ isBackgroundCheckered WRITE setBackgroundCheckered) + Q_PROPERTY(ColorComponent colorComponent READ colorComponent WRITE setColorComponent) + Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation) + Q_ENUMS(ColorComponent) +public: + + enum ColorComponent { + Red, + Green, + Blue, + Hue, + Saturation, + Value, + Alpha + }; + + QSize minimumSizeHint() const; + QSize sizeHint() const; + + QtColorLine(QWidget *parent = 0); + ~QtColorLine(); + + QColor color() const; + + void setIndicatorSize(int size); + int indicatorSize() const; + + void setIndicatorSpace(int space); + int indicatorSpace() const; + + void setFlip(bool flip); + bool flip() const; + + bool isBackgroundCheckered() const; + void setBackgroundCheckered(bool checkered); + + void setOrientation(Qt::Orientation orientation); + Qt::Orientation orientation() const; + + void setColorComponent(ColorComponent component); + ColorComponent colorComponent() const; + +public slots: + + void setColor(const QColor &color); + +signals: + + void colorChanged(const QColor &color); + +protected: + + void resizeEvent(QResizeEvent *event); + void paintEvent(QPaintEvent *event); + void mousePressEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void mouseDoubleClickEvent(QMouseEvent *event); + +private: + + QScopedPointer<class QtColorLinePrivate> d_ptr; + Q_DECLARE_PRIVATE(QtColorLine) + Q_DISABLE_COPY(QtColorLine) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientdialog.cpp b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientdialog.cpp new file mode 100644 index 00000000000..d178829fc66 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientdialog.cpp @@ -0,0 +1,353 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtgradientdialog.h" +#include "ui_qtgradientdialog.h" +#include <QtGui/QPushButton> + +QT_BEGIN_NAMESPACE + +class QtGradientDialogPrivate +{ + QtGradientDialog *q_ptr; + Q_DECLARE_PUBLIC(QtGradientDialog) +public: + + void slotAboutToShowDetails(bool details, int extensionWidthHint); + + Ui::QtGradientDialog m_ui; +}; + +void QtGradientDialogPrivate::slotAboutToShowDetails(bool details, int extensionWidthHint) +{ + if (details) { + q_ptr->resize(q_ptr->size() + QSize(extensionWidthHint, 0)); + } else { + q_ptr->setMinimumSize(1, 1); + q_ptr->resize(q_ptr->size() - QSize(extensionWidthHint, 0)); + q_ptr->setMinimumSize(0, 0); + } +} + +/*! + \class QtGradientDialog + + \brief The QtGradientDialog class provides a dialog for specifying gradients. + + The gradient dialog's function is to allow users to edit gradients. + For example, you might use this in a drawing program to allow the user to set the brush gradient. + + \table + \row + \o \inlineimage qtgradientdialog.png + \o \inlineimage qtgradientdialogextension.png + \header + \o Details extension hidden + \o Details extension visible + \endtable + + Starting from the top of the dialog there are several buttons: + + \image qtgradientdialogtopbuttons.png + + The first three buttons allow for changing a type of the gradient (QGradient::Type), while the second three allow for + changing spread of the gradient (QGradient::Spread). The last button shows or hides the details extension of the dialog. + Conceptually the default view with hidden details provides the full functional control over gradient editing. + The additional extension with details allows to set gradient's parameters more precisely. The visibility + of extension can be controlled by detailsVisible property. Moreover, if you don't want the user to + switch on or off the visibility of extension you can set the detailsButtonVisible property to false. + + Below top buttons there is an area where edited gradient is interactively previewed. + In addition the user can edit gradient type's specific parameters directly in this area by dragging + appropriate handles. + + \table + \row + \o \inlineimage qtgradientdialoglineareditor.png + \o \inlineimage qtgradientdialogradialeditor.png + \o \inlineimage qtgradientdialogconicaleditor.png + \header + \o Editing linear type + \o Editing radial type + \o Editing conical type + \row + \o The user can change the start and final point positions by dragging the circular handles. + \o The user can change the center and focal point positions by dragging the circular handles + and can change the gradient's radius by dragging horizontal or vertical line. + \o The user can change the center point by dragging the circular handle + and can change the gradient's angle by dragging the big wheel. + \endtable + + In the middle of the dialog there is an area where the user can edit gradient stops. + + \table + \row + \o \inlineimage qtgradientdialogstops.png + \o \inlineimage qtgradientdialogstopszoomed.png + \endtable + + The top part of this area contains stop handles, and bottom part shows the preview of gradient stops path. + In order to create a new gradient stop double click inside the view over the desired position. + If you double click on existing stop handle in the top part of the view, clicked handle will be duplicated + (duplicate will contain the same color). + The stop can be activated by clicking on its handle. You can activate previous or next stop by pressing + left or right key respectively. To jump to the first or last stop press home or end key respectively. + The gradient stops editor supports multiselection. + Clicking a handle holding the shift modifier key down will select a range of stops between + the active stop and clicked one. Clicking a handle holding control modifier key down will remove from or + add to selection the clicked stop depending if it was or wasn't already selected respectively. + Multiselection can also be created using rubberband (by pressing the left mouse button outside + of any handle and dragging). + Sometimes it's hard to select a stop because its handle can be partially covered by other handle. + In that case the user can zoom in the view by spinning mouse wheel. + The selected stop handles can be moved by drag & drop. In order to remove selected stops press delete key. + For convenience context menu is provided with the following actions: + + \list + \o New Stop - creates a new gradient stop + \o Delete - removes the active and all selected stops + \o Flip All - mirrors all stops + \o Select All - selects all stops + \o Zoom In - zooms in + \o Zoom Out - zooms out + \o Zoom All - goes back to original 100% zoom + \endlist + + The bottom part of the QtGradientDialog contains a set of widgets allowing to control the color of + the active and selected stops. + + \table + \row + \o \inlineimage qtgradientdialogcolorhsv.png + \o \inlineimage qtgradientdialogcolorrgb.png + \endtable + + + The color button shows the color of the active gradient stop. It also allows for choosing + a color from standard color dialog and applying it to the + active stop and all selected stops. It's also possible to drag a color directly from the color button + and to drop it in gradient stops editor at desired position (it will create new stop with dragged color) + or at desired stop handle (it will change the color of that handle). + + To the right of color button there is a set of 2 radio buttons which allows to switch between + HVS and RGB color spec. + + Finally there are 4 color sliders working either in HSVA (hue saturation value alpha) or + RGBA (red green blue alpha) mode, depending on which radio button is chosen. The radio buttons + can be controlled programatically by spec() and setSpec() methods. The sliders show the + color of the active stop. By double clicking inside color slider you can set directly the desired color. + Changes of slider's are applied to stop selection in the way that the color + component being changed is applied to stops in selection only, while other components + remain unchanged in selected stops (e.g. when the user is changing the saturation, + new saturation is applied to selected stops preventing original hue, value and alpha in multiselection). + + The convenient static functions getGradient() provide modal gradient dialogs, e.g.: + + \snippet doc/src/snippets/code/tools_shared_qtgradienteditor_qtgradientdialog.cpp 0 + + In order to have more control over the properties of QtGradientDialog use + standard QDialog::exec() method: + + \snippet doc/src/snippets/code/tools_shared_qtgradienteditor_qtgradientdialog.cpp 1 + + \sa {Gradient View Example} +*/ + +/*! + Constructs a gradient dialog with \a parent as parent widget. +*/ + +QtGradientDialog::QtGradientDialog(QWidget *parent) + : QDialog(parent), d_ptr(new QtGradientDialogPrivate()) +{ +// setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + d_ptr->q_ptr = this; + d_ptr->m_ui.setupUi(this); + QPushButton *button = d_ptr->m_ui.buttonBox->button(QDialogButtonBox::Ok); + if (button) + button->setAutoDefault(false); + button = d_ptr->m_ui.buttonBox->button(QDialogButtonBox::Cancel); + if (button) + button->setAutoDefault(false); + connect(d_ptr->m_ui.gradientEditor, SIGNAL(aboutToShowDetails(bool,int)), + this, SLOT(slotAboutToShowDetails(bool,int))); +} + +/*! + Destroys the gradient dialog +*/ + +QtGradientDialog::~QtGradientDialog() +{ +} + +/*! + \property QtGradientDialog::gradient + \brief the gradient of the dialog +*/ +void QtGradientDialog::setGradient(const QGradient &gradient) +{ + d_ptr->m_ui.gradientEditor->setGradient(gradient); +} + +QGradient QtGradientDialog::gradient() const +{ + return d_ptr->m_ui.gradientEditor->gradient(); +} + +/*! + \property QtGradientDialog::backgroundCheckered + \brief whether the background of widgets able to show the colors with alpha channel is checkered. + + \table + \row + \o \inlineimage qtgradientdialogbackgroundcheckered.png + \o \inlineimage qtgradientdialogbackgroundtransparent.png + \row + \o \snippet doc/src/snippets/code/tools_shared_qtgradienteditor_qtgradientdialog.cpp 2 + \o \snippet doc/src/snippets/code/tools_shared_qtgradienteditor_qtgradientdialog.cpp 3 + \endtable + + When this property is set to true (the default) widgets inside gradient dialog like color button, + color sliders, gradient stops editor and gradient editor will show checkered background + in case of transparent colors. Otherwise the background of these widgets is transparent. +*/ + +bool QtGradientDialog::isBackgroundCheckered() const +{ + return d_ptr->m_ui.gradientEditor->isBackgroundCheckered(); +} + +void QtGradientDialog::setBackgroundCheckered(bool checkered) +{ + d_ptr->m_ui.gradientEditor->setBackgroundCheckered(checkered); +} + +/*! + \property QtGradientDialog::detailsVisible + \brief whether details extension is visible. + + When this property is set to true the details extension is visible. By default + this property is set to false and the details extension is hidden. + + \sa detailsButtonVisible +*/ +bool QtGradientDialog::detailsVisible() const +{ + return d_ptr->m_ui.gradientEditor->detailsVisible(); +} + +void QtGradientDialog::setDetailsVisible(bool visible) +{ + d_ptr->m_ui.gradientEditor->setDetailsVisible(visible); +} + +/*! + \property QtGradientDialog::detailsButtonVisible + \brief whether the details button allowing for showing and hiding details extension is visible. + + When this property is set to true (the default) the details button is visible and the user + can show and hide details extension interactively. Otherwise the button is hidden and the details + extension is always visible or hidded depending on the value of detailsVisible property. + + \sa detailsVisible +*/ +bool QtGradientDialog::isDetailsButtonVisible() const +{ + return d_ptr->m_ui.gradientEditor->isDetailsButtonVisible(); +} + +void QtGradientDialog::setDetailsButtonVisible(bool visible) +{ + d_ptr->m_ui.gradientEditor->setDetailsButtonVisible(visible); +} + +/*! + Returns the current QColor::Spec used for the color sliders in the dialog. +*/ +QColor::Spec QtGradientDialog::spec() const +{ + return d_ptr->m_ui.gradientEditor->spec(); +} + +/*! + Sets the current QColor::Spec to \a spec used for the color sliders in the dialog. +*/ +void QtGradientDialog::setSpec(QColor::Spec spec) +{ + d_ptr->m_ui.gradientEditor->setSpec(spec); +} + +/*! + Executes a modal gradient dialog, lets the user to specify a gradient, and returns that gradient. + + If the user clicks \gui OK, the gradient specified by the user is returned. If the user clicks \gui Cancel, the \a initial gradient is returned. + + The dialog is constructed with the given \a parent. \a caption is shown as the window title of the dialog and + \a initial is the initial gradient shown in the dialog. If the \a ok parameter is not-null, + the value it refers to is set to true if the user clicks \gui OK, and set to false if the user clicks \gui Cancel. +*/ +QGradient QtGradientDialog::getGradient(bool *ok, const QGradient &initial, QWidget *parent, const QString &caption) +{ + QtGradientDialog dlg(parent); + if (!caption.isEmpty()) + dlg.setWindowTitle(caption); + dlg.setGradient(initial); + const int res = dlg.exec(); + if (ok) { + *ok = (res == QDialog::Accepted) ? true : false; + } + if (res == QDialog::Accepted) + return dlg.gradient(); + return initial; +} + +/*! + This method calls getGradient(ok, QLinearGradient(), parent, caption). +*/ +QGradient QtGradientDialog::getGradient(bool *ok, QWidget *parent, const QString &caption) +{ + return getGradient(ok, QLinearGradient(), parent, caption); +} + +QT_END_NAMESPACE + +#include "moc_qtgradientdialog.cpp" diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientdialog.h b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientdialog.h new file mode 100644 index 00000000000..f298c8ba9b9 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientdialog.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTGRADIENTDIALOG_H +#define QTGRADIENTDIALOG_H + +#include <QtGui/QDialog> + +QT_BEGIN_NAMESPACE + +class QtGradientDialog : public QDialog +{ + Q_OBJECT + Q_PROPERTY(QGradient gradient READ gradient WRITE setGradient) + Q_PROPERTY(bool backgroundCheckered READ isBackgroundCheckered WRITE setBackgroundCheckered) + Q_PROPERTY(bool detailsVisible READ detailsVisible WRITE setDetailsVisible) + Q_PROPERTY(bool detailsButtonVisible READ isDetailsButtonVisible WRITE setDetailsButtonVisible) +public: + QtGradientDialog(QWidget *parent = 0); + ~QtGradientDialog(); + + void setGradient(const QGradient &gradient); + QGradient gradient() const; + + bool isBackgroundCheckered() const; + void setBackgroundCheckered(bool checkered); + + bool detailsVisible() const; + void setDetailsVisible(bool visible); + + bool isDetailsButtonVisible() const; + void setDetailsButtonVisible(bool visible); + + QColor::Spec spec() const; + void setSpec(QColor::Spec spec); + + static QGradient getGradient(bool *ok, const QGradient &initial, QWidget *parent = 0, const QString &caption = QString()); + static QGradient getGradient(bool *ok, QWidget *parent = 0, const QString &caption = QString()); + +private: + QScopedPointer<class QtGradientDialogPrivate> d_ptr; + Q_DECLARE_PRIVATE(QtGradientDialog) + Q_DISABLE_COPY(QtGradientDialog) + Q_PRIVATE_SLOT(d_func(), void slotAboutToShowDetails(bool details, int extensionWidthHint)) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientdialog.ui b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientdialog.ui new file mode 100644 index 00000000000..3fc9586adaa --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientdialog.ui @@ -0,0 +1,121 @@ +<ui version="4.0" > + <comment>********************************************************************* +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +*********************************************************************</comment> + <class>QtGradientDialog</class> + <widget class="QDialog" name="QtGradientDialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>178</width> + <height>81</height> + </rect> + </property> + <property name="windowTitle" > + <string>Edit Gradient</string> + </property> + <layout class="QVBoxLayout" > + <item> + <widget class="QtGradientEditor" name="gradientEditor" > + <property name="sizePolicy" > + <sizepolicy vsizetype="MinimumExpanding" hsizetype="MinimumExpanding" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons" > + <set>QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>QtGradientEditor</class> + <extends>QFrame</extends> + <header>qtgradienteditor.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>QtGradientDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel" > + <x>72</x> + <y>224</y> + </hint> + <hint type="destinationlabel" > + <x>21</x> + <y>243</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>QtGradientDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel" > + <x>168</x> + <y>233</y> + </hint> + <hint type="destinationlabel" > + <x>152</x> + <y>251</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradienteditor.cpp b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradienteditor.cpp new file mode 100644 index 00000000000..d9b42beb58f --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradienteditor.cpp @@ -0,0 +1,952 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtgradienteditor.h" +#include "qtgradientstopscontroller.h" +#include "ui_qtgradienteditor.h" + +QT_BEGIN_NAMESPACE + +class QtGradientEditorPrivate +{ + QtGradientEditor *q_ptr; + Q_DECLARE_PUBLIC(QtGradientEditor) +public: + QtGradientEditorPrivate() : m_gradient(QLinearGradient()) {} + + void slotGradientStopsChanged(const QGradientStops &stops); + void slotTypeChanged(int type); + void slotSpreadChanged(int spread); + void slotStartLinearXChanged(double value); + void slotStartLinearYChanged(double value); + void slotEndLinearXChanged(double value); + void slotEndLinearYChanged(double value); + void slotCentralRadialXChanged(double value); + void slotCentralRadialYChanged(double value); + void slotFocalRadialXChanged(double value); + void slotFocalRadialYChanged(double value); + void slotRadiusRadialChanged(double value); + void slotCentralConicalXChanged(double value); + void slotCentralConicalYChanged(double value); + void slotAngleConicalChanged(double value); + + void slotDetailsChanged(bool details); + + void startLinearChanged(const QPointF &point); + void endLinearChanged(const QPointF &point); + void centralRadialChanged(const QPointF &point); + void focalRadialChanged(const QPointF &point); + void radiusRadialChanged(qreal radius); + void centralConicalChanged(const QPointF &point); + void angleConicalChanged(qreal angle); + + void setStartLinear(const QPointF &point); + void setEndLinear(const QPointF &point); + void setCentralRadial(const QPointF &point); + void setFocalRadial(const QPointF &point); + void setRadiusRadial(qreal radius); + void setCentralConical(const QPointF &point); + void setAngleConical(qreal angle); + + void setType(QGradient::Type type); + void showDetails(bool details); + + void setSpinBox(QDoubleSpinBox *spinBox, const char *slot, double max = 1.0, double step = 0.01, int decimals = 3); + void reset(); + void setLayout(bool details); + void layoutDetails(bool details); + bool row4Visible() const; + bool row5Visible() const; + int extensionWidthHint() const; + + void setCombos(bool combos); + + QGradient gradient() const; + void updateGradient(bool emitSignal); + + Ui::QtGradientEditor m_ui; + QtGradientStopsController *m_gradientStopsController; + + QDoubleSpinBox *startLinearXSpinBox; + QDoubleSpinBox *startLinearYSpinBox; + QDoubleSpinBox *endLinearXSpinBox; + QDoubleSpinBox *endLinearYSpinBox; + QDoubleSpinBox *centralRadialXSpinBox; + QDoubleSpinBox *centralRadialYSpinBox; + QDoubleSpinBox *focalRadialXSpinBox; + QDoubleSpinBox *focalRadialYSpinBox; + QDoubleSpinBox *radiusRadialSpinBox; + QDoubleSpinBox *centralConicalXSpinBox; + QDoubleSpinBox *centralConicalYSpinBox; + QDoubleSpinBox *angleConicalSpinBox; + + QButtonGroup *m_typeGroup; + QButtonGroup *m_spreadGroup; + + QGradient::Type m_type; + + QGridLayout *m_gridLayout; + QWidget *m_hiddenWidget; + QGridLayout *m_hiddenLayout; + bool m_details; + bool m_detailsButtonVisible; + bool m_backgroundCheckered; + + QGradient m_gradient; + + bool m_combos; +}; + +QGradient QtGradientEditorPrivate::gradient() const +{ + QGradient *gradient = 0; + switch (m_ui.gradientWidget->gradientType()) { + case QGradient::LinearGradient: + gradient = new QLinearGradient(m_ui.gradientWidget->startLinear(), + m_ui.gradientWidget->endLinear()); + break; + case QGradient::RadialGradient: + gradient = new QRadialGradient(m_ui.gradientWidget->centralRadial(), + m_ui.gradientWidget->radiusRadial(), + m_ui.gradientWidget->focalRadial()); + break; + case QGradient::ConicalGradient: + gradient = new QConicalGradient(m_ui.gradientWidget->centralConical(), + m_ui.gradientWidget->angleConical()); + break; + default: + break; + } + if (!gradient) + return QGradient(); + gradient->setStops(m_ui.gradientWidget->gradientStops()); + gradient->setSpread(m_ui.gradientWidget->gradientSpread()); + gradient->setCoordinateMode(QGradient::StretchToDeviceMode); + QGradient gr = *gradient; + delete gradient; + return gr; +} + +void QtGradientEditorPrivate::updateGradient(bool emitSignal) +{ + QGradient grad = gradient(); + if (m_gradient == grad) + return; + + m_gradient = grad; + if (emitSignal) + emit q_ptr->gradientChanged(m_gradient); +} + +void QtGradientEditorPrivate::setCombos(bool combos) +{ + if (m_combos == combos) + return; + + m_combos = combos; + m_ui.linearButton->setVisible(!m_combos); + m_ui.radialButton->setVisible(!m_combos); + m_ui.conicalButton->setVisible(!m_combos); + m_ui.padButton->setVisible(!m_combos); + m_ui.repeatButton->setVisible(!m_combos); + m_ui.reflectButton->setVisible(!m_combos); + m_ui.typeComboBox->setVisible(m_combos); + m_ui.spreadComboBox->setVisible(m_combos); +} + +void QtGradientEditorPrivate::setLayout(bool details) +{ + QHBoxLayout *hboxLayout = new QHBoxLayout(); + hboxLayout->setObjectName(QString::fromUtf8("hboxLayout")); + hboxLayout->addWidget(m_ui.typeComboBox); + hboxLayout->addWidget(m_ui.spreadComboBox); + QHBoxLayout *typeLayout = new QHBoxLayout(); + typeLayout->setSpacing(0); + typeLayout->addWidget(m_ui.linearButton); + typeLayout->addWidget(m_ui.radialButton); + typeLayout->addWidget(m_ui.conicalButton); + hboxLayout->addLayout(typeLayout); + QHBoxLayout *spreadLayout = new QHBoxLayout(); + spreadLayout->setSpacing(0); + spreadLayout->addWidget(m_ui.padButton); + spreadLayout->addWidget(m_ui.repeatButton); + spreadLayout->addWidget(m_ui.reflectButton); + hboxLayout->addLayout(spreadLayout); + hboxLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum)); + hboxLayout->addWidget(m_ui.detailsButton); + m_gridLayout->addLayout(hboxLayout, 0, 0, 1, 2); + int span = 1; + if (details) + span = 7; + m_gridLayout->addWidget(m_ui.frame, 1, 0, span, 2); + int row = 2; + if (details) { + row = 8; + span = 4; + } + m_gridLayout->addWidget(m_ui.gradientStopsWidget, row, 0, span, 2); + QHBoxLayout *hboxLayout1 = new QHBoxLayout(); + hboxLayout1->setObjectName(QString::fromUtf8("hboxLayout1")); + hboxLayout1->addWidget(m_ui.colorLabel); + hboxLayout1->addWidget(m_ui.colorButton); + hboxLayout1->addWidget(m_ui.hsvRadioButton); + hboxLayout1->addWidget(m_ui.rgbRadioButton); + hboxLayout1->addItem(new QSpacerItem(16, 23, QSizePolicy::Expanding, QSizePolicy::Minimum)); + int addRow = 0; + if (details) + addRow = 9; + m_gridLayout->addLayout(hboxLayout1, 3 + addRow, 0, 1, 2); + m_gridLayout->addWidget(m_ui.hLabel, 4 + addRow, 0, 1, 1); + m_gridLayout->addWidget(m_ui.frame_2, 4 + addRow, 1, 1, 1); + m_gridLayout->addWidget(m_ui.sLabel, 5 + addRow, 0, 1, 1); + m_gridLayout->addWidget(m_ui.frame_5, 5 + addRow, 1, 1, 1); + m_gridLayout->addWidget(m_ui.vLabel, 6 + addRow, 0, 1, 1); + m_gridLayout->addWidget(m_ui.frame_3, 6 + addRow, 1, 1, 1); + m_gridLayout->addWidget(m_ui.aLabel, 7 + addRow, 0, 1, 1); + m_gridLayout->addWidget(m_ui.frame_4, 7 + addRow, 1, 1, 1); + + if (details) { + layoutDetails(details); + } +} + +void QtGradientEditorPrivate::layoutDetails(bool details) +{ + QGridLayout *gridLayout = m_gridLayout; + int col = 2; + if (!details) { + col = 0; + if (!m_hiddenWidget) { + m_hiddenWidget = new QWidget(); + m_hiddenLayout = new QGridLayout(m_hiddenWidget); + m_hiddenLayout->setContentsMargins(0, 0, 0, 0); + m_hiddenLayout->setSizeConstraint(QLayout::SetFixedSize); + } + gridLayout = m_hiddenLayout; + } + gridLayout->addWidget(m_ui.label1, 1, col + 0, 1, 1); + gridLayout->addWidget(m_ui.spinBox1, 1, col + 1, 1, 1); + gridLayout->addWidget(m_ui.label2, 2, col + 0, 1, 1); + gridLayout->addWidget(m_ui.spinBox2, 2, col + 1, 1, 1); + gridLayout->addWidget(m_ui.label3, 3, col + 0, 1, 1); + gridLayout->addWidget(m_ui.spinBox3, 3, col + 1, 1, 1); + gridLayout->addWidget(m_ui.label4, 4, col + 0, 1, 1); + gridLayout->addWidget(m_ui.spinBox4, 4, col + 1, 1, 1); + gridLayout->addWidget(m_ui.label5, 5, col + 0, 1, 1); + gridLayout->addWidget(m_ui.spinBox5, 5, col + 1, 1, 1); + gridLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding), 6, col + 0, 1, 1); + gridLayout->addWidget(m_ui.line1Widget, 7, col + 0, 1, 2); + gridLayout->addWidget(m_ui.zoomLabel, 8, col + 0, 1, 1); + gridLayout->addWidget(m_ui.zoomWidget, 8, col + 1, 1, 1); + gridLayout->addWidget(m_ui.zoomButtonsWidget, 9, col + 0, 1, 1); + gridLayout->addWidget(m_ui.zoomAllButton, 9, col + 1, 1, 1); + gridLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Preferred), 10, col + 0, 1, 1); + gridLayout->addWidget(m_ui.line2Widget, 11, col + 0, 1, 2); + gridLayout->addWidget(m_ui.positionLabel, 12, col + 0, 1, 1); + gridLayout->addWidget(m_ui.positionWidget, 12, col + 1, 1, 1); + gridLayout->addWidget(m_ui.hueLabel, 13, col + 0, 1, 1); + gridLayout->addWidget(m_ui.hueWidget, 13, col + 1, 1, 1); + gridLayout->addWidget(m_ui.saturationLabel, 14, col + 0, 1, 1); + gridLayout->addWidget(m_ui.saturationWidget, 14, col + 1, 1, 1); + gridLayout->addWidget(m_ui.valueLabel, 15, col + 0, 1, 1); + gridLayout->addWidget(m_ui.valueWidget, 15, col + 1, 1, 1); + gridLayout->addWidget(m_ui.alphaLabel, 16, col + 0, 1, 1); + gridLayout->addWidget(m_ui.alphaWidget, 16, col + 1, 1, 1); + + if (details) { + if (m_hiddenLayout) { + delete m_hiddenLayout; + m_hiddenLayout = 0; + } + if (m_hiddenWidget) { + delete m_hiddenWidget; + m_hiddenWidget = 0; + } + } +} + +int QtGradientEditorPrivate::extensionWidthHint() const +{ + if (m_details) + return q_ptr->size().width() - m_ui.gradientStopsWidget->size().width(); + + const int space = m_ui.spinBox1->geometry().left() - m_ui.label1->geometry().right(); + + return m_hiddenLayout->minimumSize().width() + space; +} + +void QtGradientEditorPrivate::slotDetailsChanged(bool details) +{ + showDetails(details); +} + +bool QtGradientEditorPrivate::row4Visible() const +{ + if (m_type == QGradient::ConicalGradient) + return false; + return true; +} + +bool QtGradientEditorPrivate::row5Visible() const +{ + if (m_type == QGradient::RadialGradient) + return true; + return false; +} + +void QtGradientEditorPrivate::showDetails(bool details) +{ + if (m_details == details) + return; + + bool blocked = m_ui.detailsButton->signalsBlocked(); + m_ui.detailsButton->blockSignals(true); + m_ui.detailsButton->setChecked(details); + m_ui.detailsButton->blockSignals(blocked); + + bool updates = q_ptr->updatesEnabled(); + q_ptr->setUpdatesEnabled(false); + + if (m_gridLayout) { + m_gridLayout->setEnabled(false); + delete m_gridLayout; + m_gridLayout = 0; + } + + if (!details) { + layoutDetails(details); + } + + emit q_ptr->aboutToShowDetails(details, extensionWidthHint()); + m_details = details; + + m_gridLayout = new QGridLayout(q_ptr); + m_gridLayout->setEnabled(false); + m_gridLayout->setObjectName(QString::fromUtf8("gridLayout")); + m_gridLayout->setContentsMargins(0, 0, 0, 0); + + m_ui.label4->setVisible(row4Visible()); + m_ui.label5->setVisible(row5Visible()); + m_ui.spinBox4->setVisible(row4Visible()); + m_ui.spinBox5->setVisible(row5Visible()); + + setLayout(details); + m_gridLayout->setEnabled(true); + + q_ptr->setUpdatesEnabled(updates); + q_ptr->update(); +} + +void QtGradientEditorPrivate::setSpinBox(QDoubleSpinBox *spinBox, const char *slot, double max, double step, int decimals) +{ + bool blocked = spinBox->signalsBlocked(); + spinBox->blockSignals(true); + spinBox->setDecimals(decimals); + spinBox->setMaximum(max); + spinBox->setSingleStep(step); + spinBox->blockSignals(blocked); + QObject::connect(spinBox, SIGNAL(valueChanged(double)), q_ptr, slot); +} + +void QtGradientEditorPrivate::reset() +{ + startLinearXSpinBox = 0; + startLinearYSpinBox = 0; + endLinearXSpinBox = 0; + endLinearYSpinBox = 0; + centralRadialXSpinBox = 0; + centralRadialYSpinBox = 0; + focalRadialXSpinBox = 0; + focalRadialYSpinBox = 0; + radiusRadialSpinBox = 0; + centralConicalXSpinBox = 0; + centralConicalYSpinBox = 0; + angleConicalSpinBox = 0; +} + +void QtGradientEditorPrivate::setType(QGradient::Type type) +{ + if (m_type == type) + return; + + m_type = type; + m_ui.spinBox1->disconnect(SIGNAL(valueChanged(double))); + m_ui.spinBox2->disconnect(SIGNAL(valueChanged(double))); + m_ui.spinBox3->disconnect(SIGNAL(valueChanged(double))); + m_ui.spinBox4->disconnect(SIGNAL(valueChanged(double))); + m_ui.spinBox5->disconnect(SIGNAL(valueChanged(double))); + + reset(); + + bool ena = true; + + if (m_gridLayout) { + ena = m_gridLayout->isEnabled(); + m_gridLayout->setEnabled(false); + } + + bool spreadEnabled = true; + + if (type == QGradient::LinearGradient) { + startLinearXSpinBox = m_ui.spinBox1; + setSpinBox(startLinearXSpinBox, SLOT(slotStartLinearXChanged(double))); + m_ui.label1->setText(QApplication::translate("QtGradientEditor", "Start X", 0, QApplication::UnicodeUTF8)); + + startLinearYSpinBox = m_ui.spinBox2; + setSpinBox(startLinearYSpinBox, SLOT(slotStartLinearYChanged(double))); + m_ui.label2->setText(QApplication::translate("QtGradientEditor", "Start Y", 0, QApplication::UnicodeUTF8)); + + endLinearXSpinBox = m_ui.spinBox3; + setSpinBox(endLinearXSpinBox, SLOT(slotEndLinearXChanged(double))); + m_ui.label3->setText(QApplication::translate("QtGradientEditor", "Final X", 0, QApplication::UnicodeUTF8)); + + endLinearYSpinBox = m_ui.spinBox4; + setSpinBox(endLinearYSpinBox, SLOT(slotEndLinearYChanged(double))); + m_ui.label4->setText(QApplication::translate("QtGradientEditor", "Final Y", 0, QApplication::UnicodeUTF8)); + + setStartLinear(m_ui.gradientWidget->startLinear()); + setEndLinear(m_ui.gradientWidget->endLinear()); + } else if (type == QGradient::RadialGradient) { + centralRadialXSpinBox = m_ui.spinBox1; + setSpinBox(centralRadialXSpinBox, SLOT(slotCentralRadialXChanged(double))); + m_ui.label1->setText(QApplication::translate("QtGradientEditor", "Central X", 0, QApplication::UnicodeUTF8)); + + centralRadialYSpinBox = m_ui.spinBox2; + setSpinBox(centralRadialYSpinBox, SLOT(slotCentralRadialYChanged(double))); + m_ui.label2->setText(QApplication::translate("QtGradientEditor", "Central Y", 0, QApplication::UnicodeUTF8)); + + focalRadialXSpinBox = m_ui.spinBox3; + setSpinBox(focalRadialXSpinBox, SLOT(slotFocalRadialXChanged(double))); + m_ui.label3->setText(QApplication::translate("QtGradientEditor", "Focal X", 0, QApplication::UnicodeUTF8)); + + focalRadialYSpinBox = m_ui.spinBox4; + setSpinBox(focalRadialYSpinBox, SLOT(slotFocalRadialYChanged(double))); + m_ui.label4->setText(QApplication::translate("QtGradientEditor", "Focal Y", 0, QApplication::UnicodeUTF8)); + + radiusRadialSpinBox = m_ui.spinBox5; + setSpinBox(radiusRadialSpinBox, SLOT(slotRadiusRadialChanged(double)), 2.0); + m_ui.label5->setText(QApplication::translate("QtGradientEditor", "Radius", 0, QApplication::UnicodeUTF8)); + + setCentralRadial(m_ui.gradientWidget->centralRadial()); + setFocalRadial(m_ui.gradientWidget->focalRadial()); + setRadiusRadial(m_ui.gradientWidget->radiusRadial()); + } else if (type == QGradient::ConicalGradient) { + centralConicalXSpinBox = m_ui.spinBox1; + setSpinBox(centralConicalXSpinBox, SLOT(slotCentralConicalXChanged(double))); + m_ui.label1->setText(QApplication::translate("QtGradientEditor", "Central X", 0, QApplication::UnicodeUTF8)); + + centralConicalYSpinBox = m_ui.spinBox2; + setSpinBox(centralConicalYSpinBox, SLOT(slotCentralConicalYChanged(double))); + m_ui.label2->setText(QApplication::translate("QtGradientEditor", "Central Y", 0, QApplication::UnicodeUTF8)); + + angleConicalSpinBox = m_ui.spinBox3; + setSpinBox(angleConicalSpinBox, SLOT(slotAngleConicalChanged(double)), 360.0, 1.0, 1); + m_ui.label3->setText(QApplication::translate("QtGradientEditor", "Angle", 0, QApplication::UnicodeUTF8)); + + setCentralConical(m_ui.gradientWidget->centralConical()); + setAngleConical(m_ui.gradientWidget->angleConical()); + + spreadEnabled = false; + } + m_ui.spreadComboBox->setEnabled(spreadEnabled); + m_ui.padButton->setEnabled(spreadEnabled); + m_ui.repeatButton->setEnabled(spreadEnabled); + m_ui.reflectButton->setEnabled(spreadEnabled); + + m_ui.label4->setVisible(row4Visible()); + m_ui.spinBox4->setVisible(row4Visible()); + m_ui.label5->setVisible(row5Visible()); + m_ui.spinBox5->setVisible(row5Visible()); + + if (m_gridLayout) { + m_gridLayout->setEnabled(ena); + } +} + +void QtGradientEditorPrivate::slotGradientStopsChanged(const QGradientStops &stops) +{ + m_ui.gradientWidget->setGradientStops(stops); + updateGradient(true); +} + +void QtGradientEditorPrivate::slotTypeChanged(int idx) +{ + QGradient::Type type = QGradient::NoGradient; + if (idx == 0) + type = QGradient::LinearGradient; + else if (idx == 1) + type = QGradient::RadialGradient; + else if (idx == 2) + type = QGradient::ConicalGradient; + setType(type); + m_ui.typeComboBox->setCurrentIndex(idx); + m_typeGroup->button(idx)->setChecked(true); + m_ui.gradientWidget->setGradientType(type); + updateGradient(true); +} + +void QtGradientEditorPrivate::slotSpreadChanged(int spread) +{ + if (spread == 0) { + m_ui.gradientWidget->setGradientSpread(QGradient::PadSpread); + } else if (spread == 1) { + m_ui.gradientWidget->setGradientSpread(QGradient::RepeatSpread); + } else if (spread == 2) { + m_ui.gradientWidget->setGradientSpread(QGradient::ReflectSpread); + } + m_ui.spreadComboBox->setCurrentIndex(spread); + updateGradient(true); +} + +void QtGradientEditorPrivate::slotStartLinearXChanged(double value) +{ + QPointF point = m_ui.gradientWidget->startLinear(); + point.setX(value); + m_ui.gradientWidget->setStartLinear(point); + updateGradient(true); +} + +void QtGradientEditorPrivate::slotStartLinearYChanged(double value) +{ + QPointF point = m_ui.gradientWidget->startLinear(); + point.setY(value); + m_ui.gradientWidget->setStartLinear(point); + updateGradient(true); +} + +void QtGradientEditorPrivate::slotEndLinearXChanged(double value) +{ + QPointF point = m_ui.gradientWidget->endLinear(); + point.setX(value); + m_ui.gradientWidget->setEndLinear(point); + updateGradient(true); +} + +void QtGradientEditorPrivate::slotEndLinearYChanged(double value) +{ + QPointF point = m_ui.gradientWidget->endLinear(); + point.setY(value); + m_ui.gradientWidget->setEndLinear(point); + updateGradient(true); +} + +void QtGradientEditorPrivate::slotCentralRadialXChanged(double value) +{ + QPointF point = m_ui.gradientWidget->centralRadial(); + point.setX(value); + m_ui.gradientWidget->setCentralRadial(point); + updateGradient(true); +} + +void QtGradientEditorPrivate::slotCentralRadialYChanged(double value) +{ + QPointF point = m_ui.gradientWidget->centralRadial(); + point.setY(value); + m_ui.gradientWidget->setCentralRadial(point); + updateGradient(true); +} + +void QtGradientEditorPrivate::slotFocalRadialXChanged(double value) +{ + QPointF point = m_ui.gradientWidget->focalRadial(); + point.setX(value); + m_ui.gradientWidget->setFocalRadial(point); + updateGradient(true); +} + +void QtGradientEditorPrivate::slotFocalRadialYChanged(double value) +{ + QPointF point = m_ui.gradientWidget->focalRadial(); + point.setY(value); + m_ui.gradientWidget->setFocalRadial(point); + updateGradient(true); +} + +void QtGradientEditorPrivate::slotRadiusRadialChanged(double value) +{ + m_ui.gradientWidget->setRadiusRadial(value); + updateGradient(true); +} + +void QtGradientEditorPrivate::slotCentralConicalXChanged(double value) +{ + QPointF point = m_ui.gradientWidget->centralConical(); + point.setX(value); + m_ui.gradientWidget->setCentralConical(point); + updateGradient(true); +} + +void QtGradientEditorPrivate::slotCentralConicalYChanged(double value) +{ + QPointF point = m_ui.gradientWidget->centralConical(); + point.setY(value); + m_ui.gradientWidget->setCentralConical(point); + updateGradient(true); +} + +void QtGradientEditorPrivate::slotAngleConicalChanged(double value) +{ + m_ui.gradientWidget->setAngleConical(value); + updateGradient(true); +} + +void QtGradientEditorPrivate::startLinearChanged(const QPointF &point) +{ + setStartLinear(point); + updateGradient(true); +} + +void QtGradientEditorPrivate::endLinearChanged(const QPointF &point) +{ + setEndLinear(point); + updateGradient(true); +} + +void QtGradientEditorPrivate::centralRadialChanged(const QPointF &point) +{ + setCentralRadial(point); + updateGradient(true); +} + +void QtGradientEditorPrivate::focalRadialChanged(const QPointF &point) +{ + setFocalRadial(point); + updateGradient(true); +} + +void QtGradientEditorPrivate::radiusRadialChanged(qreal radius) +{ + setRadiusRadial(radius); + updateGradient(true); +} + +void QtGradientEditorPrivate::centralConicalChanged(const QPointF &point) +{ + setCentralConical(point); + updateGradient(true); +} + +void QtGradientEditorPrivate::angleConicalChanged(qreal angle) +{ + setAngleConical(angle); + updateGradient(true); +} + +void QtGradientEditorPrivate::setStartLinear(const QPointF &point) +{ + if (startLinearXSpinBox) + startLinearXSpinBox->setValue(point.x()); + if (startLinearYSpinBox) + startLinearYSpinBox->setValue(point.y()); +} + +void QtGradientEditorPrivate::setEndLinear(const QPointF &point) +{ + if (endLinearXSpinBox) + endLinearXSpinBox->setValue(point.x()); + if (endLinearYSpinBox) + endLinearYSpinBox->setValue(point.y()); +} + +void QtGradientEditorPrivate::setCentralRadial(const QPointF &point) +{ + if (centralRadialXSpinBox) + centralRadialXSpinBox->setValue(point.x()); + if (centralRadialYSpinBox) + centralRadialYSpinBox->setValue(point.y()); +} + +void QtGradientEditorPrivate::setFocalRadial(const QPointF &point) +{ + if (focalRadialXSpinBox) + focalRadialXSpinBox->setValue(point.x()); + if (focalRadialYSpinBox) + focalRadialYSpinBox->setValue(point.y()); +} + +void QtGradientEditorPrivate::setRadiusRadial(qreal radius) +{ + if (radiusRadialSpinBox) + radiusRadialSpinBox->setValue(radius); +} + +void QtGradientEditorPrivate::setCentralConical(const QPointF &point) +{ + if (centralConicalXSpinBox) + centralConicalXSpinBox->setValue(point.x()); + if (centralConicalYSpinBox) + centralConicalYSpinBox->setValue(point.y()); +} + +void QtGradientEditorPrivate::setAngleConical(qreal angle) +{ + if (angleConicalSpinBox) + angleConicalSpinBox->setValue(angle); +} + +QtGradientEditor::QtGradientEditor(QWidget *parent) + : QWidget(parent), d_ptr(new QtGradientEditorPrivate()) +{ + d_ptr->q_ptr = this; + d_ptr->m_type = QGradient::RadialGradient; + d_ptr->m_ui.setupUi(this); + d_ptr->m_gridLayout = 0; + d_ptr->m_hiddenLayout = 0; + d_ptr->m_hiddenWidget = 0; + bool detailsDefault = false; + d_ptr->m_details = !detailsDefault; + d_ptr->m_detailsButtonVisible = true; + bool checkeredDefault = true; + d_ptr->m_backgroundCheckered = !checkeredDefault; + d_ptr->m_gradientStopsController = new QtGradientStopsController(this); + d_ptr->m_gradientStopsController->setUi(&d_ptr->m_ui); + d_ptr->reset(); + d_ptr->setType(QGradient::LinearGradient); + d_ptr->m_combos = true; + d_ptr->setCombos(!d_ptr->m_combos); + + d_ptr->showDetails(detailsDefault); + setBackgroundCheckered(checkeredDefault); + + d_ptr->setStartLinear(QPointF(0, 0)); + d_ptr->setEndLinear(QPointF(1, 1)); + d_ptr->setCentralRadial(QPointF(0.5, 0.5)); + d_ptr->setFocalRadial(QPointF(0.5, 0.5)); + d_ptr->setRadiusRadial(0.5); + d_ptr->setCentralConical(QPointF(0.5, 0.5)); + d_ptr->setAngleConical(0); + + QIcon icon; + icon.addPixmap(style()->standardPixmap(QStyle::SP_ArrowRight), QIcon::Normal, QIcon::Off); + icon.addPixmap(style()->standardPixmap(QStyle::SP_ArrowLeft), QIcon::Normal, QIcon::On); + d_ptr->m_ui.detailsButton->setIcon(icon); + + connect(d_ptr->m_ui.detailsButton, SIGNAL(clicked(bool)), this, SLOT(slotDetailsChanged(bool))); + connect(d_ptr->m_gradientStopsController, SIGNAL(gradientStopsChanged(QGradientStops)), + this, SLOT(slotGradientStopsChanged(QGradientStops))); + + QIcon iconLinear(QLatin1String(":/trolltech/qtgradienteditor/images/typelinear.png")); + QIcon iconRadial(QLatin1String(":/trolltech/qtgradienteditor/images/typeradial.png")); + QIcon iconConical(QLatin1String(":/trolltech/qtgradienteditor/images/typeconical.png")); + + d_ptr->m_ui.typeComboBox->addItem(iconLinear, tr("Linear")); + d_ptr->m_ui.typeComboBox->addItem(iconRadial, tr("Radial")); + d_ptr->m_ui.typeComboBox->addItem(iconConical, tr("Conical")); + + d_ptr->m_ui.linearButton->setIcon(iconLinear); + d_ptr->m_ui.radialButton->setIcon(iconRadial); + d_ptr->m_ui.conicalButton->setIcon(iconConical); + + d_ptr->m_typeGroup = new QButtonGroup(this); + d_ptr->m_typeGroup->addButton(d_ptr->m_ui.linearButton, 0); + d_ptr->m_typeGroup->addButton(d_ptr->m_ui.radialButton, 1); + d_ptr->m_typeGroup->addButton(d_ptr->m_ui.conicalButton, 2); + + connect(d_ptr->m_typeGroup, SIGNAL(buttonClicked(int)), + this, SLOT(slotTypeChanged(int))); + connect(d_ptr->m_ui.typeComboBox, SIGNAL(activated(int)), + this, SLOT(slotTypeChanged(int))); + + QIcon iconPad(QLatin1String(":/trolltech/qtgradienteditor/images/spreadpad.png")); + QIcon iconRepeat(QLatin1String(":/trolltech/qtgradienteditor/images/spreadrepeat.png")); + QIcon iconReflect(QLatin1String(":/trolltech/qtgradienteditor/images/spreadreflect.png")); + + d_ptr->m_ui.spreadComboBox->addItem(iconPad, tr("Pad")); + d_ptr->m_ui.spreadComboBox->addItem(iconRepeat, tr("Repeat")); + d_ptr->m_ui.spreadComboBox->addItem(iconReflect, tr("Reflect")); + + d_ptr->m_ui.padButton->setIcon(iconPad); + d_ptr->m_ui.repeatButton->setIcon(iconRepeat); + d_ptr->m_ui.reflectButton->setIcon(iconReflect); + + d_ptr->m_spreadGroup = new QButtonGroup(this); + d_ptr->m_spreadGroup->addButton(d_ptr->m_ui.padButton, 0); + d_ptr->m_spreadGroup->addButton(d_ptr->m_ui.repeatButton, 1); + d_ptr->m_spreadGroup->addButton(d_ptr->m_ui.reflectButton, 2); + connect(d_ptr->m_spreadGroup, SIGNAL(buttonClicked(int)), + this, SLOT(slotSpreadChanged(int))); + connect(d_ptr->m_ui.spreadComboBox, SIGNAL(activated(int)), + this, SLOT(slotSpreadChanged(int))); + + connect(d_ptr->m_ui.gradientWidget, SIGNAL(startLinearChanged(QPointF)), + this, SLOT(startLinearChanged(QPointF))); + connect(d_ptr->m_ui.gradientWidget, SIGNAL(endLinearChanged(QPointF)), + this, SLOT(endLinearChanged(QPointF))); + connect(d_ptr->m_ui.gradientWidget, SIGNAL(centralRadialChanged(QPointF)), + this, SLOT(centralRadialChanged(QPointF))); + connect(d_ptr->m_ui.gradientWidget, SIGNAL(focalRadialChanged(QPointF)), + this, SLOT(focalRadialChanged(QPointF))); + connect(d_ptr->m_ui.gradientWidget, SIGNAL(radiusRadialChanged(qreal)), + this, SLOT(radiusRadialChanged(qreal))); + connect(d_ptr->m_ui.gradientWidget, SIGNAL(centralConicalChanged(QPointF)), + this, SLOT(centralConicalChanged(QPointF))); + connect(d_ptr->m_ui.gradientWidget, SIGNAL(angleConicalChanged(qreal)), + this, SLOT(angleConicalChanged(qreal))); + + QGradientStops stops = gradient().stops(); + d_ptr->m_gradientStopsController->setGradientStops(stops); + d_ptr->m_ui.gradientWidget->setGradientStops(stops); +} + +QtGradientEditor::~QtGradientEditor() +{ + if (d_ptr->m_hiddenWidget) + delete d_ptr->m_hiddenWidget; +} + +void QtGradientEditor::setGradient(const QGradient &grad) +{ + if (grad == gradient()) + return; + + QGradient::Type type = grad.type(); + int idx = 0; + switch (type) { + case QGradient::LinearGradient: idx = 0; break; + case QGradient::RadialGradient: idx = 1; break; + case QGradient::ConicalGradient: idx = 2; break; + default: return; + } + d_ptr->setType(type); + d_ptr->m_ui.typeComboBox->setCurrentIndex(idx); + d_ptr->m_ui.gradientWidget->setGradientType(type); + d_ptr->m_typeGroup->button(idx)->setChecked(true); + + QGradient::Spread spread = grad.spread(); + switch (spread) { + case QGradient::PadSpread: idx = 0; break; + case QGradient::RepeatSpread: idx = 1; break; + case QGradient::ReflectSpread: idx = 2; break; + default: idx = 0; break; + } + d_ptr->m_ui.spreadComboBox->setCurrentIndex(idx); + d_ptr->m_ui.gradientWidget->setGradientSpread(spread); + d_ptr->m_spreadGroup->button(idx)->setChecked(true); + + if (type == QGradient::LinearGradient) { + QLinearGradient *gr = (QLinearGradient *)(&grad); + d_ptr->setStartLinear(gr->start()); + d_ptr->setEndLinear(gr->finalStop()); + d_ptr->m_ui.gradientWidget->setStartLinear(gr->start()); + d_ptr->m_ui.gradientWidget->setEndLinear(gr->finalStop()); + } else if (type == QGradient::RadialGradient) { + QRadialGradient *gr = (QRadialGradient *)(&grad); + d_ptr->setCentralRadial(gr->center()); + d_ptr->setFocalRadial(gr->focalPoint()); + d_ptr->setRadiusRadial(gr->radius()); + d_ptr->m_ui.gradientWidget->setCentralRadial(gr->center()); + d_ptr->m_ui.gradientWidget->setFocalRadial(gr->focalPoint()); + d_ptr->m_ui.gradientWidget->setRadiusRadial(gr->radius()); + } else if (type == QGradient::ConicalGradient) { + QConicalGradient *gr = (QConicalGradient *)(&grad); + d_ptr->setCentralConical(gr->center()); + d_ptr->setAngleConical(gr->angle()); + d_ptr->m_ui.gradientWidget->setCentralConical(gr->center()); + d_ptr->m_ui.gradientWidget->setAngleConical(gr->angle()); + } + + d_ptr->m_gradientStopsController->setGradientStops(grad.stops()); + d_ptr->m_ui.gradientWidget->setGradientStops(grad.stops()); + d_ptr->updateGradient(false); +} + +QGradient QtGradientEditor::gradient() const +{ + return d_ptr->m_gradient; +} + +bool QtGradientEditor::isBackgroundCheckered() const +{ + return d_ptr->m_backgroundCheckered; +} + +void QtGradientEditor::setBackgroundCheckered(bool checkered) +{ + if (d_ptr->m_backgroundCheckered == checkered) + return; + + d_ptr->m_backgroundCheckered = checkered; + d_ptr->m_ui.hueColorLine->setBackgroundCheckered(checkered); + d_ptr->m_ui.saturationColorLine->setBackgroundCheckered(checkered); + d_ptr->m_ui.valueColorLine->setBackgroundCheckered(checkered); + d_ptr->m_ui.alphaColorLine->setBackgroundCheckered(checkered); + d_ptr->m_ui.gradientWidget->setBackgroundCheckered(checkered); + d_ptr->m_ui.gradientStopsWidget->setBackgroundCheckered(checkered); + d_ptr->m_ui.colorButton->setBackgroundCheckered(checkered); +} + +bool QtGradientEditor::detailsVisible() const +{ + return d_ptr->m_details; +} + +void QtGradientEditor::setDetailsVisible(bool visible) +{ + d_ptr->showDetails(visible); +} + +bool QtGradientEditor::isDetailsButtonVisible() const +{ + return d_ptr->m_detailsButtonVisible; +} + +void QtGradientEditor::setDetailsButtonVisible(bool visible) +{ + if (d_ptr->m_detailsButtonVisible == visible) + return; + + d_ptr->m_detailsButtonVisible = visible; + d_ptr->m_ui.detailsButton->setVisible(visible); +} + +QColor::Spec QtGradientEditor::spec() const +{ + return d_ptr->m_gradientStopsController->spec(); +} + +void QtGradientEditor::setSpec(QColor::Spec spec) +{ + d_ptr->m_gradientStopsController->setSpec(spec); +} + +QT_END_NAMESPACE + +#include "moc_qtgradienteditor.cpp" diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradienteditor.h b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradienteditor.h new file mode 100644 index 00000000000..5aaba5d148e --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradienteditor.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTGRADIENTEDITOR_H +#define QTGRADIENTEDITOR_H + +#include <QtGui/QWidget> + +QT_BEGIN_NAMESPACE + +class QtGradientEditor : public QWidget +{ + Q_OBJECT + Q_PROPERTY(QGradient gradient READ gradient WRITE setGradient) + Q_PROPERTY(bool backgroundCheckered READ isBackgroundCheckered WRITE setBackgroundCheckered) + Q_PROPERTY(bool detailsVisible READ detailsVisible WRITE setDetailsVisible) + Q_PROPERTY(bool detailsButtonVisible READ isDetailsButtonVisible WRITE setDetailsButtonVisible) +public: + QtGradientEditor(QWidget *parent = 0); + ~QtGradientEditor(); + + void setGradient(const QGradient &gradient); + QGradient gradient() const; + + bool isBackgroundCheckered() const; + void setBackgroundCheckered(bool checkered); + + bool detailsVisible() const; + void setDetailsVisible(bool visible); + + bool isDetailsButtonVisible() const; + void setDetailsButtonVisible(bool visible); + + QColor::Spec spec() const; + void setSpec(QColor::Spec spec); + +signals: + + void gradientChanged(const QGradient &gradient); + void aboutToShowDetails(bool details, int extenstionWidthHint); + +private: + QScopedPointer<class QtGradientEditorPrivate> d_ptr; + Q_DECLARE_PRIVATE(QtGradientEditor) + Q_DISABLE_COPY(QtGradientEditor) + Q_PRIVATE_SLOT(d_func(), void slotGradientStopsChanged(const QGradientStops &stops)) + Q_PRIVATE_SLOT(d_func(), void slotTypeChanged(int type)) + Q_PRIVATE_SLOT(d_func(), void slotSpreadChanged(int type)) + Q_PRIVATE_SLOT(d_func(), void slotStartLinearXChanged(double value)) + Q_PRIVATE_SLOT(d_func(), void slotStartLinearYChanged(double value)) + Q_PRIVATE_SLOT(d_func(), void slotEndLinearXChanged(double value)) + Q_PRIVATE_SLOT(d_func(), void slotEndLinearYChanged(double value)) + Q_PRIVATE_SLOT(d_func(), void slotCentralRadialXChanged(double value)) + Q_PRIVATE_SLOT(d_func(), void slotCentralRadialYChanged(double value)) + Q_PRIVATE_SLOT(d_func(), void slotFocalRadialXChanged(double value)) + Q_PRIVATE_SLOT(d_func(), void slotFocalRadialYChanged(double value)) + Q_PRIVATE_SLOT(d_func(), void slotRadiusRadialChanged(double value)) + Q_PRIVATE_SLOT(d_func(), void slotCentralConicalXChanged(double value)) + Q_PRIVATE_SLOT(d_func(), void slotCentralConicalYChanged(double value)) + Q_PRIVATE_SLOT(d_func(), void slotAngleConicalChanged(double value)) + Q_PRIVATE_SLOT(d_func(), void slotDetailsChanged(bool details)) + Q_PRIVATE_SLOT(d_func(), void startLinearChanged(const QPointF &)) + Q_PRIVATE_SLOT(d_func(), void endLinearChanged(const QPointF &)) + Q_PRIVATE_SLOT(d_func(), void centralRadialChanged(const QPointF &)) + Q_PRIVATE_SLOT(d_func(), void focalRadialChanged(const QPointF &)) + Q_PRIVATE_SLOT(d_func(), void radiusRadialChanged(qreal)) + Q_PRIVATE_SLOT(d_func(), void centralConicalChanged(const QPointF &)) + Q_PRIVATE_SLOT(d_func(), void angleConicalChanged(qreal)) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradienteditor.pri b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradienteditor.pri new file mode 100644 index 00000000000..4cf059e1f27 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradienteditor.pri @@ -0,0 +1,33 @@ +INCLUDEPATH += $$PWD +DEPENDPATH += $$PWD +FORMS += $$PWD/qtgradienteditor.ui \ + $$PWD/qtgradientdialog.ui \ + $$PWD/qtgradientview.ui \ + $$PWD/qtgradientviewdialog.ui +SOURCES += $$PWD/qtgradientstopsmodel.cpp \ + $$PWD/qtgradientstopswidget.cpp \ + $$PWD/qtgradientstopscontroller.cpp \ + $$PWD/qtgradientwidget.cpp \ + $$PWD/qtgradienteditor.cpp \ + $$PWD/qtgradientdialog.cpp \ + $$PWD/qtcolorbutton.cpp \ + $$PWD/qtcolorline.cpp \ + $$PWD/qtgradientview.cpp \ + $$PWD/qtgradientviewdialog.cpp \ + $$PWD/qtgradientmanager.cpp \ + $$PWD/qtgradientutils.cpp +HEADERS += $$PWD/qtgradientstopsmodel.h \ + $$PWD/qtgradientstopswidget.h \ + $$PWD/qtgradientstopscontroller.h \ + $$PWD/qtgradientwidget.h \ + $$PWD/qtgradienteditor.h \ + $$PWD/qtgradientdialog.h \ + $$PWD/qtcolorbutton.h \ + $$PWD/qtcolorline.h \ + $$PWD/qtgradientview.h \ + $$PWD/qtgradientviewdialog.h \ + $$PWD/qtgradientmanager.h \ + $$PWD/qtgradientutils.h +RESOURCES += $$PWD/qtgradienteditor.qrc + +QT += xml diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradienteditor.qrc b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradienteditor.qrc new file mode 100644 index 00000000000..cce7ba6224e --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradienteditor.qrc @@ -0,0 +1,18 @@ +<!DOCTYPE RCC><RCC version="1.0"> + <qresource prefix="/trolltech/qtgradienteditor"> + <file>images/edit.png</file> + <file>images/zoomin.png</file> + <file>images/zoomout.png</file> + <file>images/up.png</file> + <file>images/down.png</file> + <file>images/plus.png</file> + <file>images/minus.png</file> + <file>images/editdelete.png</file> + <file>images/spreadpad.png</file> + <file>images/spreadrepeat.png</file> + <file>images/spreadreflect.png</file> + <file>images/typelinear.png</file> + <file>images/typeradial.png</file> + <file>images/typeconical.png</file> + </qresource> +</RCC> diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradienteditor.ui b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradienteditor.ui new file mode 100644 index 00000000000..60d6fb6f22e --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradienteditor.ui @@ -0,0 +1,1377 @@ +<ui version="4.0" > + <comment>********************************************************************* +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +*********************************************************************</comment> + <class>QtGradientEditor</class> + <widget class="QWidget" name="QtGradientEditor" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>364</width> + <height>518</height> + </rect> + </property> + <property name="windowTitle" > + <string>Form</string> + </property> + <widget class="QFrame" name="frame" > + <property name="geometry" > + <rect> + <x>10</x> + <y>69</y> + <width>193</width> + <height>150</height> + </rect> + </property> + <property name="sizePolicy" > + <sizepolicy vsizetype="Expanding" hsizetype="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> + <layout class="QVBoxLayout" > + <property name="spacing" > + <number>6</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="QtGradientWidget" native="1" name="gradientWidget" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Expanding" hsizetype="Expanding" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip" > + <string>Gradient Editor</string> + </property> + <property name="whatsThis" > + <string>This area shows a preview of the gradient being edited. It also allows you to edit parameters specific to the gradient's type such as start and final point, radius, etc. by drag & drop.</string> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QLabel" name="label1" > + <property name="geometry" > + <rect> + <x>209</x> + <y>69</y> + <width>64</width> + <height>23</height> + </rect> + </property> + <property name="text" > + <string>1</string> + </property> + </widget> + <widget class="QDoubleSpinBox" name="spinBox1" > + <property name="geometry" > + <rect> + <x>279</x> + <y>69</y> + <width>73</width> + <height>23</height> + </rect> + </property> + <property name="keyboardTracking" > + <bool>false</bool> + </property> + <property name="decimals" > + <number>3</number> + </property> + <property name="maximum" > + <double>1.000000000000000</double> + </property> + <property name="singleStep" > + <double>0.010000000000000</double> + </property> + </widget> + <widget class="QLabel" name="label2" > + <property name="geometry" > + <rect> + <x>209</x> + <y>99</y> + <width>64</width> + <height>23</height> + </rect> + </property> + <property name="text" > + <string>2</string> + </property> + </widget> + <widget class="QDoubleSpinBox" name="spinBox2" > + <property name="geometry" > + <rect> + <x>279</x> + <y>99</y> + <width>73</width> + <height>23</height> + </rect> + </property> + <property name="keyboardTracking" > + <bool>false</bool> + </property> + <property name="decimals" > + <number>3</number> + </property> + <property name="maximum" > + <double>1.000000000000000</double> + </property> + <property name="singleStep" > + <double>0.010000000000000</double> + </property> + </widget> + <widget class="QLabel" name="label3" > + <property name="geometry" > + <rect> + <x>209</x> + <y>129</y> + <width>64</width> + <height>23</height> + </rect> + </property> + <property name="text" > + <string>3</string> + </property> + </widget> + <widget class="QDoubleSpinBox" name="spinBox3" > + <property name="geometry" > + <rect> + <x>279</x> + <y>129</y> + <width>73</width> + <height>23</height> + </rect> + </property> + <property name="keyboardTracking" > + <bool>false</bool> + </property> + <property name="decimals" > + <number>3</number> + </property> + <property name="maximum" > + <double>1.000000000000000</double> + </property> + <property name="singleStep" > + <double>0.010000000000000</double> + </property> + </widget> + <widget class="QLabel" name="label4" > + <property name="geometry" > + <rect> + <x>209</x> + <y>159</y> + <width>64</width> + <height>23</height> + </rect> + </property> + <property name="text" > + <string>4</string> + </property> + </widget> + <widget class="QDoubleSpinBox" name="spinBox4" > + <property name="geometry" > + <rect> + <x>279</x> + <y>159</y> + <width>73</width> + <height>23</height> + </rect> + </property> + <property name="keyboardTracking" > + <bool>false</bool> + </property> + <property name="decimals" > + <number>3</number> + </property> + <property name="maximum" > + <double>1.000000000000000</double> + </property> + <property name="singleStep" > + <double>0.010000000000000</double> + </property> + </widget> + <widget class="QLabel" name="label5" > + <property name="geometry" > + <rect> + <x>209</x> + <y>189</y> + <width>64</width> + <height>23</height> + </rect> + </property> + <property name="text" > + <string>5</string> + </property> + </widget> + <widget class="QDoubleSpinBox" name="spinBox5" > + <property name="geometry" > + <rect> + <x>279</x> + <y>189</y> + <width>73</width> + <height>23</height> + </rect> + </property> + <property name="keyboardTracking" > + <bool>false</bool> + </property> + <property name="decimals" > + <number>3</number> + </property> + <property name="maximum" > + <double>1.000000000000000</double> + </property> + <property name="singleStep" > + <double>0.010000000000000</double> + </property> + </widget> + <widget class="QtGradientStopsWidget" native="1" name="gradientStopsWidget" > + <property name="geometry" > + <rect> + <x>10</x> + <y>225</y> + <width>193</width> + <height>67</height> + </rect> + </property> + <property name="toolTip" > + <string>Gradient Stops Editor</string> + </property> + <property name="whatsThis" > + <string>This area allows you to edit gradient stops. Double click on the existing stop handle to duplicate it. Double click outside of the existing stop handles to create a new stop. Drag & drop the handle to reposition it. Use right mouse button to popup context menu with extra actions.</string> + </property> + </widget> + <widget class="QLabel" name="zoomLabel" > + <property name="geometry" > + <rect> + <x>209</x> + <y>231</y> + <width>64</width> + <height>23</height> + </rect> + </property> + <property name="text" > + <string>Zoom</string> + </property> + </widget> + <widget class="QToolButton" name="zoomAllButton" > + <property name="geometry" > + <rect> + <x>279</x> + <y>260</y> + <width>72</width> + <height>26</height> + </rect> + </property> + <property name="sizePolicy" > + <sizepolicy vsizetype="Fixed" hsizetype="Preferred" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip" > + <string>Reset Zoom</string> + </property> + <property name="text" > + <string>Reset Zoom</string> + </property> + </widget> + <widget class="QLabel" name="positionLabel" > + <property name="geometry" > + <rect> + <x>209</x> + <y>304</y> + <width>64</width> + <height>23</height> + </rect> + </property> + <property name="text" > + <string>Position</string> + </property> + </widget> + <widget class="QLabel" name="hLabel" > + <property name="geometry" > + <rect> + <x>10</x> + <y>335</y> + <width>32</width> + <height>18</height> + </rect> + </property> + <property name="sizePolicy" > + <sizepolicy vsizetype="Fixed" hsizetype="Preferred" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip" > + <string>Hue</string> + </property> + <property name="text" > + <string>H</string> + </property> + </widget> + <widget class="QFrame" name="frame_2" > + <property name="geometry" > + <rect> + <x>48</x> + <y>333</y> + <width>155</width> + <height>23</height> + </rect> + </property> + <property name="sizePolicy" > + <sizepolicy vsizetype="Preferred" hsizetype="Ignored" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="frameShape" > + <enum>QFrame::StyledPanel</enum> + </property> + <property name="frameShadow" > + <enum>QFrame::Raised</enum> + </property> + <layout class="QHBoxLayout" > + <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="QtColorLine" native="1" name="hueColorLine" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Preferred" hsizetype="Expanding" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip" > + <string>Hue</string> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QLabel" name="hueLabel" > + <property name="geometry" > + <rect> + <x>209</x> + <y>335</y> + <width>64</width> + <height>18</height> + </rect> + </property> + <property name="sizePolicy" > + <sizepolicy vsizetype="Fixed" hsizetype="Preferred" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text" > + <string>Hue</string> + </property> + </widget> + <widget class="QLabel" name="sLabel" > + <property name="geometry" > + <rect> + <x>10</x> + <y>364</y> + <width>32</width> + <height>18</height> + </rect> + </property> + <property name="sizePolicy" > + <sizepolicy vsizetype="Fixed" hsizetype="Preferred" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip" > + <string>Saturation</string> + </property> + <property name="text" > + <string>S</string> + </property> + </widget> + <widget class="QFrame" name="frame_5" > + <property name="geometry" > + <rect> + <x>48</x> + <y>362</y> + <width>155</width> + <height>23</height> + </rect> + </property> + <property name="sizePolicy" > + <sizepolicy vsizetype="Preferred" hsizetype="Ignored" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="frameShape" > + <enum>QFrame::StyledPanel</enum> + </property> + <property name="frameShadow" > + <enum>QFrame::Raised</enum> + </property> + <layout class="QHBoxLayout" > + <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="QtColorLine" native="1" name="saturationColorLine" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Preferred" hsizetype="Expanding" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip" > + <string>Saturation</string> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QLabel" name="saturationLabel" > + <property name="geometry" > + <rect> + <x>209</x> + <y>364</y> + <width>64</width> + <height>18</height> + </rect> + </property> + <property name="sizePolicy" > + <sizepolicy vsizetype="Fixed" hsizetype="Preferred" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text" > + <string>Sat</string> + </property> + </widget> + <widget class="QLabel" name="vLabel" > + <property name="geometry" > + <rect> + <x>10</x> + <y>393</y> + <width>32</width> + <height>18</height> + </rect> + </property> + <property name="sizePolicy" > + <sizepolicy vsizetype="Fixed" hsizetype="Preferred" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip" > + <string>Value</string> + </property> + <property name="text" > + <string>V</string> + </property> + </widget> + <widget class="QFrame" name="frame_3" > + <property name="geometry" > + <rect> + <x>48</x> + <y>391</y> + <width>155</width> + <height>23</height> + </rect> + </property> + <property name="sizePolicy" > + <sizepolicy vsizetype="Preferred" hsizetype="Ignored" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="frameShape" > + <enum>QFrame::StyledPanel</enum> + </property> + <property name="frameShadow" > + <enum>QFrame::Raised</enum> + </property> + <layout class="QHBoxLayout" > + <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="QtColorLine" native="1" name="valueColorLine" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Preferred" hsizetype="Expanding" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip" > + <string>Value</string> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QLabel" name="valueLabel" > + <property name="geometry" > + <rect> + <x>209</x> + <y>393</y> + <width>64</width> + <height>18</height> + </rect> + </property> + <property name="sizePolicy" > + <sizepolicy vsizetype="Fixed" hsizetype="Preferred" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text" > + <string>Val</string> + </property> + </widget> + <widget class="QLabel" name="aLabel" > + <property name="geometry" > + <rect> + <x>10</x> + <y>422</y> + <width>32</width> + <height>18</height> + </rect> + </property> + <property name="sizePolicy" > + <sizepolicy vsizetype="Fixed" hsizetype="Preferred" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip" > + <string>Alpha</string> + </property> + <property name="text" > + <string>A</string> + </property> + </widget> + <widget class="QFrame" name="frame_4" > + <property name="geometry" > + <rect> + <x>48</x> + <y>420</y> + <width>155</width> + <height>23</height> + </rect> + </property> + <property name="sizePolicy" > + <sizepolicy vsizetype="Preferred" hsizetype="Ignored" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="frameShape" > + <enum>QFrame::StyledPanel</enum> + </property> + <property name="frameShadow" > + <enum>QFrame::Raised</enum> + </property> + <layout class="QHBoxLayout" > + <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="QtColorLine" native="1" name="alphaColorLine" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Preferred" hsizetype="Expanding" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip" > + <string>Alpha</string> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QLabel" name="alphaLabel" > + <property name="geometry" > + <rect> + <x>209</x> + <y>422</y> + <width>64</width> + <height>18</height> + </rect> + </property> + <property name="sizePolicy" > + <sizepolicy vsizetype="Fixed" hsizetype="Preferred" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text" > + <string>Alpha</string> + </property> + </widget> + <widget class="QComboBox" name="typeComboBox" > + <property name="geometry" > + <rect> + <x>10</x> + <y>40</y> + <width>79</width> + <height>22</height> + </rect> + </property> + <property name="toolTip" > + <string>Type</string> + </property> + </widget> + <widget class="QComboBox" name="spreadComboBox" > + <property name="geometry" > + <rect> + <x>96</x> + <y>40</y> + <width>72</width> + <height>22</height> + </rect> + </property> + <property name="toolTip" > + <string>Spread</string> + </property> + </widget> + <widget class="QLabel" name="colorLabel" > + <property name="geometry" > + <rect> + <x>10</x> + <y>298</y> + <width>32</width> + <height>29</height> + </rect> + </property> + <property name="sizePolicy" > + <sizepolicy vsizetype="Preferred" hsizetype="Fixed" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text" > + <string>Color</string> + </property> + </widget> + <widget class="QtColorButton" name="colorButton" > + <property name="geometry" > + <rect> + <x>48</x> + <y>300</y> + <width>26</width> + <height>25</height> + </rect> + </property> + <property name="toolTip" > + <string>Current stop's color</string> + </property> + <property name="text" > + <string/> + </property> + </widget> + <widget class="QRadioButton" name="hsvRadioButton" > + <property name="geometry" > + <rect> + <x>80</x> + <y>301</y> + <width>49</width> + <height>23</height> + </rect> + </property> + <property name="sizePolicy" > + <sizepolicy vsizetype="Fixed" hsizetype="Fixed" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip" > + <string>Show HSV specification</string> + </property> + <property name="text" > + <string>HSV</string> + </property> + <property name="checked" > + <bool>true</bool> + </property> + </widget> + <widget class="QRadioButton" name="rgbRadioButton" > + <property name="geometry" > + <rect> + <x>135</x> + <y>301</y> + <width>49</width> + <height>23</height> + </rect> + </property> + <property name="sizePolicy" > + <sizepolicy vsizetype="Fixed" hsizetype="Fixed" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip" > + <string>Show RGB specification</string> + </property> + <property name="text" > + <string>RGB</string> + </property> + </widget> + <widget class="QWidget" native="1" name="positionWidget" > + <property name="geometry" > + <rect> + <x>279</x> + <y>304</y> + <width>73</width> + <height>23</height> + </rect> + </property> + <layout class="QVBoxLayout" > + <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="QDoubleSpinBox" name="positionSpinBox" > + <property name="toolTip" > + <string>Current stop's position</string> + </property> + <property name="keyboardTracking" > + <bool>false</bool> + </property> + <property name="decimals" > + <number>3</number> + </property> + <property name="minimum" > + <double>0.000000000000000</double> + </property> + <property name="maximum" > + <double>1.000000000000000</double> + </property> + <property name="singleStep" > + <double>0.010000000000000</double> + </property> + <property name="value" > + <double>0.000000000000000</double> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" native="1" name="hueWidget" > + <property name="geometry" > + <rect> + <x>279</x> + <y>333</y> + <width>73</width> + <height>23</height> + </rect> + </property> + <layout class="QVBoxLayout" > + <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="QSpinBox" name="hueSpinBox" > + <property name="keyboardTracking" > + <bool>false</bool> + </property> + <property name="maximum" > + <number>359</number> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" native="1" name="saturationWidget" > + <property name="geometry" > + <rect> + <x>279</x> + <y>362</y> + <width>73</width> + <height>23</height> + </rect> + </property> + <layout class="QVBoxLayout" > + <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="QSpinBox" name="saturationSpinBox" > + <property name="keyboardTracking" > + <bool>false</bool> + </property> + <property name="maximum" > + <number>255</number> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" native="1" name="valueWidget" > + <property name="geometry" > + <rect> + <x>279</x> + <y>391</y> + <width>73</width> + <height>23</height> + </rect> + </property> + <layout class="QVBoxLayout" > + <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="QSpinBox" name="valueSpinBox" > + <property name="keyboardTracking" > + <bool>false</bool> + </property> + <property name="maximum" > + <number>255</number> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" native="1" name="alphaWidget" > + <property name="geometry" > + <rect> + <x>279</x> + <y>420</y> + <width>73</width> + <height>23</height> + </rect> + </property> + <layout class="QVBoxLayout" > + <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="QSpinBox" name="alphaSpinBox" > + <property name="keyboardTracking" > + <bool>false</bool> + </property> + <property name="maximum" > + <number>255</number> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" native="1" name="zoomWidget" > + <property name="geometry" > + <rect> + <x>279</x> + <y>231</y> + <width>73</width> + <height>23</height> + </rect> + </property> + <layout class="QVBoxLayout" > + <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="QSpinBox" name="zoomSpinBox" > + <property name="keyboardTracking" > + <bool>false</bool> + </property> + <property name="suffix" > + <string>%</string> + </property> + <property name="minimum" > + <number>100</number> + </property> + <property name="maximum" > + <number>10000</number> + </property> + <property name="singleStep" > + <number>100</number> + </property> + <property name="value" > + <number>100</number> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" native="1" name="line1Widget" > + <property name="geometry" > + <rect> + <x>209</x> + <y>219</y> + <width>143</width> + <height>16</height> + </rect> + </property> + <layout class="QVBoxLayout" > + <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="Line" name="line1" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" native="1" name="line2Widget" > + <property name="geometry" > + <rect> + <x>209</x> + <y>292</y> + <width>143</width> + <height>16</height> + </rect> + </property> + <layout class="QVBoxLayout" > + <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="Line" name="line2" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" native="1" name="zoomButtonsWidget" > + <property name="geometry" > + <rect> + <x>209</x> + <y>260</y> + <width>64</width> + <height>26</height> + </rect> + </property> + <property name="sizePolicy" > + <sizepolicy vsizetype="Preferred" hsizetype="Maximum" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <layout class="QHBoxLayout" > + <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="zoomInButton" > + <property name="toolTip" > + <string>Zoom In</string> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="zoomOutButton" > + <property name="toolTip" > + <string>Zoom Out</string> + </property> + </widget> + </item> + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" > + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <widget class="QToolButton" name="detailsButton" > + <property name="geometry" > + <rect> + <x>176</x> + <y>40</y> + <width>25</width> + <height>22</height> + </rect> + </property> + <property name="sizePolicy" > + <sizepolicy vsizetype="Ignored" hsizetype="Fixed" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip" > + <string>Toggle details extension</string> + </property> + <property name="text" > + <string>></string> + </property> + <property name="checkable" > + <bool>true</bool> + </property> + <property name="autoRaise" > + <bool>true</bool> + </property> + </widget> + <widget class="QToolButton" name="linearButton" > + <property name="geometry" > + <rect> + <x>10</x> + <y>10</y> + <width>30</width> + <height>26</height> + </rect> + </property> + <property name="toolTip" > + <string>Linear Type</string> + </property> + <property name="text" > + <string>...</string> + </property> + <property name="checkable" > + <bool>true</bool> + </property> + <property name="autoRaise" > + <bool>true</bool> + </property> + </widget> + <widget class="QToolButton" name="radialButton" > + <property name="geometry" > + <rect> + <x>40</x> + <y>10</y> + <width>30</width> + <height>26</height> + </rect> + </property> + <property name="toolTip" > + <string>Radial Type</string> + </property> + <property name="text" > + <string>...</string> + </property> + <property name="checkable" > + <bool>true</bool> + </property> + <property name="autoRaise" > + <bool>true</bool> + </property> + </widget> + <widget class="QToolButton" name="conicalButton" > + <property name="geometry" > + <rect> + <x>70</x> + <y>10</y> + <width>30</width> + <height>26</height> + </rect> + </property> + <property name="toolTip" > + <string>Conical Type</string> + </property> + <property name="text" > + <string>...</string> + </property> + <property name="checkable" > + <bool>true</bool> + </property> + <property name="autoRaise" > + <bool>true</bool> + </property> + </widget> + <widget class="QToolButton" name="padButton" > + <property name="geometry" > + <rect> + <x>110</x> + <y>10</y> + <width>30</width> + <height>26</height> + </rect> + </property> + <property name="toolTip" > + <string>Pad Spread</string> + </property> + <property name="text" > + <string>...</string> + </property> + <property name="checkable" > + <bool>true</bool> + </property> + <property name="autoRaise" > + <bool>true</bool> + </property> + </widget> + <widget class="QToolButton" name="repeatButton" > + <property name="geometry" > + <rect> + <x>140</x> + <y>10</y> + <width>30</width> + <height>26</height> + </rect> + </property> + <property name="toolTip" > + <string>Repeat Spread</string> + </property> + <property name="text" > + <string>...</string> + </property> + <property name="checkable" > + <bool>true</bool> + </property> + <property name="autoRaise" > + <bool>true</bool> + </property> + </widget> + <widget class="QToolButton" name="reflectButton" > + <property name="geometry" > + <rect> + <x>170</x> + <y>10</y> + <width>30</width> + <height>26</height> + </rect> + </property> + <property name="toolTip" > + <string>Reflect Spread</string> + </property> + <property name="text" > + <string>...</string> + </property> + <property name="checkable" > + <bool>true</bool> + </property> + <property name="autoRaise" > + <bool>true</bool> + </property> + </widget> + </widget> + <customwidgets> + <customwidget> + <class>QtColorButton</class> + <extends>QToolButton</extends> + <header>qtcolorbutton.h</header> + </customwidget> + <customwidget> + <class>QtColorLine</class> + <extends>QWidget</extends> + <header>qtcolorline.h</header> + <container>1</container> + </customwidget> + <customwidget> + <class>QtGradientStopsWidget</class> + <extends>QWidget</extends> + <header>qtgradientstopswidget.h</header> + <container>1</container> + </customwidget> + <customwidget> + <class>QtGradientWidget</class> + <extends>QWidget</extends> + <header>qtgradientwidget.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <tabstops> + <tabstop>typeComboBox</tabstop> + <tabstop>spreadComboBox</tabstop> + <tabstop>detailsButton</tabstop> + <tabstop>spinBox1</tabstop> + <tabstop>spinBox2</tabstop> + <tabstop>spinBox3</tabstop> + <tabstop>spinBox4</tabstop> + <tabstop>spinBox5</tabstop> + <tabstop>zoomSpinBox</tabstop> + <tabstop>zoomInButton</tabstop> + <tabstop>zoomOutButton</tabstop> + <tabstop>zoomAllButton</tabstop> + <tabstop>colorButton</tabstop> + <tabstop>hsvRadioButton</tabstop> + <tabstop>rgbRadioButton</tabstop> + <tabstop>positionSpinBox</tabstop> + <tabstop>hueSpinBox</tabstop> + <tabstop>saturationSpinBox</tabstop> + <tabstop>valueSpinBox</tabstop> + <tabstop>alphaSpinBox</tabstop> + </tabstops> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientmanager.cpp b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientmanager.cpp new file mode 100644 index 00000000000..10c10c73917 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientmanager.cpp @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtgradientmanager.h" +#include <QtGui/QPixmap> +#include <QtCore/QMetaEnum> + +QT_BEGIN_NAMESPACE + +QtGradientManager::QtGradientManager(QObject *parent) + : QObject(parent) +{ +} + +QMap<QString, QGradient> QtGradientManager::gradients() const +{ + return m_idToGradient; +} + +QString QtGradientManager::uniqueId(const QString &id) const +{ + if (!m_idToGradient.contains(id)) + return id; + + QString base = id; + while (base.count() > 0 && base.at(base.count() - 1).isDigit()) + base = base.left(base.count() - 1); + QString newId = base; + int counter = 0; + while (m_idToGradient.contains(newId)) { + ++counter; + newId = base + QString::number(counter); + } + return newId; +} + +QString QtGradientManager::addGradient(const QString &id, const QGradient &gradient) +{ + QString newId = uniqueId(id); + + m_idToGradient[newId] = gradient; + + emit gradientAdded(newId, gradient); + + return newId; +} + +void QtGradientManager::removeGradient(const QString &id) +{ + if (!m_idToGradient.contains(id)) + return; + + emit gradientRemoved(id); + + m_idToGradient.remove(id); +} + +void QtGradientManager::renameGradient(const QString &id, const QString &newId) +{ + if (!m_idToGradient.contains(id)) + return; + + if (newId == id) + return; + + QString changedId = uniqueId(newId); + QGradient gradient = m_idToGradient.value(id); + + emit gradientRenamed(id, changedId); + + m_idToGradient.remove(id); + m_idToGradient[changedId] = gradient; +} + +void QtGradientManager::changeGradient(const QString &id, const QGradient &newGradient) +{ + if (!m_idToGradient.contains(id)) + return; + + if (m_idToGradient.value(id) == newGradient) + return; + + emit gradientChanged(id, newGradient); + + m_idToGradient[id] = newGradient; +} + +void QtGradientManager::clear() +{ + QMap<QString, QGradient> grads = gradients(); + QMapIterator<QString, QGradient> itGrad(grads); + while (itGrad.hasNext()) { + removeGradient(itGrad.next().key()); + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientmanager.h b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientmanager.h new file mode 100644 index 00000000000..b14e7322785 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientmanager.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef GRADIENTMANAGER_H +#define GRADIENTMANAGER_H + +#include <QtCore/QObject> +#include <QtCore/QMap> +#include <QtCore/QSize> +#include <QtXml/QDomDocument> +#include <QtXml/QDomElement> +#include <QtGui/QGradient> + +QT_BEGIN_NAMESPACE + +class QGradient; +class QPixmap; +class QColor; + +class QtGradientManager : public QObject +{ + Q_OBJECT +public: + QtGradientManager(QObject *parent = 0); + + QMap<QString, QGradient> gradients() const; + + QString uniqueId(const QString &id) const; + +public slots: + + QString addGradient(const QString &id, const QGradient &gradient); + void renameGradient(const QString &id, const QString &newId); + void changeGradient(const QString &id, const QGradient &newGradient); + void removeGradient(const QString &id); + + //utils + void clear(); + +signals: + + void gradientAdded(const QString &id, const QGradient &gradient); + void gradientRenamed(const QString &id, const QString &newId); + void gradientChanged(const QString &id, const QGradient &newGradient); + void gradientRemoved(const QString &id); + +private: + + QMap<QString, QGradient> m_idToGradient; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientstopscontroller.cpp b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientstopscontroller.cpp new file mode 100644 index 00000000000..b2765b5a57c --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientstopscontroller.cpp @@ -0,0 +1,724 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtgradientstopscontroller.h" +#include "ui_qtgradienteditor.h" +#include "qtgradientstopsmodel.h" + +#include <QtCore/QTimer> + +QT_BEGIN_NAMESPACE + +class QtGradientStopsControllerPrivate +{ + QtGradientStopsController *q_ptr; + Q_DECLARE_PUBLIC(QtGradientStopsController) +public: + typedef QMap<qreal, QColor> PositionColorMap; + typedef QMap<qreal, QtGradientStop *> PositionStopMap; + + void slotHsvClicked(); + void slotRgbClicked(); + + void slotCurrentStopChanged(QtGradientStop *stop); + void slotStopMoved(QtGradientStop *stop, qreal newPos); + void slotStopsSwapped(QtGradientStop *stop1, QtGradientStop *stop2); + void slotStopChanged(QtGradientStop *stop, const QColor &newColor); + void slotStopSelected(QtGradientStop *stop, bool selected); + void slotStopAdded(QtGradientStop *stop); + void slotStopRemoved(QtGradientStop *stop); + void slotUpdatePositionSpinBox(); + + void slotChangeColor(const QColor &color); + void slotChangeHue(const QColor &color); + void slotChangeSaturation(const QColor &color); + void slotChangeValue(const QColor &color); + void slotChangeAlpha(const QColor &color); + void slotChangeHue(int color); + void slotChangeSaturation(int color); + void slotChangeValue(int color); + void slotChangeAlpha(int color); + void slotChangePosition(double value); + + void slotChangeZoom(int value); + void slotZoomIn(); + void slotZoomOut(); + void slotZoomAll(); + void slotZoomChanged(double zoom); + + void enableCurrent(bool enable); + void setColorSpinBoxes(const QColor &color); + PositionColorMap stopsData(const PositionStopMap &stops) const; + QGradientStops makeGradientStops(const PositionColorMap &data) const; + void updateZoom(double zoom); + + QtGradientStopsModel *m_model; + QColor::Spec m_spec; + + Ui::QtGradientEditor *m_ui; +}; + +void QtGradientStopsControllerPrivate::enableCurrent(bool enable) +{ + m_ui->positionLabel->setEnabled(enable); + m_ui->colorLabel->setEnabled(enable); + m_ui->hLabel->setEnabled(enable); + m_ui->sLabel->setEnabled(enable); + m_ui->vLabel->setEnabled(enable); + m_ui->aLabel->setEnabled(enable); + m_ui->hueLabel->setEnabled(enable); + m_ui->saturationLabel->setEnabled(enable); + m_ui->valueLabel->setEnabled(enable); + m_ui->alphaLabel->setEnabled(enable); + + m_ui->positionSpinBox->setEnabled(enable); + m_ui->colorButton->setEnabled(enable); + + m_ui->hueColorLine->setEnabled(enable); + m_ui->saturationColorLine->setEnabled(enable); + m_ui->valueColorLine->setEnabled(enable); + m_ui->alphaColorLine->setEnabled(enable); + + m_ui->hueSpinBox->setEnabled(enable); + m_ui->saturationSpinBox->setEnabled(enable); + m_ui->valueSpinBox->setEnabled(enable); + m_ui->alphaSpinBox->setEnabled(enable); +} + +QtGradientStopsControllerPrivate::PositionColorMap QtGradientStopsControllerPrivate::stopsData(const PositionStopMap &stops) const +{ + PositionColorMap data; + PositionStopMap::ConstIterator itStop = stops.constBegin(); + while (itStop != stops.constEnd()) { + QtGradientStop *stop = itStop.value(); + data[stop->position()] = stop->color(); + + ++itStop; + } + return data; +} + +QGradientStops QtGradientStopsControllerPrivate::makeGradientStops(const PositionColorMap &data) const +{ + QGradientStops stops; + PositionColorMap::ConstIterator itData = data.constBegin(); + while (itData != data.constEnd()) { + stops << QPair<qreal, QColor>(itData.key(), itData.value()); + + ++itData; + } + return stops; +} + +void QtGradientStopsControllerPrivate::updateZoom(double zoom) +{ + m_ui->gradientStopsWidget->setZoom(zoom); + m_ui->zoomSpinBox->blockSignals(true); + m_ui->zoomSpinBox->setValue(qRound(zoom * 100)); + m_ui->zoomSpinBox->blockSignals(false); + bool zoomInEnabled = true; + bool zoomOutEnabled = true; + bool zoomAllEnabled = true; + if (zoom <= 1) { + zoomAllEnabled = false; + zoomOutEnabled = false; + } else if (zoom >= 100) { + zoomInEnabled = false; + } + m_ui->zoomInButton->setEnabled(zoomInEnabled); + m_ui->zoomOutButton->setEnabled(zoomOutEnabled); + m_ui->zoomAllButton->setEnabled(zoomAllEnabled); +} + +void QtGradientStopsControllerPrivate::slotHsvClicked() +{ + QString h = QApplication::translate("qdesigner_internal::QtGradientStopsController", "H", 0, QApplication::UnicodeUTF8); + QString s = QApplication::translate("qdesigner_internal::QtGradientStopsController", "S", 0, QApplication::UnicodeUTF8); + QString v = QApplication::translate("qdesigner_internal::QtGradientStopsController", "V", 0, QApplication::UnicodeUTF8); + + m_ui->hLabel->setText(h); + m_ui->sLabel->setText(s); + m_ui->vLabel->setText(v); + + h = QApplication::translate("qdesigner_internal::QtGradientStopsController", "Hue", 0, QApplication::UnicodeUTF8); + s = QApplication::translate("qdesigner_internal::QtGradientStopsController", "Sat", 0, QApplication::UnicodeUTF8); + v = QApplication::translate("qdesigner_internal::QtGradientStopsController", "Val", 0, QApplication::UnicodeUTF8); + + const QString hue = QApplication::translate("qdesigner_internal::QtGradientStopsController", "Hue", 0, QApplication::UnicodeUTF8); + const QString saturation = QApplication::translate("qdesigner_internal::QtGradientStopsController", "Saturation", 0, QApplication::UnicodeUTF8); + const QString value = QApplication::translate("qdesigner_internal::QtGradientStopsController", "Value", 0, QApplication::UnicodeUTF8); + + m_ui->hLabel->setToolTip(hue); + m_ui->hueLabel->setText(h); + m_ui->hueColorLine->setToolTip(hue); + m_ui->hueColorLine->setColorComponent(QtColorLine::Hue); + + m_ui->sLabel->setToolTip(saturation); + m_ui->saturationLabel->setText(s); + m_ui->saturationColorLine->setToolTip(saturation); + m_ui->saturationColorLine->setColorComponent(QtColorLine::Saturation); + + m_ui->vLabel->setToolTip(value); + m_ui->valueLabel->setText(v); + m_ui->valueColorLine->setToolTip(value); + m_ui->valueColorLine->setColorComponent(QtColorLine::Value); + + setColorSpinBoxes(m_ui->colorButton->color()); +} + +void QtGradientStopsControllerPrivate::slotRgbClicked() +{ + QString r = QApplication::translate("qdesigner_internal::QtGradientStopsController", "R", 0, QApplication::UnicodeUTF8); + QString g = QApplication::translate("qdesigner_internal::QtGradientStopsController", "G", 0, QApplication::UnicodeUTF8); + QString b = QApplication::translate("qdesigner_internal::QtGradientStopsController", "B", 0, QApplication::UnicodeUTF8); + + m_ui->hLabel->setText(r); + m_ui->sLabel->setText(g); + m_ui->vLabel->setText(b); + + QString red = QApplication::translate("qdesigner_internal::QtGradientStopsController", "Red", 0, QApplication::UnicodeUTF8); + QString green = QApplication::translate("qdesigner_internal::QtGradientStopsController", "Green", 0, QApplication::UnicodeUTF8); + QString blue = QApplication::translate("qdesigner_internal::QtGradientStopsController", "Blue", 0, QApplication::UnicodeUTF8); + + m_ui->hLabel->setToolTip(red); + m_ui->hueLabel->setText(red); + m_ui->hueColorLine->setToolTip(red); + m_ui->hueColorLine->setColorComponent(QtColorLine::Red); + + m_ui->sLabel->setToolTip(green); + m_ui->saturationLabel->setText(green); + m_ui->saturationColorLine->setToolTip(green); + m_ui->saturationColorLine->setColorComponent(QtColorLine::Green); + + m_ui->vLabel->setToolTip(blue); + m_ui->valueLabel->setText(blue); + m_ui->valueColorLine->setToolTip(blue); + m_ui->valueColorLine->setColorComponent(QtColorLine::Blue); + + setColorSpinBoxes(m_ui->colorButton->color()); +} + +void QtGradientStopsControllerPrivate::setColorSpinBoxes(const QColor &color) +{ + m_ui->hueSpinBox->blockSignals(true); + m_ui->saturationSpinBox->blockSignals(true); + m_ui->valueSpinBox->blockSignals(true); + m_ui->alphaSpinBox->blockSignals(true); + if (m_ui->hsvRadioButton->isChecked()) { + if (m_ui->hueSpinBox->maximum() != 359) + m_ui->hueSpinBox->setMaximum(359); + if (m_ui->hueSpinBox->value() != color.hue()) + m_ui->hueSpinBox->setValue(color.hue()); + if (m_ui->saturationSpinBox->value() != color.saturation()) + m_ui->saturationSpinBox->setValue(color.saturation()); + if (m_ui->valueSpinBox->value() != color.value()) + m_ui->valueSpinBox->setValue(color.value()); + } else { + if (m_ui->hueSpinBox->maximum() != 255) + m_ui->hueSpinBox->setMaximum(255); + if (m_ui->hueSpinBox->value() != color.red()) + m_ui->hueSpinBox->setValue(color.red()); + if (m_ui->saturationSpinBox->value() != color.green()) + m_ui->saturationSpinBox->setValue(color.green()); + if (m_ui->valueSpinBox->value() != color.blue()) + m_ui->valueSpinBox->setValue(color.blue()); + } + m_ui->alphaSpinBox->setValue(color.alpha()); + m_ui->hueSpinBox->blockSignals(false); + m_ui->saturationSpinBox->blockSignals(false); + m_ui->valueSpinBox->blockSignals(false); + m_ui->alphaSpinBox->blockSignals(false); +} + +void QtGradientStopsControllerPrivate::slotCurrentStopChanged(QtGradientStop *stop) +{ + if (!stop) { + enableCurrent(false); + return; + } + enableCurrent(true); + + QTimer::singleShot(0, q_ptr, SLOT(slotUpdatePositionSpinBox())); + + m_ui->colorButton->setColor(stop->color()); + m_ui->hueColorLine->setColor(stop->color()); + m_ui->saturationColorLine->setColor(stop->color()); + m_ui->valueColorLine->setColor(stop->color()); + m_ui->alphaColorLine->setColor(stop->color()); + setColorSpinBoxes(stop->color()); +} + +void QtGradientStopsControllerPrivate::slotStopMoved(QtGradientStop *stop, qreal newPos) +{ + QTimer::singleShot(0, q_ptr, SLOT(slotUpdatePositionSpinBox())); + + PositionColorMap stops = stopsData(m_model->stops()); + stops.remove(stop->position()); + stops[newPos] = stop->color(); + + QGradientStops gradStops = makeGradientStops(stops); + emit q_ptr->gradientStopsChanged(gradStops); +} + +void QtGradientStopsControllerPrivate::slotStopsSwapped(QtGradientStop *stop1, QtGradientStop *stop2) +{ + QTimer::singleShot(0, q_ptr, SLOT(slotUpdatePositionSpinBox())); + + PositionColorMap stops = stopsData(m_model->stops()); + const qreal pos1 = stop1->position(); + const qreal pos2 = stop2->position(); + stops[pos1] = stop2->color(); + stops[pos2] = stop1->color(); + + QGradientStops gradStops = makeGradientStops(stops); + emit q_ptr->gradientStopsChanged(gradStops); +} + +void QtGradientStopsControllerPrivate::slotStopAdded(QtGradientStop *stop) +{ + PositionColorMap stops = stopsData(m_model->stops()); + stops[stop->position()] = stop->color(); + + QGradientStops gradStops = makeGradientStops(stops); + emit q_ptr->gradientStopsChanged(gradStops); +} + +void QtGradientStopsControllerPrivate::slotStopRemoved(QtGradientStop *stop) +{ + PositionColorMap stops = stopsData(m_model->stops()); + stops.remove(stop->position()); + + QGradientStops gradStops = makeGradientStops(stops); + emit q_ptr->gradientStopsChanged(gradStops); +} + +void QtGradientStopsControllerPrivate::slotStopChanged(QtGradientStop *stop, const QColor &newColor) +{ + if (m_model->currentStop() == stop) { + m_ui->colorButton->setColor(newColor); + m_ui->hueColorLine->setColor(newColor); + m_ui->saturationColorLine->setColor(newColor); + m_ui->valueColorLine->setColor(newColor); + m_ui->alphaColorLine->setColor(newColor); + setColorSpinBoxes(newColor); + } + + PositionColorMap stops = stopsData(m_model->stops()); + stops[stop->position()] = newColor; + + QGradientStops gradStops = makeGradientStops(stops); + emit q_ptr->gradientStopsChanged(gradStops); +} + +void QtGradientStopsControllerPrivate::slotStopSelected(QtGradientStop *stop, bool selected) +{ + Q_UNUSED(stop) + Q_UNUSED(selected) + QTimer::singleShot(0, q_ptr, SLOT(slotUpdatePositionSpinBox())); +} + +void QtGradientStopsControllerPrivate::slotUpdatePositionSpinBox() +{ + QtGradientStop *current = m_model->currentStop(); + if (!current) + return; + + qreal min = 0.0; + qreal max = 1.0; + const qreal pos = current->position(); + + QtGradientStop *first = m_model->firstSelected(); + QtGradientStop *last = m_model->lastSelected(); + + if (first && last) { + const qreal minPos = pos - first->position() - 0.0004999; + const qreal maxPos = pos + 1.0 - last->position() + 0.0004999; + + if (max > maxPos) + max = maxPos; + if (min < minPos) + min = minPos; + + if (first->position() == 0.0) + min = pos; + if (last->position() == 1.0) + max = pos; + } + + const int spinMin = qRound(m_ui->positionSpinBox->minimum() * 1000); + const int spinMax = qRound(m_ui->positionSpinBox->maximum() * 1000); + + const int newMin = qRound(min * 1000); + const int newMax = qRound(max * 1000); + + m_ui->positionSpinBox->blockSignals(true); + if (spinMin != newMin || spinMax != newMax) { + m_ui->positionSpinBox->setRange((double)newMin / 1000, (double)newMax / 1000); + } + if (m_ui->positionSpinBox->value() != pos) + m_ui->positionSpinBox->setValue(pos); + m_ui->positionSpinBox->blockSignals(false); +} + +void QtGradientStopsControllerPrivate::slotChangeColor(const QColor &color) +{ + QtGradientStop *stop = m_model->currentStop(); + if (!stop) + return; + m_model->changeStop(stop, color); + QList<QtGradientStop *> stops = m_model->selectedStops(); + QListIterator<QtGradientStop *> itStop(stops); + while (itStop.hasNext()) { + QtGradientStop *s = itStop.next(); + if (s != stop) + m_model->changeStop(s, color); + } +} + +void QtGradientStopsControllerPrivate::slotChangeHue(const QColor &color) +{ + QtGradientStop *stop = m_model->currentStop(); + if (!stop) + return; + m_model->changeStop(stop, color); + QList<QtGradientStop *> stops = m_model->selectedStops(); + QListIterator<QtGradientStop *> itStop(stops); + while (itStop.hasNext()) { + QtGradientStop *s = itStop.next(); + if (s != stop) { + QColor c = s->color(); + if (m_ui->hsvRadioButton->isChecked()) + c.setHsvF(color.hueF(), c.saturationF(), c.valueF(), c.alphaF()); + else + c.setRgbF(color.redF(), c.greenF(), c.blueF(), c.alphaF()); + m_model->changeStop(s, c); + } + } +} + +void QtGradientStopsControllerPrivate::slotChangeHue(int color) +{ + QColor c = m_ui->hueColorLine->color(); + if (m_ui->hsvRadioButton->isChecked()) + c.setHsvF((qreal)color / 360.0, c.saturationF(), c.valueF(), c.alphaF()); + else + c.setRed(color); + slotChangeHue(c); +} + +void QtGradientStopsControllerPrivate::slotChangeSaturation(const QColor &color) +{ + QtGradientStop *stop = m_model->currentStop(); + if (!stop) + return; + m_model->changeStop(stop, color); + QList<QtGradientStop *> stops = m_model->selectedStops(); + QListIterator<QtGradientStop *> itStop(stops); + while (itStop.hasNext()) { + QtGradientStop *s = itStop.next(); + if (s != stop) { + QColor c = s->color(); + if (m_ui->hsvRadioButton->isChecked()) { + c.setHsvF(c.hueF(), color.saturationF(), c.valueF(), c.alphaF()); + int hue = c.hue(); + if (hue == 360 || hue == -1) + c.setHsvF(0.0, c.saturationF(), c.valueF(), c.alphaF()); + } else { + c.setRgbF(c.redF(), color.greenF(), c.blueF(), c.alphaF()); + } + m_model->changeStop(s, c); + } + } +} + +void QtGradientStopsControllerPrivate::slotChangeSaturation(int color) +{ + QColor c = m_ui->saturationColorLine->color(); + if (m_ui->hsvRadioButton->isChecked()) + c.setHsvF(c.hueF(), (qreal)color / 255, c.valueF(), c.alphaF()); + else + c.setGreen(color); + slotChangeSaturation(c); +} + +void QtGradientStopsControllerPrivate::slotChangeValue(const QColor &color) +{ + QtGradientStop *stop = m_model->currentStop(); + if (!stop) + return; + m_model->changeStop(stop, color); + QList<QtGradientStop *> stops = m_model->selectedStops(); + QListIterator<QtGradientStop *> itStop(stops); + while (itStop.hasNext()) { + QtGradientStop *s = itStop.next(); + if (s != stop) { + QColor c = s->color(); + if (m_ui->hsvRadioButton->isChecked()) { + c.setHsvF(c.hueF(), c.saturationF(), color.valueF(), c.alphaF()); + int hue = c.hue(); + if (hue == 360 || hue == -1) + c.setHsvF(0.0, c.saturationF(), c.valueF(), c.alphaF()); + } else { + c.setRgbF(c.redF(), c.greenF(), color.blueF(), c.alphaF()); + } + m_model->changeStop(s, c); + } + } +} + +void QtGradientStopsControllerPrivate::slotChangeValue(int color) +{ + QColor c = m_ui->valueColorLine->color(); + if (m_ui->hsvRadioButton->isChecked()) + c.setHsvF(c.hueF(), c.saturationF(), (qreal)color / 255, c.alphaF()); + else + c.setBlue(color); + slotChangeValue(c); +} + +void QtGradientStopsControllerPrivate::slotChangeAlpha(const QColor &color) +{ + QtGradientStop *stop = m_model->currentStop(); + if (!stop) + return; + m_model->changeStop(stop, color); + QList<QtGradientStop *> stops = m_model->selectedStops(); + QListIterator<QtGradientStop *> itStop(stops); + while (itStop.hasNext()) { + QtGradientStop *s = itStop.next(); + if (s != stop) { + QColor c = s->color(); + if (m_ui->hsvRadioButton->isChecked()) { + c.setHsvF(c.hueF(), c.saturationF(), c.valueF(), color.alphaF()); + int hue = c.hue(); + if (hue == 360 || hue == -1) + c.setHsvF(0.0, c.saturationF(), c.valueF(), c.alphaF()); + } else { + c.setRgbF(c.redF(), c.greenF(), c.blueF(), color.alphaF()); + } + m_model->changeStop(s, c); + } + } +} + +void QtGradientStopsControllerPrivate::slotChangeAlpha(int color) +{ + QColor c = m_ui->alphaColorLine->color(); + if (m_ui->hsvRadioButton->isChecked()) + c.setHsvF(c.hueF(), c.saturationF(), c.valueF(), (qreal)color / 255); + else + c.setAlpha(color); + slotChangeAlpha(c); +} + +void QtGradientStopsControllerPrivate::slotChangePosition(double value) +{ + QtGradientStop *stop = m_model->currentStop(); + if (!stop) + return; + + m_model->moveStops(value); +} + +void QtGradientStopsControllerPrivate::slotChangeZoom(int value) +{ + updateZoom(value / 100.0); +} + +void QtGradientStopsControllerPrivate::slotZoomIn() +{ + double newZoom = m_ui->gradientStopsWidget->zoom() * 2; + if (newZoom > 100) + newZoom = 100; + updateZoom(newZoom); +} + +void QtGradientStopsControllerPrivate::slotZoomOut() +{ + double newZoom = m_ui->gradientStopsWidget->zoom() / 2; + if (newZoom < 1) + newZoom = 1; + updateZoom(newZoom); +} + +void QtGradientStopsControllerPrivate::slotZoomAll() +{ + updateZoom(1); +} + +void QtGradientStopsControllerPrivate::slotZoomChanged(double zoom) +{ + updateZoom(zoom); +} + +QtGradientStopsController::QtGradientStopsController(QObject *parent) + : QObject(parent), d_ptr(new QtGradientStopsControllerPrivate()) +{ + d_ptr->q_ptr = this; + + d_ptr->m_spec = QColor::Hsv; +} + +void QtGradientStopsController::setUi(Ui::QtGradientEditor *ui) +{ + d_ptr->m_ui = ui; + + d_ptr->m_ui->hueColorLine->setColorComponent(QtColorLine::Hue); + d_ptr->m_ui->saturationColorLine->setColorComponent(QtColorLine::Saturation); + d_ptr->m_ui->valueColorLine->setColorComponent(QtColorLine::Value); + d_ptr->m_ui->alphaColorLine->setColorComponent(QtColorLine::Alpha); + + d_ptr->m_model = new QtGradientStopsModel(this); + d_ptr->m_ui->gradientStopsWidget->setGradientStopsModel(d_ptr->m_model); + connect(d_ptr->m_model, SIGNAL(currentStopChanged(QtGradientStop*)), + this, SLOT(slotCurrentStopChanged(QtGradientStop*))); + connect(d_ptr->m_model, SIGNAL(stopMoved(QtGradientStop*,qreal)), + this, SLOT(slotStopMoved(QtGradientStop*,qreal))); + connect(d_ptr->m_model, SIGNAL(stopsSwapped(QtGradientStop*,QtGradientStop*)), + this, SLOT(slotStopsSwapped(QtGradientStop*,QtGradientStop*))); + connect(d_ptr->m_model, SIGNAL(stopChanged(QtGradientStop*,QColor)), + this, SLOT(slotStopChanged(QtGradientStop*,QColor))); + connect(d_ptr->m_model, SIGNAL(stopSelected(QtGradientStop*,bool)), + this, SLOT(slotStopSelected(QtGradientStop*,bool))); + connect(d_ptr->m_model, SIGNAL(stopAdded(QtGradientStop*)), + this, SLOT(slotStopAdded(QtGradientStop*))); + connect(d_ptr->m_model, SIGNAL(stopRemoved(QtGradientStop*)), + this, SLOT(slotStopRemoved(QtGradientStop*))); + + connect(d_ptr->m_ui->hueColorLine, SIGNAL(colorChanged(QColor)), + this, SLOT(slotChangeHue(QColor))); + connect(d_ptr->m_ui->saturationColorLine, SIGNAL(colorChanged(QColor)), + this, SLOT(slotChangeSaturation(QColor))); + connect(d_ptr->m_ui->valueColorLine, SIGNAL(colorChanged(QColor)), + this, SLOT(slotChangeValue(QColor))); + connect(d_ptr->m_ui->alphaColorLine, SIGNAL(colorChanged(QColor)), + this, SLOT(slotChangeAlpha(QColor))); + connect(d_ptr->m_ui->colorButton, SIGNAL(colorChanged(QColor)), + this, SLOT(slotChangeColor(QColor))); + + connect(d_ptr->m_ui->hueSpinBox, SIGNAL(valueChanged(int)), + this, SLOT(slotChangeHue(int))); + connect(d_ptr->m_ui->saturationSpinBox, SIGNAL(valueChanged(int)), + this, SLOT(slotChangeSaturation(int))); + connect(d_ptr->m_ui->valueSpinBox, SIGNAL(valueChanged(int)), + this, SLOT(slotChangeValue(int))); + connect(d_ptr->m_ui->alphaSpinBox, SIGNAL(valueChanged(int)), + this, SLOT(slotChangeAlpha(int))); + + connect(d_ptr->m_ui->positionSpinBox, SIGNAL(valueChanged(double)), + this, SLOT(slotChangePosition(double))); + + connect(d_ptr->m_ui->zoomSpinBox, SIGNAL(valueChanged(int)), + this, SLOT(slotChangeZoom(int))); + connect(d_ptr->m_ui->zoomInButton, SIGNAL(clicked()), + this, SLOT(slotZoomIn())); + connect(d_ptr->m_ui->zoomOutButton, SIGNAL(clicked()), + this, SLOT(slotZoomOut())); + connect(d_ptr->m_ui->zoomAllButton, SIGNAL(clicked()), + this, SLOT(slotZoomAll())); + connect(d_ptr->m_ui->gradientStopsWidget, SIGNAL(zoomChanged(double)), + this, SLOT(slotZoomChanged(double))); + + connect(d_ptr->m_ui->hsvRadioButton, SIGNAL(clicked()), + this, SLOT(slotHsvClicked())); + connect(d_ptr->m_ui->rgbRadioButton, SIGNAL(clicked()), + this, SLOT(slotRgbClicked())); + + d_ptr->enableCurrent(false); + d_ptr->m_ui->zoomInButton->setIcon(QIcon(QLatin1String(":/trolltech/qtgradienteditor/images/zoomin.png"))); + d_ptr->m_ui->zoomOutButton->setIcon(QIcon(QLatin1String(":/trolltech/qtgradienteditor/images/zoomout.png"))); + d_ptr->updateZoom(1); +} + +QtGradientStopsController::~QtGradientStopsController() +{ +} + +void QtGradientStopsController::setGradientStops(const QGradientStops &stops) +{ + d_ptr->m_model->clear(); + QVectorIterator<QPair<qreal, QColor> > it(stops); + QtGradientStop *first = 0; + while (it.hasNext()) { + QPair<qreal, QColor> pair = it.next(); + QtGradientStop *stop = d_ptr->m_model->addStop(pair.first, pair.second); + if (!first) + first = stop; + } + if (first) + d_ptr->m_model->setCurrentStop(first); +} + +QGradientStops QtGradientStopsController::gradientStops() const +{ + QGradientStops stops; + QList<QtGradientStop *> stopsList = d_ptr->m_model->stops().values(); + QListIterator<QtGradientStop *> itStop(stopsList); + while (itStop.hasNext()) { + QtGradientStop *stop = itStop.next(); + stops << QPair<qreal, QColor>(stop->position(), stop->color()); + } + return stops; +} + +QColor::Spec QtGradientStopsController::spec() const +{ + return d_ptr->m_spec; +} + +void QtGradientStopsController::setSpec(QColor::Spec spec) +{ + if (d_ptr->m_spec == spec) + return; + + d_ptr->m_spec = spec; + if (d_ptr->m_spec == QColor::Rgb) { + d_ptr->m_ui->rgbRadioButton->setChecked(true); + d_ptr->slotRgbClicked(); + } else { + d_ptr->m_ui->hsvRadioButton->setChecked(true); + d_ptr->slotHsvClicked(); + } +} + +QT_END_NAMESPACE + +#include "moc_qtgradientstopscontroller.cpp" diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientstopscontroller.h b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientstopscontroller.h new file mode 100644 index 00000000000..ca818f83e82 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientstopscontroller.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTGRADIENTSTOPSCONTROLLER_H +#define QTGRADIENTSTOPSCONTROLLER_H + +#include <QtGui/QWidget> + +QT_BEGIN_NAMESPACE + +namespace Ui { + class QtGradientEditor; +} + +class QtGradientStopsController : public QObject +{ + Q_OBJECT +public: + QtGradientStopsController(QObject *parent = 0); + ~QtGradientStopsController(); + + void setUi(Ui::QtGradientEditor *editor); + + void setGradientStops(const QGradientStops &stops); + QGradientStops gradientStops() const; + + QColor::Spec spec() const; + void setSpec(QColor::Spec spec); + +signals: + + void gradientStopsChanged(const QGradientStops &stops); + +private: + QScopedPointer<class QtGradientStopsControllerPrivate> d_ptr; + Q_DECLARE_PRIVATE(QtGradientStopsController) + Q_DISABLE_COPY(QtGradientStopsController) + Q_PRIVATE_SLOT(d_func(), void slotHsvClicked()) + Q_PRIVATE_SLOT(d_func(), void slotRgbClicked()) + Q_PRIVATE_SLOT(d_func(), void slotCurrentStopChanged(QtGradientStop *stop)) + Q_PRIVATE_SLOT(d_func(), void slotStopMoved(QtGradientStop *stop, qreal newPos)) + Q_PRIVATE_SLOT(d_func(), void slotStopsSwapped(QtGradientStop *stop1, QtGradientStop *stop2)) + Q_PRIVATE_SLOT(d_func(), void slotStopChanged(QtGradientStop *stop, const QColor &newColor)) + Q_PRIVATE_SLOT(d_func(), void slotStopSelected(QtGradientStop *stop, bool selected)) + Q_PRIVATE_SLOT(d_func(), void slotStopAdded(QtGradientStop *stop)) + Q_PRIVATE_SLOT(d_func(), void slotStopRemoved(QtGradientStop *stop)) + Q_PRIVATE_SLOT(d_func(), void slotUpdatePositionSpinBox()) + Q_PRIVATE_SLOT(d_func(), void slotChangeColor(const QColor &color)) + Q_PRIVATE_SLOT(d_func(), void slotChangeHue(const QColor &color)) + Q_PRIVATE_SLOT(d_func(), void slotChangeSaturation(const QColor &color)) + Q_PRIVATE_SLOT(d_func(), void slotChangeValue(const QColor &color)) + Q_PRIVATE_SLOT(d_func(), void slotChangeAlpha(const QColor &color)) + Q_PRIVATE_SLOT(d_func(), void slotChangeHue(int)) + Q_PRIVATE_SLOT(d_func(), void slotChangeSaturation(int)) + Q_PRIVATE_SLOT(d_func(), void slotChangeValue(int)) + Q_PRIVATE_SLOT(d_func(), void slotChangeAlpha(int)) + //Q_PRIVATE_SLOT(d_func(), void slotChangePosition(double newPos)) + Q_PRIVATE_SLOT(d_func(), void slotChangePosition(double value)) + Q_PRIVATE_SLOT(d_func(), void slotChangeZoom(int value)) + Q_PRIVATE_SLOT(d_func(), void slotZoomIn()) + Q_PRIVATE_SLOT(d_func(), void slotZoomOut()) + Q_PRIVATE_SLOT(d_func(), void slotZoomAll()) + Q_PRIVATE_SLOT(d_func(), void slotZoomChanged(double)) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientstopsmodel.cpp b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientstopsmodel.cpp new file mode 100644 index 00000000000..5a963a81295 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientstopsmodel.cpp @@ -0,0 +1,477 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtgradientstopsmodel.h" +#include <QtGui/QColor> + +QT_BEGIN_NAMESPACE + +class QtGradientStopPrivate +{ +public: + qreal m_position; + QColor m_color; + QtGradientStopsModel *m_model; +}; + +qreal QtGradientStop::position() const +{ + return d_ptr->m_position; +} + +QColor QtGradientStop::color() const +{ + return d_ptr->m_color; +} + +QtGradientStopsModel *QtGradientStop::gradientModel() const +{ + return d_ptr->m_model; +} + +void QtGradientStop::setColor(const QColor &color) +{ + d_ptr->m_color = color; +} + +void QtGradientStop::setPosition(qreal position) +{ + d_ptr->m_position = position; +} + +QtGradientStop::QtGradientStop(QtGradientStopsModel *model) + : d_ptr(new QtGradientStopPrivate()) +{ + d_ptr->m_position = 0; + d_ptr->m_color = Qt::white; + d_ptr->m_model = model; +} + +QtGradientStop::~QtGradientStop() +{ +} + +class QtGradientStopsModelPrivate +{ + QtGradientStopsModel *q_ptr; + Q_DECLARE_PUBLIC(QtGradientStopsModel) +public: + QMap<qreal, QtGradientStop *> m_posToStop; + QMap<QtGradientStop *, qreal> m_stopToPos; + QMap<QtGradientStop *, bool> m_selection; + QtGradientStop *m_current; +}; + + + +QtGradientStopsModel::QtGradientStopsModel(QObject *parent) + : QObject(parent), d_ptr(new QtGradientStopsModelPrivate) +{ + d_ptr->q_ptr = this; + d_ptr->m_current = 0; +} + +QtGradientStopsModel::~QtGradientStopsModel() +{ + clear(); +} + +QtGradientStopsModel::PositionStopMap QtGradientStopsModel::stops() const +{ + return d_ptr->m_posToStop; +} + +QtGradientStop *QtGradientStopsModel::at(qreal pos) const +{ + if (d_ptr->m_posToStop.contains(pos)) + return d_ptr->m_posToStop[pos]; + return 0; +} + +QColor QtGradientStopsModel::color(qreal pos) const +{ + PositionStopMap gradStops = stops(); + if (gradStops.isEmpty()) + return QColor::fromRgbF(pos, pos, pos, 1.0); + if (gradStops.contains(pos)) + return gradStops[pos]->color(); + + gradStops[pos] = 0; + PositionStopMap::ConstIterator itStop = gradStops.constFind(pos); + if (itStop == gradStops.constBegin()) { + ++itStop; + return itStop.value()->color(); + } + if (itStop == --gradStops.constEnd()) { + --itStop; + return itStop.value()->color(); + } + PositionStopMap::ConstIterator itPrev = itStop; + PositionStopMap::ConstIterator itNext = itStop; + --itPrev; + ++itNext; + + double prevX = itPrev.key(); + double nextX = itNext.key(); + + double coefX = (pos - prevX) / (nextX - prevX); + QColor prevCol = itPrev.value()->color(); + QColor nextCol = itNext.value()->color(); + + QColor newColor; + newColor.setRgbF((nextCol.redF() - prevCol.redF() ) * coefX + prevCol.redF(), + (nextCol.greenF() - prevCol.greenF()) * coefX + prevCol.greenF(), + (nextCol.blueF() - prevCol.blueF() ) * coefX + prevCol.blueF(), + (nextCol.alphaF() - prevCol.alphaF()) * coefX + prevCol.alphaF()); + return newColor; +} + +QList<QtGradientStop *> QtGradientStopsModel::selectedStops() const +{ + return d_ptr->m_selection.keys(); +} + +QtGradientStop *QtGradientStopsModel::currentStop() const +{ + return d_ptr->m_current; +} + +bool QtGradientStopsModel::isSelected(QtGradientStop *stop) const +{ + if (d_ptr->m_selection.contains(stop)) + return true; + return false; +} + +QtGradientStop *QtGradientStopsModel::addStop(qreal pos, const QColor &color) +{ + qreal newPos = pos; + if (pos < 0.0) + newPos = 0.0; + if (pos > 1.0) + newPos = 1.0; + if (d_ptr->m_posToStop.contains(newPos)) + return 0; + QtGradientStop *stop = new QtGradientStop(); + stop->setPosition(newPos); + stop->setColor(color); + + d_ptr->m_posToStop[newPos] = stop; + d_ptr->m_stopToPos[stop] = newPos; + + emit stopAdded(stop); + + return stop; +} + +void QtGradientStopsModel::removeStop(QtGradientStop *stop) +{ + if (!d_ptr->m_stopToPos.contains(stop)) + return; + if (currentStop() == stop) + setCurrentStop(0); + selectStop(stop, false); + + emit stopRemoved(stop); + + qreal pos = d_ptr->m_stopToPos[stop]; + d_ptr->m_stopToPos.remove(stop); + d_ptr->m_posToStop.remove(pos); + delete stop; +} + +void QtGradientStopsModel::moveStop(QtGradientStop *stop, qreal newPos) +{ + if (!d_ptr->m_stopToPos.contains(stop)) + return; + if (d_ptr->m_posToStop.contains(newPos)) + return; + + if (newPos > 1.0) + newPos = 1.0; + else if (newPos < 0.0) + newPos = 0.0; + + emit stopMoved(stop, newPos); + + const qreal oldPos = stop->position(); + stop->setPosition(newPos); + d_ptr->m_stopToPos[stop] = newPos; + d_ptr->m_posToStop.remove(oldPos); + d_ptr->m_posToStop[newPos] = stop; +} + +void QtGradientStopsModel::swapStops(QtGradientStop *stop1, QtGradientStop *stop2) +{ + if (stop1 == stop2) + return; + if (!d_ptr->m_stopToPos.contains(stop1)) + return; + if (!d_ptr->m_stopToPos.contains(stop2)) + return; + + emit stopsSwapped(stop1, stop2); + + const qreal pos1 = stop1->position(); + const qreal pos2 = stop2->position(); + stop1->setPosition(pos2); + stop2->setPosition(pos1); + d_ptr->m_stopToPos[stop1] = pos2; + d_ptr->m_stopToPos[stop2] = pos1; + d_ptr->m_posToStop[pos1] = stop2; + d_ptr->m_posToStop[pos2] = stop1; +} + +void QtGradientStopsModel::changeStop(QtGradientStop *stop, const QColor &newColor) +{ + if (!d_ptr->m_stopToPos.contains(stop)) + return; + if (stop->color() == newColor) + return; + + emit stopChanged(stop, newColor); + + stop->setColor(newColor); +} + +void QtGradientStopsModel::selectStop(QtGradientStop *stop, bool select) +{ + if (!d_ptr->m_stopToPos.contains(stop)) + return; + bool selected = d_ptr->m_selection.contains(stop); + if (select == selected) + return; + + emit stopSelected(stop, select); + + if (select) + d_ptr->m_selection[stop] = true; + else + d_ptr->m_selection.remove(stop); +} + +void QtGradientStopsModel::setCurrentStop(QtGradientStop *stop) +{ + if (stop && !d_ptr->m_stopToPos.contains(stop)) + return; + if (stop == currentStop()) + return; + + emit currentStopChanged(stop); + + d_ptr->m_current = stop; +} + +QtGradientStop *QtGradientStopsModel::firstSelected() const +{ + PositionStopMap stopList = stops(); + PositionStopMap::ConstIterator itStop = stopList.constBegin(); + while (itStop != stopList.constEnd()) { + QtGradientStop *stop = itStop.value(); + if (isSelected(stop)) + return stop; + ++itStop; + }; + return 0; +} + +QtGradientStop *QtGradientStopsModel::lastSelected() const +{ + PositionStopMap stopList = stops(); + PositionStopMap::ConstIterator itStop = stopList.constEnd(); + while (itStop != stopList.constBegin()) { + --itStop; + + QtGradientStop *stop = itStop.value(); + if (isSelected(stop)) + return stop; + }; + return 0; +} + +QtGradientStopsModel *QtGradientStopsModel::clone() const +{ + QtGradientStopsModel *model = new QtGradientStopsModel(); + + QMap<qreal, QtGradientStop *> stopsToClone = stops(); + QMapIterator<qreal, QtGradientStop *> it(stopsToClone); + while (it.hasNext()) { + it.next(); + model->addStop(it.key(), it.value()->color()); + } + // clone selection and current also + return model; +} + +void QtGradientStopsModel::moveStops(double newPosition) +{ + QtGradientStop *current = currentStop(); + if (!current) + return; + + double newPos = newPosition; + + if (newPos > 1) + newPos = 1; + else if (newPos < 0) + newPos = 0; + + if (newPos == current->position()) + return; + + double offset = newPos - current->position(); + + QtGradientStop *first = firstSelected(); + QtGradientStop *last = lastSelected(); + + if (first && last) { // multiselection + double maxOffset = 1.0 - last->position(); + double minOffset = -first->position(); + + if (offset > maxOffset) + offset = maxOffset; + else if (offset < minOffset) + offset = minOffset; + + } + + if (offset == 0) + return; + + bool forward = (offset > 0) ? false : true; + + PositionStopMap stopList; + + QList<QtGradientStop *> selected = selectedStops(); + QListIterator<QtGradientStop *> it(selected); + while (it.hasNext()) { + QtGradientStop *stop = it.next(); + stopList[stop->position()] = stop; + } + stopList[current->position()] = current; + + PositionStopMap::ConstIterator itStop = forward ? stopList.constBegin() : stopList.constEnd(); + while (itStop != (forward ? stopList.constEnd() : stopList.constBegin())) { + if (!forward) + --itStop; + QtGradientStop *stop = itStop.value(); + double pos = stop->position() + offset; + if (pos > 1) + pos = 1; + if (pos < 0) + pos = 0; + + if (current == stop) + pos = newPos; + + QtGradientStop *oldStop = at(pos); + if (oldStop && !stopList.values().contains(oldStop)) + removeStop(oldStop); + moveStop(stop, pos); + + if (forward) + ++itStop; + } +} + +void QtGradientStopsModel::clear() +{ + QList<QtGradientStop *> stopsList = stops().values(); + QListIterator<QtGradientStop *> it(stopsList); + while (it.hasNext()) + removeStop(it.next()); +} + +void QtGradientStopsModel::clearSelection() +{ + QList<QtGradientStop *> stopsList = selectedStops(); + QListIterator<QtGradientStop *> it(stopsList); + while (it.hasNext()) + selectStop(it.next(), false); +} + +void QtGradientStopsModel::flipAll() +{ + QMap<qreal, QtGradientStop *> stopsMap = stops(); + QMapIterator<qreal, QtGradientStop *> itStop(stopsMap); + itStop.toBack(); + + QMap<QtGradientStop *, bool> swappedList; + + while (itStop.hasPrevious()) { + itStop.previous(); + + QtGradientStop *stop = itStop.value(); + if (swappedList.contains(stop)) + continue; + const double newPos = 1.0 - itStop.key(); + if (stopsMap.contains(newPos)) { + QtGradientStop *swapped = stopsMap.value(newPos); + swappedList[swapped] = true; + swapStops(stop, swapped); + } else { + moveStop(stop, newPos); + } + } +} + +void QtGradientStopsModel::selectAll() +{ + QList<QtGradientStop *> stopsList = stops().values(); + QListIterator<QtGradientStop *> it(stopsList); + while (it.hasNext()) + selectStop(it.next(), true); +} + +void QtGradientStopsModel::deleteStops() +{ + QList<QtGradientStop *> selected = selectedStops(); + QListIterator<QtGradientStop *> itSel(selected); + while (itSel.hasNext()) { + QtGradientStop *stop = itSel.next(); + removeStop(stop); + } + QtGradientStop *current = currentStop(); + if (current) + removeStop(current); +} + +QT_END_NAMESPACE diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientstopsmodel.h b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientstopsmodel.h new file mode 100644 index 00000000000..0b2dcc6c5d2 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientstopsmodel.h @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTGRADIENTSTOPSMODEL_H +#define QTGRADIENTSTOPSMODEL_H + +#include <QtCore/QObject> +#include <QtCore/QMap> + +QT_BEGIN_NAMESPACE + +class QColor; + +class QtGradientStopsModel; + +class QtGradientStop +{ +public: + qreal position() const; + QColor color() const; + QtGradientStopsModel *gradientModel() const; + +private: + void setColor(const QColor &color); + void setPosition(qreal position); + friend class QtGradientStopsModel; + QtGradientStop(QtGradientStopsModel *model = 0); + ~QtGradientStop(); + QScopedPointer<class QtGradientStopPrivate> d_ptr; +}; + +class QtGradientStopsModel : public QObject +{ + Q_OBJECT +public: + typedef QMap<qreal, QtGradientStop *> PositionStopMap; + + QtGradientStopsModel(QObject *parent = 0); + ~QtGradientStopsModel(); + + PositionStopMap stops() const; + QtGradientStop *at(qreal pos) const; + QColor color(qreal pos) const; // calculated between points + QList<QtGradientStop *> selectedStops() const; + QtGradientStop *currentStop() const; + bool isSelected(QtGradientStop *stop) const; + QtGradientStop *firstSelected() const; + QtGradientStop *lastSelected() const; + QtGradientStopsModel *clone() const; + + QtGradientStop *addStop(qreal pos, const QColor &color); + void removeStop(QtGradientStop *stop); + void moveStop(QtGradientStop *stop, qreal newPos); + void swapStops(QtGradientStop *stop1, QtGradientStop *stop2); + void changeStop(QtGradientStop *stop, const QColor &newColor); + void selectStop(QtGradientStop *stop, bool select); + void setCurrentStop(QtGradientStop *stop); + + void moveStops(double newPosition); // moves current stop to newPos and all selected stops are moved accordingly + void clear(); + void clearSelection(); + void flipAll(); + void selectAll(); + void deleteStops(); + +signals: + void stopAdded(QtGradientStop *stop); + void stopRemoved(QtGradientStop *stop); + void stopMoved(QtGradientStop *stop, qreal newPos); + void stopsSwapped(QtGradientStop *stop1, QtGradientStop *stop2); + void stopChanged(QtGradientStop *stop, const QColor &newColor); + void stopSelected(QtGradientStop *stop, bool selected); + void currentStopChanged(QtGradientStop *stop); + +private: + QScopedPointer<class QtGradientStopsModelPrivate> d_ptr; + Q_DECLARE_PRIVATE(QtGradientStopsModel) + Q_DISABLE_COPY(QtGradientStopsModel) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientstopswidget.cpp b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientstopswidget.cpp new file mode 100644 index 00000000000..411dbed4569 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientstopswidget.cpp @@ -0,0 +1,1154 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtgradientstopswidget.h" +#include "qtgradientstopsmodel.h" + +#include <QtCore/QMap> +#include <QtGui/QImage> +#include <QtGui/QPainter> +#include <QtGui/QScrollBar> +#include <QtGui/QMouseEvent> +#include <QtGui/QRubberBand> +#include <QtGui/QMenu> + +QT_BEGIN_NAMESPACE + +class QtGradientStopsWidgetPrivate +{ + QtGradientStopsWidget *q_ptr; + Q_DECLARE_PUBLIC(QtGradientStopsWidget) +public: + typedef QMap<qreal, QColor> PositionColorMap; + typedef QMap<QtGradientStop *, qreal> StopPositionMap; + + void slotStopAdded(QtGradientStop *stop); + void slotStopRemoved(QtGradientStop *stop); + void slotStopMoved(QtGradientStop *stop, qreal newPos); + void slotStopsSwapped(QtGradientStop *stop1, QtGradientStop *stop2); + void slotStopChanged(QtGradientStop *stop, const QColor &newColor); + void slotStopSelected(QtGradientStop *stop, bool selected); + void slotCurrentStopChanged(QtGradientStop *stop); + void slotNewStop(); + void slotDelete(); + void slotFlipAll(); + void slotSelectAll(); + void slotZoomIn(); + void slotZoomOut(); + void slotResetZoom(); + + double fromViewport(int x) const; + double toViewport(double x) const; + QtGradientStop *stopAt(const QPoint &viewportPos) const; + QList<QtGradientStop *> stopsAt(const QPoint &viewportPos) const; + void setupMove(QtGradientStop *stop, int x); + void ensureVisible(double x); // x = stop position + void ensureVisible(QtGradientStop *stop); + QtGradientStop *newStop(const QPoint &viewportPos); + + bool m_backgroundCheckered; + QtGradientStopsModel *m_model; + double m_handleSize; + int m_scaleFactor; + double m_zoom; + +#ifndef QT_NO_DRAGANDDROP + QtGradientStop *m_dragStop; + QtGradientStop *m_changedStop; + QtGradientStop *m_clonedStop; + QtGradientStopsModel *m_dragModel; + QColor m_dragColor; + void clearDrag(); + void removeClonedStop(); + void restoreChangedStop(); + void changeStop(qreal pos); + void cloneStop(qreal pos); +#endif + + QRubberBand *m_rubber; + QPoint m_clickPos; + + QList<QtGradientStop *> m_stops; + + bool m_moving; + int m_moveOffset; + StopPositionMap m_moveStops; + + PositionColorMap m_moveOriginal; +}; + +double QtGradientStopsWidgetPrivate::fromViewport(int x) const +{ + QSize size = q_ptr->viewport()->size(); + int w = size.width(); + int max = q_ptr->horizontalScrollBar()->maximum(); + int val = q_ptr->horizontalScrollBar()->value(); + return ((double)x * m_scaleFactor + w * val) / (w * (m_scaleFactor + max)); +} + +double QtGradientStopsWidgetPrivate::toViewport(double x) const +{ + QSize size = q_ptr->viewport()->size(); + int w = size.width(); + int max = q_ptr->horizontalScrollBar()->maximum(); + int val = q_ptr->horizontalScrollBar()->value(); + return w * (x * (m_scaleFactor + max) - val) / m_scaleFactor; +} + +QtGradientStop *QtGradientStopsWidgetPrivate::stopAt(const QPoint &viewportPos) const +{ + double posY = m_handleSize / 2; + QListIterator<QtGradientStop *> itStop(m_stops); + while (itStop.hasNext()) { + QtGradientStop *stop = itStop.next(); + + double posX = toViewport(stop->position()); + + double x = viewportPos.x() - posX; + double y = viewportPos.y() - posY; + + if ((m_handleSize * m_handleSize / 4) > (x * x + y * y)) + return stop; + } + return 0; +} + +QList<QtGradientStop *> QtGradientStopsWidgetPrivate::stopsAt(const QPoint &viewportPos) const +{ + QList<QtGradientStop *> stops; + double posY = m_handleSize / 2; + QListIterator<QtGradientStop *> itStop(m_stops); + while (itStop.hasNext()) { + QtGradientStop *stop = itStop.next(); + + double posX = toViewport(stop->position()); + + double x = viewportPos.x() - posX; + double y = viewportPos.y() - posY; + + if ((m_handleSize * m_handleSize / 4) > (x * x + y * y)) + stops.append(stop); + } + return stops; +} + +void QtGradientStopsWidgetPrivate::setupMove(QtGradientStop *stop, int x) +{ + m_model->setCurrentStop(stop); + + int viewportX = qRound(toViewport(stop->position())); + m_moveOffset = x - viewportX; + + QList<QtGradientStop *> stops = m_stops; + m_stops.clear(); + QListIterator<QtGradientStop *> itStop(stops); + while (itStop.hasNext()) { + QtGradientStop *s = itStop.next(); + if (m_model->isSelected(s) || s == stop) { + m_moveStops[s] = s->position() - stop->position(); + m_stops.append(s); + } else { + m_moveOriginal[s->position()] = s->color(); + } + } + itStop.toFront(); + while (itStop.hasNext()) { + QtGradientStop *s = itStop.next(); + if (!m_model->isSelected(s)) + m_stops.append(s); + } + m_stops.removeAll(stop); + m_stops.prepend(stop); +} + +void QtGradientStopsWidgetPrivate::ensureVisible(double x) +{ + double viewX = toViewport(x); + if (viewX < 0 || viewX > q_ptr->viewport()->size().width()) { + int max = q_ptr->horizontalScrollBar()->maximum(); + int newVal = qRound(x * (max + m_scaleFactor) - m_scaleFactor / 2); + q_ptr->horizontalScrollBar()->setValue(newVal); + } +} + +void QtGradientStopsWidgetPrivate::ensureVisible(QtGradientStop *stop) +{ + if (!stop) + return; + ensureVisible(stop->position()); +} + +QtGradientStop *QtGradientStopsWidgetPrivate::newStop(const QPoint &viewportPos) +{ + QtGradientStop *copyStop = stopAt(viewportPos); + double posX = fromViewport(viewportPos.x()); + QtGradientStop *stop = m_model->at(posX); + if (!stop) { + QColor newColor; + if (copyStop) + newColor = copyStop->color(); + else + newColor = m_model->color(posX); + if (!newColor.isValid()) + newColor = Qt::white; + stop = m_model->addStop(posX, newColor); + } + return stop; +} + +void QtGradientStopsWidgetPrivate::slotStopAdded(QtGradientStop *stop) +{ + m_stops.append(stop); + q_ptr->viewport()->update(); +} + +void QtGradientStopsWidgetPrivate::slotStopRemoved(QtGradientStop *stop) +{ + m_stops.removeAll(stop); + q_ptr->viewport()->update(); +} + +void QtGradientStopsWidgetPrivate::slotStopMoved(QtGradientStop *stop, qreal newPos) +{ + Q_UNUSED(stop) + Q_UNUSED(newPos) + q_ptr->viewport()->update(); +} + +void QtGradientStopsWidgetPrivate::slotStopsSwapped(QtGradientStop *stop1, QtGradientStop *stop2) +{ + Q_UNUSED(stop1) + Q_UNUSED(stop2) + q_ptr->viewport()->update(); +} + +void QtGradientStopsWidgetPrivate::slotStopChanged(QtGradientStop *stop, const QColor &newColor) +{ + Q_UNUSED(stop) + Q_UNUSED(newColor) + q_ptr->viewport()->update(); +} + +void QtGradientStopsWidgetPrivate::slotStopSelected(QtGradientStop *stop, bool selected) +{ + Q_UNUSED(stop) + Q_UNUSED(selected) + q_ptr->viewport()->update(); +} + +void QtGradientStopsWidgetPrivate::slotCurrentStopChanged(QtGradientStop *stop) +{ + Q_UNUSED(stop) + + if (!m_model) + return; + q_ptr->viewport()->update(); + if (stop) { + m_stops.removeAll(stop); + m_stops.prepend(stop); + } +} + +void QtGradientStopsWidgetPrivate::slotNewStop() +{ + if (!m_model) + return; + + QtGradientStop *stop = newStop(m_clickPos); + + if (!stop) + return; + + m_model->clearSelection(); + m_model->selectStop(stop, true); + m_model->setCurrentStop(stop); +} + +void QtGradientStopsWidgetPrivate::slotDelete() +{ + if (!m_model) + return; + + m_model->deleteStops(); +} + +void QtGradientStopsWidgetPrivate::slotFlipAll() +{ + if (!m_model) + return; + + m_model->flipAll(); +} + +void QtGradientStopsWidgetPrivate::slotSelectAll() +{ + if (!m_model) + return; + + m_model->selectAll(); +} + +void QtGradientStopsWidgetPrivate::slotZoomIn() +{ + double newZoom = q_ptr->zoom() * 2; + if (newZoom > 100) + newZoom = 100; + if (newZoom == q_ptr->zoom()) + return; + + q_ptr->setZoom(newZoom); + emit q_ptr->zoomChanged(q_ptr->zoom()); +} + +void QtGradientStopsWidgetPrivate::slotZoomOut() +{ + double newZoom = q_ptr->zoom() / 2; + if (newZoom < 1) + newZoom = 1; + if (newZoom == q_ptr->zoom()) + return; + + q_ptr->setZoom(newZoom); + emit q_ptr->zoomChanged(q_ptr->zoom()); +} + +void QtGradientStopsWidgetPrivate::slotResetZoom() +{ + if (1 == q_ptr->zoom()) + return; + + q_ptr->setZoom(1); + emit q_ptr->zoomChanged(1); +} + +QtGradientStopsWidget::QtGradientStopsWidget(QWidget *parent) + : QAbstractScrollArea(parent), d_ptr(new QtGradientStopsWidgetPrivate) +{ + d_ptr->q_ptr = this; + d_ptr->m_backgroundCheckered = true; + d_ptr->m_model = 0; + d_ptr->m_handleSize = 25.0; + d_ptr->m_scaleFactor = 1000; + d_ptr->m_moving = false; + d_ptr->m_zoom = 1; + d_ptr->m_rubber = new QRubberBand(QRubberBand::Rectangle, this); +#ifndef QT_NO_DRAGANDDROP + d_ptr->m_dragStop = 0; + d_ptr->m_changedStop = 0; + d_ptr->m_clonedStop = 0; + d_ptr->m_dragModel = 0; +#endif + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + horizontalScrollBar()->setRange(0, (int)(d_ptr->m_scaleFactor * (d_ptr->m_zoom - 1) + 0.5)); + horizontalScrollBar()->setPageStep(d_ptr->m_scaleFactor); + horizontalScrollBar()->setSingleStep(4); + viewport()->setAutoFillBackground(false); + + setAcceptDrops(true); + + setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred)); +} + +QtGradientStopsWidget::~QtGradientStopsWidget() +{ +} + +QSize QtGradientStopsWidget::sizeHint() const +{ + return QSize(qRound(2 * d_ptr->m_handleSize), qRound(3 * d_ptr->m_handleSize) + horizontalScrollBar()->sizeHint().height()); +} + +QSize QtGradientStopsWidget::minimumSizeHint() const +{ + return QSize(qRound(2 * d_ptr->m_handleSize), qRound(3 * d_ptr->m_handleSize) + horizontalScrollBar()->minimumSizeHint().height()); +} + +void QtGradientStopsWidget::setBackgroundCheckered(bool checkered) +{ + if (d_ptr->m_backgroundCheckered == checkered) + return; + d_ptr->m_backgroundCheckered = checkered; + update(); +} + +bool QtGradientStopsWidget::isBackgroundCheckered() const +{ + return d_ptr->m_backgroundCheckered; +} + +void QtGradientStopsWidget::setGradientStopsModel(QtGradientStopsModel *model) +{ + if (d_ptr->m_model == model) + return; + + if (d_ptr->m_model) { + disconnect(d_ptr->m_model, SIGNAL(stopAdded(QtGradientStop*)), + this, SLOT(slotStopAdded(QtGradientStop*))); + disconnect(d_ptr->m_model, SIGNAL(stopRemoved(QtGradientStop*)), + this, SLOT(slotStopRemoved(QtGradientStop*))); + disconnect(d_ptr->m_model, SIGNAL(stopMoved(QtGradientStop*,qreal)), + this, SLOT(slotStopMoved(QtGradientStop*,qreal))); + disconnect(d_ptr->m_model, SIGNAL(stopsSwapped(QtGradientStop*,QtGradientStop*)), + this, SLOT(slotStopsSwapped(QtGradientStop*,QtGradientStop*))); + disconnect(d_ptr->m_model, SIGNAL(stopChanged(QtGradientStop*,QColor)), + this, SLOT(slotStopChanged(QtGradientStop*,QColor))); + disconnect(d_ptr->m_model, SIGNAL(stopSelected(QtGradientStop*,bool)), + this, SLOT(slotStopSelected(QtGradientStop*,bool))); + disconnect(d_ptr->m_model, SIGNAL(currentStopChanged(QtGradientStop*)), + this, SLOT(slotCurrentStopChanged(QtGradientStop*))); + + d_ptr->m_stops.clear(); + } + + d_ptr->m_model = model; + + if (d_ptr->m_model) { + connect(d_ptr->m_model, SIGNAL(stopAdded(QtGradientStop*)), + this, SLOT(slotStopAdded(QtGradientStop*))); + connect(d_ptr->m_model, SIGNAL(stopRemoved(QtGradientStop*)), + this, SLOT(slotStopRemoved(QtGradientStop*))); + connect(d_ptr->m_model, SIGNAL(stopMoved(QtGradientStop*,qreal)), + this, SLOT(slotStopMoved(QtGradientStop*,qreal))); + connect(d_ptr->m_model, SIGNAL(stopsSwapped(QtGradientStop*,QtGradientStop*)), + this, SLOT(slotStopsSwapped(QtGradientStop*,QtGradientStop*))); + connect(d_ptr->m_model, SIGNAL(stopChanged(QtGradientStop*,QColor)), + this, SLOT(slotStopChanged(QtGradientStop*,QColor))); + connect(d_ptr->m_model, SIGNAL(stopSelected(QtGradientStop*,bool)), + this, SLOT(slotStopSelected(QtGradientStop*,bool))); + connect(d_ptr->m_model, SIGNAL(currentStopChanged(QtGradientStop*)), + this, SLOT(slotCurrentStopChanged(QtGradientStop*))); + + QList<QtGradientStop *> stops = d_ptr->m_model->stops().values(); + QListIterator<QtGradientStop *> itStop(stops); + while (itStop.hasNext()) + d_ptr->slotStopAdded(itStop.next()); + + QList<QtGradientStop *> selected = d_ptr->m_model->selectedStops(); + QListIterator<QtGradientStop *> itSelect(selected); + while (itSelect.hasNext()) + d_ptr->slotStopSelected(itSelect.next(), true); + + d_ptr->slotCurrentStopChanged(d_ptr->m_model->currentStop()); + } +} + +void QtGradientStopsWidget::mousePressEvent(QMouseEvent *e) +{ + typedef QtGradientStopsModel::PositionStopMap PositionStopMap; + if (!d_ptr->m_model) + return; + + if (e->button() != Qt::LeftButton) + return; + + d_ptr->m_moving = true; + + d_ptr->m_moveStops.clear(); + d_ptr->m_moveOriginal.clear(); + d_ptr->m_clickPos = e->pos(); + QtGradientStop *stop = d_ptr->stopAt(e->pos()); + if (stop) { + if (e->modifiers() & Qt::ControlModifier) { + d_ptr->m_model->selectStop(stop, !d_ptr->m_model->isSelected(stop)); + } else if (e->modifiers() & Qt::ShiftModifier) { + QtGradientStop *oldCurrent = d_ptr->m_model->currentStop(); + if (oldCurrent) { + PositionStopMap stops = d_ptr->m_model->stops(); + PositionStopMap::ConstIterator itSt = stops.constFind(oldCurrent->position()); + if (itSt != stops.constEnd()) { + while (itSt != stops.constFind(stop->position())) { + d_ptr->m_model->selectStop(itSt.value(), true); + if (oldCurrent->position() < stop->position()) + ++itSt; + else + --itSt; + } + } + } + d_ptr->m_model->selectStop(stop, true); + } else { + if (!d_ptr->m_model->isSelected(stop)) { + d_ptr->m_model->clearSelection(); + d_ptr->m_model->selectStop(stop, true); + } + } + d_ptr->setupMove(stop, e->pos().x()); + } else { + d_ptr->m_model->clearSelection(); + d_ptr->m_rubber->setGeometry(QRect(d_ptr->m_clickPos, QSize())); + d_ptr->m_rubber->show(); + } + viewport()->update(); +} + +void QtGradientStopsWidget::mouseReleaseEvent(QMouseEvent *e) +{ + if (!d_ptr->m_model) + return; + + if (e->button() != Qt::LeftButton) + return; + + d_ptr->m_moving = false; + d_ptr->m_rubber->hide(); + d_ptr->m_moveStops.clear(); + d_ptr->m_moveOriginal.clear(); +} + +void QtGradientStopsWidget::mouseMoveEvent(QMouseEvent *e) +{ + typedef QtGradientStopsWidgetPrivate::PositionColorMap PositionColorMap; + typedef QtGradientStopsModel::PositionStopMap PositionStopMap; + typedef QtGradientStopsWidgetPrivate::StopPositionMap StopPositionMap; + if (!d_ptr->m_model) + return; + + if (!(e->buttons() & Qt::LeftButton)) + return; + + if (!d_ptr->m_moving) + return; + + if (!d_ptr->m_moveStops.isEmpty()) { + double maxOffset = 0.0; + double minOffset = 0.0; + bool first = true; + StopPositionMap::ConstIterator itStop = d_ptr->m_moveStops.constBegin(); + while (itStop != d_ptr->m_moveStops.constEnd()) { + double offset = itStop.value(); + + if (first) { + maxOffset = offset; + minOffset = offset; + first = false; + } else { + if (maxOffset < offset) + maxOffset = offset; + else if (minOffset > offset) + minOffset = offset; + } + ++itStop; + } + + double viewportMin = d_ptr->toViewport(-minOffset); + double viewportMax = d_ptr->toViewport(1.0 - maxOffset); + + PositionStopMap newPositions; + + int viewportX = e->pos().x() - d_ptr->m_moveOffset; + + if (viewportX > viewport()->size().width()) + viewportX = viewport()->size().width(); + else if (viewportX < 0) + viewportX = 0; + + double posX = d_ptr->fromViewport(viewportX); + + if (viewportX > viewportMax) + posX = 1.0 - maxOffset; + else if (viewportX < viewportMin) + posX = -minOffset; + + itStop = d_ptr->m_moveStops.constBegin(); + while (itStop != d_ptr->m_moveStops.constEnd()) { + QtGradientStop *stop = itStop.key(); + + newPositions[posX + itStop.value()] = stop; + + ++itStop; + } + + bool forward = true; + PositionStopMap::ConstIterator itNewPos = newPositions.constBegin(); + if (itNewPos.value()->position() < itNewPos.key()) + forward = false; + + itNewPos = forward ? newPositions.constBegin() : newPositions.constEnd(); + while (itNewPos != (forward ? newPositions.constEnd() : newPositions.constBegin())) { + if (!forward) + --itNewPos; + QtGradientStop *stop = itNewPos.value(); + double newPos = itNewPos.key(); + if (newPos > 1) + newPos = 1; + else if (newPos < 0) + newPos = 0; + + QtGradientStop *existingStop = d_ptr->m_model->at(newPos); + if (existingStop && !d_ptr->m_moveStops.contains(existingStop)) + d_ptr->m_model->removeStop(existingStop); + d_ptr->m_model->moveStop(stop, newPos); + + if (forward) + ++itNewPos; + } + + PositionColorMap::ConstIterator itOld = d_ptr->m_moveOriginal.constBegin(); + while (itOld != d_ptr->m_moveOriginal.constEnd()) { + double position = itOld.key(); + if (!d_ptr->m_model->at(position)) + d_ptr->m_model->addStop(position, itOld.value()); + + ++itOld; + } + + } else { + QRect r(QRect(d_ptr->m_clickPos, e->pos()).normalized()); + r.translate(1, 0); + d_ptr->m_rubber->setGeometry(r); + //d_ptr->m_model->clearSelection(); + + int xv1 = d_ptr->m_clickPos.x(); + int xv2 = e->pos().x(); + if (xv1 > xv2) { + int temp = xv1; + xv1 = xv2; + xv2 = temp; + } + int yv1 = d_ptr->m_clickPos.y(); + int yv2 = e->pos().y(); + if (yv1 > yv2) { + int temp = yv1; + yv1 = yv2; + yv2 = temp; + } + + QPoint p1, p2; + + if (yv2 < d_ptr->m_handleSize / 2) { + p1 = QPoint(xv1, yv2); + p2 = QPoint(xv2, yv2); + } else if (yv1 > d_ptr->m_handleSize / 2) { + p1 = QPoint(xv1, yv1); + p2 = QPoint(xv2, yv1); + } else { + p1 = QPoint(xv1, qRound(d_ptr->m_handleSize / 2)); + p2 = QPoint(xv2, qRound(d_ptr->m_handleSize / 2)); + } + + QList<QtGradientStop *> beginList = d_ptr->stopsAt(p1); + QList<QtGradientStop *> endList = d_ptr->stopsAt(p2); + + double x1 = d_ptr->fromViewport(xv1); + double x2 = d_ptr->fromViewport(xv2); + + QListIterator<QtGradientStop *> itStop(d_ptr->m_stops); + while (itStop.hasNext()) { + QtGradientStop *stop = itStop.next(); + if ((stop->position() >= x1 && stop->position() <= x2) || + beginList.contains(stop) || endList.contains(stop)) + d_ptr->m_model->selectStop(stop, true); + else + d_ptr->m_model->selectStop(stop, false); + } + } +} + +void QtGradientStopsWidget::mouseDoubleClickEvent(QMouseEvent *e) +{ + if (!d_ptr->m_model) + return; + + if (e->button() != Qt::LeftButton) + return; + + if (d_ptr->m_clickPos != e->pos()) { + mousePressEvent(e); + return; + } + d_ptr->m_moving = true; + d_ptr->m_moveStops.clear(); + d_ptr->m_moveOriginal.clear(); + + QtGradientStop *stop = d_ptr->newStop(e->pos()); + + if (!stop) + return; + + d_ptr->m_model->clearSelection(); + d_ptr->m_model->selectStop(stop, true); + + d_ptr->setupMove(stop, e->pos().x()); + + viewport()->update(); +} + +void QtGradientStopsWidget::keyPressEvent(QKeyEvent *e) +{ + typedef QtGradientStopsModel::PositionStopMap PositionStopMap; + if (!d_ptr->m_model) + return; + + if (e->key() == Qt::Key_Delete || e->key() == Qt::Key_Backspace) { + d_ptr->m_model->deleteStops(); + } else if (e->key() == Qt::Key_Left || e->key() == Qt::Key_Right || + e->key() == Qt::Key_Home || e->key() == Qt::Key_End) { + PositionStopMap stops = d_ptr->m_model->stops(); + if (stops.isEmpty()) + return; + QtGradientStop *newCurrent = 0; + QtGradientStop *current = d_ptr->m_model->currentStop(); + if (!current || e->key() == Qt::Key_Home || e->key() == Qt::Key_End) { + if (e->key() == Qt::Key_Left || e->key() == Qt::Key_Home) + newCurrent = stops.constBegin().value(); + else if (e->key() == Qt::Key_Right || e->key() == Qt::Key_End) + newCurrent = (--stops.constEnd()).value(); + } else { + PositionStopMap::ConstIterator itStop = stops.constBegin(); + while (itStop.value() != current) + ++itStop; + if (e->key() == Qt::Key_Left && itStop != stops.constBegin()) + --itStop; + else if (e->key() == Qt::Key_Right && itStop != --stops.constEnd()) + ++itStop; + newCurrent = itStop.value(); + } + d_ptr->m_model->clearSelection(); + d_ptr->m_model->selectStop(newCurrent, true); + d_ptr->m_model->setCurrentStop(newCurrent); + d_ptr->ensureVisible(newCurrent); + } else if (e->key() == Qt::Key_A) { + if (e->modifiers() & Qt::ControlModifier) + d_ptr->m_model->selectAll(); + } +} + +void QtGradientStopsWidget::paintEvent(QPaintEvent *e) +{ + Q_UNUSED(e) + if (!d_ptr->m_model) + return; + + QtGradientStopsModel *model = d_ptr->m_model; +#ifndef QT_NO_DRAGANDDROP + if (d_ptr->m_dragModel) + model = d_ptr->m_dragModel; +#endif + + QSize size = viewport()->size(); + int w = size.width(); + double h = size.height() - d_ptr->m_handleSize; + if (w <= 0) + return; + + QPixmap pix(size); + QPainter p; + + if (d_ptr->m_backgroundCheckered) { + int pixSize = 20; + QPixmap pm(2 * pixSize, 2 * pixSize); + QPainter pmp(&pm); + pmp.fillRect(0, 0, pixSize, pixSize, Qt::white); + pmp.fillRect(pixSize, pixSize, pixSize, pixSize, Qt::white); + pmp.fillRect(0, pixSize, pixSize, pixSize, Qt::black); + pmp.fillRect(pixSize, 0, pixSize, pixSize, Qt::black); + + p.begin(&pix); + p.setBrushOrigin((size.width() % pixSize + pixSize) / 2, (size.height() % pixSize + pixSize) / 2); + p.fillRect(viewport()->rect(), pm); + p.setBrushOrigin(0, 0); + } else { + p.begin(viewport()); + } + + double viewBegin = (double)w * horizontalScrollBar()->value() / d_ptr->m_scaleFactor; + + int val = horizontalScrollBar()->value(); + int max = horizontalScrollBar()->maximum(); + + double begin = (double)val / (d_ptr->m_scaleFactor + max); + double end = (double)(val + d_ptr->m_scaleFactor) / (d_ptr->m_scaleFactor + max); + double width = end - begin; + + if (h > 0) { + QLinearGradient lg(0, 0, w, 0); + QMap<qreal, QtGradientStop *> stops = model->stops(); + QMapIterator<qreal, QtGradientStop *> itStop(stops); + while (itStop.hasNext()) { + QtGradientStop *stop = itStop.next().value(); + double pos = stop->position(); + if (pos >= begin && pos <= end) { + double gradPos = (pos - begin) / width; + QColor c = stop->color(); + lg.setColorAt(gradPos, c); + } + //lg.setColorAt(stop->position(), stop->color()); + } + lg.setColorAt(0, model->color(begin)); + lg.setColorAt(1, model->color(end)); + QImage img(w, 1, QImage::Format_ARGB32_Premultiplied); + QPainter p1(&img); + p1.setCompositionMode(QPainter::CompositionMode_Source); + + /* + if (viewBegin != 0) + p1.translate(-viewBegin, 0); + if (d_ptr->m_zoom != 1) + p1.scale(d_ptr->m_zoom, 1); + */ + p1.fillRect(0, 0, w, 1, lg); + + p.fillRect(QRectF(0, d_ptr->m_handleSize, w, h), QPixmap::fromImage(img)); + } + + + double handleWidth = d_ptr->m_handleSize * d_ptr->m_scaleFactor / (w * (d_ptr->m_scaleFactor + max)); + + QColor insideColor = QColor::fromRgb(0x20, 0x20, 0x20, 0xFF); + QColor borderColor = QColor(Qt::white); + QColor drawColor; + QColor back1 = QColor(Qt::lightGray); + QColor back2 = QColor(Qt::darkGray); + QColor back = QColor::fromRgb((back1.red() + back2.red()) / 2, + (back1.green() + back2.green()) / 2, + (back1.blue() + back2.blue()) / 2); + + QPen pen; + p.setRenderHint(QPainter::Antialiasing); + QListIterator<QtGradientStop *> itStop(d_ptr->m_stops); + itStop.toBack(); + while (itStop.hasPrevious()) { + QtGradientStop *stop = itStop.previous(); + double x = stop->position(); + if (x >= begin - handleWidth / 2 && x <= end + handleWidth / 2) { + double viewX = x * w * (d_ptr->m_scaleFactor + max) / d_ptr->m_scaleFactor - viewBegin; + p.save(); + QColor c = stop->color(); +#ifndef QT_NO_DRAGANDDROP + if (stop == d_ptr->m_dragStop) + c = d_ptr->m_dragColor; +#endif + if ((0.3 * c.redF() + 0.59 * c.greenF() + 0.11 * c.blueF()) * c.alphaF() + + (0.3 * back.redF() + 0.59 * back.greenF() + 0.11 * back.blueF()) * (1.0 - c.alphaF()) < 0.5) { + drawColor = QColor::fromRgb(0xC0, 0xC0, 0xC0, 0xB0); + } else { + drawColor = QColor::fromRgb(0x40, 0x40, 0x40, 0x80); + } + QRectF rect(viewX - d_ptr->m_handleSize / 2, 0, d_ptr->m_handleSize, d_ptr->m_handleSize); + rect.adjust(0.5, 0.5, -0.5, -0.5); + if (h > 0) { + pen.setWidthF(1); + QLinearGradient lg(0, d_ptr->m_handleSize, 0, d_ptr->m_handleSize + h / 2); + lg.setColorAt(0, drawColor); + QColor alphaZero = drawColor; + alphaZero.setAlpha(0); + lg.setColorAt(1, alphaZero); + pen.setBrush(lg); + p.setPen(pen); + p.drawLine(QPointF(viewX, d_ptr->m_handleSize), QPointF(viewX, d_ptr->m_handleSize + h / 2)); + + pen.setWidthF(1); + pen.setBrush(drawColor); + p.setPen(pen); + QRectF r1 = rect.adjusted(0.5, 0.5, -0.5, -0.5); + QRectF r2 = rect.adjusted(1.5, 1.5, -1.5, -1.5); + QColor inColor = QColor::fromRgb(0x80, 0x80, 0x80, 0x80); + if (!d_ptr->m_model->isSelected(stop)) { + p.setBrush(c); + p.drawEllipse(rect); + } else { + pen.setBrush(insideColor); + pen.setWidthF(2); + p.setPen(pen); + p.setBrush(Qt::NoBrush); + p.drawEllipse(r1); + + pen.setBrush(inColor); + pen.setWidthF(1); + p.setPen(pen); + p.setBrush(c); + p.drawEllipse(r2); + } + + if (d_ptr->m_model->currentStop() == stop) { + p.setBrush(Qt::NoBrush); + pen.setWidthF(5); + pen.setBrush(drawColor); + int corr = 4; + if (!d_ptr->m_model->isSelected(stop)) { + corr = 3; + pen.setWidthF(7); + } + p.setPen(pen); + p.drawEllipse(rect.adjusted(corr, corr, -corr, -corr)); + } + + } + p.restore(); + } + } + if (d_ptr->m_backgroundCheckered) { + p.end(); + p.begin(viewport()); + p.drawPixmap(0, 0, pix); + } + p.end(); +} + +void QtGradientStopsWidget::focusInEvent(QFocusEvent *e) +{ + Q_UNUSED(e) + viewport()->update(); +} + +void QtGradientStopsWidget::focusOutEvent(QFocusEvent *e) +{ + Q_UNUSED(e) + viewport()->update(); +} + +void QtGradientStopsWidget::contextMenuEvent(QContextMenuEvent *e) +{ + if (!d_ptr->m_model) + return; + + d_ptr->m_clickPos = e->pos(); + + QMenu menu(this); + QAction *newStopAction = new QAction(tr("New Stop"), &menu); + QAction *deleteAction = new QAction(tr("Delete"), &menu); + QAction *flipAllAction = new QAction(tr("Flip All"), &menu); + QAction *selectAllAction = new QAction(tr("Select All"), &menu); + QAction *zoomInAction = new QAction(tr("Zoom In"), &menu); + QAction *zoomOutAction = new QAction(tr("Zoom Out"), &menu); + QAction *zoomAllAction = new QAction(tr("Reset Zoom"), &menu); + if (d_ptr->m_model->selectedStops().isEmpty() && !d_ptr->m_model->currentStop()) + deleteAction->setEnabled(false); + if (zoom() <= 1) { + zoomOutAction->setEnabled(false); + zoomAllAction->setEnabled(false); + } else if (zoom() >= 100) { + zoomInAction->setEnabled(false); + } + connect(newStopAction, SIGNAL(triggered()), this, SLOT(slotNewStop())); + connect(deleteAction, SIGNAL(triggered()), this, SLOT(slotDelete())); + connect(flipAllAction, SIGNAL(triggered()), this, SLOT(slotFlipAll())); + connect(selectAllAction, SIGNAL(triggered()), this, SLOT(slotSelectAll())); + connect(zoomInAction, SIGNAL(triggered()), this, SLOT(slotZoomIn())); + connect(zoomOutAction, SIGNAL(triggered()), this, SLOT(slotZoomOut())); + connect(zoomAllAction, SIGNAL(triggered()), this, SLOT(slotResetZoom())); + menu.addAction(newStopAction); + menu.addAction(deleteAction); + menu.addAction(flipAllAction); + menu.addAction(selectAllAction); + menu.addSeparator(); + menu.addAction(zoomInAction); + menu.addAction(zoomOutAction); + menu.addAction(zoomAllAction); + menu.exec(e->globalPos()); +} + +void QtGradientStopsWidget::wheelEvent(QWheelEvent *e) +{ + int numDegrees = e->delta() / 8; + int numSteps = numDegrees / 15; + + int shift = numSteps; + if (shift < 0) + shift = -shift; + int pow = 1 << shift; + //const double c = 0.7071067; // 2 steps per doubled value + const double c = 0.5946036; // 4 steps pre doubled value + // in general c = pow(2, 1 / n) / 2; where n is the step + double factor = pow * c; + + double newZoom = zoom(); + if (numSteps < 0) + newZoom /= factor; + else + newZoom *= factor; + if (newZoom > 100) + newZoom = 100; + if (newZoom < 1) + newZoom = 1; + + if (newZoom == zoom()) + return; + + setZoom(newZoom); + emit zoomChanged(zoom()); +} + +#ifndef QT_NO_DRAGANDDROP +void QtGradientStopsWidget::dragEnterEvent(QDragEnterEvent *event) +{ + const QMimeData *mime = event->mimeData(); + if (!mime->hasColor()) + return; + event->accept(); + d_ptr->m_dragModel = d_ptr->m_model->clone(); + + d_ptr->m_dragColor = qvariant_cast<QColor>(mime->colorData()); + update(); +} + +void QtGradientStopsWidget::dragMoveEvent(QDragMoveEvent *event) +{ + QRectF rect = viewport()->rect(); + rect.adjust(0, d_ptr->m_handleSize, 0, 0); + double x = d_ptr->fromViewport(event->pos().x()); + QtGradientStop *dragStop = d_ptr->stopAt(event->pos()); + if (dragStop) { + event->accept(); + d_ptr->removeClonedStop(); + d_ptr->changeStop(dragStop->position()); + } else if (rect.contains(event->pos())) { + event->accept(); + if (d_ptr->m_model->at(x)) { + d_ptr->removeClonedStop(); + d_ptr->changeStop(x); + } else { + d_ptr->restoreChangedStop(); + d_ptr->cloneStop(x); + } + } else { + event->ignore(); + d_ptr->removeClonedStop(); + d_ptr->restoreChangedStop(); + } + + update(); +} + +void QtGradientStopsWidget::dragLeaveEvent(QDragLeaveEvent *event) +{ + event->accept(); + d_ptr->clearDrag(); + update(); +} + +void QtGradientStopsWidget::dropEvent(QDropEvent *event) +{ + event->accept(); + if (!d_ptr->m_dragModel) + return; + + if (d_ptr->m_changedStop) + d_ptr->m_model->changeStop(d_ptr->m_model->at(d_ptr->m_changedStop->position()), d_ptr->m_dragColor); + else if (d_ptr->m_clonedStop) + d_ptr->m_model->addStop(d_ptr->m_clonedStop->position(), d_ptr->m_dragColor); + + d_ptr->clearDrag(); + update(); +} + +void QtGradientStopsWidgetPrivate::clearDrag() +{ + removeClonedStop(); + restoreChangedStop(); + delete m_dragModel; + m_dragModel = 0; +} + +void QtGradientStopsWidgetPrivate::removeClonedStop() +{ + if (!m_clonedStop) + return; + m_dragModel->removeStop(m_clonedStop); + m_clonedStop = 0; +} + +void QtGradientStopsWidgetPrivate::restoreChangedStop() +{ + if (!m_changedStop) + return; + m_dragModel->changeStop(m_changedStop, m_model->at(m_changedStop->position())->color()); + m_changedStop = 0; + m_dragStop = 0; +} + +void QtGradientStopsWidgetPrivate::changeStop(qreal pos) +{ + QtGradientStop *stop = m_dragModel->at(pos); + if (!stop) + return; + + m_dragModel->changeStop(stop, m_dragColor); + m_changedStop = stop; + m_dragStop = m_model->at(stop->position()); +} + +void QtGradientStopsWidgetPrivate::cloneStop(qreal pos) +{ + if (m_clonedStop) { + m_dragModel->moveStop(m_clonedStop, pos); + return; + } + QtGradientStop *stop = m_dragModel->at(pos); + if (stop) + return; + + m_clonedStop = m_dragModel->addStop(pos, m_dragColor); +} + +#endif + +void QtGradientStopsWidget::setZoom(double zoom) +{ + double z = zoom; + if (z < 1) + z = 1; + else if (z > 100) + z = 100; + + if (d_ptr->m_zoom == z) + return; + + d_ptr->m_zoom = z; + int oldMax = horizontalScrollBar()->maximum(); + int oldVal = horizontalScrollBar()->value(); + horizontalScrollBar()->setRange(0, qRound(d_ptr->m_scaleFactor * (d_ptr->m_zoom - 1))); + int newMax = horizontalScrollBar()->maximum(); + double newVal = (oldVal + (double)d_ptr->m_scaleFactor / 2) * (newMax + d_ptr->m_scaleFactor) + / (oldMax + d_ptr->m_scaleFactor) - (double)d_ptr->m_scaleFactor / 2; + horizontalScrollBar()->setValue(qRound(newVal)); + viewport()->update(); +} + +double QtGradientStopsWidget::zoom() const +{ + return d_ptr->m_zoom; +} + +QT_END_NAMESPACE + +#include "moc_qtgradientstopswidget.cpp" diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientstopswidget.h b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientstopswidget.h new file mode 100644 index 00000000000..8260b3c9e9a --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientstopswidget.h @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTGRADIENTSTOPSWIDGET_H +#define QTGRADIENTSTOPSWIDGET_H + +#include <QtGui/QAbstractScrollArea> + +QT_BEGIN_NAMESPACE + +class QtGradientStopsModel; +class QtGradientStopsWidgetPrivate; + +class QtGradientStopsWidget : public QAbstractScrollArea +{ + Q_OBJECT + Q_PROPERTY(bool backgroundCheckered READ isBackgroundCheckered WRITE setBackgroundCheckered) +public: + QtGradientStopsWidget(QWidget *parent = 0); + ~QtGradientStopsWidget(); + + QSize minimumSizeHint() const; + QSize sizeHint() const; + + bool isBackgroundCheckered() const; + void setBackgroundCheckered(bool checkered); + + void setGradientStopsModel(QtGradientStopsModel *model); + + void setZoom(double zoom); + double zoom() const; + +signals: + + void zoomChanged(double zoom); + +protected: + void paintEvent(QPaintEvent *e); + void mousePressEvent(QMouseEvent *e); + void mouseReleaseEvent(QMouseEvent *e); + void mouseMoveEvent(QMouseEvent *e); + void mouseDoubleClickEvent(QMouseEvent *e); + void keyPressEvent(QKeyEvent *e); + void focusInEvent(QFocusEvent *e); + void focusOutEvent(QFocusEvent *e); + void contextMenuEvent(QContextMenuEvent *e); + void wheelEvent(QWheelEvent *e); +#ifndef QT_NO_DRAGANDDROP + void dragEnterEvent(QDragEnterEvent *event); + void dragMoveEvent(QDragMoveEvent *event); + void dragLeaveEvent(QDragLeaveEvent *event); + void dropEvent(QDropEvent *event); +#endif + +private: + QScopedPointer<QtGradientStopsWidgetPrivate> d_ptr; + Q_DECLARE_PRIVATE(QtGradientStopsWidget) + Q_DISABLE_COPY(QtGradientStopsWidget) + Q_PRIVATE_SLOT(d_func(), void slotStopAdded(QtGradientStop *stop)) + Q_PRIVATE_SLOT(d_func(), void slotStopRemoved(QtGradientStop *stop)) + Q_PRIVATE_SLOT(d_func(), void slotStopMoved(QtGradientStop *stop, qreal newPos)) + Q_PRIVATE_SLOT(d_func(), void slotStopsSwapped(QtGradientStop *stop1, QtGradientStop *stop2)) + Q_PRIVATE_SLOT(d_func(), void slotStopChanged(QtGradientStop *stop, const QColor &newColor)) + Q_PRIVATE_SLOT(d_func(), void slotStopSelected(QtGradientStop *stop, bool selected)) + Q_PRIVATE_SLOT(d_func(), void slotCurrentStopChanged(QtGradientStop *stop)) + Q_PRIVATE_SLOT(d_func(), void slotNewStop()) + Q_PRIVATE_SLOT(d_func(), void slotDelete()) + Q_PRIVATE_SLOT(d_func(), void slotFlipAll()) + Q_PRIVATE_SLOT(d_func(), void slotSelectAll()) + Q_PRIVATE_SLOT(d_func(), void slotZoomIn()) + Q_PRIVATE_SLOT(d_func(), void slotZoomOut()) + Q_PRIVATE_SLOT(d_func(), void slotResetZoom()) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientutils.cpp b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientutils.cpp new file mode 100644 index 00000000000..dec53332a33 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientutils.cpp @@ -0,0 +1,420 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtgradientutils.h" +#include "qtgradientmanager.h" +#include <QtGui/QLinearGradient> +#include <QtGui/QRadialGradient> +#include <QtGui/QConicalGradient> +#include <QtXml/QDomDocument> +#include <QtCore/QDebug> + +QT_BEGIN_NAMESPACE + +static QString gradientTypeToString(QGradient::Type type) +{ + if (type == QGradient::LinearGradient) + return QLatin1String("LinearGradient"); + if (type == QGradient::RadialGradient) + return QLatin1String("RadialGradient"); + if (type == QGradient::ConicalGradient) + return QLatin1String("ConicalGradient"); + return QLatin1String("NoGradient"); +} + +static QGradient::Type stringToGradientType(const QString &name) +{ + if (name == QLatin1String("LinearGradient")) + return QGradient::LinearGradient; + if (name == QLatin1String("RadialGradient")) + return QGradient::RadialGradient; + if (name == QLatin1String("ConicalGradient")) + return QGradient::ConicalGradient; + return QGradient::NoGradient; +} + +static QString gradientSpreadToString(QGradient::Spread spread) +{ + if (spread == QGradient::PadSpread) + return QLatin1String("PadSpread"); + if (spread == QGradient::RepeatSpread) + return QLatin1String("RepeatSpread"); + if (spread == QGradient::ReflectSpread) + return QLatin1String("ReflectSpread"); + return QLatin1String("PadSpread"); +} + +static QGradient::Spread stringToGradientSpread(const QString &name) +{ + if (name == QLatin1String("PadSpread")) + return QGradient::PadSpread; + if (name == QLatin1String("RepeatSpread")) + return QGradient::RepeatSpread; + if (name == QLatin1String("ReflectSpread")) + return QGradient::ReflectSpread; + return QGradient::PadSpread; +} + +static QString gradientCoordinateModeToString(QGradient::CoordinateMode mode) +{ + if (mode == QGradient::LogicalMode) + return QLatin1String("LogicalMode"); + if (mode == QGradient::StretchToDeviceMode) + return QLatin1String("StretchToDeviceMode"); + if (mode == QGradient::ObjectBoundingMode) + return QLatin1String("ObjectBoundingMode"); + return QLatin1String("StretchToDeviceMode"); +} + +static QGradient::CoordinateMode stringToGradientCoordinateMode(const QString &name) +{ + if (name == QLatin1String("LogicalMode")) + return QGradient::LogicalMode; + if (name == QLatin1String("StretchToDeviceMode")) + return QGradient::StretchToDeviceMode; + if (name == QLatin1String("ObjectBoundingMode")) + return QGradient::ObjectBoundingMode; + return QGradient::StretchToDeviceMode; +} + +static QDomElement saveColor(QDomDocument &doc, const QColor &color) +{ + QDomElement colorElem = doc.createElement(QLatin1String("colorData")); + + colorElem.setAttribute(QLatin1String("r"), QString::number(color.red())); + colorElem.setAttribute(QLatin1String("g"), QString::number(color.green())); + colorElem.setAttribute(QLatin1String("b"), QString::number(color.blue())); + colorElem.setAttribute(QLatin1String("a"), QString::number(color.alpha())); + + return colorElem; +} + +static QDomElement saveGradientStop(QDomDocument &doc, const QGradientStop &stop) +{ + QDomElement stopElem = doc.createElement(QLatin1String("stopData")); + + stopElem.setAttribute(QLatin1String("position"), QString::number(stop.first)); + + const QDomElement colorElem = saveColor(doc, stop.second); + stopElem.appendChild(colorElem); + + return stopElem; +} + +static QDomElement saveGradient(QDomDocument &doc, const QGradient &gradient) +{ + QDomElement gradElem = doc.createElement(QLatin1String("gradientData")); + + const QGradient::Type type = gradient.type(); + gradElem.setAttribute(QLatin1String("type"), gradientTypeToString(type)); + gradElem.setAttribute(QLatin1String("spread"), gradientSpreadToString(gradient.spread())); + gradElem.setAttribute(QLatin1String("coordinateMode"), gradientCoordinateModeToString(gradient.coordinateMode())); + + QGradientStops stops = gradient.stops(); + QVectorIterator<QGradientStop > it(stops); + while (it.hasNext()) + gradElem.appendChild(saveGradientStop(doc, it.next())); + + if (type == QGradient::LinearGradient) { + const QLinearGradient &g = *static_cast<const QLinearGradient *>(&gradient); + gradElem.setAttribute(QLatin1String("startX"), QString::number(g.start().x())); + gradElem.setAttribute(QLatin1String("startY"), QString::number(g.start().y())); + gradElem.setAttribute(QLatin1String("endX"), QString::number(g.finalStop().x())); + gradElem.setAttribute(QLatin1String("endY"), QString::number(g.finalStop().y())); + } else if (type == QGradient::RadialGradient) { + const QRadialGradient &g = *static_cast<const QRadialGradient *>(&gradient); + gradElem.setAttribute(QLatin1String("centerX"), QString::number(g.center().x())); + gradElem.setAttribute(QLatin1String("centerY"), QString::number(g.center().y())); + gradElem.setAttribute(QLatin1String("focalX"), QString::number(g.focalPoint().x())); + gradElem.setAttribute(QLatin1String("focalY"), QString::number(g.focalPoint().y())); + gradElem.setAttribute(QLatin1String("radius"), QString::number(g.radius())); + } else if (type == QGradient::ConicalGradient) { + const QConicalGradient &g = *static_cast<const QConicalGradient*>(&gradient); + gradElem.setAttribute(QLatin1String("centerX"), QString::number(g.center().x())); + gradElem.setAttribute(QLatin1String("centerY"), QString::number(g.center().y())); + gradElem.setAttribute(QLatin1String("angle"), QString::number(g.angle())); + } + + return gradElem; +} + +static QColor loadColor(const QDomElement &elem) +{ + if (elem.tagName() != QLatin1String("colorData")) + return QColor(); + + return QColor(elem.attribute(QLatin1String("r")).toInt(), + elem.attribute(QLatin1String("g")).toInt(), + elem.attribute(QLatin1String("b")).toInt(), + elem.attribute(QLatin1String("a")).toInt()); +} + +static QGradientStop loadGradientStop(const QDomElement &elem) +{ + if (elem.tagName() != QLatin1String("stopData")) + return QGradientStop(); + + const qreal pos = static_cast<qreal>(elem.attribute(QLatin1String("position")).toDouble()); + return qMakePair(pos, loadColor(elem.firstChild().toElement())); +} + +static QGradient loadGradient(const QDomElement &elem) +{ + if (elem.tagName() != QLatin1String("gradientData")) + return QLinearGradient(); + + const QGradient::Type type = stringToGradientType(elem.attribute(QLatin1String("type"))); + const QGradient::Spread spread = stringToGradientSpread(elem.attribute(QLatin1String("spread"))); + const QGradient::CoordinateMode mode = stringToGradientCoordinateMode(elem.attribute(QLatin1String("coordinateMode"))); + + QGradient gradient = QLinearGradient(); + + if (type == QGradient::LinearGradient) { + QLinearGradient g; + g.setStart(elem.attribute(QLatin1String("startX")).toDouble(), elem.attribute(QLatin1String("startY")).toDouble()); + g.setFinalStop(elem.attribute(QLatin1String("endX")).toDouble(), elem.attribute(QLatin1String("endY")).toDouble()); + gradient = g; + } else if (type == QGradient::RadialGradient) { + QRadialGradient g; + g.setCenter(elem.attribute(QLatin1String("centerX")).toDouble(), elem.attribute(QLatin1String("centerY")).toDouble()); + g.setFocalPoint(elem.attribute(QLatin1String("focalX")).toDouble(), elem.attribute(QLatin1String("focalY")).toDouble()); + g.setRadius(elem.attribute(QLatin1String("radius")).toDouble()); + gradient = g; + } else if (type == QGradient::ConicalGradient) { + QConicalGradient g; + g.setCenter(elem.attribute(QLatin1String("centerX")).toDouble(), elem.attribute(QLatin1String("centerY")).toDouble()); + g.setAngle(elem.attribute(QLatin1String("angle")).toDouble()); + gradient = g; + } + + QDomElement stopElem = elem.firstChildElement(); + while (!stopElem.isNull()) { + QGradientStop stop = loadGradientStop(stopElem); + + gradient.setColorAt(stop.first, stop.second); + + stopElem = stopElem.nextSiblingElement(); + } + + gradient.setSpread(spread); + gradient.setCoordinateMode(mode); + + return gradient; +} + +QString QtGradientUtils::saveState(const QtGradientManager *manager) +{ + QDomDocument doc; + + QDomElement rootElem = doc.createElement(QLatin1String("gradients")); + + QMap<QString, QGradient> grads = manager->gradients(); + QMapIterator<QString, QGradient> itGrad(grads); + while (itGrad.hasNext()) { + itGrad.next(); + QDomElement idElem = doc.createElement(QLatin1String("gradient")); + idElem.setAttribute(QLatin1String("name"), itGrad.key()); + QDomElement gradElem = saveGradient(doc, itGrad.value()); + idElem.appendChild(gradElem); + + rootElem.appendChild(idElem); + } + + doc.appendChild(rootElem); + + return doc.toString(); +} + +void QtGradientUtils::restoreState(QtGradientManager *manager, const QString &state) +{ + manager->clear(); + + QDomDocument doc; + doc.setContent(state); + + QDomElement rootElem = doc.documentElement(); + + QDomElement gradElem = rootElem.firstChildElement(); + while (!gradElem.isNull()) { + const QString name = gradElem.attribute(QLatin1String("name")); + const QGradient gradient = loadGradient(gradElem.firstChildElement()); + + manager->addGradient(name, gradient); + gradElem = gradElem.nextSiblingElement(); + } +} + +QPixmap QtGradientUtils::gradientPixmap(const QGradient &gradient, const QSize &size, bool checkeredBackground) +{ + QImage image(size, QImage::Format_ARGB32); + QPainter p(&image); + p.setCompositionMode(QPainter::CompositionMode_Source); + + if (checkeredBackground) { + int pixSize = 20; + QPixmap pm(2 * pixSize, 2 * pixSize); + + QPainter pmp(&pm); + pmp.fillRect(0, 0, pixSize, pixSize, Qt::lightGray); + pmp.fillRect(pixSize, pixSize, pixSize, pixSize, Qt::lightGray); + pmp.fillRect(0, pixSize, pixSize, pixSize, Qt::darkGray); + pmp.fillRect(pixSize, 0, pixSize, pixSize, Qt::darkGray); + + p.setBrushOrigin((size.width() % pixSize + pixSize) / 2, (size.height() % pixSize + pixSize) / 2); + p.fillRect(0, 0, size.width(), size.height(), pm); + p.setBrushOrigin(0, 0); + p.setCompositionMode(QPainter::CompositionMode_SourceOver); + } + + const qreal scaleFactor = 0.999999; + p.scale(scaleFactor, scaleFactor); + QGradient grad = gradient; + grad.setCoordinateMode(QGradient::StretchToDeviceMode); + p.fillRect(QRect(0, 0, size.width(), size.height()), grad); + p.drawRect(QRect(0, 0, size.width() - 1, size.height() - 1)); + + return QPixmap::fromImage(image); +} + +static QString styleSheetFillName(const QGradient &gradient) +{ + QString result; + + switch (gradient.type()) { + case QGradient::LinearGradient: + result += QLatin1String("qlineargradient"); + break; + case QGradient::RadialGradient: + result += QLatin1String("qradialgradient"); + break; + case QGradient::ConicalGradient: + result += QLatin1String("qconicalgradient"); + break; + default: + qWarning() << "QtGradientUtils::styleSheetFillName(): gradient type" << gradient.type() << "not supported!"; + break; + } + + return result; +} + +static QStringList styleSheetParameters(const QGradient &gradient) +{ + QStringList result; + + if (gradient.type() != QGradient::ConicalGradient) { + QString spread; + switch (gradient.spread()) { + case QGradient::PadSpread: + spread = QLatin1String("pad"); + break; + case QGradient::ReflectSpread: + spread = QLatin1String("reflect"); + break; + case QGradient::RepeatSpread: + spread = QLatin1String("repeat"); + break; + default: + qWarning() << "QtGradientUtils::styleSheetParameters(): gradient spread" << gradient.spread() << "not supported!"; + break; + } + result << QLatin1String("spread:") + spread; + } + + switch (gradient.type()) { + case QGradient::LinearGradient: { + const QLinearGradient *linearGradient = static_cast<const QLinearGradient*>(&gradient); + result << QLatin1String("x1:") + QString::number(linearGradient->start().x()) + << QLatin1String("y1:") + QString::number(linearGradient->start().y()) + << QLatin1String("x2:") + QString::number(linearGradient->finalStop().x()) + << QLatin1String("y2:") + QString::number(linearGradient->finalStop().y()); + break; + } + case QGradient::RadialGradient: { + const QRadialGradient *radialGradient = static_cast<const QRadialGradient*>(&gradient); + result << QLatin1String("cx:") + QString::number(radialGradient->center().x()) + << QLatin1String("cy:") + QString::number(radialGradient->center().y()) + << QLatin1String("radius:") + QString::number(radialGradient->radius()) + << QLatin1String("fx:") + QString::number(radialGradient->focalPoint().x()) + << QLatin1String("fy:") + QString::number(radialGradient->focalPoint().y()); + break; + } + case QGradient::ConicalGradient: { + const QConicalGradient *conicalGradient = static_cast<const QConicalGradient*>(&gradient); + result << QLatin1String("cx:") + QString::number(conicalGradient->center().x()) + << QLatin1String("cy:") + QString::number(conicalGradient->center().y()) + << QLatin1String("angle:") + QString::number(conicalGradient->angle()); + break; + } + default: + qWarning() << "QtGradientUtils::styleSheetParameters(): gradient type" << gradient.type() << "not supported!"; + break; + } + + return result; +} + +static QStringList styleSheetStops(const QGradient &gradient) +{ + QStringList result; + foreach (QGradientStop stop, gradient.stops()) { + const QColor color = stop.second; + + const QString stopDescription = QLatin1String("stop:") + QString::number(stop.first) + QLatin1String(" rgba(") + + QString::number(color.red()) + QLatin1String(", ") + + QString::number(color.green()) + QLatin1String(", ") + + QString::number(color.blue()) + QLatin1String(", ") + + QString::number(color.alpha()) + QLatin1Char(')'); + result << stopDescription; + } + + return result; +} + +QString QtGradientUtils::styleSheetCode(const QGradient &gradient) +{ + QStringList gradientParameters; + gradientParameters << styleSheetParameters(gradient) << styleSheetStops(gradient); + + return styleSheetFillName(gradient) + QLatin1Char('(') + gradientParameters.join(QLatin1String(", ")) + QLatin1Char(')'); +} + +QT_END_NAMESPACE diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientutils.h b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientutils.h new file mode 100644 index 00000000000..63622b7239d --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientutils.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef GRADIENTUTILS_H +#define GRADIENTUTILS_H + +#include <QtGui/QGradient> +#include <QtGui/QPainter> + +QT_BEGIN_NAMESPACE + +class QtGradientManager; + +class QtGradientUtils +{ +public: + static QString styleSheetCode(const QGradient &gradient); + // utils methods, they could be outside of this class + static QString saveState(const QtGradientManager *manager); + static void restoreState(QtGradientManager *manager, const QString &state); + + static QPixmap gradientPixmap(const QGradient &gradient, const QSize &size = QSize(64, 64), bool checkeredBackground = false); + +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientview.cpp b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientview.cpp new file mode 100644 index 00000000000..c9d8aac2f43 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientview.cpp @@ -0,0 +1,292 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtgradientview.h" +#include "qtgradientmanager.h" +#include "qtgradientdialog.h" +#include "qtgradientutils.h" +#include <QtGui/QPainter> +#include <QtGui/QMessageBox> +#include <QtGui/QClipboard> + +QT_BEGIN_NAMESPACE + +void QtGradientView::slotGradientAdded(const QString &id, const QGradient &gradient) +{ + QListWidgetItem *item = new QListWidgetItem(QtGradientUtils::gradientPixmap(gradient), id, m_ui.listWidget); + item->setToolTip(id); + item->setSizeHint(QSize(72, 84)); + item->setFlags(item->flags() | Qt::ItemIsEditable); + + m_idToItem[id] = item; + m_itemToId[item] = id; +} + +void QtGradientView::slotGradientRenamed(const QString &id, const QString &newId) +{ + if (!m_idToItem.contains(id)) + return; + + QListWidgetItem *item = m_idToItem.value(id); + item->setText(newId); + item->setToolTip(newId); + m_itemToId[item] = newId; + m_idToItem.remove(id); + m_idToItem[newId] = item; +} + +void QtGradientView::slotGradientChanged(const QString &id, const QGradient &newGradient) +{ + if (!m_idToItem.contains(id)) + return; + + QListWidgetItem *item = m_idToItem.value(id); + item->setIcon(QtGradientUtils::gradientPixmap(newGradient)); +} + +void QtGradientView::slotGradientRemoved(const QString &id) +{ + if (!m_idToItem.contains(id)) + return; + + QListWidgetItem *item = m_idToItem.value(id); + delete item; + m_itemToId.remove(item); + m_idToItem.remove(id); +} + +void QtGradientView::slotNewGradient() +{ + bool ok; + QListWidgetItem *item = m_ui.listWidget->currentItem(); + QGradient grad = QLinearGradient(); + if (item) + grad = m_manager->gradients().value(m_itemToId.value(item)); + QGradient gradient = QtGradientDialog::getGradient(&ok, grad, this); + if (!ok) + return; + + QString id = m_manager->addGradient(tr("Grad"), gradient); + m_ui.listWidget->setCurrentItem(m_idToItem.value(id)); +} + +void QtGradientView::slotEditGradient() +{ + bool ok; + QListWidgetItem *item = m_ui.listWidget->currentItem(); + if (!item) + return; + + const QString id = m_itemToId.value(item); + QGradient grad = m_manager->gradients().value(id); + QGradient gradient = QtGradientDialog::getGradient(&ok, grad, this); + if (!ok) + return; + + m_manager->changeGradient(id, gradient); +} + +void QtGradientView::slotRemoveGradient() +{ + QListWidgetItem *item = m_ui.listWidget->currentItem(); + if (!item) + return; + + if (QMessageBox::question(this, tr("Remove Gradient"), + tr("Are you sure you want to remove the selected gradient?"), + QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel) != QMessageBox::Yes) + return; + + const QString id = m_itemToId.value(item); + m_manager->removeGradient(id); +} + +void QtGradientView::slotRenameGradient() +{ + QListWidgetItem *item = m_ui.listWidget->currentItem(); + if (!item) + return; + + m_ui.listWidget->editItem(item); +} + +void QtGradientView::slotRenameGradient(QListWidgetItem *item) +{ + if (!item) + return; + + const QString id = m_itemToId.value(item); + m_manager->renameGradient(id, item->text()); +} + +void QtGradientView::slotCurrentItemChanged(QListWidgetItem *item) +{ + m_editAction->setEnabled(item); + m_renameAction->setEnabled(item); + m_removeAction->setEnabled(item); + emit currentGradientChanged(m_itemToId.value(item)); +} + +void QtGradientView::slotGradientActivated(QListWidgetItem *item) +{ + const QString id = m_itemToId.value(item); + if (!id.isEmpty()) + emit gradientActivated(id); +} + +QtGradientView::QtGradientView(QWidget *parent) + : QWidget(parent) +{ + m_manager = 0; + + m_ui.setupUi(this); + + m_ui.listWidget->setViewMode(QListView::IconMode); + m_ui.listWidget->setMovement(QListView::Static); + m_ui.listWidget->setTextElideMode(Qt::ElideRight); + m_ui.listWidget->setResizeMode(QListWidget::Adjust); + m_ui.listWidget->setIconSize(QSize(64, 64)); + m_ui.listWidget->setEditTriggers(QAbstractItemView::NoEditTriggers); + + QPalette pal = m_ui.listWidget->viewport()->palette(); + int pixSize = 18; + QPixmap pm(2 * pixSize, 2 * pixSize); + + QColor c1 = palette().color(QPalette::Midlight); + QColor c2 = palette().color(QPalette::Dark); + QPainter pmp(&pm); + pmp.fillRect(0, 0, pixSize, pixSize, c1); + pmp.fillRect(pixSize, pixSize, pixSize, pixSize, c1); + pmp.fillRect(0, pixSize, pixSize, pixSize, c2); + pmp.fillRect(pixSize, 0, pixSize, pixSize, c2); + + pal.setBrush(QPalette::Base, QBrush(pm)); + m_ui.listWidget->viewport()->setPalette(pal); + + connect(m_ui.listWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(slotGradientActivated(QListWidgetItem*))); + connect(m_ui.listWidget, SIGNAL(itemChanged(QListWidgetItem*)), this, SLOT(slotRenameGradient(QListWidgetItem*))); + connect(m_ui.listWidget, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), this, SLOT(slotCurrentItemChanged(QListWidgetItem*))); + + m_newAction = new QAction(QIcon(QLatin1String(":/trolltech/qtgradienteditor/images/plus.png")), tr("New..."), this); + m_editAction = new QAction(QIcon(QLatin1String(":/trolltech/qtgradienteditor/images/edit.png")), tr("Edit..."), this); + m_renameAction = new QAction(tr("Rename"), this); + m_removeAction = new QAction(QIcon(QLatin1String(":/trolltech/qtgradienteditor/images/minus.png")), tr("Remove"), this); + + connect(m_newAction, SIGNAL(triggered()), this, SLOT(slotNewGradient())); + connect(m_editAction, SIGNAL(triggered()), this, SLOT(slotEditGradient())); + connect(m_removeAction, SIGNAL(triggered()), this, SLOT(slotRemoveGradient())); + connect(m_renameAction, SIGNAL(triggered()), this, SLOT(slotRenameGradient())); + + m_ui.listWidget->addAction(m_newAction); + m_ui.listWidget->addAction(m_editAction); + m_ui.listWidget->addAction(m_renameAction); + m_ui.listWidget->addAction(m_removeAction); + + m_ui.newButton->setDefaultAction(m_newAction); + m_ui.editButton->setDefaultAction(m_editAction); + m_ui.renameButton->setDefaultAction(m_renameAction); + m_ui.removeButton->setDefaultAction(m_removeAction); + + m_ui.listWidget->setContextMenuPolicy(Qt::ActionsContextMenu); +} + +void QtGradientView::setGradientManager(QtGradientManager *manager) +{ + if (m_manager == manager) + return; + + if (m_manager) { + disconnect(m_manager, SIGNAL(gradientAdded(QString,QGradient)), + this, SLOT(slotGradientAdded(QString,QGradient))); + disconnect(m_manager, SIGNAL(gradientRenamed(QString,QString)), + this, SLOT(slotGradientRenamed(QString,QString))); + disconnect(m_manager, SIGNAL(gradientChanged(QString,QGradient)), + this, SLOT(slotGradientChanged(QString,QGradient))); + disconnect(m_manager, SIGNAL(gradientRemoved(QString)), + this, SLOT(slotGradientRemoved(QString))); + + m_ui.listWidget->clear(); + m_idToItem.clear(); + m_itemToId.clear(); + } + + m_manager = manager; + + if (!m_manager) + return; + + QMap<QString, QGradient> gradients = m_manager->gradients(); + QMapIterator<QString, QGradient> itGrad(gradients); + while (itGrad.hasNext()) { + itGrad.next(); + slotGradientAdded(itGrad.key(), itGrad.value()); + } + + connect(m_manager, SIGNAL(gradientAdded(QString,QGradient)), + this, SLOT(slotGradientAdded(QString,QGradient))); + connect(m_manager, SIGNAL(gradientRenamed(QString,QString)), + this, SLOT(slotGradientRenamed(QString,QString))); + connect(m_manager, SIGNAL(gradientChanged(QString,QGradient)), + this, SLOT(slotGradientChanged(QString,QGradient))); + connect(m_manager, SIGNAL(gradientRemoved(QString)), + this, SLOT(slotGradientRemoved(QString))); +} + +QtGradientManager *QtGradientView::gradientManager() const +{ + return m_manager; +} + +void QtGradientView::setCurrentGradient(const QString &id) +{ + QListWidgetItem *item = m_idToItem.value(id); + if (!item) + return; + + m_ui.listWidget->setCurrentItem(item); +} + +QString QtGradientView::currentGradient() const +{ + return m_itemToId.value(m_ui.listWidget->currentItem()); +} + +QT_END_NAMESPACE diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientview.h b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientview.h new file mode 100644 index 00000000000..3f7662abab9 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientview.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef GRADIENTVIEW_H +#define GRADIENTVIEW_H + +#include <QtGui/QWidget> +#include <QtCore/QMap> +#include "ui_qtgradientview.h" + +QT_BEGIN_NAMESPACE + +class QtGradientManager; +class QListViewItem; +class QAction; + +class QtGradientView : public QWidget +{ + Q_OBJECT +public: + QtGradientView(QWidget *parent = 0); + + void setGradientManager(QtGradientManager *manager); + QtGradientManager *gradientManager() const; + + void setCurrentGradient(const QString &id); + QString currentGradient() const; + +signals: + void currentGradientChanged(const QString &id); + void gradientActivated(const QString &id); + +private slots: + void slotGradientAdded(const QString &id, const QGradient &gradient); + void slotGradientRenamed(const QString &id, const QString &newId); + void slotGradientChanged(const QString &id, const QGradient &newGradient); + void slotGradientRemoved(const QString &id); + void slotNewGradient(); + void slotEditGradient(); + void slotRemoveGradient(); + void slotRenameGradient(); + void slotRenameGradient(QListWidgetItem *item); + void slotCurrentItemChanged(QListWidgetItem *item); + void slotGradientActivated(QListWidgetItem *item); + +private: + QMap<QString, QListWidgetItem *> m_idToItem; + QMap<QListWidgetItem *, QString> m_itemToId; + + QAction *m_newAction; + QAction *m_editAction; + QAction *m_renameAction; + QAction *m_removeAction; + + QtGradientManager *m_manager; + Ui::QtGradientView m_ui; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientview.ui b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientview.ui new file mode 100644 index 00000000000..af7267ea2b0 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientview.ui @@ -0,0 +1,135 @@ +<ui version="4.0" > + <class>QtGradientView</class> + <widget class="QWidget" name="QtGradientView" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>484</width> + <height>228</height> + </rect> + </property> + <property name="windowTitle" > + <string>Gradient View</string> + </property> + <layout class="QVBoxLayout" > + <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="QHBoxLayout" > + <item> + <widget class="QToolButton" name="newButton" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Minimum" hsizetype="Minimum" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text" > + <string>New...</string> + </property> + <property name="toolButtonStyle" > + <enum>Qt::ToolButtonTextBesideIcon</enum> + </property> + <property name="autoRaise" > + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="editButton" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Minimum" hsizetype="Minimum" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text" > + <string>Edit...</string> + </property> + <property name="toolButtonStyle" > + <enum>Qt::ToolButtonTextBesideIcon</enum> + </property> + <property name="autoRaise" > + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="renameButton" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Minimum" hsizetype="Minimum" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text" > + <string>Rename</string> + </property> + <property name="toolButtonStyle" > + <enum>Qt::ToolButtonTextBesideIcon</enum> + </property> + <property name="autoRaise" > + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="removeButton" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Minimum" hsizetype="Minimum" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text" > + <string>Remove</string> + </property> + <property name="toolButtonStyle" > + <enum>Qt::ToolButtonTextBesideIcon</enum> + </property> + <property name="autoRaise" > + <bool>true</bool> + </property> + </widget> + </item> + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0" > + <size> + <width>71</width> + <height>26</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item> + <widget class="QListWidget" name="listWidget" /> + </item> + </layout> + </widget> + <tabstops> + <tabstop>listWidget</tabstop> + <tabstop>newButton</tabstop> + <tabstop>editButton</tabstop> + <tabstop>renameButton</tabstop> + <tabstop>removeButton</tabstop> + </tabstops> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientviewdialog.cpp b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientviewdialog.cpp new file mode 100644 index 00000000000..6f3d88a9373 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientviewdialog.cpp @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtgradientviewdialog.h" +#include "qtgradientmanager.h" +#include <QtGui/QPushButton> + +QT_BEGIN_NAMESPACE + +QtGradientViewDialog::QtGradientViewDialog(QWidget *parent) + : QDialog(parent) +{ + m_ui.setupUi(this); + m_ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + connect(m_ui.gradientView, SIGNAL(currentGradientChanged(QString)), + this, SLOT(slotGradientSelected(QString))); + connect(m_ui.gradientView, SIGNAL(gradientActivated(QString)), + this, SLOT(slotGradientActivated(QString))); +} + +void QtGradientViewDialog::setGradientManager(QtGradientManager *manager) +{ + m_ui.gradientView->setGradientManager(manager); +} + +QGradient QtGradientViewDialog::getGradient(bool *ok, QtGradientManager *manager, QWidget *parent, const QString &caption) +{ + QtGradientViewDialog dlg(parent); + dlg.setGradientManager(manager); + dlg.setWindowTitle(caption); + QGradient grad = QLinearGradient(); + const int res = dlg.exec(); + if (res == QDialog::Accepted) + grad = dlg.m_ui.gradientView->gradientManager()->gradients().value(dlg.m_ui.gradientView->currentGradient()); + if (ok) + *ok = res == QDialog::Accepted; + return grad; +} + +void QtGradientViewDialog::slotGradientSelected(const QString &id) +{ + m_ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!id.isEmpty()); +} + +void QtGradientViewDialog::slotGradientActivated(const QString &id) +{ + Q_UNUSED(id) + accept(); +} + +QT_END_NAMESPACE diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientviewdialog.h b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientviewdialog.h new file mode 100644 index 00000000000..f30799b5d17 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientviewdialog.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef GRADIENTVIEWDIALOG_H +#define GRADIENTVIEWDIALOG_H + +#include <QtGui/QWidget> +#include <QtCore/QMap> +#include "ui_qtgradientviewdialog.h" + +QT_BEGIN_NAMESPACE + +class QtGradientManager; + +class QtGradientViewDialog : public QDialog +{ + Q_OBJECT +public: + QtGradientViewDialog(QWidget *parent = 0); + + void setGradientManager(QtGradientManager *manager); + QtGradientManager *gradientManager() const; + + static QGradient getGradient(bool *ok, QtGradientManager *manager, QWidget *parent = 0, const QString &caption = tr("Select Gradient", 0)); + +private slots: + void slotGradientSelected(const QString &id); + void slotGradientActivated(const QString &id); + +private: + Ui::QtGradientViewDialog m_ui; +}; + +QT_END_NAMESPACE + +#endif + diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientviewdialog.ui b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientviewdialog.ui new file mode 100644 index 00000000000..6b3bebbb0c0 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientviewdialog.ui @@ -0,0 +1,121 @@ +<ui version="4.0" > + <comment>********************************************************************* +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +*********************************************************************</comment> + <class>QtGradientViewDialog</class> + <widget class="QDialog" name="QtGradientViewDialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>178</width> + <height>72</height> + </rect> + </property> + <property name="windowTitle" > + <string>Select Gradient</string> + </property> + <layout class="QVBoxLayout" > + <item> + <widget class="QtGradientView" name="gradientView" > + <property name="sizePolicy" > + <sizepolicy vsizetype="MinimumExpanding" hsizetype="MinimumExpanding" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons" > + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>QtGradientView</class> + <extends>QFrame</extends> + <header>qtgradientview.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>QtGradientViewDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel" > + <x>72</x> + <y>224</y> + </hint> + <hint type="destinationlabel" > + <x>21</x> + <y>243</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>QtGradientViewDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel" > + <x>168</x> + <y>233</y> + </hint> + <hint type="destinationlabel" > + <x>152</x> + <y>251</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientwidget.cpp b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientwidget.cpp new file mode 100644 index 00000000000..779126f735b --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientwidget.cpp @@ -0,0 +1,815 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtgradientwidget.h" +#include <QtCore/QMap> +#include <QtGui/QImage> +#include <QtGui/QPainter> +#include <QtGui/QScrollBar> +#include <QtGui/QMouseEvent> + +#define _USE_MATH_DEFINES + + +#include "math.h" + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +QT_BEGIN_NAMESPACE + +class QtGradientWidgetPrivate +{ + QtGradientWidget *q_ptr; + Q_DECLARE_PUBLIC(QtGradientWidget) +public: + QPointF fromViewport(const QPointF &point) const; + QPointF toViewport(const QPointF &point) const; +// void setupDrag(QtGradientStop *stop, int x); + + QPointF checkRange(const QPointF &point) const; + QRectF pointRect(const QPointF &point, double size) const; + + double correctAngle(double angle) const; + void setAngleConical(double angle); + + void paintPoint(QPainter *painter, const QPointF &point, double size) const; + + double m_handleSize; + bool m_backgroundCheckered; + + QGradientStops m_gradientStops; + QGradient::Type m_gradientType; + QGradient::Spread m_gradientSpread; + QPointF m_startLinear; + QPointF m_endLinear; + QPointF m_centralRadial; + QPointF m_focalRadial; + qreal m_radiusRadial; + QPointF m_centralConical; + qreal m_angleConical; + + enum Handle { + NoHandle, + StartLinearHandle, + EndLinearHandle, + CentralRadialHandle, + FocalRadialHandle, + RadiusRadialHandle, + CentralConicalHandle, + AngleConicalHandle + }; + + Handle m_dragHandle; + QPointF m_dragOffset; + //double m_radiusOffset; + double m_radiusFactor; + double m_dragRadius; + double m_angleOffset; + double m_dragAngle; +}; + +double QtGradientWidgetPrivate::correctAngle(double angle) const +{ + double a = angle; + while (a >= 360) + a -= 360; + while (a < 0) + a += 360; + return a; +} + +void QtGradientWidgetPrivate::setAngleConical(double angle) +{ + double a = correctAngle(angle); + if (m_angleConical == a) + return; + m_angleConical = a; + emit q_ptr->angleConicalChanged(m_angleConical); +} + +QRectF QtGradientWidgetPrivate::pointRect(const QPointF &point, double size) const +{ + return QRectF(point.x() - size / 2, point.y() - size / 2, size, size); +} + +QPointF QtGradientWidgetPrivate::checkRange(const QPointF &point) const +{ + QPointF p = point; + if (p.x() > 1) + p.setX(1); + else if (p.x() < 0) + p.setX(0); + if (p.y() > 1) + p.setY(1); + else if (p.y() < 0) + p.setY(0); + return p; +} + +QPointF QtGradientWidgetPrivate::fromViewport(const QPointF &point) const +{ + QSize size = q_ptr->size(); + return QPointF(point.x() / size.width(), point.y() / size.height()); +} + +QPointF QtGradientWidgetPrivate::toViewport(const QPointF &point) const +{ + QSize size = q_ptr->size(); + return QPointF(point.x() * size.width(), point.y() * size.height()); +} + +void QtGradientWidgetPrivate::paintPoint(QPainter *painter, const QPointF &point, double size) const +{ + QPointF pf = toViewport(point); + QRectF rf = pointRect(pf, size); + + QPen pen; + pen.setWidthF(1); + QColor alphaZero = Qt::white; + alphaZero.setAlpha(0); + + painter->save(); + painter->drawEllipse(rf); + + /* + painter->save(); + + QLinearGradient lgV(0, rf.top(), 0, rf.bottom()); + lgV.setColorAt(0, alphaZero); + lgV.setColorAt(0.25, Qt::white); + lgV.setColorAt(0.25, Qt::white); + lgV.setColorAt(1, alphaZero); + pen.setBrush(lgV); + painter->setPen(pen); + + painter->drawLine(QPointF(pf.x(), rf.top()), QPointF(pf.x(), rf.bottom())); + + QLinearGradient lgH(rf.left(), 0, rf.right(), 0); + lgH.setColorAt(0, alphaZero); + lgH.setColorAt(0.5, Qt::white); + lgH.setColorAt(1, alphaZero); + pen.setBrush(lgH); + painter->setPen(pen); + + painter->drawLine(QPointF(rf.left(), pf.y()), QPointF(rf.right(), pf.y())); + + painter->restore(); + */ + + painter->restore(); +} + +/* +void QtGradientWidgetPrivate::setupDrag(QtGradientStop *stop, int x) +{ + m_model->setCurrentStop(stop); + + int viewportX = qRound(toViewport(stop->position())); + m_dragOffset = x - viewportX; + + QList<QtGradientStop *> stops = m_stops; + m_stops.clear(); + QListIterator<QtGradientStop *> itStop(stops); + while (itStop.hasNext()) { + QtGradientStop *s = itStop.next(); + if (m_model->isSelected(s) || s == stop) { + m_dragStops[s] = s->position() - stop->position(); + m_stops.append(s); + } else { + m_dragOriginal[s->position()] = s->color(); + } + } + itStop.toFront(); + while (itStop.hasNext()) { + QtGradientStop *s = itStop.next(); + if (!m_model->isSelected(s)) + m_stops.append(s); + } + m_stops.removeAll(stop); + m_stops.prepend(stop); +} +*/ +//////////////////////////// + +QtGradientWidget::QtGradientWidget(QWidget *parent) + : QWidget(parent), d_ptr(new QtGradientWidgetPrivate) +{ + d_ptr->q_ptr = this; + d_ptr->m_backgroundCheckered = true; + d_ptr->m_handleSize = 20.0; + d_ptr->m_gradientType = QGradient::LinearGradient; + d_ptr->m_startLinear = QPointF(0, 0); + d_ptr->m_endLinear = QPointF(1, 1); + d_ptr->m_centralRadial = QPointF(0.5, 0.5); + d_ptr->m_focalRadial = QPointF(0.5, 0.5); + d_ptr->m_radiusRadial = 0.5; + d_ptr->m_centralConical = QPointF(0.5, 0.5); + d_ptr->m_angleConical = 0; + d_ptr->m_dragHandle = QtGradientWidgetPrivate::NoHandle; + + setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred)); +} + +QtGradientWidget::~QtGradientWidget() +{ +} + +QSize QtGradientWidget::sizeHint() const +{ + return QSize(176, 176); +} + +QSize QtGradientWidget::minimumSizeHint() const +{ + return QSize(128, 128); +} + +int QtGradientWidget::heightForWidth(int w) const +{ + return w; +} + +void QtGradientWidget::setBackgroundCheckered(bool checkered) +{ + if (d_ptr->m_backgroundCheckered == checkered) + return; + d_ptr->m_backgroundCheckered = checkered; + update(); +} + +bool QtGradientWidget::isBackgroundCheckered() const +{ + return d_ptr->m_backgroundCheckered; +} + +void QtGradientWidget::mousePressEvent(QMouseEvent *e) +{ + if (e->button() != Qt::LeftButton) + return; + + QPoint p = e->pos(); + if (d_ptr->m_gradientType == QGradient::LinearGradient) { + QPointF startPoint = d_ptr->toViewport(d_ptr->m_startLinear); + double x = p.x() - startPoint.x(); + double y = p.y() - startPoint.y(); + + if ((d_ptr->m_handleSize * d_ptr->m_handleSize / 4) > (x * x + y * y)) { + d_ptr->m_dragHandle = QtGradientWidgetPrivate::StartLinearHandle; + d_ptr->m_dragOffset = QPointF(x, y); + update(); + return; + } + + QPointF endPoint = d_ptr->toViewport(d_ptr->m_endLinear); + x = p.x() - endPoint.x(); + y = p.y() - endPoint.y(); + + if ((d_ptr->m_handleSize * d_ptr->m_handleSize / 4) > (x * x + y * y)) { + d_ptr->m_dragHandle = QtGradientWidgetPrivate::EndLinearHandle; + d_ptr->m_dragOffset = QPointF(x, y); + update(); + return; + } + } else if (d_ptr->m_gradientType == QGradient::RadialGradient) { + QPointF focalPoint = d_ptr->toViewport(d_ptr->m_focalRadial); + double x = p.x() - focalPoint.x(); + double y = p.y() - focalPoint.y(); + + if ((d_ptr->m_handleSize * d_ptr->m_handleSize / 9) > (x * x + y * y)) { + d_ptr->m_dragHandle = QtGradientWidgetPrivate::FocalRadialHandle; + d_ptr->m_dragOffset = QPointF(x, y); + update(); + return; + } + + QPointF centralPoint = d_ptr->toViewport(d_ptr->m_centralRadial); + x = p.x() - centralPoint.x(); + y = p.y() - centralPoint.y(); + + if ((d_ptr->m_handleSize * d_ptr->m_handleSize / 4) > (x * x + y * y)) { + d_ptr->m_dragHandle = QtGradientWidgetPrivate::CentralRadialHandle; + d_ptr->m_dragOffset = QPointF(x, y); + update(); + return; + } + + QPointF central = d_ptr->toViewport(d_ptr->m_centralRadial); + QRectF r = d_ptr->pointRect(central, 2 * d_ptr->m_handleSize / 3); + QRectF r1(0, r.y(), size().width(), r.height()); + QRectF r2(r.x(), 0, r.width(), r.y()); + QRectF r3(r.x(), r.y() + r.height(), r.width(), size().height() - r.y() - r.height()); + QPointF pF(p.x(), p.y()); + if (r1.contains(pF) || r2.contains(pF) || r3.contains(pF)) { + x = pF.x() / size().width() - d_ptr->m_centralRadial.x(); + y = pF.y() / size().height() - d_ptr->m_centralRadial.y(); + double clickRadius = sqrt(x * x + y * y); + //d_ptr->m_radiusOffset = d_ptr->m_radiusRadial - clickRadius; + d_ptr->m_radiusFactor = d_ptr->m_radiusRadial / clickRadius; + if (d_ptr->m_radiusFactor == 0) + d_ptr->m_radiusFactor = 1; + d_ptr->m_dragRadius = d_ptr->m_radiusRadial; + d_ptr->m_dragHandle = QtGradientWidgetPrivate::RadiusRadialHandle; + mouseMoveEvent(e); + update(); + return; + } + } else if (d_ptr->m_gradientType == QGradient::ConicalGradient) { + QPointF centralPoint = d_ptr->toViewport(d_ptr->m_centralConical); + double x = p.x() - centralPoint.x(); + double y = p.y() - centralPoint.y(); + + if ((d_ptr->m_handleSize * d_ptr->m_handleSize / 4) > (x * x + y * y)) { + d_ptr->m_dragHandle = QtGradientWidgetPrivate::CentralConicalHandle; + d_ptr->m_dragOffset = QPointF(x, y); + update(); + return; + } + double radius = size().width(); + if (size().height() < radius) + radius = size().height(); + radius /= 2; + double corr = d_ptr->m_handleSize / 3; + radius -= corr; + QPointF vp = d_ptr->toViewport(d_ptr->m_centralConical); + x = p.x() - vp.x(); + y = p.y() - vp.y(); + if (((radius - corr) * (radius - corr) < (x * x + y * y)) && + ((radius + corr) * (radius + corr) > (x * x + y * y))) { + QPointF central = d_ptr->toViewport(d_ptr->m_centralConical); + QPointF current(e->pos().x(), e->pos().y()); + x = current.x() - central.x(); + y = current.y() - central.y(); + x /= size().width() / 2; + y /= size().height() / 2; + double r = sqrt(x * x + y * y); + + double arcSin = asin(y / r); + double arcCos = acos(x / r); + + double angle = arcCos * 180 / M_PI; + if (arcSin > 0) { + angle = -angle; + } + + d_ptr->m_angleOffset = d_ptr->m_angleConical - angle; + d_ptr->m_dragAngle = d_ptr->m_angleConical; + d_ptr->m_dragHandle = QtGradientWidgetPrivate::AngleConicalHandle; + update(); + return; + } + } +} + +void QtGradientWidget::mouseReleaseEvent(QMouseEvent *e) +{ + Q_UNUSED(e) + d_ptr->m_dragHandle = QtGradientWidgetPrivate::NoHandle; + update(); +} + +void QtGradientWidget::mouseMoveEvent(QMouseEvent *e) +{ + if (d_ptr->m_dragHandle == QtGradientWidgetPrivate::NoHandle) + return; + + QPointF newPos = QPointF((double)e->pos().x() - d_ptr->m_dragOffset.x(), + (double)e->pos().y() - d_ptr->m_dragOffset.y()); + QPointF newPoint = d_ptr->fromViewport(newPos); + if (newPoint.x() < 0) + newPoint.setX(0); + else if (newPoint.x() > 1) + newPoint.setX(1); + if (newPoint.y() < 0) + newPoint.setY(0); + else if (newPoint.y() > 1) + newPoint.setY(1); + + if (d_ptr->m_dragHandle == QtGradientWidgetPrivate::StartLinearHandle) { + d_ptr->m_startLinear = newPoint; + emit startLinearChanged(newPoint); + } else if (d_ptr->m_dragHandle == QtGradientWidgetPrivate::EndLinearHandle) { + d_ptr->m_endLinear = newPoint; + emit endLinearChanged(newPoint); + } else if (d_ptr->m_dragHandle == QtGradientWidgetPrivate::CentralRadialHandle) { + d_ptr->m_centralRadial = newPoint; + emit centralRadialChanged(newPoint); + } else if (d_ptr->m_dragHandle == QtGradientWidgetPrivate::FocalRadialHandle) { + d_ptr->m_focalRadial = newPoint; + emit focalRadialChanged(newPoint); + } else if (d_ptr->m_dragHandle == QtGradientWidgetPrivate::RadiusRadialHandle) { + QPointF centralPoint = d_ptr->toViewport(d_ptr->m_centralRadial); + QPointF pF(e->pos().x(), e->pos().y()); + double x = pF.x() - centralPoint.x(); + double y = pF.y() - centralPoint.y(); + + if ((d_ptr->m_handleSize * d_ptr->m_handleSize / 4) > (x * x + y * y)) { + if (d_ptr->m_radiusRadial != d_ptr->m_dragRadius) { + d_ptr->m_radiusRadial = d_ptr->m_dragRadius; + emit radiusRadialChanged(d_ptr->m_radiusRadial); + } + } else { + x = pF.x() / size().width() - d_ptr->m_centralRadial.x(); + y = pF.y() / size().height() - d_ptr->m_centralRadial.y(); + double moveRadius = sqrt(x * x + y * y); + //double newRadius = moveRadius + d_ptr->m_radiusOffset; + double newRadius = moveRadius * d_ptr->m_radiusFactor; + if (newRadius > 2) + newRadius = 2; + d_ptr->m_radiusRadial = newRadius; + emit radiusRadialChanged(d_ptr->m_radiusRadial); + } + } else if (d_ptr->m_dragHandle == QtGradientWidgetPrivate::CentralConicalHandle) { + d_ptr->m_centralConical = newPoint; + emit centralConicalChanged(newPoint); + } else if (d_ptr->m_dragHandle == QtGradientWidgetPrivate::AngleConicalHandle) { + QPointF centralPoint = d_ptr->toViewport(d_ptr->m_centralConical); + QPointF pF(e->pos().x(), e->pos().y()); + double x = pF.x() - centralPoint.x(); + double y = pF.y() - centralPoint.y(); + + if ((d_ptr->m_handleSize * d_ptr->m_handleSize / 4) > (x * x + y * y)) { + if (d_ptr->m_angleConical != d_ptr->m_dragAngle) { + d_ptr->m_angleConical = d_ptr->m_dragAngle; + emit angleConicalChanged(d_ptr->m_angleConical); + } + } else { + QPointF central = d_ptr->toViewport(d_ptr->m_centralConical); + QPointF current = pF; + x = current.x() - central.x(); + y = current.y() - central.y(); + x /= size().width() / 2; + y /= size().height() / 2; + double r = sqrt(x * x + y * y); + + double arcSin = asin(y / r); + double arcCos = acos(x / r); + + double angle = arcCos * 180 / M_PI; + if (arcSin > 0) { + angle = -angle; + } + + angle += d_ptr->m_angleOffset; + + d_ptr->setAngleConical(angle); + } + } + update(); +} + +void QtGradientWidget::mouseDoubleClickEvent(QMouseEvent *e) +{ + mousePressEvent(e); +} + +void QtGradientWidget::paintEvent(QPaintEvent *e) +{ + Q_UNUSED(e) + + QPainter p(this); + + if (d_ptr->m_backgroundCheckered) { + int pixSize = 40; + QPixmap pm(2 * pixSize, 2 * pixSize); + + QPainter pmp(&pm); + pmp.fillRect(0, 0, pixSize, pixSize, Qt::white); + pmp.fillRect(pixSize, pixSize, pixSize, pixSize, Qt::white); + pmp.fillRect(0, pixSize, pixSize, pixSize, Qt::black); + pmp.fillRect(pixSize, 0, pixSize, pixSize, Qt::black); + + p.setBrushOrigin((size().width() % pixSize + pixSize) / 2, (size().height() % pixSize + pixSize) / 2); + p.fillRect(rect(), pm); + p.setBrushOrigin(0, 0); + } + + QGradient *gradient = 0; + switch (d_ptr->m_gradientType) { + case QGradient::LinearGradient: + gradient = new QLinearGradient(d_ptr->m_startLinear, d_ptr->m_endLinear); + break; + case QGradient::RadialGradient: + gradient = new QRadialGradient(d_ptr->m_centralRadial, d_ptr->m_radiusRadial, d_ptr->m_focalRadial); + break; + case QGradient::ConicalGradient: + gradient = new QConicalGradient(d_ptr->m_centralConical, d_ptr->m_angleConical); + break; + default: + break; + } + if (!gradient) + return; + + gradient->setStops(d_ptr->m_gradientStops); + gradient->setSpread(d_ptr->m_gradientSpread); + + p.save(); + p.scale(size().width(), size().height()); + p.fillRect(QRect(0, 0, 1, 1), *gradient); + p.restore(); + + p.setRenderHint(QPainter::Antialiasing); + + QColor c = QColor::fromRgbF(0.5, 0.5, 0.5, 0.5); + QBrush br(c); + p.setBrush(br); + QPen pen(Qt::white); + pen.setWidthF(1); + p.setPen(pen); + QPen dragPen = pen; + dragPen.setWidthF(2); + if (d_ptr->m_gradientType == QGradient::LinearGradient) { + p.save(); + if (d_ptr->m_dragHandle == QtGradientWidgetPrivate::StartLinearHandle) + p.setPen(dragPen); + d_ptr->paintPoint(&p, d_ptr->m_startLinear, d_ptr->m_handleSize); + p.restore(); + + p.save(); + if (d_ptr->m_dragHandle == QtGradientWidgetPrivate::EndLinearHandle) + p.setPen(dragPen); + d_ptr->paintPoint(&p, d_ptr->m_endLinear, d_ptr->m_handleSize); + p.restore(); + } else if (d_ptr->m_gradientType == QGradient::RadialGradient) { + QPointF central = d_ptr->toViewport(d_ptr->m_centralRadial); + + p.save(); + QRectF r = d_ptr->pointRect(central, 2 * d_ptr->m_handleSize / 3); + QRectF r1(0, r.y(), size().width(), r.height()); + QRectF r2(r.x(), 0, r.width(), r.y()); + QRectF r3(r.x(), r.y() + r.height(), r.width(), size().height() - r.y() - r.height()); + p.fillRect(r1, c); + p.fillRect(r2, c); + p.fillRect(r3, c); + p.setBrush(Qt::NoBrush); + p.save(); + if (d_ptr->m_dragHandle == QtGradientWidgetPrivate::CentralRadialHandle) + p.setPen(dragPen); + d_ptr->paintPoint(&p, d_ptr->m_centralRadial, d_ptr->m_handleSize); + p.restore(); + + QRectF rect = QRectF(central.x() - d_ptr->m_radiusRadial * size().width(), + central.y() - d_ptr->m_radiusRadial * size().height(), + 2 * d_ptr->m_radiusRadial * size().width(), + 2 * d_ptr->m_radiusRadial * size().height()); + p.setClipRect(r1); + p.setClipRect(r2, Qt::UniteClip); + p.setClipRect(r3, Qt::UniteClip); + p.drawEllipse(rect); + if (d_ptr->m_dragHandle == QtGradientWidgetPrivate::RadiusRadialHandle) { + p.save(); + p.setPen(dragPen); + QRectF rect = QRectF(central.x() - d_ptr->m_radiusRadial / d_ptr->m_radiusFactor * size().width(), + central.y() - d_ptr->m_radiusRadial / d_ptr->m_radiusFactor * size().height(), + 2 * d_ptr->m_radiusRadial / d_ptr->m_radiusFactor * size().width(), + 2 * d_ptr->m_radiusRadial / d_ptr->m_radiusFactor * size().height()); + p.drawEllipse(rect); + + p.restore(); + } + p.restore(); + + p.save(); + if (d_ptr->m_dragHandle == QtGradientWidgetPrivate::FocalRadialHandle) + p.setPen(dragPen); + d_ptr->paintPoint(&p, d_ptr->m_focalRadial, 2 * d_ptr->m_handleSize / 3); + p.restore(); + } else if (d_ptr->m_gradientType == QGradient::ConicalGradient) { + double radius = size().width(); + if (size().height() < radius) + radius = size().height(); + radius /= 2; + double corr = d_ptr->m_handleSize / 3; + radius -= corr; + QPointF central = d_ptr->toViewport(d_ptr->m_centralConical); + + p.save(); + p.setBrush(Qt::NoBrush); + QPen pen2(c); + pen2.setWidthF(2 * d_ptr->m_handleSize / 3); + p.setPen(pen2); + p.drawEllipse(d_ptr->pointRect(central, 2 * radius)); + p.restore(); + + p.save(); + p.setBrush(Qt::NoBrush); + int pointCount = 2; + for (int i = 0; i < pointCount; i++) { + QPointF ang(cos(M_PI * (i * 180.0 / pointCount + d_ptr->m_angleConical) / 180) * size().width() / 2, + -sin(M_PI * (i * 180.0 / pointCount + d_ptr->m_angleConical) / 180) * size().height() / 2); + double mod = sqrt(ang.x() * ang.x() + ang.y() * ang.y()); + p.drawLine(QPointF(central.x() + ang.x() * (radius - corr) / mod, + central.y() + ang.y() * (radius - corr) / mod), + QPointF(central.x() + ang.x() * (radius + corr) / mod, + central.y() + ang.y() * (radius + corr) / mod)); + p.drawLine(QPointF(central.x() - ang.x() * (radius - corr) / mod, + central.y() - ang.y() * (radius - corr) / mod), + QPointF(central.x() - ang.x() * (radius + corr) / mod, + central.y() - ang.y() * (radius + corr) / mod)); + } + if (d_ptr->m_dragHandle == QtGradientWidgetPrivate::AngleConicalHandle) { + p.save(); + p.setPen(dragPen); + QPointF ang(cos(M_PI * (d_ptr->m_angleConical - d_ptr->m_angleOffset) / 180) * size().width() / 2, + -sin(M_PI * (d_ptr->m_angleConical - d_ptr->m_angleOffset) / 180) * size().height() / 2); + double mod = sqrt(ang.x() * ang.x() + ang.y() * ang.y()); + p.drawLine(QPointF(central.x() + ang.x() * (radius - corr) / mod, + central.y() + ang.y() * (radius - corr) / mod), + QPointF(central.x() + ang.x() * (radius + corr) / mod, + central.y() + ang.y() * (radius + corr) / mod)); + p.restore(); + } + + p.restore(); + + p.save(); + if (d_ptr->m_dragHandle == QtGradientWidgetPrivate::CentralConicalHandle) + p.setPen(dragPen); + d_ptr->paintPoint(&p, d_ptr->m_centralConical, d_ptr->m_handleSize); + p.restore(); + + } + + delete gradient; +} + +void QtGradientWidget::setGradientStops(const QGradientStops &stops) +{ + d_ptr->m_gradientStops = stops; + update(); +} + +QGradientStops QtGradientWidget::gradientStops() const +{ + return d_ptr->m_gradientStops; +} + +void QtGradientWidget::setGradientType(QGradient::Type type) +{ + if (type == QGradient::NoGradient) + return; + if (d_ptr->m_gradientType == type) + return; + + d_ptr->m_gradientType = type; + update(); +} + +QGradient::Type QtGradientWidget::gradientType() const +{ + return d_ptr->m_gradientType; +} + +void QtGradientWidget::setGradientSpread(QGradient::Spread spread) +{ + if (d_ptr->m_gradientSpread == spread) + return; + + d_ptr->m_gradientSpread = spread; + update(); +} + +QGradient::Spread QtGradientWidget::gradientSpread() const +{ + return d_ptr->m_gradientSpread; +} + +void QtGradientWidget::setStartLinear(const QPointF &point) +{ + if (d_ptr->m_startLinear == point) + return; + + d_ptr->m_startLinear = d_ptr->checkRange(point); + update(); +} + +QPointF QtGradientWidget::startLinear() const +{ + return d_ptr->m_startLinear; +} + +void QtGradientWidget::setEndLinear(const QPointF &point) +{ + if (d_ptr->m_endLinear == point) + return; + + d_ptr->m_endLinear = d_ptr->checkRange(point); + update(); +} + +QPointF QtGradientWidget::endLinear() const +{ + return d_ptr->m_endLinear; +} + +void QtGradientWidget::setCentralRadial(const QPointF &point) +{ + if (d_ptr->m_centralRadial == point) + return; + + d_ptr->m_centralRadial = point; + update(); +} + +QPointF QtGradientWidget::centralRadial() const +{ + return d_ptr->m_centralRadial; +} + +void QtGradientWidget::setFocalRadial(const QPointF &point) +{ + if (d_ptr->m_focalRadial == point) + return; + + d_ptr->m_focalRadial = point; + update(); +} + +QPointF QtGradientWidget::focalRadial() const +{ + return d_ptr->m_focalRadial; +} + +void QtGradientWidget::setRadiusRadial(qreal radius) +{ + if (d_ptr->m_radiusRadial == radius) + return; + + d_ptr->m_radiusRadial = radius; + update(); +} + +qreal QtGradientWidget::radiusRadial() const +{ + return d_ptr->m_radiusRadial; +} + +void QtGradientWidget::setCentralConical(const QPointF &point) +{ + if (d_ptr->m_centralConical == point) + return; + + d_ptr->m_centralConical = point; + update(); +} + +QPointF QtGradientWidget::centralConical() const +{ + return d_ptr->m_centralConical; +} + +void QtGradientWidget::setAngleConical(qreal angle) +{ + if (d_ptr->m_angleConical == angle) + return; + + d_ptr->m_angleConical = angle; + update(); +} + +qreal QtGradientWidget::angleConical() const +{ + return d_ptr->m_angleConical; +} + +QT_END_NAMESPACE diff --git a/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientwidget.h b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientwidget.h new file mode 100644 index 00000000000..afb98bf7640 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/qtgradienteditor/qtgradientwidget.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTGRADIENTWIDGET_H +#define QTGRADIENTWIDGET_H + +#include <QtGui/QWidget> + +QT_BEGIN_NAMESPACE + +class QtGradientWidget : public QWidget +{ + Q_OBJECT + Q_PROPERTY(bool backgroundCheckered READ isBackgroundCheckered WRITE setBackgroundCheckered) +public: + QtGradientWidget(QWidget *parent = 0); + ~QtGradientWidget(); + + QSize minimumSizeHint() const; + QSize sizeHint() const; + int heightForWidth(int w) const; + + bool isBackgroundCheckered() const; + void setBackgroundCheckered(bool checkered); + + QGradientStops gradientStops() const; + + void setGradientType(QGradient::Type type); + QGradient::Type gradientType() const; + + void setGradientSpread(QGradient::Spread spread); + QGradient::Spread gradientSpread() const; + + void setStartLinear(const QPointF &point); + QPointF startLinear() const; + + void setEndLinear(const QPointF &point); + QPointF endLinear() const; + + void setCentralRadial(const QPointF &point); + QPointF centralRadial() const; + + void setFocalRadial(const QPointF &point); + QPointF focalRadial() const; + + void setRadiusRadial(qreal radius); + qreal radiusRadial() const; + + void setCentralConical(const QPointF &point); + QPointF centralConical() const; + + void setAngleConical(qreal angle); + qreal angleConical() const; + +public slots: + void setGradientStops(const QGradientStops &stops); +signals: + + void startLinearChanged(const QPointF &point); + void endLinearChanged(const QPointF &point); + void centralRadialChanged(const QPointF &point); + void focalRadialChanged(const QPointF &point); + void radiusRadialChanged(qreal radius); + void centralConicalChanged(const QPointF &point); + void angleConicalChanged(qreal angle); + +protected: + void paintEvent(QPaintEvent *e); + void mousePressEvent(QMouseEvent *e); + void mouseReleaseEvent(QMouseEvent *e); + void mouseMoveEvent(QMouseEvent *e); + void mouseDoubleClickEvent(QMouseEvent *e); + +private: + QScopedPointer<class QtGradientWidgetPrivate> d_ptr; + Q_DECLARE_PRIVATE(QtGradientWidget) + Q_DISABLE_COPY(QtGradientWidget) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/qmldesigner/components/propertyeditor/resetwidget.cpp b/src/plugins/qmldesigner/components/propertyeditor/resetwidget.cpp new file mode 100644 index 00000000000..3d74b2815e9 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/resetwidget.cpp @@ -0,0 +1,129 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + + +#include "qml.h" +#include "resetwidget.h" +#include <QVBoxLayout> +#include <QHBoxLayout> +#include <QStringList> +#include <QTableWidget> +#include <QHeaderView> +#include <QPushButton> + +#include <QDebug> +#include <QApplication> + +QML_DECLARE_TYPE(QmlDesigner::ResetWidget); +QML_DEFINE_TYPE(Bauhaus, 1, 0, ResetWidget, QmlDesigner::ResetWidget); + + +namespace QmlDesigner { + + +ResetWidget::ResetWidget(QWidget *parent) : QGroupBox(parent), m_backendObject(0) +{ + m_vlayout = new QVBoxLayout(this); + m_vlayout->setContentsMargins(2,2,2,2); + + QPushButton *b = new QPushButton(this); + b->setText("reset all properties"); + m_vlayout->addWidget(b); + + setLayout(m_vlayout); +} + +void ResetWidget::resetView() +{ + m_tableWidget->clear(); + delete m_tableWidget; + setupView(); +} + +void ResetWidget::setupView() +{ + m_tableWidget = new QTableWidget(this); + m_vlayout->addWidget(m_tableWidget); + + m_tableWidget->setAlternatingRowColors(true); + m_tableWidget->horizontalHeader()->hide(); + m_tableWidget->verticalHeader()->hide(); + m_tableWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + m_tableWidget->setShowGrid(false); + m_tableWidget->setSortingEnabled(true); + m_tableWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + + const QMetaObject *metaObject = m_backendObject->metaObject(); + int count = metaObject->propertyCount(); + + m_tableWidget->setColumnCount(3); + m_tableWidget->setRowCount(count); + for (int i=0;i<count;i++) { + QMetaProperty metaProperty = metaObject->property(i); + addPropertyItem(metaProperty.name(), i); + } + m_tableWidget->resizeRowsToContents(); + m_tableWidget->resizeColumnsToContents(); + m_tableWidget->sortItems(0); + m_tableWidget->setColumnWidth(2, 40); + parentWidget()->resize(parentWidget()->width(), count * 28); + qApp->processEvents(); + +} + +void ResetWidget::addPropertyItem(const QString &name, int row) +{ + QTableWidgetItem *newItem = new QTableWidgetItem(name); + m_tableWidget->setItem(row, 0, newItem); + ResetWidgetPushButton *b = new ResetWidgetPushButton(m_tableWidget); + b->setName(name); + b->setText("reset"); + connect(b, SIGNAL(pressed(const QString &)), this, SLOT(buttonPressed(const QString &))); + b->setMaximumHeight(15); + b->setMinimumHeight(10); + m_tableWidget->setCellWidget(row, 2, b); +} + +void ResetWidget::buttonPressed(const QString &) +{ +} + +ResetWidgetPushButton::ResetWidgetPushButton(QWidget *parent) : QPushButton(parent) +{ + connect(this, SIGNAL(pressed()), this, SLOT(myPressed())); +} + +void ResetWidgetPushButton::myPressed() +{ + emit pressed(m_name); +} + + +} + diff --git a/src/plugins/qmldesigner/components/propertyeditor/resetwidget.h b/src/plugins/qmldesigner/components/propertyeditor/resetwidget.h new file mode 100644 index 00000000000..ddc4c5c1de9 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/resetwidget.h @@ -0,0 +1,102 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef ResetWidget_h +#define ResetWidget_h + +#include <QtGui/QGroupBox> +#include <QtGui/QPushButton> + +class QListWidget; +class QVBoxLayout; +class QTableWidget; + +namespace QmlDesigner { + +class ResetWidget : public QGroupBox +{ + + Q_OBJECT + + Q_PROPERTY(QObject *backendObject READ backendObject WRITE setBackendObject) + +public slots: + void resetView(); + +public: + ResetWidget(QWidget *parent = 0); + QObject* backendObject() + { + return m_backendObject; + } + + void setBackendObject(QObject* backendObject) + { + m_backendObject = backendObject; + setupView(); + } + +public slots: + void buttonPressed(const QString &name); + + +private: + void setupView(); + + void addPropertyItem(const QString &name, int row); + + QObject* m_backendObject; + QVBoxLayout *m_vlayout; + QTableWidget *m_tableWidget; + +}; + +class ResetWidgetPushButton : public QPushButton +{ + Q_OBJECT + +public slots: + void myPressed(); + void setName(const QString &name) + { m_name = name; } + +signals: + void pressed(const QString &name); + +public: + ResetWidgetPushButton(QWidget *parent = 0); +private: + QString m_name; + +}; + +} + +#endif //ResetWidget_h + diff --git a/src/plugins/qmldesigner/components/resources/images/checkbox_checked.png b/src/plugins/qmldesigner/components/resources/images/checkbox_checked.png new file mode 100644 index 0000000000000000000000000000000000000000..18d7f6f011a78549adcd411c6542ba0904d23516 GIT binary patch literal 299 zcmeAS@N?(olHy`uVBq!ia0vp@Ak4u6ByT*@`3|JmOFVsD*>5qj@GF}iu-+F76yhxK zh%9DckUI#%jE6Hrf`Ni8>5jgR3=A9lx&I`x0{Oc<T^vIyZY6J7pOBGofK5u`?SJD3 z3_1!)=R_9HKhgC5MsU%BprZJjbq8AbPo3S~Z^d-ImEro)+l%Tq#qMTKGIne^=gGi% zj(^uxPWzi8@6_X4UeC(ebMxrOy1hZ~rkBeo>8G#%bDrb+(vM9q@7EpO@V<X%rJZm5 zDXvnXh*MuP59G16h0DCLzdgOHuI(oGR_*sKH#7UzFV!xIU-<L2;C2D;qYL-9&-}mt sapMnemq(nuX|3~lw(-<&JP^h3^051^*OfWjfWBbxboFyt=akR{0MDCywEzGB literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/resources/images/checkbox_checked_hover.png b/src/plugins/qmldesigner/components/resources/images/checkbox_checked_hover.png new file mode 100644 index 0000000000000000000000000000000000000000..14cc4c7d6617b92cb564ba7b93fc9da37a800d89 GIT binary patch literal 302 zcmeAS@N?(olHy`uVBq!ia0vp@Ak4u6ByT*@`3|JmOFVsD*>5qj@GF}{8ohrG6yhxK zh%9DckUI#%jE6Hrf`Ni8>5jgR3=A9lx&I`x0{MGAT^vIyZY7^tlaP^cfK5um_`mT3 z1|5Y(X<YyPQx5jO(X|ZFJh{HdKH*UL6TX|#vW>5u8m>Q*Ui^QP?M~(-W5<?x-VB`Q z?(bSH^lww=o$Wh0u5Z2lqpAB^t^CrT@%xHC?DL8GIbTpu_2;8Q`?I_H_t!s5_${)3 z%0qQ#_B#%9moM8bDPdP^@V_VS)K|%)A9;BC)bDZHUiuWL{EO>s{P~?fBW?KpTAumz vUz`6(yn;}9hJn%Se<>SMe{{3{V`MNs=s9PToX`)TM;JU^{an^LB{Ts5qjGuq literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/resources/images/checkbox_checked_pressed.png b/src/plugins/qmldesigner/components/resources/images/checkbox_checked_pressed.png new file mode 100644 index 0000000000000000000000000000000000000000..25e4bde1d223c1d88d6918b9197eed04afa716a7 GIT binary patch literal 474 zcmV<00VV#4P)<h;3K|Lk000e1NJLTq000dD000dL1^@s6a_i)L00007bV*G`2igGy z4<k7zgOgeS000SaNLh0L03N{r03N{s!)a7g00004XF*Lt006O%3;baP0004LNkl<Z zc-mvk%*?D{U|?uqARaWZ0Eu=IG=M-m3yh^8ExnA5jqL*q3(I$AX6EnAOibTdpfnRB z<F}u`elecAc5MMjEnE`^7uRbZ4vzP%tgPSIn3=z^0cjBX^}BaW`;Q&7FwxUH08<Os z#11qKsPHRH6Nvu$_3NM28#lIn`|(5l+xPFxB4T1-wM;Ng%n*~m0yTg%ewCD!<=MJ_ ze>+G6Q2MQ!vho&~TDYcbH*UzxS+b=0-m_;eE<Qf1*KFKa@#yhmHK5ctLmiz&eJ!oW z7|#3p?HlLs-@k>o?c5ph>g`*zlP6EA0)_u7$;o9WhKFCcdG8({hE0w(HhcJad5`}6 z`<Df%0b~P%n21PKL2Bw=^e{*GiHYf3Oi)lWFt{Ir_`Ezk6KnGGmt%`4xbxnB{>;Y3 z!ST=E!z1<b_3JWLrluFqUb!L)lmGnX3one#2uw!*NlX!7=T!pnPZDq?0F$$jC;^v4 Q3jhEB07*qoM6N<$f*|73NB{r; literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/resources/images/checkbox_unchecked.png b/src/plugins/qmldesigner/components/resources/images/checkbox_unchecked.png new file mode 100644 index 0000000000000000000000000000000000000000..688c1a82d46bd0a1f1753e8fdac7b9c6471de9cf GIT binary patch literal 190 zcmeAS@N?(olHy`uVBq!ia0vp@Ak4u6ByT*@`3|JmOFVsD*>5qj@XM&kL|@$x6yhxK zh%9DckUIpzjCmJI?SO(T>5jgR3=A9lx&I`x0{On4E{-7;x01K4Psm6(z$PW}_P_B1 z1|5Z@b0Q1ppJ;l2Be-ZmP*MENItA~Ke;6nF@K_vFn8;-PPjI^c_p1F4D)TNRg#9>~ gaQcHd_Zn7)dmNGyE7LTRfd(^py85}Sb4q9e0438rPyhe` literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/resources/images/checkbox_unchecked_hover.png b/src/plugins/qmldesigner/components/resources/images/checkbox_unchecked_hover.png new file mode 100644 index 0000000000000000000000000000000000000000..118c5896beb3a14baaaf37e6f7a7bb1450b975e9 GIT binary patch literal 190 zcmeAS@N?(olHy`uVBq!ia0vp@Ak4u6ByT*@`3|JmOFVsD*>5qj@XM%u3f(dpD8yOd z5n0T@Aa@9a8S^fb+5rVw(j9#r85lP9bN@+X1@e78T^vIyZY7^tlaP^cfK5um_`mT3 z1|5Y(X<YyPQx5jO(X|ZFJh{Hde!|in{~A2h*ybcm@Mx6%KWFxQ)~oRfLjD;Bv;U=R fNd3{xdX0&}^S9)ZSDj)Y*D-jy`njxgN@xNAI=(#w literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/resources/images/checkbox_unchecked_pressed.png b/src/plugins/qmldesigner/components/resources/images/checkbox_unchecked_pressed.png new file mode 100644 index 0000000000000000000000000000000000000000..670fc43ca778e861535a5b6c699412ae36fe8224 GIT binary patch literal 345 zcmeAS@N?(olHy`uVBq!ia0vp@Ak4uAB#T}@sR2^#C7!;n?6(+M_+^xv>>Xr)LYxI2 zk;M!Qa)&^eG4DdD9Z--Z-O<;Pfnj4m_n$;oApeD@i(^Q|t*Mg^@--Rov_)ReZFwPF z@bPN}^WMtOrZzt<jtT2OWaKq!V%ZRCCiYRmcbD1C|D|uuCZ^sr>snO%W&hiW(+)E= zylv3Cz2v25cCGV~D6dHG2C3s5PB)(Y6nSoAzv-HD;iQ=G!WfOEYo=Yw>YAf{q${~_ zfAi(469J||VSHSSWs^=ws@Tt-%D$!SgY=u<a?b@qO?46!8MPQJ)IE)w_i$Y7bGk3Z zRPykvue*`RnFKcuhF|Zk=ig%9kd}3v@$mZgPXU3`c4gkKSkQTUowSPqbM()OHR%^r m&-?$c<E+~tTe<l_xqg>gF0XWb|6!n?89ZJ6T-G@yGywpPxP`0$ literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/resources/images/down_arrow.png b/src/plugins/qmldesigner/components/resources/images/down_arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..20f3aa243684927a6f26f7485340dedb0a2c2985 GIT binary patch literal 271 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)I!3HFqj;YoHDeV&1h!W?b)Wnj^{5*w_%-mE4 zUm(Lo!7DLO!N|bSK*7+=%E;8p#85#S$TMgQG-d^vQ{w6C%6^NHg<nciDAP^@D8yOd z5n0T@z%2yAjF;}#{Q(NHq&xaLGB9lH=l+uj(wFY(;uunKOZU`9UI#%Dw}<y-y-L1F zB?oAJkrI}0%oj55@X`$1p+Bkol)n2J%bi=>4BHOOzcy=b@q#GrAg=%~L&1X*hwoL} zv~LwNbldm6=>6RDmNHHco_~3_b(+Rf2d~aK#^3He`pKUBNq5(}Bvp{t89ZJ6T-G@y GGywpAepOrm literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/resources/images/down_arrow_disabled.png b/src/plugins/qmldesigner/components/resources/images/down_arrow_disabled.png new file mode 100644 index 0000000000000000000000000000000000000000..d5b3655e490c649a42053e5cee0fcde29c2831dd GIT binary patch literal 258 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)I!3HFqj;YoHDeV&1h!W?b)Wnj^{5*w_%-mE4 zUm(Lo!7DLO!N|bSK*7+=%E;8p#85#S$TMgQG-d^vQ{w6C%6^NHm0L(edJDS>P>8d@ zBeIx*fm;ZK886+f`vVkYNq6*hWMJ6X&;2J6q%X?T#WAGfmg$L&oD7NrE*DR_9<e&Y zD#$HhoO`UQr^iRPurJ!+2hZmX-_zDkJtncI>U~VMpr-1*>YGuEw~8h6{8eN*WZ6Dv t^PxGx=EqLlf4KL$m!{400GA(cnC~@cW@qS}ISjOg!PC{xWt~$(69A$(P-OrB literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/resources/images/frame.png b/src/plugins/qmldesigner/components/resources/images/frame.png new file mode 100644 index 0000000000000000000000000000000000000000..302dae3962f46f18e8e49eacd7957097468f5e80 GIT binary patch literal 544 zcmV+*0^j|KP)<h;3K|Lk000e1NJLTq003bC000#T1^@s6R<(e100007bV*G`2igGy z4<sBT#?jgU000SaNLh0L03N{r03N{s!)a7g00004XF*Lt006O%3;baP00056Nkl<Z zc-rlmOOC=Y5Ja613lM^1aKRjd8*sx&Y#<N>BpF_7Dl*Y*7(!+i{iMdR<5iWrP3&a1 z+XdKew_R{XB}tM#pzFFm=oUqh+B{5n7|_tkvMgP%*I_=N^BmGN?OW$H5V~#KaKGOz znx?U_+wB&P$0K8Co;-}mlFep=E$wo-OjoOw37+S<i66V@{0^;r3?VQ9Lr$ktsHzGh zvYd%z00tmF;o^@AT53_V$jJ<z>$=V`#Oe$sNEtxu27CszmCgjZ+c-b$0m=a3*)R^n z%~{zno6W*vv9K{SfmBhtXrePdLnF`}%mgn%(;=Nznm`+~n=m1<5!S{XXIh?2aQ+rI z;U8co@Fgdrvkm71#M<(q2Dv)EF;9n#(BijD(BV?-l4rwb!2o4KZ-NG5yflqVoB`;q zGbAfVbzMJwJa}mukKrQL!p4Bh<zmmK`r&Zsof%v_xFK&6>lYtqqc!&mpmNkQz{~6P z`eSKw-a{n_ebJfwgzt-;kQPN@R#jEimSt%#nMmMP{P)#)9aR`D@bmc`_WQjqbOQP( ie#blw@jbIp0(=3pIwuvYoUwTT0000<MNUMnLSTaKWa!-h literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/resources/images/pushbutton.png b/src/plugins/qmldesigner/components/resources/images/pushbutton.png new file mode 100644 index 0000000000000000000000000000000000000000..be3d533622651d699bdec647a0a7078df9c43eae GIT binary patch literal 508 zcmV<Y0R#StP)<h;3K|Lk000e1NJLTq001xm000vR1^@s6);f+K00007bV*G`2igGy z4=Nf|CRt$s000SaNLh0L03N{r03N{s!)a7g00004XF*Lt006O%3;baP0004tNkl<Z zc-qyMJx;?w5QW#VH$MnV6d;380zx?eN@Y4L9irkGxdAuH6)2EssB#2Q3JPctae+WI z1Qe9SiT8&$iOrt`gv#D0ee!Jgo@ZudvsSCcpei^4PeA7w1#Z9*u*tiHVjEm?&K=+P zr=I6=mScrssJh*51?3q698!SOdcD3M1i`%n;at*MXYKPhMF_z=oz4d812P;m8VzRK z1xrdHU|weJ@GsSB)d?nM6vuIs0<3Vyi9{URN~zqNPvyGHy5E0ZrvUTqkdh%l*1i_k ze98;X{Gb`8$EX0wo?1x<5CG<>3#KT&Xx=-Zl`tv*`y7DMC?Oecp{#BF!~d5KFpQ8e zD*#g;0cZ!P14<YaKz$z}!#a@OqfUB|(%c;wN+BH;i`f{NqR}{_$AAKy-}b`2HMeAp z`<KCM5i78S@`Aobcd4ipd9<^&Ijoe+xt$}Wv_<B7cMmR(SD?v~9~vKAv+RFZ7GVmG yK@F6wB*0AVc6${M57=41?H@z|9>Iiw_sJWN>=zcuH^9~a0000<MNUMnLSTXs_{&=W literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/resources/images/pushbutton_hover.png b/src/plugins/qmldesigner/components/resources/images/pushbutton_hover.png new file mode 100644 index 0000000000000000000000000000000000000000..bba1b6d97c96466ef059f5a8fe92708146f935ff GIT binary patch literal 503 zcmV<T0SNwyP)<h;3K|Lk000e1NJLTq001xm000vR1^@s6);f+K00007bV*G`2igGy z4=NP1o^)ma000SaNLh0L03N{r03N{s!)a7g00004XF*Lt006O%3;baP0004oNkl<Z zc-qyMKTpCy7{;&O6_9^%Ft(ZiF2=Yy7AM1GB8wk`Z{RoKD>#_AIPnoQ3?}}GabQE2 zN@7w3dcE^`Ed?D=wsKGNOW*7CJiYgAuB_E+F{lDAfe-WwqreL|2b{cHD0ab}ZQH{0 zyouvDHd|r!dOg+ccGpo}BfuF2xN=?hq}got1%!20Yn`^wzcfM!yVL1xqdq0W$Mt$$ z3&FWkk%ajuZHND<R;!NDF~cwn8x&w8m&<)dk)i-9ZF9@2TrO$fe|JUlK&!~o${7Fg zNHex3l9g73IRU0qYKdf}=+fo{NFs>1qy);S9bo!LfY`qV<^)K-^z$J`3u%*E$j~94 zw-A{(sfB*eKs?5VwGf*H2&9ypTimD?3W6csJ}AIVzyEqv+1ZjSeu$gNe;;H*Df1|A z=`?ysMX^{I9qjJ~g?wJ7ZE%s$;+F3ZGWSo<B`hC6gH3;EJn+cU|6y5%3Ag}NkhP`( tzSVBGH*s?SPy1~z+63?h#ss{NzW`yX0@rSlb^8DS002ovPDHLkV1nc_)=dBa literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/resources/images/pushbutton_pressed.png b/src/plugins/qmldesigner/components/resources/images/pushbutton_pressed.png new file mode 100644 index 0000000000000000000000000000000000000000..0644f6890d9d203cb8ed9cbc4a243d628e7fd313 GIT binary patch literal 485 zcmV<B0UG{^P)<h;3K|Lk000e1NJLTq001xm000vR1^@s6);f+K00007bV*G`2igGy z4=N5${EZv{000SaNLh0L03N{r03N{s!)a7g00004XF*Lt006O%3;baP0004WNkl<Z zc-qyMK}y3w7=<TI$TY=;qGE$6J%SrSa3e+Bxbhgefj7w&TnKJFg|50#1U*2SSQL|n zrXe%FKbe#+R9ZKaJoqvb$l|^EXTm?s^E?I}fLkyDvcU-O3a)`k-VIC!U<9neem~{h zE?85A2%mAx$L}8-c)Le{D++K2F3wMn1Jg8R`JI%yt^TQTG;u~RZ!V^n79MA`@?;(| z1Yivhu)DpLVMxJ{0&F@?Hz)+m3t{Lsx2k$wTc)#lp8_y}1utN}>04)6%4)}|UL-=u z#_#<fWK;oIRfRVBudOaBAlp^}<<+X^?XCc0>I~?%wZ5vqkf>-=fZD67fcgvRcJ&V} z&OnK7H*0A53{0~uGew*B50vLQ4PPBR9>OScs0TuXRuCtN4c|TRNWISyP0-BET-Tj) z&KuiKQ?r@Cd>Dq*PCtPmQx}>O@XYjISXN;UZonbnh6+&1_x&9Vd%)Da+P@V61c2J< b4BoSExPK-fpHmg~00000NkvXXu0mjf?=Zv8 literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/resources/images/radiobutton_checked.png b/src/plugins/qmldesigner/components/resources/images/radiobutton_checked.png new file mode 100644 index 0000000000000000000000000000000000000000..8ab9157578050442b58d511ad791f9ba12d545d1 GIT binary patch literal 355 zcmeAS@N?(olHy`uVBq!ia0vp@Ak4uGBwbBK{D4$-fKQ0)|NsBH!Jx0NZ^DEL{r&yl zzkmPo`SXVlA3lBj_~y-<w{PFRfBW|Jix>Cq-Me$=&f|v<AKtwSRC)i_t$R0b0vU%7 zAHH?v%ArGtuAD!A>GbIfCr&I_u;ApOLm;W$yAN*PesISQAlcK?17rZnLpydH+PU-a zu3cC8{cC~F&@2h^3ua(3ZlAyG#HrJ#&zw1X@zUk1H*Vd!efQqI`;VSHd;aFbr?20> z|NQyu*RQ{J+H607n({nd978ywWBVML4jM49Z1*%?v~=N;CN9^H|HZAoEPA$Yv$E&1 z9QoW#70=eQv5#l#-FBMvHk;wqJukIzpOBj;R1>yFGM&_9yK%4WwUpD97Y{%Ev*~}} xew1PP@9Z1S9YvgC?Vs~*ot^u6|Nd$B&*|^yW<R!aaR|s}22WQ%mvv4FO#n&n$7%oo literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/resources/images/radiobutton_checked_hover.png b/src/plugins/qmldesigner/components/resources/images/radiobutton_checked_hover.png new file mode 100644 index 0000000000000000000000000000000000000000..d68cb009a7c087babcda871b7614d2c5a0c38cb8 GIT binary patch literal 532 zcmV+v0_**WP)<h;3K|Lk000e1NJLTq000dD000dL1^@s6a_i)L00004b3#c}2nYxW zd<bNS0000PbVXQnQ*UN;cVTj60C#tHE@^ISb7Ns}WiD@WXPfRk8UO$Rrb$FWR47w@ zQp--lP!u%sFUFYoSGv`>aplsV&_rt#@ByWiM}USFD4Mn^&lVpMOn_Jf(Vb~r7@Q$u zYGPcRoSt)Mdd|$fbCbzrZWe+dEF!iMaYXeyvtIP$X5au~9pQvSAzO?_ZxgcIj!Tkr zdUA4gba-fdp9lP;jRQewNwL_Yn$N$PhB0W?>)k8Ma?7RCIFnA_go42`FzZti2+N#6 zLRr^eT-UWd&ogH+e12|RUtSKBY}N*`Ks-s?T!|9~uG=K=bu_AVJDodb)}!IDVHw6y z62$^m-6AO-6U93;2LwFz`;MN?zU#T1$IP02r`habnh_2L_eiQH6NwHr@X)SS`v8BG z73GteHT&)R`%Y2QYRBPlaykhvwJ7?(Nw<#ERTbqNq@AgzGuTR(DEco=uj6sOR;jot zSx#fU&8d43XVO->MA3ftNNCx1PcIfN{9FQd>F1t!544po*^Nf;0UB<w5+{IQijhbR znAKk|0vET2rUqNJTqbi-P}NFaQA$`Tg$X3A05ktX^1nUa0|5IFoI>p4dlS6{Kkf@f Wm-WvSU@v9>0000<MNUMnLSTY+7UXyU literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/resources/images/radiobutton_checked_pressed.png b/src/plugins/qmldesigner/components/resources/images/radiobutton_checked_pressed.png new file mode 100644 index 0000000000000000000000000000000000000000..e3cd5a59e4be580940ff6d62a734ae09abd00995 GIT binary patch literal 599 zcmV-d0;v6oP)<h;3K|Lk000e1NJLTq000dD000dL1^@s6a_i)L00004b3#c}2nYxW zd<bNS0000PbVXQnQ*UN;cVTj60C#tHE@^ISb7Ns}WiD@WXPfRk8UO$R=}AOER47wT zl0Rq^K@`SkcW3vK<z&$-3>b2pSISvwVQFJ&VIe3;ihzg_3qfozrGmu5Ld3=@rGiaJ zCD<i(3|L%D5Ogm%YuMYJ{X5U^ox9+|V`kXz_ulvBjU*z`e$U6_ORnp#I*xPI7}K|H z`>3w#y|OIdKO2wtTb*pg&^b?h-(MdL25t}pIrdeWrhTQ<#ly+u){BjeHz=?PxSm4{ z^m@I=qtQsCQr2c%@H}<4+l`!+m3VS|d^al!w)Il7C9co_rUi78M0z&kP<fK5z;(X? zx(EHX^gQpua5!|^3Z|~BG>&sXi&>f~9mi7!Gk~!5xfEhmT9$Q%dtjg}3JriD9%66f z0+!3FDv;oy79z5Qh#|Wp(cCiZrMcEIXM|v>swzpwco5i*lou)?rhpXvgPwAwAHPpd zIu23DJ!F9K^SmH($+k6sJ%-S%!Wsz6I+U2h?lcUqo#}Mu-WjZkzIwjDgjpR#QS^nY zusyP@%c!(3Vdd@5FuY;9-9KfTPV=HroHfxpYQ!zz0C<RaBze-f#&^F?Pj4;~ZHdNz zE9L|M7I;30kF1x?SYE-BnF+CG3-Jl(UG#N?IENmt<9r|S0r8r}bBLb?mQ7ULF+{hq lUxT=h^*i@~SpWa+{RKoIGO1496c_*i002ovPDHLkV1m=P5Uc<I literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/resources/images/radiobutton_unchecked.png b/src/plugins/qmldesigner/components/resources/images/radiobutton_unchecked.png new file mode 100644 index 0000000000000000000000000000000000000000..321a9a1558aaa2923dec91f9b509d5f192f6b482 GIT binary patch literal 240 zcmeAS@N?(olHy`uVBq!ia0vp@Ak4uGBwbBK{D72AfKQ0)_wV2T|NlQ>!i4Vb?!LZ0 zAo=Fa8zA@a;luat-TUz2!-541-oAYc6ghP0P=9~_ojZ4c>>e;snB5);G)JT)$S?Q= z!w2JbmNWD3T{`{z%&q&&F5mt6D?~Ft4JhyL>Eakt5f|DQ$<<&W;5>O|=IXm!zwck& z5i#StxRcm~D_7o4o1!A&(`P)tJwhR4>7%7$${|X2i!|Jh`^)cm7WVnjspzLqZRdZU fTX(+xqrE)4G}GhuCV>w?qZvG1{an^LB{Ts5Zwqe& literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/resources/images/radiobutton_unchecked_hover.png b/src/plugins/qmldesigner/components/resources/images/radiobutton_unchecked_hover.png new file mode 100644 index 0000000000000000000000000000000000000000..666a3b0bdd4a41fad993b4f8ba9596daec337bfa GIT binary patch literal 492 zcmV<I0Tcd-P)<h;3K|Lk000e1NJLTq000dD000dL1^@s6a_i)L00004b3#c}2nYxW zd<bNS0000PbVXQnQ*UN;cVTj60C#tHE@^ISb7Ns}WiD@WXPfRk8UO$Ren~_@R47w@ zlFLrQU=W3gyo(_wzLjnzZdkeGB{b0*EER=Pt^pg$Rnr#a))tTmCa`F!qFa`DrlF)7 z!s4Xu-}7~5&eXSFuYIl<h6zAnC<S#kS9%V3eXiLL1tEirMs+@s7-iDwNlFlmhght2 z7m3K5_kJ&Qe#<ZeA({M;N~Iq~mMyK<o3&KcY}9JYLOwr?vTOsMpaTIorV0c!>h&+v zH1*YLrMR*ei~eh~X^F+64zNOgg2L1iRj^D`^8&6uZM(0^vMum@1+88XluGja8xE~q zfGb1O=BOrfEPG8*C!5JkX@-5!WIUc$%jNDp$7P*LNNGSr?}J8$F_T2`8PKIO(<5S~ zlxXN(!1r?5>$clwE}hP!y^xb0hEph3N{NR44-9mDUawYFd|ZI{XuBuc1I0=y>HYrv z9WxBk5>)`8@bP#Op3^^n1QvIO!!lwmKNSiu6-jEBM6rgJa;QMS2|Nc|$oucn9$>h^ iz&Yp&_aU4edVRm5PW-+l9wg2H0000<MNUMnLSTYBL($m) literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/resources/images/radiobutton_unchecked_pressed.png b/src/plugins/qmldesigner/components/resources/images/radiobutton_unchecked_pressed.png new file mode 100644 index 0000000000000000000000000000000000000000..c4b0567aa9cfa14324b7be191360d934038370e3 GIT binary patch literal 556 zcmV+{0@MA8P)<h;3K|Lk000e1NJLTq000dD000dL1^@s6a_i)L00004b3#c}2nYxW zd<bNS0000PbVXQnQ*UN;cVTj60C#tHE@^ISb7Ns}WiD@WXPfRk8UO$RzDYzuR47wj zk~>QSK@^2&H=7tD5==S;F*X(^h>iFY#KuDG6bsQp!9qdpvRg*M2UrOTcG_qu`Ue#4 z1UnH0gCr)qBqrHMc4j<xNrF0XxHH4~=H8h*nv{~YM^4vA!r}0Os;U!0h(W|R&iTFX z`)6Ca{@nIyEprHCJr;|trc$YJJRWy3cPf^Z_$n4B^Piv7JGy?1;|~Dy03slnOs-`z z85@}#+Oz9*OA*5AiAK%-M52~2m1dekP+5B#l|&iR>9hkt2+n=GR<j^85MUQE%K1x^ zu{ro}G#Hntq(~&<G(FG$1GMIz>zc@5g&4c$QqF6*>^Sb=xwifN-%M)yzQq|cNvQ1+ z@S=uS8c?MCs8=eb224&1pgB(M00Nl+yfZ~H=po)pg@PNBve?ezG#VXa8-gsMD1GGD z(1(X|xjfpfss#dE&m(hS`VDLvMh%ZJ1?ZW|J29)(3!kxXDMaDFnGOKp0_X*>46SI? z>0CB@T4ijqi!o{-ft}K}3_}2j!@LV!l0RrJE_W#KpKQi9usVnCu$M<11~Asja0MNa uJ%H$IAne0%i#Q8yLqpIj^aQQ`{(C<e#7U-l2imLv0000<MNUMnLSTY|OX>vx literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/resources/images/sizegrip.png b/src/plugins/qmldesigner/components/resources/images/sizegrip.png new file mode 100644 index 0000000000000000000000000000000000000000..350583aaac4aa474ac449eaea2cc7ddd060276b9 GIT binary patch literal 129 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!D3?x-;bCrM;TYyi9E0A8dZe4lyHC-T!u_VYZ zn8D%MjWi%f)6>N<q#};>z(!sM1rC-2ha+zM<2rMwpeI*@Z@PO%TWH}e*?iSqXK(y9 X<zkV(c2jB+P!ofvtDnm{r-UW|dkiGS literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/resources/images/spindown.png b/src/plugins/qmldesigner/components/resources/images/spindown.png new file mode 100644 index 0000000000000000000000000000000000000000..9283d40d6c78e609f4c5c5fc343437ff636fccda GIT binary patch literal 227 zcmeAS@N?(olHy`uVBq!ia0vp^f<Vm8!2~45oIVBsDfSXiUsv{9j4b@j`fit*Rsw}M z3p^r=85raafiPp<g;G19AWOQVuOkD)#(wTUiL5|=k*AAeNX4z>DU&7zg@pY1_Ews? z@WIp5({&uYLPEaW-Ce%q<xf_&jH3Vl|Fci(>FJqqD?;ItfanZ{)fOJ-?F|$z<%+yu zv}$Pk<}gK&gW2T6<Kz5!d3i~18MPRWu^YU)dgY2l<I4t*{DQ*5lP6Am`1p~LVIxc6 VH?<l5`+>GIc)I$ztaD0e0sz(mQ1k!* literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/resources/images/spindown_hover.png b/src/plugins/qmldesigner/components/resources/images/spindown_hover.png new file mode 100644 index 0000000000000000000000000000000000000000..851d0af2b1730998496f21fa2029b48ba2d3ce0d GIT binary patch literal 227 zcmeAS@N?(olHy`uVBq!ia0vp^f<Vm8!2~45oIVBsDfSXiUsv{9j4b@(hLeh3%m)f_ z7I;J!Gcd><0%69y3#E2IL6&q!Uq=Rpjs4tz5?O)#B2O2`kcwN$A%TH`At8Uhy_IGz zeDL)2bR7q;kdQBTcb6}D`ID6`qv-$t|Ll``dU|Huicq*DAUcC#wS~uddjo|_xgswZ zts2_CIZP4cU^e;i_&9%FUS85$MlFV8>;|u{Ub!OC__Dzxzo4-2<cSj>K7M3m__-#? UDC42sdZ6tLp00i_>zopr02@tFr2qf` literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/resources/images/spindown_off.png b/src/plugins/qmldesigner/components/resources/images/spindown_off.png new file mode 100644 index 0000000000000000000000000000000000000000..a90ab3f03846fef2972fef05be6ba0e5e6c9b7a0 GIT binary patch literal 249 zcmeAS@N?(olHy`uVBq!ia0vp^{6Ngj!3HE3xL=O}QY^(zo*^7SP{WbZ0pv@TxJHx& z=ckpFCl;kLl$V$5W#(lUCnpx9>g5-u&wghk1yt1S>EaloF>&q%L!l-Ek(P%~MW5+i zIxYED_B@NM`i5l+76C7Ogzn`p-n;O}skwI}rP~zy9{*Tz_50s6#W~Zq*;cO=IFw`Y zX6cG)Dv}SXPp)D;@gTwYgg~82Pu<z-FkQu&7Z%=p@XvOQUPMve9nZD}&z{biboRU{ vLpgI?@SQjOXGOx7rAFV^jMtAX|ILtResx3o_8NJhvlu*G{an^LB{Ts53&mmK literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/resources/images/spindown_pressed.png b/src/plugins/qmldesigner/components/resources/images/spindown_pressed.png new file mode 100644 index 0000000000000000000000000000000000000000..cab3eae28fb3891c347586b8b37e192a9e70e4b7 GIT binary patch literal 222 zcmeAS@N?(olHy`uVBq!ia0vp^f<Vm8!2~45oIVBsDfSXiUsv{9j4b>L5)Q{N90v+< z7I;J!Gcd><1YyR*nIXYIL6&q!Uq=Rpjs4tz5?O)#Tu&FrkcwMdCvN0ApupoIEO7VE zokx%U|DV;TB)eth%`F$ETr!t1D33GXU@$xurTyZBrL>-6OVBCn^M;XC$vk#~h1(@F zyOW-O`}gTfXVEr(Ik^YNWhUDGIp~yq)^hpT#Ktu$OIv0=XU*0XtbFg!bS2PFJ1J5F PXfcDQtDnm{r-UW|B2Y}7 literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/resources/images/spinup.png b/src/plugins/qmldesigner/components/resources/images/spinup.png new file mode 100644 index 0000000000000000000000000000000000000000..9250c0c3b89dd282a7c839aeec992a0c07b6dcaf GIT binary patch literal 236 zcmeAS@N?(olHy`uVBq!ia0vp^f<Vm8!2~45oIVBsDfSXiUsv{9j4b@jhI{VVp8^VT z7I;J!Gcd><0%69y3#E2IL6&q!Uq=Rpjs4tz5?O)#YEKu(kcwMNCn)kAP~dT8pLtk7 z<zSK6yZny}1UXnVwO8MqruurDx<|&ZAG(L4b{hydX)K*nT(Pc#U(v>D?bbQPJr}F( z=Nh_cKE2T>TJvL(WOCJeO;t}>@w{$^1zXo`JtuSHVUFt4GZoJT;$>$|U1a%5vwh~b eWV2uKYSPs+i*^`<98U&1gTd3)&t;ucLK6UD;ZdLf literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/resources/images/spinup_hover.png b/src/plugins/qmldesigner/components/resources/images/spinup_hover.png new file mode 100644 index 0000000000000000000000000000000000000000..8793a9890ea4bc518e7df41e5776e3477d426a2e GIT binary patch literal 236 zcmeAS@N?(olHy`uVBq!ia0vp^f<Vm8!2~45oIVBsDfSXiUsv{9j4b>T?6(3EPXdKF z3p^r=85raafiPp<g;G19AWOQVuOkD)#(wTUiL5|=wWo_?NX4zC6BPLlDDb$l&pa$( zB6RN1UtKv9rbdO-RMXp=Qtp0Z;q;Z??`rW}jLnH->6EQv=SANuwzx0fI<2_Ru<HGC z<6|02Wvn+e@yDAa`@FYZs<QI&p<F?RE!VbQo5^fFXYG<XlJ8|6t#;lVWa4i3)ZlX6 e8MptZQ<(Rq7G3+JmDdDx27{-opUXO@geCxwrcliQ literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/resources/images/spinup_off.png b/src/plugins/qmldesigner/components/resources/images/spinup_off.png new file mode 100644 index 0000000000000000000000000000000000000000..02dad1fb2923afe80b92931537b72a7c1cc8ace5 GIT binary patch literal 274 zcmeAS@N?(olHy`uVBq!ia0vp^{6Ngj!3HE3xL=O}QY^(zo*^7SP{WbZ0pv@TxJHx& z=ckpFCl;kLl$V$5W#(lUCnpx9>g5-u&wghk1yr=e)5S4F<NVwSihPG01YH07sA&sK z5MXJGTX14t7k7bx&2h&PQ}&2frvL-r@^pdLmMa@>KmX@n7x!sp?(Od4yzc)qX0BYd zFXZbTle3;PDmSjrc9okF6xAxwcdvT##tsdK)mhieuRnKWDClD5GE^&CZ24?PCTG)c zH9<wj9;35&^E(O?8XG^Kx%|3O=yOTm`NZNgY>j74W;?bmd3}3t^_0BDub%PFP72k1 T%#ptc=w1d-S3j3^P6<r_-V18j literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/resources/images/spinup_pressed.png b/src/plugins/qmldesigner/components/resources/images/spinup_pressed.png new file mode 100644 index 0000000000000000000000000000000000000000..8acb9c176ed749a7ff4a2958a2c94705a9571f41 GIT binary patch literal 232 zcmeAS@N?(olHy`uVBq!ia0vp^f<Vm8!2~45oIVBsDfSXiUsv{9j4b>LqIP9#s(?bA z1s;*b3=DDyL74GyW=JqlkR{#G*O7r?V?XzwL{=cb+|$J|q~g}n35I+P3Oug5g$jv^ zXL4j(f6s5^Omkxk(kTl}PP)2%>&x@m+}#puw|>7T!5nf#LY{f;)*vr0&6W*8w#Qa% zl~Z5A%rJdM@w)Hxio<LQA8zaHxE3Yk$N$d5FDT3EUh0hXlJ}-wo%G?P@|M3hdjD&u ZFz?t>l+rKFX99ErgQu&X%Q~loCIBS4PJaLZ literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/resources/images/up_arrow.png b/src/plugins/qmldesigner/components/resources/images/up_arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..048b2fd2ff9caf1bc3e5cca0460d99e3bbffc2a8 GIT binary patch literal 273 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)I!3HFqj;YoHDeV&1h!W?b)Wnj^{5*w_%-mE4 zUm(Lo!7DLO!N|bSK*7+=%E;8p#85#S$TMgQG-d^vQ{w6C%6^NHg<p#M;RXK$pb%$) zM`SSr1Gf+eGhVt|_XjA*lJ4m1$iT3%pZiZDNMEL>i(^Q|E!m!pysd&FZtKn4z1}Pg z^T=5kB;lYUb!|hRWJ#4_<NNcq%WY0t9<p@rxhAEl`fBgxd(j(y@BQwf**eGgtmVpU zUO`?PmP;tV+pDqkK$Q1d(>cWgP7M*+i9H9e)*qEfez12r|MSU4g&#H~e*s#`;OXk; Jvd$@?2>|EBS}y<q literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/resources/images/up_arrow_disabled.png b/src/plugins/qmldesigner/components/resources/images/up_arrow_disabled.png new file mode 100644 index 0000000000000000000000000000000000000000..5fe22ba043d73417acd58e6785d36561102d30db GIT binary patch literal 266 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)I!3HFqj;YoHDeV&1h!W?b)Wnj^{5*w_%-mE4 zUm(Lo!7DLO!N|bSK*7+=%E;8p#85#S$TMgQG-d^vQ{w6C%6^NHm0L)-^}?ifpb%$) zM`SSr1Gf+eGhVt|_XjA*lJ4m1$iT3%pZiZDNMDkti(^Q|Ez=W<Tnvf~E*BG5OT1BA ztsr63aN~=c+4@AI$i40DMPlM+dp_7t3EJVrF{k*F<?;Wv&t)W%BeIX)lU{3@v9{8F z&-a*Y!=)~os$QD$(b=7bY{@+$P7kIRr@f!6pJSoj`M+lENuX^Ep00i_>zopr09y-D AQvd(} literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/resources/resources.pri b/src/plugins/qmldesigner/components/resources/resources.pri new file mode 100644 index 00000000000..f2ad9b0f6b1 --- /dev/null +++ b/src/plugins/qmldesigner/components/resources/resources.pri @@ -0,0 +1,3 @@ +VPATH += $$PWD +INCLUDEPATH += $$PWD +RESOURCES += resources.qrc \ No newline at end of file diff --git a/src/plugins/qmldesigner/components/resources/resources.qrc b/src/plugins/qmldesigner/components/resources/resources.qrc new file mode 100644 index 00000000000..9968a0971e4 --- /dev/null +++ b/src/plugins/qmldesigner/components/resources/resources.qrc @@ -0,0 +1,37 @@ +<RCC> + <qresource prefix="/qmldesigner"> + <file>templates/Standard/Form.xml</file> + <file>stylesheet.css</file> + <file>toolbutton.css</file> + <file>scrollbar.css</file> + <file>images/checkbox_checked.png</file> + <file>images/checkbox_checked_hover.png</file> + <file>images/checkbox_checked_pressed.png</file> + <file>images/checkbox_unchecked.png</file> + <file>images/checkbox_unchecked_hover.png</file> + <file>images/checkbox_unchecked_pressed.png</file> + <file>images/down_arrow.png</file> + <file>images/down_arrow_disabled.png</file> + <file>images/frame.png</file> + <file>images/pushbutton.png</file> + <file>images/pushbutton_hover.png</file> + <file>images/pushbutton_pressed.png</file> + <file>images/radiobutton_checked.png</file> + <file>images/radiobutton_checked_hover.png</file> + <file>images/radiobutton_checked_pressed.png</file> + <file>images/radiobutton_unchecked.png</file> + <file>images/radiobutton_unchecked_hover.png</file> + <file>images/radiobutton_unchecked_pressed.png</file> + <file>images/sizegrip.png</file> + <file>images/spindown.png</file> + <file>images/spindown_hover.png</file> + <file>images/spindown_off.png</file> + <file>images/spindown_pressed.png</file> + <file>images/spinup.png</file> + <file>images/spinup_hover.png</file> + <file>images/spinup_off.png</file> + <file>images/spinup_pressed.png</file> + <file>images/up_arrow.png</file> + <file>images/up_arrow_disabled.png</file> + </qresource> +</RCC> diff --git a/src/plugins/qmldesigner/components/resources/scrollbar.css b/src/plugins/qmldesigner/components/resources/scrollbar.css new file mode 100644 index 00000000000..a540b67b71f --- /dev/null +++ b/src/plugins/qmldesigner/components/resources/scrollbar.css @@ -0,0 +1,42 @@ + QScrollBar:vertical { + border: 1px solid #8F8F8F; + background: #707070; + width: 10px; + margin: 2px 0 2px 0; + } + + QScrollBar::handle:vertical { + background: qlineargradient(x1: 0, y1: 0, x2: 0.5, y2: 0, x3: 0.8, y3: 0, + stop: 0 #cEcEcE, + stop: 1 #8E8E8E, + stop: 2 #101010); + min-height: 20px; + border-radius: 10px; + } + + QScrollBar::add-line:vertical { + border: 0px solid grey; + background: #808080; + height: 0px; + subcontrol-position: bottom; + subcontrol-origin: margin; + } + + QScrollBar::sub-line:vertical { + border: 0px solid grey; + background: #32CC99; + height: 0px; + subcontrol-position: top; + subcontrol-origin: margin; + } + QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical { + border: 2px solid grey; + width: 3px; + height: 3px; + background: white; + } + + QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { + background: none; + } + \ No newline at end of file diff --git a/src/plugins/qmldesigner/components/resources/stylesheet.css b/src/plugins/qmldesigner/components/resources/stylesheet.css new file mode 100644 index 00000000000..b660897c29a --- /dev/null +++ b/src/plugins/qmldesigner/components/resources/stylesheet.css @@ -0,0 +1,72 @@ +QFrame { + border: 2px; + background-color: #707070; +} + +QLabel { + border: none; + border-image: none; + padding: 0; + background: none; + color: #E1E1E1; +} + +QScrollArea { + border: 0px; + background-color: #707070; +} + +QStackedWidget { + border: 0px; + background-color: #707070; +} + + +QGraphicsView { + border: 0px; + background-color: #707070; +} + +QTabWidget { + border: 0px; + background-color: #707070; +} + +QTabWidget::pane { /* The tab widget frame */ + border: 0px; + background-color: #707070; +} + +QLineEdit +{ + border: 2px solid #8F8F8F; + border-radius: 4px; + padding: 0 8px; + background-color: white; + selection-background-color: darkgray; +} + + QTreeView { + color: white; + selection-color: white + } + + QTreeView::item:selected { + color: white; + } + + QListView { + color: white; + selection-color: white + } + + QListView::item:selected { + color: white; + } + +QComboBox QAbstractItemView { + color: black; + border: 2px solid darkgray; + selection-background-color: lightgray; + } + \ No newline at end of file diff --git a/src/plugins/qmldesigner/components/resources/templates/Standard/Form.xml b/src/plugins/qmldesigner/components/resources/templates/Standard/Form.xml new file mode 100644 index 00000000000..4a9aa660d51 --- /dev/null +++ b/src/plugins/qmldesigner/components/resources/templates/Standard/Form.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<QWidget objectName="Form" width="186" height="141"> + <windowTitle><?i18n?>Form</windowTitle> + <QVBoxLayout objectName="verticalLayout"> + <!--Start of QLayoutItem 1--><QLabel objectName="label"> + <text><?i18n?>Hello world</text> + </QLabel> + <!--End of QLayoutItem 1--><!--Start of QLayoutItem 1--><QPlainTextEdit objectName="plainTextEdit"/> + <!--End of QLayoutItem 1--></QVBoxLayout> +</QWidget> diff --git a/src/plugins/qmldesigner/components/resources/toolbutton.css b/src/plugins/qmldesigner/components/resources/toolbutton.css new file mode 100644 index 00000000000..ccae16b91a1 --- /dev/null +++ b/src/plugins/qmldesigner/components/resources/toolbutton.css @@ -0,0 +1,23 @@ +QToolButton { + border-image: url(:/qmldesigner/images/pushbutton.png) 3; + border-width: 3; + border-radius: 6px; + margin: 4px 4px 4px 4px; + color: white; +} + +QToolButton:hover { + border-image: url(:/qmldesigner/images/pushbutton_hover.png) 3; + border-width: 3; +} + +QToolButton:pressed { + border-image: url(:/qmldesigner/images/pushbutton_pressed.png) 3; + border-width: 3; +} + +QToolButton:checked +{ + border-image: url(:/qmldesigner/images/pushbutton_pressed.png) 3; + border-width: 3; +} diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditor.pri b/src/plugins/qmldesigner/components/stateseditor/stateseditor.pri new file mode 100644 index 00000000000..4905aec4afd --- /dev/null +++ b/src/plugins/qmldesigner/components/stateseditor/stateseditor.pri @@ -0,0 +1,10 @@ +VPATH += $$PWD +INCLUDEPATH += $$PWD +SOURCES += stateseditorwidget.cpp \ + stateseditormodel.cpp \ + stateseditorview.cpp +HEADERS += stateseditorwidget.h \ + stateseditormodel.h \ + stateseditorview.h +RESOURCES += $$PWD/stateseditor.qrc +OTHER_FILES +=$$PWD/stateslist.qml diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditor.qrc b/src/plugins/qmldesigner/components/stateseditor/stateseditor.qrc new file mode 100644 index 00000000000..d23bab446d9 --- /dev/null +++ b/src/plugins/qmldesigner/components/stateseditor/stateseditor.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/stateseditor" > + <file>stateslist.qml</file> + </qresource> +</RCC> diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.cpp b/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.cpp new file mode 100644 index 00000000000..e9b43028995 --- /dev/null +++ b/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.cpp @@ -0,0 +1,147 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "stateseditormodel.h" +#include "stateseditorview.h" + +#include <QtCore/QDebug> + +enum { + debug = false +}; + + +namespace QmlDesigner { +namespace Internal { + +StatesEditorModel::StatesEditorModel(QObject *parent) : + QAbstractListModel(parent) +{ + + QHash<int, QByteArray> roleNames; + roleNames.insert(StateNameRole, "stateName"); + roleNames.insert(StatesPixmapRole, "statePixmap"); + setRoleNames(roleNames); +} + + +int StatesEditorModel::count() const +{ + return m_stateNames.count(); +} + +int StatesEditorModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + return m_stateNames.count(); +} + +QVariant StatesEditorModel::data(const QModelIndex &index, int role) const +{ + if (index.parent().isValid() || index.column() != 0) + return QVariant(); + + QVariant result; + switch (role) { + case StateNameRole: { + if ((index.row()==0) && (m_stateNames.at(index.row())=="")) + result = QString("base state"); + else + result = m_stateNames.at(index.row()); + break; + } + case StatesPixmapRole: { + // TODO: Maybe cache this? + if (!m_statesView.isNull()) { + result = m_statesView->renderState(index.row()); + } + break; + } + } + + return result; +} + +void StatesEditorModel::insertState(int i, const QString &name) +{ + beginInsertRows(QModelIndex(), i, i); + + m_stateNames.insert(i, name); + + endInsertRows(); + + emit dataChanged(createIndex(i, 0), createIndex(i, 0)); + emit countChanged(); +} + +void StatesEditorModel::removeState(int i) +{ + beginRemoveRows(QModelIndex(), i, i); + + m_stateNames.removeAt(i); + + endRemoveRows(); + + emit dataChanged(createIndex(i, 0), createIndex(i, 0)); + emit countChanged(); +} + +void StatesEditorModel::renameState(int i, const QString &newName) +{ + Q_ASSERT(i > 0 && i < m_stateNames.count()); + + if (m_stateNames[i] != newName) { + m_stateNames.replace(i, newName); + m_statesView->renameState(i,newName); + + emit dataChanged(createIndex(i, 0), createIndex(i, 0)); + } +} + +void StatesEditorModel::updateState(int i) +{ + Q_ASSERT(i >= 0 && i < m_stateNames.count()); + + emit dataChanged(createIndex(i, 0), createIndex(i, 0)); + emit countChanged(); +} + +void StatesEditorModel::setStatesEditorView(StatesEditorView *statesView) +{ + m_statesView = statesView; +} + +void StatesEditorModel::emitChangedToState(int n) +{ + emit changedToState(n); +} + +} // namespace Internal +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.h b/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.h new file mode 100644 index 00000000000..13fe4b9d58c --- /dev/null +++ b/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.h @@ -0,0 +1,79 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef STATESEDITORMODEL_H +#define STATESEDITORMODEL_H + +#include <QAbstractListModel> +#include <QWeakPointer> + +#include <stateseditorview.h> + +namespace QmlDesigner { + +namespace Internal { + +class StatesEditorModel : public QAbstractListModel +{ + Q_OBJECT + + Q_PROPERTY(int count READ count NOTIFY countChanged) + + enum { + StateNameRole = Qt::DisplayRole, + StatesPixmapRole = Qt::UserRole + }; + +public: + StatesEditorModel(QObject *parent); + + int count() const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + + void insertState(int i, const QString &name); + void removeState(int i); + Q_INVOKABLE void renameState(int i, const QString &newName); + void updateState(int i); + void setStatesEditorView(StatesEditorView *statesView); + void emitChangedToState(int n); + +signals: + void countChanged(); + void changedToState(int n); + +private: + QList<QString> m_stateNames; + QWeakPointer<StatesEditorView> m_statesView; +}; + +} // namespace Itnernal +} // namespace QmlDesigner + +#endif // STATESEDITORMODEL_H diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp b/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp new file mode 100644 index 00000000000..a53c967e544 --- /dev/null +++ b/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp @@ -0,0 +1,438 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "stateseditorview.h" +#include "stateseditormodel.h" +#include <customnotifications.h> + +#include <QPainter> +#include <QTimerEvent> +#include <QDebug> + +enum { + debug = false +}; + +namespace QmlDesigner { +namespace Internal { + +/** + We always have 'one' current state, where we get updates from (see sceneChanged()). In case + the current state is the base state, we render the base state + all other states. + */ +StatesEditorView::StatesEditorView(StatesEditorModel *editorModel, QObject *parent) : + QmlModelView(parent), + m_editorModel(editorModel) +{ + Q_ASSERT(m_editorModel); + + + connect(nodeInstanceView(), SIGNAL(instanceRemoved(NodeInstance)), this, SLOT(sceneChanged())); + connect(nodeInstanceView(), SIGNAL(transformPropertyChanged(NodeInstance)), this, SLOT(sceneChanged())); + connect(nodeInstanceView(), SIGNAL(parentPropertyChanged(NodeInstance)), this, SLOT(sceneChanged())); + connect(nodeInstanceView(), SIGNAL(otherPropertyChanged(NodeInstance)), this, SLOT(sceneChanged())); + connect(nodeInstanceView(), SIGNAL(updateItem(NodeInstance)), this, SLOT(sceneChanged())); +} + +void StatesEditorView::setCurrentStateSilent(int index) +{ + // TODO + QmlModelState state(m_modelStates.at(index)); + if (!state.isValid()) + return; + if (state == currentState()) + return; + QmlModelView::stateChanged(state, currentState()); +} + +void StatesEditorView::setCurrentState(int index) +{ + // TODO + if (m_modelStates.indexOf(currentState()) == index) + return; + + if (index >= m_modelStates.count()) + return; + + QmlModelState state(m_modelStates.at(index)); + Q_ASSERT(state.isValid()); + QmlModelView::setCurrentState(state); +} + + +void StatesEditorView::setBackCurrentState(int index, const QmlModelState &oldState) +{ + // TODO + QmlModelState state(m_modelStates.at(index)); + if (!state.isValid()) + return; + if (state == oldState) + return; + QmlModelView::stateChanged(oldState, state); +} + +void StatesEditorView::createState(const QString &name) +{ + stateRootNode().states().addState(name); +} + +void StatesEditorView::removeState(int index) +{ + Q_ASSERT(index > 0 && index < m_modelStates.size()); + QmlModelState state = m_modelStates.at(index); + Q_ASSERT(state.isValid()); + m_modelStates.removeAll(state); + m_editorModel->removeState(index); + QmlModelView::setCurrentState(baseState()); + state.destroy(); +} + +void StatesEditorView::renameState(int index,const QString &newName) +{ + Q_ASSERT(index > 0 && index < m_modelStates.size()); + QmlModelState state = m_modelStates.at(index); + Q_ASSERT(state.isValid()); + if (state.name() != newName) { + // Jump to base state for the change + QmlModelState oldState = currentState(); + setCurrentStateSilent(0); + state.setName(newName); + setBackCurrentState(0, oldState); + } +} + +void StatesEditorView::duplicateCurrentState(int index) +{ + Q_ASSERT(index > 0 && index < m_modelStates.size()); + QmlModelState state = m_modelStates.at(index); + Q_ASSERT(state.isValid()); + QString newName = state.name(); + + // Strip out numbers at the end of the string + QRegExp regEx(QString("[0-9]+$")); + int numberIndex = newName.indexOf(regEx); + if ((numberIndex != -1) && (numberIndex+regEx.matchedLength()==newName.length())) + newName = newName.left(numberIndex); + + int i = 1; + QStringList stateNames = state.stateGroup().names(); + while (stateNames.contains(newName + QString::number(i))) + i++; + state.duplicate(newName + QString::number(i)); +} + +void StatesEditorView::modelAttached(Model *model) +{ + if (model == QmlModelView::model()) + return; + + Q_ASSERT(model); + QmlModelView::modelAttached(model); + Q_ASSERT(m_editorModel->rowCount(QModelIndex()) == 0); + + // find top level states + m_stateRootNode = QmlItemNode(rootModelNode()); + if (!m_stateRootNode.isValid()) + return; + + clearModelStates(); + + // Add base state + m_modelStates.insert(0, baseState()); + m_editorModel->insertState(0, baseState().name()); + + // Add custom states + for (int i = 0; i < m_stateRootNode.states().allStates().size(); ++i) { + QmlModelState state = QmlItemNode(rootModelNode()).states().allStates().at(i); + insertModelState(i, state); + } +} + +void StatesEditorView::modelAboutToBeDetached(Model *model) +{ + if (debug) + qDebug() << __FUNCTION__; + + clearModelStates(); + + QmlModelView::modelAboutToBeDetached(model); +} + +void StatesEditorView::propertiesAboutToBeRemoved(const QList<AbstractProperty>& propertyList) +{ + foreach (const AbstractProperty &property, propertyList) { + // remove all states except base state + if ((property.name()=="states") && (property.parentModelNode().isRootNode())) { + foreach (const QmlModelState &state, m_modelStates) { + if (!state.isBaseState()) + removeModelState(state); + } + } + } +} + +void StatesEditorView::propertiesRemoved(const QList<AbstractProperty>& propertyList) +{ + QmlModelView::propertiesRemoved(propertyList); + foreach (const AbstractProperty &property, propertyList) { + ModelNode node (property.parentModelNode().parentProperty().parentModelNode()); + if (QmlModelState(node).isValid()) { + startUpdateTimer(modelStateIndex(node) + 1, 0); + } + } +} + +void StatesEditorView::variantPropertiesChanged(const QList<VariantProperty>& propertyList, PropertyChangeFlags propertyChange) +{ + QmlModelView::variantPropertiesChanged(propertyList, propertyChange); + foreach (const VariantProperty &property, propertyList) { + ModelNode node (property.parentModelNode()); + if (QmlModelState(node).isValid() && (property.name() == QLatin1String("name"))) { + int index = m_modelStates.indexOf(node); + if (index != -1) + m_editorModel->renameState(index, property.value().toString()); + } + } + foreach (const AbstractProperty &property, propertyList) { + ModelNode node (property.parentModelNode().parentProperty().parentModelNode()); + if (QmlModelState(node).isValid()) { + startUpdateTimer(modelStateIndex(node) + 1, 0); + } else { //a change to the base state update all + for (int i = 0; i < m_modelStates.count(); ++i) + startUpdateTimer(i, 0); + } + } +} + +void StatesEditorView::nodeAboutToBeRemoved(const ModelNode &removedNode) +{ + if (removedNode.parentProperty().parentModelNode() == m_stateRootNode + && QmlModelState(removedNode).isValid()) { + removeModelState(removedNode); + } + QmlModelView::nodeAboutToBeRemoved(removedNode); +} + + +void StatesEditorView::nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent, const NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags propertyChange) +{ + QmlModelView::nodeReparented(node, newPropertyParent, oldPropertyParent, propertyChange); + + // this would be sliding + Q_ASSERT(newPropertyParent != oldPropertyParent); + + if (QmlModelState(node).isValid()) { + if (oldPropertyParent.parentModelNode() == m_stateRootNode) { + if (oldPropertyParent.isNodeListProperty() + && oldPropertyParent.name() == "states") { + removeModelState(node); + } else { + qWarning() << "States Editor: Reparented model state was not in states property list"; + } + } + + if (newPropertyParent.parentModelNode() == m_stateRootNode) { + if (newPropertyParent.isNodeListProperty() + && newPropertyParent.name() == "states") { + NodeListProperty statesProperty = newPropertyParent.toNodeListProperty(); + int index = statesProperty.toModelNodeList().indexOf(node); + Q_ASSERT(index >= 0); + insertModelState(index, node); + } else { + qWarning() << "States Editor: Reparented model state is not in the states property list"; + } + } + } +} + +void StatesEditorView::nodeSlidedToIndex(const NodeListProperty &listProperty, int newIndex, int oldIndex) +{ + QmlModelView::nodeSlidedToIndex(listProperty, newIndex, oldIndex); + if (listProperty.parentModelNode() == m_stateRootNode + && listProperty.name() == "states") { + int index = newIndex; + if (oldIndex < newIndex) + --index; + QmlModelState state = listProperty.toModelNodeList().at(index); + if (state.isValid()) { + Q_ASSERT(oldIndex == modelStateIndex(state)); + removeModelState(state); + insertModelState(newIndex, state); + Q_ASSERT(newIndex == modelStateIndex(state)); + } + } +} + +void StatesEditorView::stateChanged(const QmlModelState &newQmlModelState, const QmlModelState &oldQmlModelState) +{ + QmlModelView::stateChanged(newQmlModelState, oldQmlModelState); + + if (newQmlModelState.isBaseState()) + m_editorModel->emitChangedToState(0); + else + m_editorModel->emitChangedToState(m_modelStates.indexOf(newQmlModelState)); +} + +void StatesEditorView::customNotification(const AbstractView *view, const QString &identifier, const QList<ModelNode> &nodeList, const QList<QVariant> &data) +{ + QmlModelView::customNotification(view, identifier, nodeList, data); + if (identifier == StartRewriterAmend) { + m_oldRewriterAmendState = currentState(); + QmlModelView::setCurrentState(baseState()); + } else if (identifier == EndRewriterAmend) { + if (m_oldRewriterAmendState.isValid()) + QmlModelView::setCurrentState(m_oldRewriterAmendState); + } +} + +QPixmap StatesEditorView::renderState(int i) +{ + Q_ASSERT(i >= 0 && i < m_modelStates.size()); + nodeInstanceView()->setBlockChangeSignal(true); + QmlModelState oldState = currentState(); + setCurrentStateSilent(i); + + Q_ASSERT(nodeInstanceView()); + + const int checkerbordSize= 10; + QPixmap tilePixmap(checkerbordSize * 2, checkerbordSize * 2); + tilePixmap.fill(Qt::white); + QPainter tilePainter(&tilePixmap); + QColor color(220, 220, 220); + tilePainter.fillRect(0, 0, checkerbordSize, checkerbordSize, color); + tilePainter.fillRect(checkerbordSize, checkerbordSize, checkerbordSize, checkerbordSize, color); + tilePainter.end(); + + + QSize pixmapSize(nodeInstanceView()->sceneRect().size().toSize()); + if (pixmapSize.width() > 150 || pixmapSize.height() > 150) // sensible maximum size + pixmapSize.scale(QSize(150, 150), Qt::KeepAspectRatio); + QPixmap pixmap(pixmapSize); + + QPainter painter(&pixmap); + painter.drawTiledPixmap(pixmap.rect(), tilePixmap); + nodeInstanceView()->render(&painter, pixmap.rect(), nodeInstanceView()->sceneRect()); + + setBackCurrentState(i, oldState); + nodeInstanceView()->setBlockChangeSignal(false); + Q_ASSERT(oldState == currentState()); + + return pixmap; +} + +void StatesEditorView::sceneChanged() +{ + // If we are in base state we have to update the pixmaps of all states + // otherwise only the pixmpap for the current state + + if (currentState().isValid()) { //during setup we might get sceneChanged signals with an invalid currentState() + if (currentState().isBaseState()) { + for (int i = 0; i < m_modelStates.count(); ++i) + startUpdateTimer(i, i * 80); + } else { + startUpdateTimer(modelStateIndex(currentState()) + 1, 0); + } + } +} + +void StatesEditorView::startUpdateTimer(int i, int offset) { + if (i < m_updateTimerIdList.size() && m_updateTimerIdList.at(i) != 0) + return; + // TODO: Add an offset so not all states are rendered at once + Q_ASSERT(i >= 0 && i < m_modelStates.count()); + if (i < m_updateTimerIdList.size() && i > 0) + if (m_updateTimerIdList.at(i)) + killTimer(m_updateTimerIdList.at(i)); + int j = i; + + while (m_updateTimerIdList.size() <= i) { + m_updateTimerIdList.insert(j, 0); + j++; + } + m_updateTimerIdList[i] = startTimer(100 + offset); +} + +// index without base state +void StatesEditorView::insertModelState(int i, const QmlModelState &state) +{ + Q_ASSERT(state.isValid()); + Q_ASSERT(!state.isBaseState()); + // For m_modelStates / m_editorModel, i=0 is base state + m_modelStates.insert(i+1, state); + m_editorModel->insertState(i+1, state.name()); +} + +void StatesEditorView::removeModelState(const QmlModelState &state) +{ + Q_ASSERT(state.isValid()); + Q_ASSERT(!state.isBaseState()); + int index = m_modelStates.indexOf(state); + if (index != -1) { + m_modelStates.removeOne(state); + + if (m_updateTimerIdList.contains(index)) { + killTimer(m_updateTimerIdList[index]); + m_updateTimerIdList[index] = 0; + } + m_editorModel->removeState(index); + } +} + +void StatesEditorView::clearModelStates() +{ + // For m_modelStates / m_editorModel, i=0 is base state + while (m_modelStates.size()) { + m_modelStates.removeFirst(); + m_editorModel->removeState(0); + } +} + +// index without base state +int StatesEditorView::modelStateIndex(const QmlModelState &state) +{ + return m_modelStates.indexOf(state) - 1; +} + +void StatesEditorView::timerEvent(QTimerEvent *event) +{ + int index = m_updateTimerIdList.indexOf(event->timerId()); + if (index > -1) { + event->accept(); + Q_ASSERT(index >= 0); + if (index < m_modelStates.count()) //there might be updates for a state already deleted 100ms are long + m_editorModel->updateState(index); + killTimer(m_updateTimerIdList[index]); + m_updateTimerIdList[index] = 0; + } else { + QmlModelView::timerEvent(event); + } +} + +} // namespace Internal +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditorview.h b/src/plugins/qmldesigner/components/stateseditor/stateseditorview.h new file mode 100644 index 00000000000..284d939fb01 --- /dev/null +++ b/src/plugins/qmldesigner/components/stateseditor/stateseditorview.h @@ -0,0 +1,100 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef STATESEDITORVIEW_H +#define STATESEDITORVIEW_H + +#include <qmlmodelview.h> + +namespace QmlDesigner { + +namespace Internal { + +class StatesEditorModel; + +class StatesEditorView : public QmlModelView { + Q_OBJECT + +public: + StatesEditorView(StatesEditorModel *model, QObject *parent = 0); + + void setCurrentState(int index); + void setCurrentStateSilent(int index); + void setBackCurrentState(int index, const QmlModelState &oldState); + void createState(const QString &name); + void removeState(int index); + void renameState(int index,const QString &newName); + void duplicateCurrentState(int index); + + QPixmap renderState(int i); + QmlItemNode stateRootNode() { return m_stateRootNode; } + + // AbstractView + void modelAttached(Model *model); + void modelAboutToBeDetached(Model *model); + void propertiesAboutToBeRemoved(const QList<AbstractProperty>& propertyList); + void propertiesRemoved(const QList<AbstractProperty>& propertyList); + void variantPropertiesChanged(const QList<VariantProperty>& propertyList, PropertyChangeFlags propertyChange); + + void nodeAboutToBeRemoved(const ModelNode &removedNode); + void nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent, const NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags propertyChange); + void nodeSlidedToIndex(const NodeListProperty &listProperty, int newIndex, int oldIndex); + + // QmlModelView + void stateChanged(const QmlModelState &newQmlModelState, const QmlModelState &oldQmlModelState); + + void customNotification(const AbstractView *view, const QString &identifier, const QList<ModelNode> &nodeList, const QList<QVariant> &data); + + +protected: + void timerEvent(QTimerEvent*); + +private slots: + void sceneChanged(); + +private: + void insertModelState(int i, const QmlModelState &state); + void removeModelState(const QmlModelState &state); + void clearModelStates(); + int modelStateIndex(const QmlModelState &state); + + void startUpdateTimer(int i, int offset); + + QList<QmlModelState> m_modelStates; + StatesEditorModel *m_editorModel; + QmlItemNode m_stateRootNode; + + QList<int> m_updateTimerIdList; + QmlModelState m_oldRewriterAmendState; +}; + +} // namespace Internal +} // namespace QmlDesigner + +#endif // STATESEDITORVIEW_H diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditorwidget.cpp b/src/plugins/qmldesigner/components/stateseditor/stateseditorwidget.cpp new file mode 100644 index 00000000000..d2950d9a150 --- /dev/null +++ b/src/plugins/qmldesigner/components/stateseditor/stateseditorwidget.cpp @@ -0,0 +1,218 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "stateseditorwidget.h" +#include "stateseditormodel.h" +#include "stateseditorview.h" + +#include <qmlitemnode.h> +#include <invalidargumentexception.h> +#include <invalidqmlsourceexception.h> + +#include <QtCore/QFile> +#include <qapplication.h> + +#include <QtGui/QBoxLayout> +#include <QtGui/QListWidget> +#include <QtGui/QPushButton> +#include <QtGui/QMessageBox> + +#include <QtDeclarative/QmlView> +#include <QtDeclarative/QmlContext> +#include <QtDeclarative/QmlEngine> +#include <QtDeclarative/QmlGraphicsItem> + +enum { + debug = false +}; + +namespace QmlDesigner { + +namespace Internal { + +class StatesEditorWidgetPrivate : QObject +{ + Q_OBJECT + +private: + StatesEditorWidgetPrivate(StatesEditorWidget *q); + + int currentIndex() const; + void setCurrentIndex(int i); + + bool validStateName(const QString &name) const; + +private slots: + void currentStateChanged(); + void addState(); + void removeState(); + void duplicateCurrentState(); + +private: + StatesEditorWidget *m_q; + QWeakPointer<Model> model; + QWeakPointer<QmlView> listView; + QWeakPointer<Internal::StatesEditorModel> statesEditorModel; + QWeakPointer<Internal::StatesEditorView> statesEditorView; + friend class QmlDesigner::StatesEditorWidget; +}; + +StatesEditorWidgetPrivate::StatesEditorWidgetPrivate(StatesEditorWidget *q) : + QObject(q), + m_q(q) +{ +} + +int StatesEditorWidgetPrivate::currentIndex() const +{ + Q_ASSERT(listView->root()); + Q_ASSERT(listView->root()->property("currentStateIndex").isValid()); + return listView->root()->property("currentStateIndex").toInt(); +} + +void StatesEditorWidgetPrivate::setCurrentIndex(int i) +{ + listView->root()->setProperty("currentStateIndex", i); +} + +bool StatesEditorWidgetPrivate::validStateName(const QString &name) const +{ + if (name == tr("base state")) + return false; + QList<QmlModelState> modelStates = statesEditorView->stateRootNode().states().allStates(); + foreach (const QmlModelState &state, modelStates) { + if (state.name() == name) + return false; + } + return true; +} + +void StatesEditorWidgetPrivate::currentStateChanged() +{ + statesEditorView->setCurrentState(currentIndex()); +} + +void StatesEditorWidgetPrivate::addState() +{ + QStringList modelStateNames = statesEditorView->stateRootNode().states().names(); + + QString newStateName; + int index = 1; + while (1) { + newStateName = tr("State%1").arg(index++); + if (!modelStateNames.contains(newStateName)) + break; + } + statesEditorView->createState(newStateName); +} + +void StatesEditorWidgetPrivate::removeState() +{ + statesEditorView->removeState(currentIndex()); +} + +void StatesEditorWidgetPrivate::duplicateCurrentState() +{ + statesEditorView->duplicateCurrentState(currentIndex()); +} + +} // namespace Internal + +StatesEditorWidget::StatesEditorWidget(QWidget *parent): + QWidget(parent), + m_d(new Internal::StatesEditorWidgetPrivate(this)) +{ + QFile qmlFile(":/stateseditor/stateslist.qml"); + qmlFile.open(QFile::ReadOnly); + Q_ASSERT(qmlFile.isOpen()); + + m_d->statesEditorModel = new Internal::StatesEditorModel(this); + m_d->listView = new QmlView(this); + + m_d->listView->setAcceptDrops(false); + + QVBoxLayout *layout = new QVBoxLayout(this); + layout->setMargin(0); + layout->setSpacing(0); + layout->addWidget(m_d->listView.data()); + + QString qmlSource(qmlFile.readAll()); + m_d->listView->setQml(qmlSource,":/stateseditor/stateslist.qml"); + + + m_d->listView->setContentResizable(true); + + m_d->listView->rootContext()->setContextProperty(QLatin1String("statesEditorModel"), m_d->statesEditorModel.data()); + + m_d->listView->execute(); + + if (!m_d->listView->root()) + throw InvalidQmlSourceException(__LINE__, __FUNCTION__, __FILE__, qmlSource); + + m_d->listView->setFocusPolicy(Qt::ClickFocus); + QApplication::sendEvent(m_d->listView->scene(), new QEvent(QEvent::WindowActivate)); + + connect(m_d->listView->root(), SIGNAL(currentStateIndexChanged()), m_d, SLOT(currentStateChanged())); + connect(m_d->listView->root(), SIGNAL(createNewState()), m_d, SLOT(addState())); + connect(m_d->listView->root(), SIGNAL(duplicateCurrentState()), m_d, SLOT(duplicateCurrentState())); + connect(m_d->listView->root(), SIGNAL(deleteCurrentState()), m_d, SLOT(removeState())); + + setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred)); + + setWindowTitle(tr("States", "Title of Editor widget")); +} + +StatesEditorWidget::~StatesEditorWidget() +{ + delete m_d; +} + +void StatesEditorWidget::setup(Model *model) +{ + m_d->model = model; + if (m_d->statesEditorView.isNull()) + m_d->statesEditorView = new Internal::StatesEditorView(m_d->statesEditorModel.data(), this); + m_d->statesEditorModel->setStatesEditorView(m_d->statesEditorView.data()); + + m_d->model->attachView(m_d->statesEditorView.data()); + + // select first state (which is the base state) + m_d->currentStateChanged(); +} + + + +QSize StatesEditorWidget::sizeHint() const +{ + return QSize(9999, 188); +} + +} + +#include "stateseditorwidget.moc" diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditorwidget.h b/src/plugins/qmldesigner/components/stateseditor/stateseditorwidget.h new file mode 100644 index 00000000000..902ebb53ddc --- /dev/null +++ b/src/plugins/qmldesigner/components/stateseditor/stateseditorwidget.h @@ -0,0 +1,65 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef STATESEDITORWIDGET_H +#define STATESEDITORWIDGET_H + +#include <QtGui/QWidget> +#include <QtGui/QListWidgetItem> + +namespace QmlDesigner { + +class FormWindow; +class Model; +class ModelState; + +namespace Internal { +class StatesEditorWidgetPrivate; +} + +class StatesEditorWidget : public QWidget +{ + Q_OBJECT + +public: + StatesEditorWidget(QWidget *parent = 0); + virtual ~StatesEditorWidget(); + + void setup(Model *model); + + QSize sizeHint() const; + +private: + friend class Internal::StatesEditorWidgetPrivate; + class Internal::StatesEditorWidgetPrivate *m_d; +}; + +} + +#endif // STATESEDITORWIDGET_H diff --git a/src/plugins/qmldesigner/components/stateseditor/stateslist.qml b/src/plugins/qmldesigner/components/stateseditor/stateslist.qml new file mode 100644 index 00000000000..8486a0d69b8 --- /dev/null +++ b/src/plugins/qmldesigner/components/stateseditor/stateslist.qml @@ -0,0 +1,337 @@ +import Qt 4.6 + +// Shows list of states +// the model has to expose the path through an iconUrl property +Rectangle { + id: root + property int currentStateIndex : 0 + signal createNewState + signal deleteCurrentState + signal duplicateCurrentState + + color: "#707070"; + + function adjustCurrentStateIndex() { + if (currentStateIndex >= statesEditorModel.count) + currentStateIndex = statesEditorModel.count-1; + } + + Connection { + sender: statesEditorModel + signal: "countChanged()" + script: adjustCurrentStateIndex(); + } + + Connection { + sender: statesEditorModel + signal: "changedToState(n)" + script: {root.currentStateIndex = n} + } + + + Flickable { + id: listView + + anchors.fill: root; + + anchors.bottomMargin: 10; + anchors.topMargin: 2; + anchors.leftMargin: 29; + anchors.rightMargin: 40; + + viewportHeight: height + viewportWidth: listViewRow.width + + Row { + id: listViewRow + spacing: 4 + Repeater { + model: statesEditorModel + delegate: Delegate + } + } + + focus: true; + clip: true; + overShoot: false; + interactive:false; + + } + + Component { + id: Delegate + Item { + id: container + width: Math.max(img.width, txt.width) + 4 + height: img.height + txt.height + 4 + + Rectangle { + id: Highlight + anchors.fill: parent + color: "#0303e0" + radius: 4 + opacity: (index==root.currentStateIndex); + onOpacityChanged: if (opacity) { + // If the current item becomes selected, check if it is in the viewport + if (container.x < listView.viewportX) + horizontalScrollbar.viewPosition = container.x; + if (container.x+container.width > listView.viewportX + listView.width) + horizontalScrollbar.viewPosition = container.x+container.width - listView.width; + } + } + + Image { + id: img + pixmap: statePixmap + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + anchors.topMargin: 2 + } + + MouseRegion { + id: itemRegion + anchors.fill: container + onClicked: { + root.currentStateIndex = index + focus=true; // steal focus from textinput + } + } + + TextInput { + anchors.top: img.bottom + anchors.horizontalCenter: img.horizontalCenter + id: txt + text: stateName + color: "#E1E1E1"; + onAccepted: { + // force focus to move to another item, otherwise this one will be reactivated at mode change + focus=false; + itemRegion.focus=true; + } + onFocusChanged: if (!focus) { + txtRegion.enabled=true + statesEditorModel.renameState(index, text) + } + MouseRegion { + id: txtRegion + anchors.fill:parent + property bool wasDoubleClicked:false + onClicked: { + root.currentStateIndex = index; + itemRegion.focus=true; + } + onDoubleClicked: if (index!=0) { // base state not editable + parent.focus=true; + enabled=false; + } + } + } + } + } + + // The add button + Rectangle { + id: addState + + anchors.left: root.left + anchors.top: root.top + anchors.topMargin: 4; + anchors.leftMargin: 4; + + width: 20 + height: 50 + + // Appearance + gradient: Gradient { + GradientStop { position: 0.0; color: "#909090" } + GradientStop { position: 1.0; color: "#AEAEAE" } + } + border.width : 1 + border.color : "#4F4F4F" + radius: 4 + + // "clicked" overlay + Rectangle { + anchors.fill:parent + opacity:parent.state=="Pressed" + gradient: Gradient { + GradientStop { position: 0.0; color: "#727272" } + GradientStop { position: 1.0; color: "#909090" } + } + border.width : 1 + border.color : "#4F4F4F" + radius: 4 + } + + states: State{ name: "Pressed"; } + + // "plus" sign + Rectangle { + width:12 + height:2 + color:"black" + anchors.centerIn:parent + effect: Blur { blurRadius: 1; } + } + Rectangle { + width:2 + height:12 + color:"black" + anchors.centerIn:parent + effect: Blur { blurRadius: 1; } + } + + MouseRegion { + anchors.fill:parent + onClicked: { + if (root.currentStateIndex == 0) + root.createNewState(); //create new state + else + root.duplicateCurrentState(); //duplicate current state + + listView.viewportX = horizontalScrollbar.viewPosition; + // select the newly created state + root.currentStateIndex=statesEditorModel.count - 1; + } + onPressed: {parent.state="Pressed"} + onReleased: {parent.state=""} + } + } + + // The erase button + Rectangle { + id: removeState + + anchors.left: root.left + anchors.top: addState.bottom + anchors.topMargin: 4; + anchors.leftMargin: 4; + + width: 20 + height: 50 + + // Appearance + gradient: Gradient { + GradientStop { position: 0.0; color: "#909090" } + GradientStop { position: 1.0; color: "#AEAEAE" } + } + border.width : 1 + border.color : "#4F4F4F" + radius: 4 + + // "clicked" overlay + Rectangle { + anchors.fill:parent + opacity:parent.state=="Pressed" + gradient: Gradient { + GradientStop { position: 0.0; color: "#727272" } + GradientStop { position: 1.0; color: "#909090" } + } + border.width : 1 + border.color : "#4F4F4F" + radius: 4 + } + + states: State{ name: "Pressed"; } + + // "minus" sign + Rectangle { + width:12 + height:2 + color:"black" + anchors.centerIn:parent + effect: Blur { blurRadius: 1; } + } + + visible: { root.currentStateIndex != 0 } + + MouseRegion { + anchors.fill:parent + onClicked: { + root.deleteCurrentState(); + if (root.currentStateIndex >= statesEditorModel.count) + root.currentStateIndex = root.currentStateIndex-1; + horizontalScrollbar.totalLengthDecreased(); + } + onPressed: {parent.state="Pressed"} + onReleased: {parent.state=""} + enabled: { root.currentStateIndex != 0 } + } + } + + Item { + id: horizontalScrollbar + // Current listView implementation sometimes has negative width or viewportWidth + property int viewPosition: 0; + property int viewLength: ( listView.width>=0 ? listView.width : 0 ); + property int totalLength: ( listView.viewportWidth>=0 ? listView.viewportWidth : 0 ); + + + onViewPositionChanged: listView.viewportX=viewPosition; + onViewLengthChanged: { + if ((totalLength>viewLength) && (viewPosition > totalLength-viewLength)) + viewPosition = totalLength-viewLength; + } + + function totalLengthDecreased() { + if ((totalLength>viewLength) && (viewPosition > totalLength-viewLength)) + viewPosition = totalLength-viewLength; + } + + opacity: viewLength < totalLength; + + anchors.left : listView.left + anchors.right : listView.right + anchors.bottom : root.bottom + anchors.bottomMargin: 4 + + height: 10; + + // the bar itself + Item { + id: draggableBar + width: if (horizontalScrollbar.viewLength>horizontalScrollbar.totalLength) parent.width; + else horizontalScrollbar.viewLength/horizontalScrollbar.totalLength * parent.width; + height: parent.height; + x: horizontalScrollbar.viewPosition*horizontalScrollbar.width/horizontalScrollbar.totalLength; + + + Rectangle { + height:parent.height-1 + width:parent.width + y:1 + + gradient: Gradient { + GradientStop { position: 0.0; color: "#C6C6C6" } + GradientStop { position: 1.0; color: "#7E7E7E" } + } + } + + MouseRegion { + anchors.fill:parent + property int dragging:0; + property int originalX:0; + onPressed: { dragging = 1; originalX = mouse.x; } + onReleased: { dragging = 0; } + onPositionChanged: if (dragging) + { + var newX = mouse.x - originalX + parent.x; + if (newX<0) newX=0; + if (newX>horizontalScrollbar.width-draggableBar.width) + newX=horizontalScrollbar.width-draggableBar.width; + horizontalScrollbar.viewPosition = newX*horizontalScrollbar.totalLength/horizontalScrollbar.width; + } + } + } + + // border + Rectangle { + anchors.fill:parent; + color:"Transparent"; + border.width:1; + border.color:"#8F8F8F"; + } + + } + +} diff --git a/src/plugins/qmldesigner/components/themeloader/qts60stylethemeio.cpp b/src/plugins/qmldesigner/components/themeloader/qts60stylethemeio.cpp new file mode 100644 index 00000000000..2ad3e7c8842 --- /dev/null +++ b/src/plugins/qmldesigner/components/themeloader/qts60stylethemeio.cpp @@ -0,0 +1,357 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#include "qts60stylethemeio.h" + +#if !defined(QT_NO_STYLE_S60) + +#include "qs60style.h" +#include "qapplication.h" +#include "qwebview.h" +#include "qwebframe.h" +#include "qeventloop.h" +#include "qpicture.h" +#include "qpicture.h" +#include "qpainter.h" +#include "qfile.h" +#include "qdir.h" +#include "qfileinfo.h" +#include "qxmlstream.h" +#include "qbuffer.h" + +#include "qdebug.h" + +static const quint32 blobVersion = 1; +static const int pictureSize = 256; + +void dumpPartPictures(const QHash<QString, QPicture> &partPictures) { + foreach (const QString &partKey, partPictures.keys()) { + QPicture partPicture = partPictures.value(partKey); + qDebug() << partKey << partPicture.boundingRect(); + QImage image(partPicture.boundingRect().size(), QImage::Format_ARGB32); + image.fill(Qt::transparent); + QPainter p(&image); + partPicture.play(&p); + image.save(partKey + QString::fromLatin1(".png")); + } +} + +void dumpColors(const QHash<QPair<QString, int>, QColor> &colors) { + foreach (const QColor &color, colors.values()) { + const QPair<QString, int> key = colors.key(color); + qDebug() << key << color; + } +} + +bool setS60Theme(QHash<QString, QPicture> &partPictures, + QHash<QPair<QString, int>, QColor> &colors, + QS60Style *s60Style) +{ + if (!s60Style) + s60Style = qobject_cast<QS60Style *>(QApplication::style()); + if (!s60Style || !qobject_cast<QS60Style *>(s60Style)) { + qWarning() << __FUNCTION__ << ": No QS60Style found."; + return false; + } + s60Style->setS60Theme(partPictures, colors); + return true; +} + +#ifndef QT_NO_WEBKIT +class WebKitSVGRenderer : public QWebView +{ + Q_OBJECT + +public: + WebKitSVGRenderer(QWidget *parent = 0); + QPicture svgToQPicture(const QString &svgFileName); + +private slots: + void loadFinishedSlot(bool ok); + +private: + QEventLoop m_loop; + QPicture m_result; +}; + +WebKitSVGRenderer::WebKitSVGRenderer(QWidget *parent) + : QWebView(parent) +{ + connect(this, SIGNAL(loadFinished(bool)), SLOT(loadFinishedSlot(bool))); + setFixedSize(pictureSize, pictureSize); + QPalette pal = palette(); + pal.setColor(QPalette::Base, Qt::transparent); + setPalette(pal); +} + +QPicture WebKitSVGRenderer::svgToQPicture(const QString &svgFileName) +{ + load(QUrl::fromLocalFile(svgFileName)); + m_loop.exec(); + return m_result; +} + +void WebKitSVGRenderer::loadFinishedSlot(bool ok) +{ + // crude error-checking + if (!ok) + qDebug() << "Failed loading " << qPrintable(url().toString()); + + page()->mainFrame()->evaluateJavaScript( + "document.rootElement.preserveAspectRatio.baseVal.align = SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_NONE;" + "document.rootElement.style.width = '100%';" + "document.rootElement.style.height = '100%';" + "document.rootElement.width.baseVal.newValueSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_PERCENTAGE, 100);" + "document.rootElement.height.baseVal.newValueSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_PERCENTAGE, 100);" + ); + + m_result = QPicture(); // "Clear" + QPainter p(&m_result); + page()->mainFrame()->render(&p); + p.end(); + m_result.setBoundingRect(QRect(0, 0, pictureSize, pictureSize)); + + m_loop.exit(); +} + +bool parseTdfFile(const QString &tdfFile, + QHash<QString, QString> &partSvgs, + QHash<QPair<QString, int>, QColor> &colors) +{ + const QLatin1String elementKey("element"); + const QLatin1String partKey("part"); + const QLatin1String elementIdKey("id"); + const QLatin1String layerKey("layer"); + const QLatin1String layerFileNameKey("filename"); + const QLatin1String layerColourrgbKey("colourrgb"); + const QString annoyingPrefix("S60_2_6%"); + QFile file(tdfFile); + if (!file.open(QIODevice::ReadOnly)) + return false; + QXmlStreamReader reader(&file); + QString partId; + QPair<QString, int> colorId; + // Somebody with a sense of aesthetics may implement proper XML parsing, here. + while (!reader.atEnd()) { + const QXmlStreamReader::TokenType token = reader.readNext(); + switch (token) { + case QXmlStreamReader::StartElement: + if (reader.name() == elementKey || reader.name() == partKey) { + QString id = reader.attributes().value(elementIdKey).toString(); + if (QS60Style::partKeys().contains(id)) { + partId = id; + } else if (!id.isEmpty() && id.at(id.length()-1).isDigit()) { + QString idText = id; + idText.remove(QRegExp("[0-9]")); + if (QS60Style::colorListKeys().contains(idText)) { + QString idNumber = id; + idNumber.remove(QRegExp("[a-zA-Z]")); + colorId = QPair<QString, int>(idText, idNumber.toInt()); + } + } else if (QS60Style::partKeys().contains(id.mid(annoyingPrefix.length()))) { + partId = id.mid(annoyingPrefix.length()); + } + } else if (reader.name() == layerKey) { + if (!partId.isEmpty()) { + const QString svgFile = reader.attributes().value(layerFileNameKey).toString(); + partSvgs.insert(partId, svgFile); + partId.clear(); + } else if (!colorId.first.isEmpty()) { + const QColor colorValue(reader.attributes().value(layerColourrgbKey).toString().toInt(NULL, 16)); + colors.insert(colorId, colorValue); + colorId.first.clear(); + } + } + break; + case QXmlStreamReader::EndElement: + if (reader.tokenString() == elementKey || reader.name() == partKey) + partId.clear(); + break; + default: + break; + } + } + return true; +} + +bool loadThemeFromTdf(const QString &tdfFile, + QHash<QString, QPicture> &partPictures, + QHash<QPair<QString, int>, QColor> &colors) +{ + QHash<QString, QString> parsedPartSvgs; + QHash<QString, QPicture> parsedPartPictures; + QHash<QPair<QString, int>, QColor> parsedColors; + bool success = parseTdfFile(tdfFile, parsedPartSvgs, parsedColors); + if (!success) + return false; + const QString tdfBasePath = QFileInfo(tdfFile).absolutePath(); + WebKitSVGRenderer renderer; + foreach(const QString& partKey, parsedPartSvgs.keys()) { + const QString tdfFullName = + tdfBasePath + QDir::separator() + parsedPartSvgs.value(partKey); + if (!QFile(tdfFullName).exists()) + qWarning() << "Could not load part " << tdfFullName; + const QPicture partPicture = renderer.svgToQPicture(tdfFullName); + parsedPartPictures.insert(partKey, partPicture); + } +// dumpPartPictures(parsedPartPictures); +// dumpColors(colors); + partPictures = parsedPartPictures; + colors = parsedColors; + return true; +} + +bool QtS60StyleThemeIO::loadThemeFromTdf(const QString &themeTdf, QS60Style *s60Style) +{ + QHash<QString, QPicture> partPictures; + QHash<QPair<QString, int>, QColor> colors; + + if (!::loadThemeFromTdf(themeTdf, partPictures, colors)) + return false; + + return ::setS60Theme(partPictures, colors, s60Style); +} + +bool QtS60StyleThemeIO::convertTdfToBlob(const QString &themeTdf, const QString &themeBlob) +{ + QHash<QString, QPicture> partPictures; + QHash<QPair<QString, int>, QColor> colors; + + if (!::loadThemeFromTdf(themeTdf, partPictures, colors)) + return false; + + QFile blob(themeBlob); + if (!blob.open(QIODevice::WriteOnly)) { + qWarning() << __FUNCTION__ << ": Could not create blob: " << themeBlob; + return false; + } + + QByteArray data; + QBuffer dataBuffer(&data); + dataBuffer.open(QIODevice::WriteOnly); + QDataStream dataOut(&dataBuffer); + + const int colorsCount = colors.count(); + dataOut << colorsCount; + const QList<QPair<QString, int> > colorKeys = colors.keys(); + for (int i = 0; i < colorsCount; ++i) { + const QPair<QString, int> &key = colorKeys.at(i); + dataOut << key; + const QColor color = colors.value(key); + dataOut << color; + } + + const int picturesCount = partPictures.count(); + dataOut << picturesCount; + foreach (const QString &key, partPictures.keys()) { + const QPicture picture = partPictures.value(key); + dataOut << key; + dataOut << picture; + } + + QDataStream blobOut(&blob); + blobOut << blobVersion; + blobOut << qCompress(data); + return blobOut.status() == QDataStream::Ok; +} +#endif // !QT_NO_WEBKIT + +bool QtS60StyleThemeIO::loadThemeFromBlob(const QString &themeBlob, QS60Style *s60Style) +{ + QHash<QString, QPicture> partPictures; + QHash<QPair<QString, int>, QColor> colors; + + QFile blob(themeBlob); + if (!blob.open(QIODevice::ReadOnly)) { + qWarning() << __FUNCTION__ << ": Could not read blob: " << themeBlob; + return false; + } + QDataStream blobIn(&blob); + + quint32 version; + blobIn >> version; + + if (version != blobVersion) { + qWarning() << __FUNCTION__ << ": Invalid blob version: " << version << " ...expected: " << blobVersion; + return false; + } + + QByteArray data; + blobIn >> data; + data = qUncompress(data); + QBuffer dataBuffer(&data); + dataBuffer.open(QIODevice::ReadOnly); + QDataStream dataIn(&dataBuffer); + + int colorsCount; + dataIn >> colorsCount; + for (int i = 0; i < colorsCount; ++i) { + QPair<QString, int> key; + dataIn >> key; + QColor value; + dataIn >> value; + colors.insert(key, value); + } + + int picturesCount; + dataIn >> picturesCount; + for (int i = 0; i < picturesCount; ++i) { + QString key; + dataIn >> key; + QPicture value; + dataIn >> value; + value.setBoundingRect(QRect(0, 0, pictureSize, pictureSize)); // Bug? The forced bounding rect was not deserialized. + partPictures.insert(key, value); + } + + if (dataIn.status() != QDataStream::Ok) { + qWarning() << __FUNCTION__ << ": Invalid data blob: " << themeBlob; + return false; + } + +// dumpPartPictures(partPictures); +// dumpColors(colors); + + return ::setS60Theme(partPictures, colors, s60Style); +} + +#include "qts60stylethemeio.moc" + +#endif // QT_NO_STYLE_S60 diff --git a/src/plugins/qmldesigner/components/themeloader/qts60stylethemeio.h b/src/plugins/qmldesigner/components/themeloader/qts60stylethemeio.h new file mode 100644 index 00000000000..40cc0c5da25 --- /dev/null +++ b/src/plugins/qmldesigner/components/themeloader/qts60stylethemeio.h @@ -0,0 +1,58 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#ifndef QTS60STYLETHEMEIO_H +#define QTS60STYLETHEMEIO_H + +#include "qstring.h" + +class QS60Style; + +class QtS60StyleThemeIO +{ +public: + static bool loadThemeFromBlob(const QString &themeBlob, QS60Style *s60Style = 0); +#ifndef QT_NO_WEBKIT + static bool loadThemeFromTdf(const QString &themeTdf, QS60Style *s60Style = 0); + static bool convertTdfToBlob(const QString &themeTdf, const QString &themeBlob); +#endif +}; + +#endif // QTS60STYLETHEMEIO_H diff --git a/src/plugins/qmldesigner/components/themeloader/qts60stylethemeio.pri b/src/plugins/qmldesigner/components/themeloader/qts60stylethemeio.pri new file mode 100644 index 00000000000..ec800883be0 --- /dev/null +++ b/src/plugins/qmldesigner/components/themeloader/qts60stylethemeio.pri @@ -0,0 +1,6 @@ +contains(QT_CONFIG, webkit) { + QT += webkit +} +#SOURCES += $$PWD/qts60stylethemeio.cpp +#INCLUDE += $$PWD/qts60stylethemeio.h +#INCLUDEPATH += $$PWD diff --git a/src/plugins/qmldesigner/config.pri b/src/plugins/qmldesigner/config.pri new file mode 100644 index 00000000000..c96394ccd4b --- /dev/null +++ b/src/plugins/qmldesigner/config.pri @@ -0,0 +1,27 @@ +contains(QT_CONFIG, reduce_exports):CONFIG += hide_symbols + +# MOC_DIR = moc +DEFINES += QT_NO_STYLE_S60 + +debug { + DEFINES += VIEWLOGGER +} + +linux-g++-64 { + // note that -Werror = return-type \ + is \ + only \ + supported \ + with \ + gcc \ + >4.3 + QMAKE_CXXFLAGS += "-Werror=return-type -Werror=init-self" + QMAKE_LFLAGS += "-rdynamic" +} +TMP_BAUHAUS_NO_OUTPUT = $$[BAUHAUS_NO_OUTPUT] +equals(TMP_BAUHAUS_NO_OUTPUT, true):DEFINES += QT_NO_DEBUG_OUTPUT \ + QT_NO_WARNING_OUTPUT +DEFINES += ENABLE_TEXT_VIEW \ + QWEAKPOINTER_ENABLE_ARROW + +isEmpty($$(BAUHAUS_OUTPUT_IN_TEST)):DEFINES += QDEBUG_IN_TESTS WARNINGS_IN_TESTS diff --git a/src/plugins/qmldesigner/core/core.pri b/src/plugins/qmldesigner/core/core.pri new file mode 100644 index 00000000000..0768c58fd72 --- /dev/null +++ b/src/plugins/qmldesigner/core/core.pri @@ -0,0 +1,183 @@ +include($$PWD/filemanager/filemanager.pri) +include (../config.pri) +QT += script \ + declarative \ + opengl +DEFINES += TEST_EXPORTS +INCLUDEPATH += $$PWD \ + $$PWD/include +DEPENDPATH += $$PWD $$PWD/include +INCLUDEPATH += $$QT_SOURCE_TREE/src/declarative/qml +SOURCES += $$PWD/model/abstractview.cpp \ + $$PWD/instances/nodeinstanceview.cpp \ + $$PWD/model/rewriterview.cpp \ + $$PWD/metainfo/enumeratormetainfo.cpp \ + $$PWD/metainfo/metainfo.cpp \ + $$PWD/metainfo/metainfoparser.cpp \ + $$PWD/metainfo/propertymetainfo.cpp \ + $$PWD/metainfo/nodemetainfo.cpp \ + $$PWD/metainfo/itemlibraryinfo.cpp \ + $$PWD/metainfo/subcomponentmanager.cpp \ + $$PWD/model/internalproperty.cpp \ + $$PWD/model/model.cpp \ + $$PWD/model/modelnode.cpp \ + $$PWD/model/painteventfilter.cpp \ + $$PWD/model/internalnode.cpp \ + $$PWD/model/propertyparser.cpp \ + $$PWD/model/propertycontainer.cpp \ + $$PWD/pluginmanager/widgetpluginmanager.cpp \ + $$PWD/pluginmanager/widgetpluginpath.cpp \ + $$PWD/instances/nodeinstance.cpp \ + $$PWD/instances/objectnodeinstance.cpp \ + $$PWD/instances/widgetnodeinstance.cpp \ + $$PWD/instances/graphicswidgetnodeinstance.cpp \ + $$PWD/instances/qmlgraphicsitemnodeinstance.cpp \ + $$PWD/instances/graphicsscenenodeinstance.cpp \ + $$PWD/instances/graphicsviewnodeinstance.cpp \ + $$PWD/instances/proxywidgetnodeinstance.cpp \ + $$PWD/instances/qmlviewnodeinstance.cpp \ + $$PWD/instances/dummynodeinstance.cpp \ + $$PWD/instances/qmlpropertychangesnodeinstance.cpp \ + $$PWD/instances/qmlstatenodeinstance.cpp \ + $$PWD/exceptions/exception.cpp \ + $$PWD/exceptions/invalidnodeinstanceexception.cpp \ + $$PWD/exceptions/invalidpropertyexception.cpp \ + $$PWD/exceptions/invalidmodelnodeexception.cpp \ + $$PWD/exceptions/invalidreparentingexception.cpp \ + $$PWD/exceptions/invalidmetainfoexception.cpp \ + $$PWD/exceptions/invalidargumentexception.cpp \ + $$PWD/exceptions/noanchoringpossibleexception.cpp \ + $$PWD/exceptions/notimplementedexception.cpp \ + $$PWD/exceptions/invalidnodestateexception.cpp \ + $$PWD/model/variantparser.cpp \ + $$PWD/exceptions/invalidmodelstateexception.cpp \ + $$PWD/exceptions/removebasestateexception.cpp \ + $$PWD/exceptions/invalididexception.cpp \ + $$PWD/model/propertynode.cpp \ + $$PWD/exceptions/invalidslideindexexception.cpp \ + $$PWD/instances/graphicsobjectnodeinstance.cpp \ + $$PWD/model/import.cpp \ + $$PWD/instances/componentnodeinstance.cpp \ + $$PWD/exceptions/invalidqmlsourceexception.cpp \ + $$PWD/model/viewlogger.cpp \ + $$PWD/model/internalvariantproperty.cpp \ + $$PWD/model/internalnodelistproperty.cpp \ + $$PWD/model/variantproperty.cpp \ + $$PWD/model/nodelistproperty.cpp \ + $$PWD/model/abstractproperty.cpp \ + $$PWD/model/internalbindingproperty.cpp \ + $$PWD/model/bindingproperty.cpp \ + $$PWD/model/internalnodeproperty.cpp \ + $$PWD/model/internalnodeabstractproperty.cpp \ + $$PWD/model/nodeabstractproperty.cpp \ + $$PWD/model/nodeproperty.cpp \ + $$PWD/model/modeltotextmerger.cpp \ + $$PWD/model/texttomodelmerger.cpp \ + $$PWD/model/plaintexteditmodifier.cpp \ + $$PWD/model/componenttextmodifier.cpp \ + $$PWD/model/textmodifier.cpp \ + $$PWD/model/qmlmodelview.cpp \ + $$PWD/model/qmlitemnode.cpp \ + $$PWD/model/qmlstate.cpp \ + $$PWD/model/qmlchangeset.cpp \ + $$PWD/model/qmlmodelnodefacade.cpp \ + $$PWD/model/qmlobjectnode.cpp \ + $$PWD/model/qmlanchors.cpp \ + $$PWD/rewritertransaction.cpp \ + $$PWD/model/rewriteaction.cpp \ + $$PWD/model/modelnodepositionstorage.cpp \ + $$PWD/model/modelnodepositionrecalculator.cpp \ + $$PWD/model/rewriteactioncompressor.cpp \ + $$PWD/model/qmltextgenerator.cpp \ + $$PWD/model/modelmerger.cpp \ + $$PWD/instances/qmltransitionnodeinstance.cpp +HEADERS += $$PWD/include/corelib_global.h \ + $$PWD/include/abstractview.h \ + $$PWD/include/nodeinstanceview.h \ + $$PWD/include/rewriterview.h \ + $$PWD/include/enumeratormetainfo.h \ + $$PWD/include/metainfo.h \ + $$PWD/include/metainfoparser.h \ + $$PWD/include/nodemetainfo.h \ + $$PWD/include/propertymetainfo.h \ + $$PWD/include/itemlibraryinfo.h \ + $$PWD/model/internalproperty.h \ + $$PWD/include/modelnode.h \ + $$PWD/include/model.h \ + $$PWD/include/nodeproperty.h \ + $$PWD/include/widgetqueryview.h \ + $$PWD/include/subcomponentmanager.h \ + $$PWD/include/propertycontainer.h \ + $$PWD/model/internalnode_p.h \ + $$PWD/model/model_p.h \ + $$PWD/model/painteventfilter_p.h \ + $$PWD/model/propertyparser.h \ + $$PWD/pluginmanager/widgetpluginmanager.h \ + $$PWD/pluginmanager/widgetpluginpath.h \ + $$PWD/include/nodeinstance.h \ + $$PWD/instances/objectnodeinstance.h \ + $$PWD/instances/widgetnodeinstance.h \ + $$PWD/instances/graphicswidgetnodeinstance.h \ + $$PWD/instances/qmlgraphicsitemnodeinstance.h \ + $$PWD/instances/graphicsscenenodeinstance.h \ + $$PWD/instances/graphicsviewnodeinstance.h \ + $$PWD/instances/proxywidgetnodeinstance.h \ + $$PWD/instances/qmlviewnodeinstance.h \ + $$PWD/instances/dummynodeinstance.h \ + $$PWD/instances/qmlpropertychangesnodeinstance.h \ + $$PWD/instances/qmlstatenodeinstance.h \ + $$PWD/include/exception.h \ + $$PWD/include/invalidnodeinstanceexception.h \ + $$PWD/include/invalidmodelnodeexception.h \ + $$PWD/include/invalidreparentingexception.h \ + $$PWD/include/invalidmetainfoexception.h \ + $$PWD/include/invalidargumentexception.h \ + $$PWD/include/notimplementedexception.h \ + $$PWD/include/invalidpropertyexception.h \ + $$PWD/include/invalidmodelstateexception.h \ + $$PWD/include/removebasestateexception.h \ + $$PWD/include/invalididexception.h \ + $$PWD/model/variantparser.h \ + $$PWD/include/propertynode.h \ + $$PWD/include/invalidslideindexexception.h \ + $$PWD/instances/graphicsobjectnodeinstance.h \ + $$PWD/include/import.h \ + $$PWD/instances/componentnodeinstance.h \ + $$PWD/include/invalidqmlsourceexception.h \ + $$PWD/model/viewlogger.h \ + $$PWD/model/internalvariantproperty.h \ + $$PWD/model/internalnodelistproperty.h \ + $$PWD/include/variantproperty.h \ + $$PWD/include/nodelistproperty.h \ + $$PWD/include/abstractproperty.h \ + $$PWD/model/internalbindingproperty.h \ + $$PWD/include/bindingproperty.h \ + $$PWD/model/internalnodeproperty.h \ + $$PWD/model/internalnodeabstractproperty.h \ + $$PWD/include/nodeabstractproperty.h \ + $$PWD/include/plaintexteditmodifier.h \ + $$PWD/include/componenttextmodifier.h \ + $$PWD/include/textmodifier.h \ + $$PWD/model/modeltotextmerger.h \ + $$PWD/model/texttomodelmerger.h \ + $$PWD/include/qmlmodelview.h \ + $$PWD/include/qmlitemnode.h \ + $$PWD/include/qmlstate.h \ + $$PWD/include/qmlchangeset.h \ + $$PWD/include/qmlmodelnodefacade.h \ + $$PWD/include/forwardview.h \ + $$PWD/include/qmlobjectnode.h \ + $$PWD/include/qmlanchors.h \ + $$PWD/rewritertransaction.h \ + $$PWD/model/rewriteaction.h \ + $$PWD/include/modelnodepositionstorage.h \ + $$PWD/model/modelnodepositionrecalculator.h \ + $$PWD/model/rewriteactioncompressor.h \ + $$PWD/model/qmltextgenerator.h \ + $$PWD/include/modelmerger.h \ + $$PWD/include/mathutils.h \ + $$PWD/instances/qmltransitionnodeinstance.h \ + $$PWD/include/customnotifications.h +DISTFILES += $$PWD/metafile/widget.metafile +RESOURCES += $$PWD/core.qrc +OTHER_FILES += $$PWD/metainfo/gui.metainfo diff --git a/src/plugins/qmldesigner/core/core.qrc b/src/plugins/qmldesigner/core/core.qrc new file mode 100644 index 00000000000..d79916781db --- /dev/null +++ b/src/plugins/qmldesigner/core/core.qrc @@ -0,0 +1,6 @@ + <!DOCTYPE RCC> +<RCC version="1.0"> + <qresource> + <file>metainfo/gui.metainfo</file> + </qresource> + </RCC> diff --git a/src/plugins/qmldesigner/core/depend_paths.pri b/src/plugins/qmldesigner/core/depend_paths.pri new file mode 100644 index 00000000000..c2059ccbec5 --- /dev/null +++ b/src/plugins/qmldesigner/core/depend_paths.pri @@ -0,0 +1 @@ +DEPENDPATH += $$PWD/qtpropertybrowser/src/ \ No newline at end of file diff --git a/src/plugins/qmldesigner/core/exceptions/exception.cpp b/src/plugins/qmldesigner/core/exceptions/exception.cpp new file mode 100644 index 00000000000..309da4d9ce2 --- /dev/null +++ b/src/plugins/qmldesigner/core/exceptions/exception.cpp @@ -0,0 +1,192 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "exception.h" + +#ifdef Q_OS_LINUX +#include <execinfo.h> +#include <cxxabi.h> +#endif + +#include <QRegExp> + +/*! +\defgroup CoreExceptions +*/ +/*! +\class QmlDesigner::Exception +\ingroup CoreExceptions +\brief This is the abstract base class for all excetions. + Exceptions should be used in cases there is no other way to say something goes wrong. For example + the result would be a inconsistent model or a crash. +*/ + + +namespace QmlDesigner { + +#ifdef Q_OS_LINUX +const char* demangle(const char* name) +{ + char buf[1024]; + size_t size = 1024; + int status; + char* res; + res = abi::__cxa_demangle(name, + buf, + &size, + &status); + return res; +} +#else +const char* demangle(const char* name) +{ + return name; +} +#endif + + +bool Exception::s_shouldAssert = true; + +void Exception::setShouldAssert(bool assert) +{ + s_shouldAssert = assert; +} + +bool Exception::shouldAssert() +{ + return s_shouldAssert; +} + +/*! +\brief Constructor + +\param line use the __LINE__ macro +\param function use the __FUNCTION__ or the Q_FUNC_INFO macro +\param file use the __FILE__ macro +*/ +Exception::Exception(int line, + const QString &function, + const QString &file) + : m_line(line), + m_function(function), + m_file(file) +{ +#ifdef Q_OS_LINUX + void * array[50]; + int nSize = backtrace(array, 50); + char ** symbols = backtrace_symbols(array, nSize); + + for (int i = 0; i < nSize; i++) + { + m_backTrace.append(QString("%1\n").arg(symbols[i])); + } + + free(symbols); +#endif + +if (s_shouldAssert) + Q_ASSERT_X(false, function.toLatin1(), QString("%1:%2 - %3").arg(file).arg(line).arg(function).toLatin1()); +} + +Exception::~Exception() +{ +} + +/*! +\brief Returns the unmangled backtrace of this exception + +\returns the backtrace as a string +*/ +QString Exception::backTrace() const +{ + return m_backTrace; +} + +/*! +\brief Returns the optional description of this exception + +\returns the description as string +*/ +QString Exception::description() const +{ + return QString(); +} + +/*! +\brief Returns the line number where this exception was thrown + +\returns the line number as integer +*/ +int Exception::line() const +{ + return m_line; +} + +/*! +\brief Returns the function name where this exception was thrown + +\returns the function name as string +*/ +QString Exception::function() const +{ + return m_function; +} + +/*! +\brief Returns the file name where this exception was thrown + +\returns the file name as string +*/ +QString Exception::file() const +{ + return m_file; +} + +QDebug operator<<(QDebug debug, const Exception &exception) +{ + debug.nospace() << "Exception: " << exception.type() << "\n" + "Function: " << exception.function() << "\n" + "File: " << exception.file() << "\n" + "Line: " << exception.line() << "\n"; + if (!exception.description().isEmpty()) + debug.nospace() << exception.description(); + + if (!exception.backTrace().isEmpty()) + debug.nospace() << exception.backTrace(); + + return debug.space(); +} + +/*! +\fn QString Exception::type() const +\brief Returns the type of this exception + +\returns the type as a string +*/ +} diff --git a/src/plugins/qmldesigner/core/exceptions/invalidargumentexception.cpp b/src/plugins/qmldesigner/core/exceptions/invalidargumentexception.cpp new file mode 100644 index 00000000000..e85149640e9 --- /dev/null +++ b/src/plugins/qmldesigner/core/exceptions/invalidargumentexception.cpp @@ -0,0 +1,75 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "invalidargumentexception.h" + +/*! +\class QmlDesigner::InvalidArgumentException +\ingroup CoreExceptions +\brief Exception for a invalid argument + +*/ +namespace QmlDesigner { + +/*! +\brief Constructor + +\param line use the __LINE__ macro +\param function use the __FUNCTION__ or the Q_FUNC_INFO macro +\param file use the __FILE__ macro +*/ +InvalidArgumentException::InvalidArgumentException(int line, + const QString &function, + const QString &file, + const QString &argument) + : Exception(line, function, file), m_argument(argument) +{ +} + +/*! +\brief Returns the type of this exception + +\returns the type as a string +*/ +QString InvalidArgumentException::type() const +{ + return "InvalidArgumentException"; +} + +/*! +\brief Returns the argument of this exception + +\returns the argument as a string +*/ +QString InvalidArgumentException::argument() const +{ + return m_argument; +} + +} diff --git a/src/plugins/qmldesigner/core/exceptions/invalididexception.cpp b/src/plugins/qmldesigner/core/exceptions/invalididexception.cpp new file mode 100644 index 00000000000..e94145a74c8 --- /dev/null +++ b/src/plugins/qmldesigner/core/exceptions/invalididexception.cpp @@ -0,0 +1,45 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "invalididexception.h" + +namespace QmlDesigner { + +InvalidIdException::InvalidIdException(int line, + const QString &function, + const QString &file): + InvalidArgumentException(line, function, file, "id") +{ +} + +QString InvalidIdException::type() const +{ + return "InvalidIdException"; +} +} diff --git a/src/plugins/qmldesigner/core/exceptions/invalidmetainfoexception.cpp b/src/plugins/qmldesigner/core/exceptions/invalidmetainfoexception.cpp new file mode 100644 index 00000000000..d102339cacd --- /dev/null +++ b/src/plugins/qmldesigner/core/exceptions/invalidmetainfoexception.cpp @@ -0,0 +1,64 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "invalidmetainfoexception.h" + +/*! +\class QmlDesigner::InvalidMetaInfoException +\ingroup CoreExceptions +\brief Exception for a invalid meta info + +\see NodeMetaInfo PropertyMetaInfo MetaInfo +*/ +namespace QmlDesigner { +/*! +\brief Constructor + +\param line use the __LINE__ macro +\param function use the __FUNCTION__ or the Q_FUNC_INFO macro +\param file use the __FILE__ macro +*/ +InvalidMetaInfoException::InvalidMetaInfoException(int line, + const QString &function, + const QString &file) + : Exception(line, function, file) +{ +} + +/*! +\brief Returns the type of this exception + +\returns the type as a string +*/ +QString InvalidMetaInfoException::type() const +{ + return "InvalidMetaInfoException"; +} + +} diff --git a/src/plugins/qmldesigner/core/exceptions/invalidmodelnodeexception.cpp b/src/plugins/qmldesigner/core/exceptions/invalidmodelnodeexception.cpp new file mode 100644 index 00000000000..7b85bc103ca --- /dev/null +++ b/src/plugins/qmldesigner/core/exceptions/invalidmodelnodeexception.cpp @@ -0,0 +1,64 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "invalidmodelnodeexception.h" + +/*! +\class QmlDesigner::InvalidModelNodeException +\ingroup CoreExceptions +\brief Exception for a invalid model node + +\see ModelNode +*/ +namespace QmlDesigner { +/*! +\brief Constructor + +\param line use the __LINE__ macro +\param function use the __FUNCTION__ or the Q_FUNC_INFO macro +\param file use the __FILE__ macro +*/ +InvalidModelNodeException::InvalidModelNodeException(int line, + const QString &function, + const QString &file) + : Exception(line, function, file) +{ +} + +/*! +\brief Returns the type of this exception + +\returns the type as a string +*/ +QString InvalidModelNodeException::type() const +{ + return "InvalidModelNodeException"; +} + +} diff --git a/src/plugins/qmldesigner/core/exceptions/invalidmodelstateexception.cpp b/src/plugins/qmldesigner/core/exceptions/invalidmodelstateexception.cpp new file mode 100644 index 00000000000..adb0905f860 --- /dev/null +++ b/src/plugins/qmldesigner/core/exceptions/invalidmodelstateexception.cpp @@ -0,0 +1,65 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "invalidmodelstateexception.h" + +/*! +\class QmlDesigner::InvalidModelStateException +\ingroup CoreExceptions +\brief Exception for a invalid model state + + +\see ModelState +*/ +namespace QmlDesigner { +/*! +\brief Constructor + +\param line use the __LINE__ macro +\param function use the __FUNCTION__ or the Q_FUNC_INFO macro +\param file use the __FILE__ macro +*/ +InvalidModelStateException::InvalidModelStateException(int line, + const QString &function, + const QString &file) + : Exception(line, function, file) +{ +} + +/*! +\brief Returns the type of this exception + +\returns the type as a string +*/ +QString InvalidModelStateException::type() const +{ + return "InvalidModelStateException"; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/core/exceptions/invalidnodeinstanceexception.cpp b/src/plugins/qmldesigner/core/exceptions/invalidnodeinstanceexception.cpp new file mode 100644 index 00000000000..44c2641a00c --- /dev/null +++ b/src/plugins/qmldesigner/core/exceptions/invalidnodeinstanceexception.cpp @@ -0,0 +1,65 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "invalidnodeinstanceexception.h" + +/*! +\class QmlDesigner::InvalidNodeInstanceException +\ingroup CoreExceptions +\brief Exception for a invalid ndoe instance + + +\see NodeInstance +*/ +namespace QmlDesigner { +/*! +\brief Constructor + +\param line use the __LINE__ macro +\param function use the __FUNCTION__ or the Q_FUNC_INFO macro +\param file use the __FILE__ macro +*/ +InvalidNodeInstanceException::InvalidNodeInstanceException(int line, + const QString &function, + const QString &file) + : Exception(line, function, file) +{ +} + +/*! +\brief Returns the type of this exception + +\returns the type as a string +*/ +QString InvalidNodeInstanceException::type() const +{ + return "InvalidNodeInstanceException"; +} + +} diff --git a/src/plugins/qmldesigner/core/exceptions/invalidnodestateexception.cpp b/src/plugins/qmldesigner/core/exceptions/invalidnodestateexception.cpp new file mode 100644 index 00000000000..d2323abe642 --- /dev/null +++ b/src/plugins/qmldesigner/core/exceptions/invalidnodestateexception.cpp @@ -0,0 +1,65 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "invalidnodestateexception.h" + +/*! +\class QmlDesigner::InvalidNodeStateException +\ingroup CoreExceptions +\brief Exception for a invalid node states + + +/see NodeState +*/ +namespace QmlDesigner { +/*! +\brief Constructor + +\param line use the __LINE__ macro +\param function use the __FUNCTION__ or the Q_FUNC_INFO macro +\param file use the __FILE__ macro +*/ +InvalidNodeStateException::InvalidNodeStateException(int line, + const QString &function, + const QString &file) + : Exception(line, function, file) +{ +} + +/*! +\brief Returns the type of this exception + +\returns the type as a string +*/ +QString InvalidNodeStateException::type() const +{ + return "InvalidNodeStateException"; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/core/exceptions/invalidpropertyexception.cpp b/src/plugins/qmldesigner/core/exceptions/invalidpropertyexception.cpp new file mode 100644 index 00000000000..3d95399f658 --- /dev/null +++ b/src/plugins/qmldesigner/core/exceptions/invalidpropertyexception.cpp @@ -0,0 +1,74 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "invalidpropertyexception.h" +/*! +\class QmlDesigner::InvalidPropertyException +\ingroup CoreExceptions +\brief Exception for a invalid property + +\see AbstractProperty +*/ +namespace QmlDesigner { +/*! +\brief Constructor + +\param line use the __LINE__ macro +\param function use the __FUNCTION__ or the Q_FUNC_INFO macro +\param file use the __FILE__ macro +*/ +InvalidPropertyException::InvalidPropertyException(int line, + const QString &function, + const QString &file, + const QString &argument) + : Exception(line, function, file), m_argument(argument) +{ +} + +/*! +\brief Returns the type of this exception + +\returns the type as a string +*/ +QString InvalidPropertyException::type() const +{ + return "InvalidPropertyException"; +} + +/*! +\brief Returns the argument of the property of this exception + +\returns the argument as a string +*/ +QString InvalidPropertyException::argument() const +{ + return m_argument; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/core/exceptions/invalidqmlsourceexception.cpp b/src/plugins/qmldesigner/core/exceptions/invalidqmlsourceexception.cpp new file mode 100644 index 00000000000..ab3f680e0a4 --- /dev/null +++ b/src/plugins/qmldesigner/core/exceptions/invalidqmlsourceexception.cpp @@ -0,0 +1,71 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "invalidqmlsourceexception.h" + +/*! +\class QmlDesigner::InvalidQmlSourceException +\ingroup CoreExceptions +\brief Exception for invalid qml source code + +*/ +namespace QmlDesigner { +/*! +\brief Constructor + +\param line use the __LINE__ macro +\param function use the __FUNCTION__ or the Q_FUNC_INFO macro +\param file use the __FILE__ macro +\param qmlSource qml source string +*/ +InvalidQmlSourceException::InvalidQmlSourceException(int line, + const QString &function, + const QString &file, + const QString &qmlSource) + : Exception(line, function, file), + m_qmlSource(qmlSource) +{ +} + +/*! +\brief Returns the type of this exception + +\returns the type as a string +*/ +QString InvalidQmlSourceException::type() const +{ + return "InvalidQmlSourceException"; +} + +QString InvalidQmlSourceException::description() const +{ + return m_qmlSource; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/core/exceptions/invalidreparentingexception.cpp b/src/plugins/qmldesigner/core/exceptions/invalidreparentingexception.cpp new file mode 100644 index 00000000000..529b7e08c35 --- /dev/null +++ b/src/plugins/qmldesigner/core/exceptions/invalidreparentingexception.cpp @@ -0,0 +1,62 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "invalidreparentingexception.h" +/*! +\class QmlDesigner::InvalidReparentingException +\ingroup CoreExceptions +\brief Exception for a invalid reparinting + +\see ModelNode +*/ +namespace QmlDesigner { +/*! +\brief Constructor + +\param line use the __LINE__ macro +\param function use the __FUNCTION__ or the Q_FUNC_INFO macro +\param file use the __FILE__ macro +*/ +InvalidReparentingException::InvalidReparentingException(int line, + const QString &function, + const QString &file) + : Exception(line, function, file) +{ +} + +/*! +\brief Returns the type of this exception + +\returns the type as a string +*/ +QString InvalidReparentingException::type() const +{ + return "InvalidReparentingException"; +} +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/core/exceptions/invalidslideindexexception.cpp b/src/plugins/qmldesigner/core/exceptions/invalidslideindexexception.cpp new file mode 100644 index 00000000000..64e07942fb3 --- /dev/null +++ b/src/plugins/qmldesigner/core/exceptions/invalidslideindexexception.cpp @@ -0,0 +1,64 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "invalidslideindexexception.h" + +/*! +\class QmlDesigner::InvalidSlideIndexException +\ingroup CoreExceptions +\brief Exception for a invalid index for s slide + +\see ModelNode +*/ +namespace QmlDesigner { +/*! +\brief Constructor + +\param line use the __LINE__ macro +\param function use the __FUNCTION__ or the Q_FUNC_INFO macro +\param file use the __FILE__ macro +*/ +InvalidSlideIndexException::InvalidSlideIndexException(int line, + const QString &function, + const QString &file) + : Exception(line, function, file) +{ +} + +/*! +\brief Returns the type of this exception + +\returns the type as a string +*/ +QString InvalidSlideIndexException::type() const +{ + return "InvalidSlideIndexException"; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/core/exceptions/modificationgroupexception.cpp b/src/plugins/qmldesigner/core/exceptions/modificationgroupexception.cpp new file mode 100644 index 00000000000..6a8c679c2b8 --- /dev/null +++ b/src/plugins/qmldesigner/core/exceptions/modificationgroupexception.cpp @@ -0,0 +1,63 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "modificationgroupexception.h" + +/*! +\class QmlDesigner::ModificationGroupException +\ingroup CoreExceptions +\brief Exception for a modification group + +*/ +namespace QmlDesigner { +/*! +\brief Constructor + +\param line use the __LINE__ macro +\param function use the __FUNCTION__ or the Q_FUNC_INFO macro +\param file use the __FILE__ macro +*/ +ModificationGroupException::ModificationGroupException(int line, + const QString &function, + const QString &file) + : Exception(line, function, file) +{ +} + +/*! +\brief Returns the type of this exception + +\returns the type as a string +*/ +QString ModificationGroupException::type() const +{ + return "ModificationGroupException"; +} + +} diff --git a/src/plugins/qmldesigner/core/exceptions/noanchoringpossibleexception.cpp b/src/plugins/qmldesigner/core/exceptions/noanchoringpossibleexception.cpp new file mode 100644 index 00000000000..10dab894fef --- /dev/null +++ b/src/plugins/qmldesigner/core/exceptions/noanchoringpossibleexception.cpp @@ -0,0 +1,46 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "noanchoringpossibleexception.h" + +namespace QmlDesigner { + +NoAnchoringPossibleException::NoAnchoringPossibleException(int line, + const QString &function, + const QString &file) + : Exception(line,function, file) +{ +} + + +QString NoAnchoringPossibleException::type() const +{ + return "NoAnchoringPossibleException"; +} +} // namespace QKinecticDesigner diff --git a/src/plugins/qmldesigner/core/exceptions/notimplementedexception.cpp b/src/plugins/qmldesigner/core/exceptions/notimplementedexception.cpp new file mode 100644 index 00000000000..15c01605af2 --- /dev/null +++ b/src/plugins/qmldesigner/core/exceptions/notimplementedexception.cpp @@ -0,0 +1,45 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "notimplementedexception.h" + +namespace QmlDesigner { + +NotImplementedException::NotImplementedException(int line, + const QString &function, + const QString &file): + Exception(line, function, file) +{ +} + +QString NotImplementedException::type() const +{ + return "NotImplementedException"; +} +} diff --git a/src/plugins/qmldesigner/core/exceptions/removebasestateexception.cpp b/src/plugins/qmldesigner/core/exceptions/removebasestateexception.cpp new file mode 100644 index 00000000000..93ebbdd1d1b --- /dev/null +++ b/src/plugins/qmldesigner/core/exceptions/removebasestateexception.cpp @@ -0,0 +1,64 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "removebasestateexception.h" + +/*! +\class QmlDesigner::RemoveBaseStateException +\ingroup CoreExceptions +\brief Exception if you try to remove a BaseState + +/see NodeState ModelState +*/ +namespace QmlDesigner { +/*! +\brief Constructor + +\param line use the __LINE__ macro +\param function use the __FUNCTION__ or the Q_FUNC_INFO macro +\param file use the __FILE__ macro +*/ +RemoveBaseStateException::RemoveBaseStateException(int line, + const QString &function, + const QString &file) + : Exception(line, function, file) +{ +} + +/*! +\brief Returns the type of this exception + +\returns the type as a string +*/ +QString RemoveBaseStateException::type() const +{ + return "RemoveBaseStateException"; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/core/filemanager/addarraymembervisitor.cpp b/src/plugins/qmldesigner/core/filemanager/addarraymembervisitor.cpp new file mode 100644 index 00000000000..026591f85fd --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/addarraymembervisitor.cpp @@ -0,0 +1,118 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include <qmljsast_p.h> +#include <qmljsengine_p.h> + +#include "addarraymembervisitor.h" + +using namespace QmlEditor; +using namespace QmlEditor::Internal; +using namespace QmlJS; +using namespace QmlJS::AST; + +AddArrayMemberVisitor::AddArrayMemberVisitor(QmlDesigner::TextModifier &modifier, + quint32 parentLocation, + const QString &propertyName, + const QString &content): + QMLRewriter(modifier), + m_parentLocation(parentLocation), + m_propertyName(propertyName), + m_content(content), + m_convertObjectBindingIntoArrayBinding(false) +{ +} + +void AddArrayMemberVisitor::findArrayBindingAndInsert(const QString &m_propertyName, UiObjectMemberList *ast) +{ + for (UiObjectMemberList *iter = ast; iter; iter = iter->next) { + if (UiArrayBinding *arrayBinding = cast<UiArrayBinding*>(iter->member)) { + if (flatten(arrayBinding->qualifiedId) == m_propertyName) + insertInto(arrayBinding); + } else if (UiObjectBinding *objectBinding = cast<UiObjectBinding*>(iter->member)) { + if (flatten(objectBinding->qualifiedId) == m_propertyName && willConvertObjectBindingIntoArrayBinding()) + convertAndAdd(objectBinding); + } + } +} + +bool AddArrayMemberVisitor::visit(QmlJS::AST::UiObjectBinding *ast) +{ + if (didRewriting()) + return false; + + if (ast->firstSourceLocation().offset == m_parentLocation) + findArrayBindingAndInsert(m_propertyName, ast->initializer->members); + + return !didRewriting(); +} + +bool AddArrayMemberVisitor::visit(QmlJS::AST::UiObjectDefinition *ast) +{ + if (didRewriting()) + return false; + + if (ast->firstSourceLocation().offset == m_parentLocation) + findArrayBindingAndInsert(m_propertyName, ast->initializer->members); + + return !didRewriting(); +} + +void AddArrayMemberVisitor::insertInto(QmlJS::AST::UiArrayBinding *arrayBinding) +{ + UiObjectMember *lastMember = 0; + for (UiArrayMemberList *iter = arrayBinding->members; iter; iter = iter->next) + if (iter->member) + lastMember = iter->member; + + if (!lastMember) + return; // an array binding cannot be empty, so there will (or should) always be a last member. + + const int insertionPoint = lastMember->lastSourceLocation().end(); + const int indentDepth = calculateIndentDepth(lastMember->firstSourceLocation()); + + replace(insertionPoint, 0, QLatin1String(",\n") + addIndentation(m_content, indentDepth)); + + setDidRewriting(true); +} + +void AddArrayMemberVisitor::convertAndAdd(QmlJS::AST::UiObjectBinding *objectBinding) +{ + const int indentDepth = calculateIndentDepth(objectBinding->firstSourceLocation()); + const QString arrayPrefix = QLatin1String("[\n") + addIndentation(QLatin1String(""), indentDepth); + replace(objectBinding->qualifiedTypeNameId->identifierToken.offset, 0, arrayPrefix); + const int insertionPoint = objectBinding->lastSourceLocation().end(); + replace(insertionPoint, 0, + QLatin1String(",\n") + + addIndentation(m_content, indentDepth) + QLatin1Char('\n') + + addIndentation(QLatin1String("]"), indentDepth) + ); + + setDidRewriting(true); +} diff --git a/src/plugins/qmldesigner/core/filemanager/addarraymembervisitor.h b/src/plugins/qmldesigner/core/filemanager/addarraymembervisitor.h new file mode 100644 index 00000000000..2b595215745 --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/addarraymembervisitor.h @@ -0,0 +1,72 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef ADDARRAYMEMBERVISITOR_H +#define ADDARRAYMEMBERVISITOR_H + +#include "qmlrewriter.h" + +namespace QmlEditor { +namespace Internal { + +class AddArrayMemberVisitor: public QMLRewriter +{ +public: + AddArrayMemberVisitor(QmlDesigner::TextModifier &modifier, + quint32 parentLocation, + const QString &propertyName, + const QString &content); + + bool willConvertObjectBindingIntoArrayBinding() const + { return m_convertObjectBindingIntoArrayBinding; } + + void setConvertObjectBindingIntoArrayBinding(bool convertObjectBindingIntoArrayBinding) + { m_convertObjectBindingIntoArrayBinding = convertObjectBindingIntoArrayBinding; } + +protected: + virtual bool visit(QmlJS::AST::UiObjectBinding *ast); + virtual bool visit(QmlJS::AST::UiObjectDefinition *ast); + +private: + void findArrayBindingAndInsert(const QString &m_propertyName, QmlJS::AST::UiObjectMemberList *ast); + + void insertInto(QmlJS::AST::UiArrayBinding *arrayBinding); + void convertAndAdd(QmlJS::AST::UiObjectBinding *objectBinding); + +private: + quint32 m_parentLocation; + QString m_propertyName; + QString m_content; + bool m_convertObjectBindingIntoArrayBinding; +}; + +} // namespace Internal +} // namespace QmlEditor + +#endif // ADDARRAYMEMBERVISITOR_H diff --git a/src/plugins/qmldesigner/core/filemanager/addobjectvisitor.cpp b/src/plugins/qmldesigner/core/filemanager/addobjectvisitor.cpp new file mode 100644 index 00000000000..a9b5c9ad321 --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/addobjectvisitor.cpp @@ -0,0 +1,93 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include <qmljsast_p.h> +#include <qmljsengine_p.h> + +#include "addobjectvisitor.h" + +using namespace QmlEditor; +using namespace QmlEditor::Internal; +using namespace QmlJS; +using namespace QmlJS::AST; + +AddObjectVisitor::AddObjectVisitor(QmlDesigner::TextModifier &modifier, + quint32 parentLocation, + const QString &content, + const QStringList &propertyOrder): + QMLRewriter(modifier), + m_parentLocation(parentLocation), + m_content(content), + m_propertyOrder(propertyOrder) +{ +} + +bool AddObjectVisitor::visit(QmlJS::AST::UiObjectBinding *ast) +{ + if (didRewriting()) + return false; + + if (ast->qualifiedTypeNameId->identifierToken.offset == m_parentLocation) + insertInto(ast->initializer); + + return !didRewriting(); +} + +bool AddObjectVisitor::visit(QmlJS::AST::UiObjectDefinition *ast) +{ + if (didRewriting()) + return false; + + if (ast->firstSourceLocation().offset == m_parentLocation) + insertInto(ast->initializer); + + return !didRewriting(); +} + +void AddObjectVisitor::insertInto(QmlJS::AST::UiObjectInitializer *ast) +{ + UiObjectMemberList *insertAfter = searchMemberToInsertAfter(ast->members, QString::null, m_propertyOrder); + + int insertionPoint; + int indentDepth; + QString textToInsert; + if (insertAfter && insertAfter->member) { + insertionPoint = insertAfter->member->lastSourceLocation().end(); + indentDepth = calculateIndentDepth(insertAfter->member->lastSourceLocation()); + textToInsert += QLatin1String("\n"); + } else { + insertionPoint = ast->lbraceToken.end(); + indentDepth = calculateIndentDepth(ast->lbraceToken) + 4; + } + + textToInsert += addIndentation(m_content, indentDepth); + replace(insertionPoint, 0, QLatin1String("\n") + textToInsert); + + setDidRewriting(true); +} diff --git a/src/plugins/qmldesigner/core/filemanager/addobjectvisitor.h b/src/plugins/qmldesigner/core/filemanager/addobjectvisitor.h new file mode 100644 index 00000000000..4a3097ffa5d --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/addobjectvisitor.h @@ -0,0 +1,62 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef ADDOBJECTVISITOR_H +#define ADDOBJECTVISITOR_H + +#include "qmlrewriter.h" + +namespace QmlEditor { +namespace Internal { + +class AddObjectVisitor: public QMLRewriter +{ +public: + AddObjectVisitor(QmlDesigner::TextModifier &modifier, + quint32 parentLocation, + const QString &content, + const QStringList &propertyOrder); + +protected: + virtual bool visit(QmlJS::AST::UiObjectBinding *ast); + virtual bool visit(QmlJS::AST::UiObjectDefinition *ast); + +private: + void insertInto(QmlJS::AST::UiObjectInitializer *ast); + +private: + quint32 m_parentLocation; + QString m_content; + QStringList m_propertyOrder; +}; + +} // namespace Internal +} // namespace QmlEditor + +#endif // ADDOBJECTVISITOR_H diff --git a/src/plugins/qmldesigner/core/filemanager/addpropertyvisitor.cpp b/src/plugins/qmldesigner/core/filemanager/addpropertyvisitor.cpp new file mode 100644 index 00000000000..b93792e563b --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/addpropertyvisitor.cpp @@ -0,0 +1,136 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include <qmljsast_p.h> +#include <qmljsengine_p.h> + +#include "addpropertyvisitor.h" + +using namespace QmlEditor; +using namespace QmlEditor::Internal; +using namespace QmlJS; +using namespace QmlJS::AST; + +AddPropertyVisitor::AddPropertyVisitor(QmlDesigner::TextModifier &modifier, + quint32 parentLocation, + const QString &name, + const QString &value, + QmlRefactoring::PropertyType propertyType, + const QStringList &propertyOrder): + QMLRewriter(modifier), + m_parentLocation(parentLocation), + m_name(name), + m_value(value), + m_propertyType(propertyType), + m_propertyOrder(propertyOrder) +{ +} + +bool AddPropertyVisitor::visit(QmlJS::AST::UiObjectDefinition *ast) +{ + if (didRewriting()) + return false; + + if (ast->firstSourceLocation().offset == m_parentLocation) { + addInMembers(ast->initializer); + return false; + } + + return !didRewriting(); +} + +bool AddPropertyVisitor::visit(QmlJS::AST::UiObjectBinding *ast) +{ + if (didRewriting()) + return false; + + if (ast->qualifiedTypeNameId->identifierToken.offset == m_parentLocation) { + addInMembers(ast->initializer); + return false; + } + + return !didRewriting(); +} + +void AddPropertyVisitor::addInMembers(QmlJS::AST::UiObjectInitializer *initializer) +{ + UiObjectMemberList *insertAfter = searchMemberToInsertAfter(initializer->members, m_name, m_propertyOrder); + SourceLocation endOfPreviousMember; + SourceLocation startOfNextMember; + unsigned indentDepth; + + if (insertAfter == 0 || insertAfter->member == 0) { + // insert as first member + endOfPreviousMember = initializer->lbraceToken; + + if (initializer->members && initializer->members->member) + startOfNextMember = initializer->members->member->firstSourceLocation(); + else + startOfNextMember = initializer->rbraceToken; + + indentDepth = calculateIndentDepth(endOfPreviousMember) + 4; + } else { + endOfPreviousMember = insertAfter->member->lastSourceLocation(); + + if (insertAfter->next && insertAfter->next->member) + startOfNextMember = insertAfter->next->member->firstSourceLocation(); + else + startOfNextMember = initializer->rbraceToken; + + indentDepth = calculateIndentDepth(endOfPreviousMember); + } + + QString newPropertyTemplate; + switch (m_propertyType) { + case QmlRefactoring::ArrayBinding: + newPropertyTemplate = QLatin1String("%1: [\n%2\n]"); + m_value = addIndentation(m_value, 4); + break; + + case QmlRefactoring::ObjectBinding: + newPropertyTemplate = QLatin1String("%1: %2"); + break; + + case QmlRefactoring::ScriptBinding: + newPropertyTemplate = QLatin1String("%1: %2"); + break; + + default: + Q_ASSERT(!"unknown property type"); + } + + const bool isOneLiner = endOfPreviousMember.startLine == startOfNextMember.startLine; + if (isOneLiner) + newPropertyTemplate += QLatin1Char('\n'); + + const QString newPropertyText = addIndentation(newPropertyTemplate.arg(m_name, m_value), indentDepth); + replace(endOfPreviousMember.end(), 0, QLatin1Char('\n') + newPropertyText); + + setDidRewriting(true); +} diff --git a/src/plugins/qmldesigner/core/filemanager/addpropertyvisitor.h b/src/plugins/qmldesigner/core/filemanager/addpropertyvisitor.h new file mode 100644 index 00000000000..a375698a08e --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/addpropertyvisitor.h @@ -0,0 +1,68 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef ADDPROPERTYVISITOR_H +#define ADDPROPERTYVISITOR_H + +#include "qmlrefactoring.h" +#include "qmlrewriter.h" + +namespace QmlEditor { +namespace Internal { + +class AddPropertyVisitor: public QMLRewriter +{ +public: +public: + AddPropertyVisitor(QmlDesigner::TextModifier &modifier, + quint32 parentLocation, + const QString &name, + const QString &value, + QmlEditor::QmlRefactoring::PropertyType propertyType, + const QStringList &propertyOrder); + +protected: + virtual bool visit(QmlJS::AST::UiObjectDefinition *ast); + virtual bool visit(QmlJS::AST::UiObjectBinding *ast); + +private: + void addInMembers(QmlJS::AST::UiObjectInitializer *initializer); + +private: + quint32 m_parentLocation; + QString m_name; + QString m_value; + QmlEditor::QmlRefactoring::PropertyType m_propertyType; + QStringList m_propertyOrder; +}; + +} // namespace Internal +} // namespace QmlEditor + +#endif // ADDPROPERTYVISITOR_H diff --git a/src/plugins/qmldesigner/core/filemanager/astobjecttextextractor.cpp b/src/plugins/qmldesigner/core/filemanager/astobjecttextextractor.cpp new file mode 100644 index 00000000000..e7b75904183 --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/astobjecttextextractor.cpp @@ -0,0 +1,75 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "astobjecttextextractor.h" +#include "qmljsast_p.h" + +using namespace QmlEditor; +using namespace QmlJS::AST; + +ASTObjectTextExtractor::ASTObjectTextExtractor(const QString &text): + m_document(QmlDocument::create("<ASTObjectTextExtractor>")) +{ + m_document->setSource(text); + m_document->parse(); +} + +QString ASTObjectTextExtractor::operator ()(int location) +{ + Q_ASSERT(location >= 0); + + m_location = location; + m_text.clear(); + + Node::accept(m_document->program(), this); + + return m_text; +} + +bool ASTObjectTextExtractor::visit(QmlJS::AST::UiObjectBinding *ast) +{ + if (!m_text.isEmpty()) + return false; + + if (ast->qualifiedTypeNameId->identifierToken.offset == m_location) + m_text = m_document->source().mid(m_location, ast->lastSourceLocation().end() - m_location); + + return m_text.isEmpty(); +} + +bool ASTObjectTextExtractor::visit(QmlJS::AST::UiObjectDefinition *ast) +{ + if (!m_text.isEmpty()) + return false; + + if (ast->firstSourceLocation().offset == m_location) + m_text = m_document->source().mid(m_location, ast->lastSourceLocation().end() - m_location); + + return m_text.isEmpty(); +} diff --git a/src/plugins/qmldesigner/core/filemanager/astobjecttextextractor.h b/src/plugins/qmldesigner/core/filemanager/astobjecttextextractor.h new file mode 100644 index 00000000000..029106f4405 --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/astobjecttextextractor.h @@ -0,0 +1,59 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef ASTOBJECTTEXTEXTRACTOR_H +#define ASTOBJECTTEXTEXTRACTOR_H + +#include <QtCore/QString> + +#include "qmldocument.h" +#include "qmljsastvisitor_p.h" + +namespace QmlEditor { + +class ASTObjectTextExtractor: public QmlJS::AST::Visitor +{ +public: + ASTObjectTextExtractor(const QString &text); + + QString operator()(int location); + +protected: + virtual bool visit(QmlJS::AST::UiObjectBinding *ast); + virtual bool visit(QmlJS::AST::UiObjectDefinition *ast); + +private: + QmlDocument::Ptr m_document; + quint32 m_location; + QString m_text; +}; + +} // namespace QmlEditor + +#endif // ASTOBJECTTEXTEXTRACTOR_H diff --git a/src/plugins/qmldesigner/core/filemanager/changeimportsvisitor.cpp b/src/plugins/qmldesigner/core/filemanager/changeimportsvisitor.cpp new file mode 100644 index 00000000000..5ecac14e1a7 --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/changeimportsvisitor.cpp @@ -0,0 +1,80 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include <qmljsast_p.h> +#include <qmljsengine_p.h> + +#include "changeimportsvisitor.h" + +using namespace QmlJS; +using namespace QmlJS::AST; + +using namespace QmlDesigner; +using namespace QmlDesigner::Internal; +using namespace QmlEditor::Internal; + +ChangeImportsVisitor::ChangeImportsVisitor(TextModifier &textModifier, const QSet<Import> &addedImports, const QSet<Import> &removedImports, const QString &source): + QMLRewriter(textModifier), + CopyPasteUtil(source), + m_addedImports(addedImports), + m_removedImports(removedImports) +{ +} + +bool ChangeImportsVisitor::visit(QmlJS::AST::UiProgram *ast) +{ + if (ast->imports) + accept(ast->imports); + + return false; +} + +bool ChangeImportsVisitor::visit(QmlJS::AST::UiImportList *ast) +{ + if (!ast) + return false; + + quint32 prevEnd = 0; + for (UiImportList *it = ast; it; it = it->next) { + UiImport *imp = it->import; + if (!imp) + continue; + + if (m_removedImports.remove(createImport(imp))) + replace(prevEnd, imp->lastSourceLocation().end() - prevEnd, ""); + + prevEnd = imp->lastSourceLocation().end(); + } + + foreach (const Import &i, m_addedImports) { + replace(prevEnd, 0, i.toString(false) + "\n"); + } + + return false; +} diff --git a/src/plugins/qmldesigner/core/filemanager/changeimportsvisitor.h b/src/plugins/qmldesigner/core/filemanager/changeimportsvisitor.h new file mode 100644 index 00000000000..92377994154 --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/changeimportsvisitor.h @@ -0,0 +1,61 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef CHANGEIMPORTSVISITOR_H +#define CHANGEIMPORTSVISITOR_H + +#include <QtCore/QSet> + +#include <model/copypasteutil.h> +#include "import.h" +#include "qmlrewriter.h" + +namespace QmlEditor { +namespace Internal { + +class ChangeImportsVisitor: public QMLRewriter, protected QmlDesigner::Internal::CopyPasteUtil +{ +public: + ChangeImportsVisitor(QmlDesigner::TextModifier &textModifier, + const QSet<QmlDesigner::Import> &addedImports, + const QSet<QmlDesigner::Import> &removedImports, const QString &source); + +protected: + virtual bool visit(QmlJS::AST::UiProgram *ast); + virtual bool visit(QmlJS::AST::UiImportList *ast); + +private: + QSet<QmlDesigner::Import> m_addedImports; + QSet<QmlDesigner::Import> m_removedImports; +}; + +} // namespace Internal +} // namespace QmlEditor + +#endif // CHANGEIMPORTSVISITOR_H diff --git a/src/plugins/qmldesigner/core/filemanager/changeobjecttypevisitor.cpp b/src/plugins/qmldesigner/core/filemanager/changeobjecttypevisitor.cpp new file mode 100644 index 00000000000..d0624f6ab8f --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/changeobjecttypevisitor.cpp @@ -0,0 +1,81 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "changeobjecttypevisitor.h" +#include <qmljsast_p.h> +#include <qmljsengine_p.h> + +using namespace QmlJS; +using namespace QmlJS::AST; +using namespace QmlEditor::Internal; + +ChangeObjectTypeVisitor::ChangeObjectTypeVisitor(QmlDesigner::TextModifier &modifier, + quint32 nodeLocation, + const QString &newType): + QMLRewriter(modifier), + m_nodeLocation(nodeLocation), + m_newType(newType) +{ +} + +bool ChangeObjectTypeVisitor::visit(UiObjectDefinition *ast) +{ + if (ast->firstSourceLocation().offset == m_nodeLocation) { + replaceType(ast->qualifiedTypeNameId); + return false; + } + + return !didRewriting(); +} + +bool ChangeObjectTypeVisitor::visit(UiObjectBinding *ast) +{ + const quint32 start = ast->qualifiedTypeNameId->identifierToken.offset; + + if (start == m_nodeLocation) { + replaceType(ast->qualifiedTypeNameId); + return false; + } + + return !didRewriting(); +} + +void ChangeObjectTypeVisitor::replaceType(UiQualifiedId *typeId) +{ + Q_ASSERT(typeId); + + const int startOffset = typeId->identifierToken.offset; + int endOffset = typeId->identifierToken.end(); + for (UiQualifiedId *iter = typeId->next; iter; iter = iter->next) + if (!iter->next) + endOffset = iter->identifierToken.end(); + + replace(startOffset, endOffset - startOffset, m_newType); + setDidRewriting(true); +} diff --git a/src/plugins/qmldesigner/core/filemanager/changeobjecttypevisitor.h b/src/plugins/qmldesigner/core/filemanager/changeobjecttypevisitor.h new file mode 100644 index 00000000000..01c2e360a73 --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/changeobjecttypevisitor.h @@ -0,0 +1,60 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef CHANGEOBJECTTYPEVISITOR_H +#define CHANGEOBJECTTYPEVISITOR_H + +#include "qmlrewriter.h" + +namespace QmlEditor { +namespace Internal { + +class ChangeObjectTypeVisitor: public QMLRewriter +{ +public: + ChangeObjectTypeVisitor(QmlDesigner::TextModifier &modifier, + quint32 nodeLocation, + const QString &newType); + +protected: + virtual bool visit(QmlJS::AST::UiObjectDefinition *ast); + virtual bool visit(QmlJS::AST::UiObjectBinding *ast); + +private: + void replaceType(QmlJS::AST::UiQualifiedId *typeId); + +private: + quint32 m_nodeLocation; + QString m_newType; +}; + +} // namespace Internal +} // namespace QmlEditor + +#endif // CHANGEOBJECTTYPEVISITOR_H diff --git a/src/plugins/qmldesigner/core/filemanager/changepropertyvisitor.cpp b/src/plugins/qmldesigner/core/filemanager/changepropertyvisitor.cpp new file mode 100644 index 00000000000..885671d1835 --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/changepropertyvisitor.cpp @@ -0,0 +1,191 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include <qmljsast_p.h> +#include <qmljsengine_p.h> + +#include "changepropertyvisitor.h" + +using namespace QmlJS; +using namespace QmlJS::AST; +using namespace QmlEditor; +using namespace QmlEditor::Internal; + +ChangePropertyVisitor::ChangePropertyVisitor(QmlDesigner::TextModifier &modifier, + quint32 parentLocation, + const QString &name, + const QString &value, + QmlRefactoring::PropertyType propertyType): + QMLRewriter(modifier), + m_parentLocation(parentLocation), + m_name(name), + m_value(value), + m_propertyType(propertyType) +{ +} + +bool ChangePropertyVisitor::visit(QmlJS::AST::UiObjectDefinition *ast) +{ + if (didRewriting()) + return false; + + const quint32 objectStart = ast->firstSourceLocation().offset; + + if (objectStart == m_parentLocation) { + replaceInMembers(ast->initializer); + return false; + } + + return !didRewriting(); +} + +bool ChangePropertyVisitor::visit(QmlJS::AST::UiObjectBinding *ast) +{ + if (didRewriting()) + return false; + + const quint32 objectStart = ast->qualifiedTypeNameId->identifierToken.offset; + + if (objectStart == m_parentLocation) { + replaceInMembers(ast->initializer); + return false; + } + + return !didRewriting(); +} + +void ChangePropertyVisitor::replaceInMembers(UiObjectInitializer *initializer) +{ + for (UiObjectMemberList *members = initializer->members; members; members = members->next) { + UiObjectMember *propertyMember = members->member; + + if (isMatchingPropertyMember(propertyMember)) { + switch (m_propertyType) { + case QmlRefactoring::ArrayBinding: + insertIntoArray(cast<UiArrayBinding*>(propertyMember)); + break; + + case QmlRefactoring::ObjectBinding: + replaceMemberValue(propertyMember, false); + break; + + case QmlRefactoring::ScriptBinding: + replaceMemberValue(propertyMember, nextMemberOnSameLine(members)); + break; + + default: + Q_ASSERT(!"Unhandled QmlRefactoring::PropertyType"); + } + + break; + } + } +} + +void ChangePropertyVisitor::replaceMemberValue(UiObjectMember *propertyMember, bool needsSemicolon) +{ + QString replacement = m_value; + int startOffset = -1; + int endOffset = -1; + if (UiObjectBinding *objectBinding = AST::cast<UiObjectBinding *>(propertyMember)) { + startOffset = objectBinding->qualifiedTypeNameId->identifierToken.offset; + endOffset = objectBinding->initializer->rbraceToken.end(); + } else if (UiScriptBinding *scriptBinding = AST::cast<UiScriptBinding *>(propertyMember)) { + startOffset = scriptBinding->statement->firstSourceLocation().offset; + endOffset = scriptBinding->statement->lastSourceLocation().end(); + } else if (UiArrayBinding *arrayBinding = AST::cast<UiArrayBinding *>(propertyMember)) { + startOffset = arrayBinding->lbracketToken.offset; + endOffset = arrayBinding->rbracketToken.end(); + } else if (UiPublicMember *publicMember = AST::cast<UiPublicMember*>(propertyMember)) { + if (publicMember->expression) { + startOffset = publicMember->expression->firstSourceLocation().offset; + if (publicMember->semicolonToken.isValid()) + endOffset = publicMember->semicolonToken.end(); + else + endOffset = publicMember->expression->lastSourceLocation().offset; + } else { + startOffset = publicMember->lastSourceLocation().end(); + endOffset = startOffset; + if (publicMember->semicolonToken.isValid()) + startOffset = publicMember->semicolonToken.offset; + replacement.prepend(QLatin1String(": ")); + } + } else { + return; + } + + if (needsSemicolon) + replacement += ';'; + + replace(startOffset, endOffset - startOffset, replacement); + setDidRewriting(true); +} + +bool ChangePropertyVisitor::isMatchingPropertyMember(QmlJS::AST::UiObjectMember *member) const +{ + if (UiObjectBinding *objectBinding = AST::cast<UiObjectBinding *>(member)) { + return m_name == flatten(objectBinding->qualifiedId); + } else if (UiScriptBinding *scriptBinding = AST::cast<UiScriptBinding *>(member)) { + return m_name == flatten(scriptBinding->qualifiedId); + } else if (UiArrayBinding *arrayBinding = AST::cast<UiArrayBinding *>(member)) { + return m_name == flatten(arrayBinding->qualifiedId); + } else if (UiPublicMember *publicMember = AST::cast<UiPublicMember *>(member)) { + return m_name == publicMember->name->asString(); + } else { + return false; + } +} + +bool ChangePropertyVisitor::nextMemberOnSameLine(UiObjectMemberList *members) +{ + if (members && members->next && members->next->member) { + return members->next->member->firstSourceLocation().startLine == members->member->lastSourceLocation().startLine; + } else { + return false; + } +} + +void ChangePropertyVisitor::insertIntoArray(QmlJS::AST::UiArrayBinding *ast) +{ + if (!ast) + return; + + UiObjectMember *lastMember = 0; + for (UiArrayMemberList *iter = ast->members; iter; iter = iter->next) { + } + + if (!lastMember) + return; + + const int insertionPoint = lastMember->lastSourceLocation().end(); + const int indentDepth = calculateIndentDepth(lastMember->firstSourceLocation()); + const QString indentedArrayMember = addIndentation(m_value, indentDepth); + replace(insertionPoint, 0, QLatin1String(",\n") + indentedArrayMember); + setDidRewriting(true); +} diff --git a/src/plugins/qmldesigner/core/filemanager/changepropertyvisitor.h b/src/plugins/qmldesigner/core/filemanager/changepropertyvisitor.h new file mode 100644 index 00000000000..6cc41d8f917 --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/changepropertyvisitor.h @@ -0,0 +1,70 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef CHANGEPROPERTYVISITOR_H +#define CHANGEPROPERTYVISITOR_H + +#include "qmlrefactoring.h" +#include "qmlrewriter.h" + +namespace QmlEditor { +namespace Internal { + +class ChangePropertyVisitor: public QMLRewriter +{ +public: + ChangePropertyVisitor(QmlDesigner::TextModifier &modifier, + quint32 parentLocation, + const QString &name, + const QString &value, + QmlRefactoring::PropertyType propertyType); + +protected: + virtual bool visit(QmlJS::AST::UiObjectDefinition *ast); + virtual bool visit(QmlJS::AST::UiObjectBinding *ast); + +private: + void replaceInMembers(QmlJS::AST::UiObjectInitializer *initializer); + void replaceMemberValue(QmlJS::AST::UiObjectMember *propertyMember, bool needsSemicolon); + bool isMatchingPropertyMember(QmlJS::AST::UiObjectMember *member) const; + static bool nextMemberOnSameLine(QmlJS::AST::UiObjectMemberList *members); + + void insertIntoArray(QmlJS::AST::UiArrayBinding* ast); + +private: + quint32 m_parentLocation; + QString m_name; + QString m_value; + QmlRefactoring::PropertyType m_propertyType; +}; + +} // namespace Internal +} // namespace QmlEditor + +#endif // CHANGEPROPERTYVISITOR_H diff --git a/src/plugins/qmldesigner/core/filemanager/changeset.cpp b/src/plugins/qmldesigner/core/filemanager/changeset.cpp new file mode 100644 index 00000000000..b006106a16b --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/changeset.cpp @@ -0,0 +1,359 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "changeset.h" + +namespace Utils { + +ChangeSet::ChangeSet() + : m_string(0), m_cursor(0), m_error(false) +{ +} + +static bool overlaps(int posA, int lengthA, int posB, int lengthB) { + if (lengthB > 0) { + return + // right edge of B contained in A + (posA < posB + lengthB && posA + lengthA >= posB + lengthB) + // left edge of B contained in A + || (posA <= posB && posA + lengthA > posB) + // A contained in B + || (posB < posA && posB + lengthB > posA + lengthA); + } else { + return (posB > posA && posB < posA + lengthA); + } +} + +bool ChangeSet::hasOverlap(int pos, int length) +{ + QListIterator<EditOp> i(m_operationList); + while (i.hasNext()) { + const EditOp &cmd = i.next(); + + switch (cmd.type) { + case EditOp::Replace: + if (overlaps(pos, length, cmd.pos1, cmd.length1)) + return true; + break; + + case EditOp::Move: + if (overlaps(pos, length, cmd.pos1, cmd.length1)) + return true; + if (cmd.pos2 > pos && cmd.pos2 < pos + length) + return true; + break; + + case EditOp::Insert: + if (cmd.pos1 > pos && cmd.pos1 < pos + length) + return true; + break; + + case EditOp::Remove: + if (overlaps(pos, length, cmd.pos1, cmd.length1)) + return true; + break; + + case EditOp::Flip: + if (overlaps(pos, length, cmd.pos1, cmd.length1)) + return true; + if (overlaps(pos, length, cmd.pos2, cmd.length2)) + return true; + break; + + case EditOp::Copy: + if (overlaps(pos, length, cmd.pos1, cmd.length1)) + return true; + if (cmd.pos2 > pos && cmd.pos2 < pos + length) + return true; + break; + + case EditOp::Unset: + break; + } + } + + return false; +} + +bool ChangeSet::isEmpty() const +{ + return m_operationList.isEmpty(); +} + +QList<ChangeSet::EditOp> ChangeSet::operationList() const +{ + return m_operationList; +} + +void ChangeSet::clear() +{ + m_string = 0; + m_cursor = 0; + m_operationList.clear(); + m_error = false; +} + +bool ChangeSet::replace(int pos, int length, const QString &replacement) +{ + if (hasOverlap(pos, length)) + m_error = true; + + EditOp cmd(EditOp::Replace); + cmd.pos1 = pos; + cmd.length1 = length; + cmd.text = replacement; + m_operationList += cmd; + + return !m_error; +} + +bool ChangeSet::move(int pos, int length, int to) +{ + if (hasOverlap(pos, length) + || hasOverlap(to, 0) + || overlaps(pos, length, to, 0)) + m_error = true; + + EditOp cmd(EditOp::Move); + cmd.pos1 = pos; + cmd.length1 = length; + cmd.pos2 = to; + m_operationList += cmd; + + return !m_error; +} + +bool ChangeSet::insert(int pos, const QString &text) +{ + if (hasOverlap(pos, 0)) + m_error = true; + + EditOp cmd(EditOp::Insert); + cmd.pos1 = pos; + cmd.text = text; + m_operationList += cmd; + + return !m_error; +} + +bool ChangeSet::remove(int pos, int length) +{ + if (hasOverlap(pos, length)) + m_error = true; + + EditOp cmd(EditOp::Remove); + cmd.pos1 = pos; + cmd.length1 = length; + m_operationList += cmd; + + return !m_error; +} + +bool ChangeSet::flip(int pos1, int length1, int pos2, int length2) +{ + if (hasOverlap(pos1, length1) + || hasOverlap(pos2, length2) + || overlaps(pos1, length1, pos2, length2)) + m_error = true; + + EditOp cmd(EditOp::Flip); + cmd.pos1 = pos1; + cmd.length1 = length1; + cmd.pos2 = pos2; + cmd.length2 = length2; + m_operationList += cmd; + + return !m_error; +} + +bool ChangeSet::copy(int pos, int length, int to) +{ + if (hasOverlap(pos, length) + || hasOverlap(to, 0) + || overlaps(pos, length, to, 0)) + m_error = true; + + EditOp cmd(EditOp::Copy); + cmd.pos1 = pos; + cmd.length1 = length; + cmd.pos2 = to; + m_operationList += cmd; + + return !m_error; +} + +void ChangeSet::doReplace(const EditOp &replace, QList<EditOp> *replaceList) +{ + Q_ASSERT(replace.type == EditOp::Replace); + + { + QMutableListIterator<EditOp> i(*replaceList); + while (i.hasNext()) { + EditOp &c = i.next(); + if (replace.pos1 <= c.pos1) + c.pos1 += replace.text.size(); + if (replace.pos1 < c.pos1) + c.pos1 -= replace.length1; + } + } + + if (m_string) { + m_string->replace(replace.pos1, replace.length1, replace.text); + } else if (m_cursor) { + m_cursor->setPosition(replace.pos1); + m_cursor->setPosition(replace.pos1 + replace.length1, QTextCursor::KeepAnchor); + m_cursor->insertText(replace.text); + } +} + +void ChangeSet::convertToReplace(const EditOp &op, QList<EditOp> *replaceList) +{ + EditOp replace1(EditOp::Replace); + EditOp replace2(EditOp::Replace); + + switch (op.type) { + case EditOp::Replace: + replaceList->append(op); + break; + + case EditOp::Move: + replace1.pos1 = op.pos1; + replace1.length1 = op.length1; + replaceList->append(replace1); + + replace2.pos1 = op.pos2; + replace2.text = textAt(op.pos1, op.length1); + replaceList->append(replace2); + break; + + case EditOp::Insert: + replace1.pos1 = op.pos1; + replace1.text = op.text; + replaceList->append(replace1); + break; + + case EditOp::Remove: + replace1.pos1 = op.pos1; + replace1.length1 = op.length1; + replaceList->append(replace1); + break; + + case EditOp::Flip: + replace1.pos1 = op.pos1; + replace1.length1 = op.length1; + replace1.text = textAt(op.pos2, op.length2); + replaceList->append(replace1); + + replace2.pos1 = op.pos2; + replace2.length1 = op.length2; + replace2.text = textAt(op.pos1, op.length1); + replaceList->append(replace2); + break; + + case EditOp::Copy: + replace1.pos1 = op.pos2; + replace1.text = textAt(op.pos1, op.length1); + replaceList->append(replace1); + break; + + case EditOp::Unset: + break; + } +} + +bool ChangeSet::hadErrors() +{ + return m_error; +} + +void ChangeSet::apply(QString *s) +{ + m_string = s; + apply_helper(); + m_string = 0; +} + +void ChangeSet::apply(QTextCursor *textCursor) +{ + m_cursor = textCursor; + apply_helper(); + m_cursor = 0; +} + +QString ChangeSet::textAt(int pos, int length) +{ + if (m_string) { + return m_string->mid(pos, length); + } else if (m_cursor) { + m_cursor->setPosition(pos); + m_cursor->setPosition(pos + length, QTextCursor::KeepAnchor); + return m_cursor->selectedText(); + } + return QString(); +} + +void ChangeSet::apply_helper() +{ + // convert all ops to replace + QList<EditOp> replaceList; + { + while (!m_operationList.isEmpty()) { + const EditOp cmd(m_operationList.first()); + m_operationList.removeFirst(); + convertToReplace(cmd, &replaceList); + } + } + + // execute replaces + if (m_cursor) + m_cursor->beginEditBlock(); + + while (!replaceList.isEmpty()) { + const EditOp cmd(replaceList.first()); + replaceList.removeFirst(); + doReplace(cmd, &replaceList); + } + + if (m_cursor) + m_cursor->endEditBlock(); +} + +} // end namespace Utils diff --git a/src/plugins/qmldesigner/core/filemanager/changeset.h b/src/plugins/qmldesigner/core/filemanager/changeset.h new file mode 100644 index 00000000000..2f46bd835d3 --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/changeset.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CHANGESET_H +#define CHANGESET_H + +#include "utils_global.h" + +#include <QtCore/QString> +#include <QtCore/QList> +#include <QtCore/QSharedPointer> +#include <QtGui/QTextCursor> + +namespace Utils { + +class QTCREATOR_UTILS_EXPORT ChangeSet +{ +public: + struct EditOp { + enum Type + { + Unset, + Replace, + Move, + Insert, + Remove, + Flip, + Copy + }; + + EditOp(): type(Unset), pos1(0), pos2(0), length1(0), length2(0) {} + EditOp(Type t): type(t), pos1(0), pos2(0), length1(0), length2(0) {} + + Type type; + int pos1; + int pos2; + int length1; + int length2; + QString text; + }; + +public: + ChangeSet(); + + bool isEmpty() const; + + QList<EditOp> operationList() const; + + void clear(); + + bool replace(int pos, int length, const QString &replacement); + bool move(int pos, int length, int to); + bool insert(int pos, const QString &text); + bool remove(int pos, int length); + bool flip(int pos1, int length1, int pos2, int length2); + bool copy(int pos, int length, int to); + + bool hadErrors(); + + void apply(QString *s); + void apply(QTextCursor *textCursor); + +private: + bool hasOverlap(int pos, int length); + QString textAt(int pos, int length); + + void doReplace(const EditOp &replace, QList<EditOp> *replaceList); + void convertToReplace(const EditOp &op, QList<EditOp> *replaceList); + + void apply_helper(); + +private: + QString *m_string; + QTextCursor *m_cursor; + + QList<EditOp> m_operationList; + bool m_error; +}; + +} // namespace Utils + +#endif // CHANGESET_H diff --git a/src/plugins/qmldesigner/core/filemanager/filemanager.pri b/src/plugins/qmldesigner/core/filemanager/filemanager.pri new file mode 100644 index 00000000000..fb51eda8d59 --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/filemanager.pri @@ -0,0 +1,41 @@ +include($$PWD/parser/parser.pri) +SOURCES += $$PWD/qmldocument.cpp \ + $$PWD/idcollector.cpp \ + $$PWD/qmlsymbol.cpp \ + $$PWD/qmlrewriter.cpp \ + $$PWD/qmlrefactoring.cpp \ + $$PWD/changeobjecttypevisitor.cpp \ + $$PWD/changepropertyvisitor.cpp \ + $$PWD/removeuiobjectmembervisitor.cpp \ + $$PWD/removepropertyvisitor.cpp \ + $$PWD/addpropertyvisitor.cpp \ + $$PWD/moveobjectvisitor.cpp \ + $$PWD/addobjectvisitor.cpp \ + $$PWD/addarraymembervisitor.cpp \ + $$PWD/astobjecttextextractor.cpp \ + $$PWD/indenter.cpp \ + $$PWD/changeset.cpp \ + $$PWD/objectlengthcalculator.cpp \ + $$PWD/firstdefinitionfinder.cpp \ + $$PWD/moveobjectbeforeobjectvisitor.cpp +HEADERS += $$PWD/qmldocument.h \ + $$PWD/idcollector.h \ + $$PWD/qmleditor_global.h \ + $$PWD/qmlsymbol.h \ + $$PWD/qmlrewriter.h \ + $$PWD/qmlrefactoring.h \ + $$PWD/changeobjecttypevisitor.h \ + $$PWD/changepropertyvisitor.h \ + $$PWD/removeuiobjectmembervisitor.h \ + $$PWD/removepropertyvisitor.h \ + $$PWD/addpropertyvisitor.h \ + $$PWD/moveobjectvisitor.h \ + $$PWD/addobjectvisitor.h \ + $$PWD/addarraymembervisitor.h \ + $$PWD/astobjecttextextractor.h \ + $$PWD/indenter.h \ + $$PWD/changeset.h \ + $$PWD/utils_global.h \ + $$PWD/objectlengthcalculator.h \ + $$PWD/firstdefinitionfinder.h \ + $$PWD/moveobjectbeforeobjectvisitor.h diff --git a/src/plugins/qmldesigner/core/filemanager/firstdefinitionfinder.cpp b/src/plugins/qmldesigner/core/filemanager/firstdefinitionfinder.cpp new file mode 100644 index 00000000000..64905c35aaf --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/firstdefinitionfinder.cpp @@ -0,0 +1,98 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "firstdefinitionfinder.h" + +#include <qmljsast_p.h> + +using namespace QmlDesigner; +using namespace QmlEditor; +using namespace QmlJS::AST; + +FirstDefinitionFinder::FirstDefinitionFinder(const QString &text): + m_doc(QmlDocument::create("<internal>")) +{ + m_doc->setSource(text); + bool ok = m_doc->parse(); + + Q_ASSERT(ok); +} + +/*! + \brief Finds the first object definition inside the object given by offset + + + \arg the offset of the object to search in + \return the offset of the first object definition + */ +quint32 FirstDefinitionFinder::operator()(quint32 offset) +{ + m_offset = offset; + m_firstObjectDefinition = 0; + + Node::accept(m_doc->program(), this); + + return m_firstObjectDefinition->firstSourceLocation().offset; +} + +void FirstDefinitionFinder::extractFirstObjectDefinition(UiObjectInitializer* ast) +{ + if (!ast) + return; + + for (UiObjectMemberList *iter = ast->members; iter; iter = iter->next) { + if (UiObjectDefinition *def = cast<UiObjectDefinition*>(iter->member)) { + m_firstObjectDefinition = def; + } + } +} + +bool FirstDefinitionFinder::visit(QmlJS::AST::UiObjectBinding *ast) +{ + if (ast->qualifiedTypeNameId && ast->qualifiedTypeNameId->identifierToken.isValid()) { + const quint32 start = ast->qualifiedTypeNameId->identifierToken.offset; + + if (start == m_offset) { + extractFirstObjectDefinition(ast->initializer); + return false; + } + } + return true; +} + +bool FirstDefinitionFinder::visit(QmlJS::AST::UiObjectDefinition *ast) +{ + const quint32 start = ast->firstSourceLocation().offset; + + if (start == m_offset) { + extractFirstObjectDefinition(ast->initializer); + return false; + } + return true; +} diff --git a/src/plugins/qmldesigner/core/filemanager/firstdefinitionfinder.h b/src/plugins/qmldesigner/core/filemanager/firstdefinitionfinder.h new file mode 100644 index 00000000000..170b7a2381f --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/firstdefinitionfinder.h @@ -0,0 +1,63 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef FIRSTDEFINITIONFINDER_H +#define FIRSTDEFINITIONFINDER_H + +#include "qmldocument.h" + +#include <qmljsastvisitor_p.h> + +namespace QmlDesigner { + +class FirstDefinitionFinder: protected QmlJS::AST::Visitor +{ +public: + FirstDefinitionFinder(const QString &text); + + quint32 operator()(quint32 offset); + +protected: + using QmlJS::AST::Visitor::visit; + + virtual bool visit(QmlJS::AST::UiObjectBinding *ast); + virtual bool visit(QmlJS::AST::UiObjectDefinition *ast); + + void extractFirstObjectDefinition(QmlJS::AST::UiObjectInitializer* ast); + +private: + QmlEditor::QmlDocument::Ptr m_doc; + quint32 m_offset; + QmlJS::AST::UiObjectDefinition *m_firstObjectDefinition; + +}; + +} // namespace QmlDesigner + +#endif // FIRSTDEFINITIONFINDER_H diff --git a/src/plugins/qmldesigner/core/filemanager/idcollector.cpp b/src/plugins/qmldesigner/core/filemanager/idcollector.cpp new file mode 100644 index 00000000000..54284a9fad4 --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/idcollector.cpp @@ -0,0 +1,93 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "idcollector.h" +#include "qmljsast_p.h" +#include "qmljsengine_p.h" + +using namespace QmlJS; +using namespace QmlJS::AST; +using namespace QmlEditor; +using namespace QmlEditor::Internal; + +QMap<QString, QmlIdSymbol*> IdCollector::operator()(const QString &fileName, QmlJS::AST::UiProgram *ast) +{ + _fileName = fileName; + _ids.clear(); + + Node::accept(ast, this); + + return _ids; +} + +bool IdCollector::visit(QmlJS::AST::UiObjectBinding *ast) +{ + _scopes.push(ast); + return true; +} + +bool IdCollector::visit(QmlJS::AST::UiObjectDefinition *ast) +{ + _scopes.push(ast); + return true; +} + +void IdCollector::endVisit(QmlJS::AST::UiObjectBinding *) +{ + _scopes.pop(); +} + +void IdCollector::endVisit(QmlJS::AST::UiObjectDefinition *) +{ + _scopes.pop(); +} + +bool IdCollector::visit(QmlJS::AST::UiScriptBinding *ast) +{ + if (!(ast->qualifiedId->next) && ast->qualifiedId->name->asString() == "id") + if (ExpressionStatement *e = cast<ExpressionStatement*>(ast->statement)) + if (IdentifierExpression *i = cast<IdentifierExpression*>(e->expression)) + addId(i->name->asString(), ast); + + return false; +} + +void IdCollector::addId(const QString &id, QmlJS::AST::UiScriptBinding *ast) +{ + if (!_ids.contains(id)) { + Node *parent = _scopes.top(); + + if (UiObjectBinding *binding = cast<UiObjectBinding*>(parent)) + _ids[id] = new QmlIdSymbol(_fileName, ast, QmlSymbolFromFile(_fileName, binding)); + else if (UiObjectDefinition *definition = cast<UiObjectDefinition*>(parent)) + _ids[id] = new QmlIdSymbol(_fileName, ast, QmlSymbolFromFile(_fileName, definition)); + else + Q_ASSERT(!"Unknown parent for id"); + } +} diff --git a/src/plugins/qmldesigner/core/filemanager/idcollector.h b/src/plugins/qmldesigner/core/filemanager/idcollector.h new file mode 100644 index 00000000000..b9f3211e9ea --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/idcollector.h @@ -0,0 +1,69 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef IDCOLLECTOR_H +#define IDCOLLECTOR_H + +#include <QMap> +#include <QPair> +#include <QStack> +#include <QString> + +#include "qmljsastvisitor_p.h" +#include "qmlsymbol.h" + +namespace QmlEditor { +namespace Internal { + +class IdCollector: protected QmlJS::AST::Visitor +{ +public: + QMap<QString, QmlIdSymbol*> operator()(const QString &fileName, QmlJS::AST::UiProgram *ast); + +protected: + virtual bool visit(QmlJS::AST::UiObjectBinding *ast); + virtual bool visit(QmlJS::AST::UiObjectDefinition *ast); + virtual bool visit(QmlJS::AST::UiScriptBinding *ast); + + virtual void endVisit(QmlJS::AST::UiObjectBinding *); + virtual void endVisit(QmlJS::AST::UiObjectDefinition *); + +private: + void addId(const QString &id, QmlJS::AST::UiScriptBinding *ast); + +private: + QString _fileName; + QMap<QString, QmlIdSymbol*> _ids; + QStack<QmlJS::AST::Node *> _scopes; +}; + +} // namespace Internal +} // namespace QmlEditor + +#endif // IDCOLLECTOR_H diff --git a/src/plugins/qmldesigner/core/filemanager/indenter.cpp b/src/plugins/qmldesigner/core/filemanager/indenter.cpp new file mode 100644 index 00000000000..55e709d7f27 --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/indenter.cpp @@ -0,0 +1,90 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "indenter.h" + +using namespace QmlEditor; + +Indenter::Indenter() +{ +} + +//static int blockStartState(const QTextBlock &block) +//{ +// int state = block.userState(); +// +// if (state == -1) +// return 0; +// else +// return state & 0xff; +//} +// +//void ScriptEditor::indentBlock(QTextDocument *, QTextBlock block, QChar typedChar) +//{ +// TextEditor::TabSettings ts = tabSettings(); +// +// QTextCursor tc(block); +// +// const QString blockText = block.text(); +// int startState = blockStartState(block.previous()); +// +// QScriptIncrementalScanner scanner; +// const QList<QScriptIncrementalScanner::Token> tokens = scanner(blockText, startState); +// +// if (! tokens.isEmpty()) { +// const QScriptIncrementalScanner::Token tk = tokens.first(); +// +// if (tk.is(QScriptIncrementalScanner::Token::RightBrace) +// || tk.is(QScriptIncrementalScanner::Token::RightBracket)) { +// if (TextEditor::TextBlockUserData::findPreviousBlockOpenParenthesis(&tc)) { +// const QString text = tc.block().text(); +// int indent = ts.columnAt(text, ts.firstNonSpace(text)); +// ts.indentLine(block, indent); +// return; +// } +// } +// } +// +// int initialIndent = 0; +// for (QTextBlock it = block.previous(); it.isValid(); it = it.previous()) { +// const QString text = it.text(); +// +// if (! text.isEmpty()) { +// initialIndent = ts.columnAt(text, ts.firstNonSpace(text)); +// break; +// } +// } +// +// const int braceDepth = blockBraceDepth(block.previous()); +// const int previousBraceDepth = blockBraceDepth(block.previous().previous()); +// const int delta = qMax(0, braceDepth - previousBraceDepth); +// int indent = initialIndent + (delta * ts.m_indentSize); +// ts.indentLine(block, indent); +//} +// diff --git a/src/plugins/qmldesigner/core/filemanager/indenter.h b/src/plugins/qmldesigner/core/filemanager/indenter.h new file mode 100644 index 00000000000..b862003b2ca --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/indenter.h @@ -0,0 +1,43 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef INDENTER_H +#define INDENTER_H + +namespace QmlEditor { + +class Indenter +{ +public: + Indenter(); +}; + +} // namespace QmlEditor + +#endif // INDENTER_H diff --git a/src/plugins/qmldesigner/core/filemanager/moveobjectbeforeobjectvisitor.cpp b/src/plugins/qmldesigner/core/filemanager/moveobjectbeforeobjectvisitor.cpp new file mode 100644 index 00000000000..62a8ed5429d --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/moveobjectbeforeobjectvisitor.cpp @@ -0,0 +1,158 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include <QtCore/QDebug> + +#include <qmljsast_p.h> +#include <qmljsengine_p.h> + +#include "moveobjectbeforeobjectvisitor.h" +#include "textmodifier.h" + +using namespace QmlJS; +using namespace QmlJS::AST; +using namespace QmlEditor::Internal; +using namespace QmlDesigner; + +MoveObjectBeforeObjectVisitor::MoveObjectBeforeObjectVisitor(TextModifier &modifier, + quint32 movingObjectLocation): + QMLRewriter(modifier), + movingObjectLocation(movingObjectLocation), + toEnd(true), + beforeObjectLocation(0) +{} + +MoveObjectBeforeObjectVisitor::MoveObjectBeforeObjectVisitor(TextModifier &modifier, + quint32 movingObjectLocation, + quint32 beforeObjectLocation): + QMLRewriter(modifier), + movingObjectLocation(movingObjectLocation), + toEnd(false), + beforeObjectLocation(beforeObjectLocation) +{} + +bool MoveObjectBeforeObjectVisitor::operator ()(QmlJS::AST::UiProgram *ast) +{ + movingObject = 0; + beforeObject = 0; + movingObjectParents.clear(); + + QMLRewriter::operator ()(ast); + + if (foundEverything()) { + doMove(); + } + + return didRewriting(); +} + +bool MoveObjectBeforeObjectVisitor::preVisit(Node *ast) +{ if (ast) parents.push(ast); return true; } + +void MoveObjectBeforeObjectVisitor::postVisit(Node *ast) +{ if (ast) parents.pop(); } + +bool MoveObjectBeforeObjectVisitor::visit(UiObjectDefinition *ast) +{ + if (foundEverything()) + return false; + + const quint32 start = ast->firstSourceLocation().offset; + if (start == movingObjectLocation) { + movingObject = ast; + movingObjectParents = parents; + movingObjectParents.pop(); + } else if (!toEnd && start == beforeObjectLocation) { + beforeObject = ast; + } + + if (movingObjectLocation < start) + return false; + else if (!toEnd && beforeObjectLocation < start) + return false; + else if (foundEverything()) + return false; + else + return true; +} + +void MoveObjectBeforeObjectVisitor::doMove() +{ + Q_ASSERT(movingObject); + Q_ASSERT(!movingObjectParents.isEmpty()); + + TextModifier::MoveInfo moveInfo; + + int start = movingObject->firstSourceLocation().offset; + int end = movingObject->lastSourceLocation().end(); + + moveInfo.objectStart = start; + moveInfo.objectEnd = end; + + includeSurroundingWhitespace(start, end); + moveInfo.leadingCharsToRemove = moveInfo.objectStart - start; + moveInfo.trailingCharsToRemove = end - moveInfo.objectEnd; + + if (beforeObject) { + moveInfo.destination = beforeObject->firstSourceLocation().offset; + int dummy = -1; + includeSurroundingWhitespace(moveInfo.destination, dummy); + + moveInfo.prefixToInsert = QString(moveInfo.leadingCharsToRemove, QLatin1Char(' ')); + moveInfo.suffixToInsert = QLatin1String("\n\n"); + } else { + const SourceLocation insertionPoint = lastParentLocation(); + Q_ASSERT(insertionPoint.isValid()); + moveInfo.destination = insertionPoint.offset; + int dummy = -1; + includeSurroundingWhitespace(moveInfo.destination, dummy); + + moveInfo.prefixToInsert = QString(moveInfo.leadingCharsToRemove, QLatin1Char(' ')); + moveInfo.suffixToInsert = QLatin1String("\n"); + } + + move(moveInfo); + setDidRewriting(true); +} + +SourceLocation MoveObjectBeforeObjectVisitor::lastParentLocation() const +{ + dump(movingObjectParents); + + Node *parent; + if (movingObjectParents.size() > 1) + parent = movingObjectParents.at(movingObjectParents.size() - 2); + else + parent = 0; + + if (UiObjectInitializer *initializer = cast<UiObjectInitializer*>(parent)) + return initializer->rbraceToken; + else + return SourceLocation(); +} diff --git a/src/plugins/qmldesigner/core/filemanager/moveobjectbeforeobjectvisitor.h b/src/plugins/qmldesigner/core/filemanager/moveobjectbeforeobjectvisitor.h new file mode 100644 index 00000000000..4b405dd306e --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/moveobjectbeforeobjectvisitor.h @@ -0,0 +1,79 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef MOVEOBJECTBEFOREOBJECTVISITOR_H +#define MOVEOBJECTBEFOREOBJECTVISITOR_H + +#include "qmlrewriter.h" + +#include <QtCore/QStack> + +namespace QmlEditor { +namespace Internal { + +class MoveObjectBeforeObjectVisitor: public QMLRewriter +{ +public: + MoveObjectBeforeObjectVisitor(QmlDesigner::TextModifier &modifier, + quint32 movingObjectLocation); + MoveObjectBeforeObjectVisitor(QmlDesigner::TextModifier &modifier, + quint32 movingObjectLocation, + quint32 beforeObjectLocation); + + bool operator ()(QmlJS::AST::UiProgram *ast); + +protected: + virtual bool preVisit(QmlJS::AST::Node *ast); + virtual void postVisit(QmlJS::AST::Node *ast); + + virtual bool visit(QmlJS::AST::UiObjectDefinition *ast); + +private: + bool foundEverything() const + { return movingObject != 0 && !movingObjectParents.isEmpty() && (toEnd || beforeObject != 0); } + + void doMove(); + + QmlJS::AST::SourceLocation lastParentLocation() const; + +private: + QStack<QmlJS::AST::Node *> parents; + quint32 movingObjectLocation; + bool toEnd; + quint32 beforeObjectLocation; + + QmlJS::AST::UiObjectDefinition *movingObject; + QmlJS::AST::UiObjectDefinition *beforeObject; + ASTPath movingObjectParents; +}; + +} // namespace Internal +} // namespace QmlEditor + +#endif // MOVEOBJECTBEFOREOBJECTVISITOR_H diff --git a/src/plugins/qmldesigner/core/filemanager/moveobjectvisitor.cpp b/src/plugins/qmldesigner/core/filemanager/moveobjectvisitor.cpp new file mode 100644 index 00000000000..ce95b03bad6 --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/moveobjectvisitor.cpp @@ -0,0 +1,293 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include <QtCore/QDebug> + +#include <qmljsast_p.h> +#include <qmljsengine_p.h> + +#include "moveobjectvisitor.h" +#include "textmodifier.h" + +using namespace QmlJS; +using namespace QmlJS::AST; +using namespace QmlEditor::Internal; +using namespace QmlDesigner; + +class Inserter: public QMLRewriter +{ +public: + Inserter(QmlDesigner::TextModifier &modifier, + quint32 targetParentObjectLocation, + const QString &targetPropertyName, + bool targetIsArrayBinding, + TextModifier::MoveInfo moveInfo, + const QStringList &propertyOrder): + QMLRewriter(modifier), + targetParentObjectLocation(targetParentObjectLocation), + targetPropertyName(targetPropertyName), + targetIsArrayBinding(targetIsArrayBinding), + moveInfo(moveInfo), + propertyOrder(propertyOrder) + {} + +protected: + virtual bool visit(UiObjectBinding *ast) + { + if (didRewriting()) + return false; + + if (ast->qualifiedTypeNameId->identifierToken.offset == targetParentObjectLocation) { + insertInto(ast->initializer); + } + + return !didRewriting(); + } + + virtual bool visit(UiObjectDefinition *ast) + { + if (didRewriting()) + return false; + + if (ast->firstSourceLocation().offset == targetParentObjectLocation) { + insertInto(ast->initializer); + } + + return !didRewriting(); + } + +private: + void insertInto(UiObjectInitializer *ast) + { + if (targetPropertyName.isEmpty()) { + // insert as UiObjectDefinition: + UiObjectMemberList *insertAfter = searchMemberToInsertAfter(ast->members, QString::null, propertyOrder); + + if (insertAfter && insertAfter->member) { + moveInfo.destination = insertAfter->member->lastSourceLocation().end(); + moveInfo.prefixToInsert = QLatin1String("\n\n"); + } else { + moveInfo.destination = ast->lbraceToken.end(); + moveInfo.prefixToInsert = QLatin1String("\n"); + } + + move(moveInfo); + + setDidRewriting(true); + return; + } + + // see if we need to insert into an UiArrayBinding: + for (UiObjectMemberList *iter = ast->members; iter; iter = iter->next) { + UiObjectMember *member = iter->member; + + if (UiArrayBinding *arrayBinding = cast<UiArrayBinding*>(member)) { + if (flatten(arrayBinding->qualifiedId) == targetPropertyName) { + appendToArray(arrayBinding); + + setDidRewriting(true); + return; + } + } + } + + { // insert (create) a UiObjectBinding: + UiObjectMemberList *insertAfter = searchMemberToInsertAfter(ast->members, targetPropertyName, propertyOrder); + moveInfo.prefixToInsert = QLatin1String("\n") + targetPropertyName + (targetIsArrayBinding ? QLatin1String(": [") : QLatin1String(": ")); + moveInfo.suffixToInsert = targetIsArrayBinding ? QLatin1String("\n]") : QLatin1String(""); + + if (insertAfter && insertAfter->member) { + moveInfo.destination = insertAfter->member->lastSourceLocation().end(); + } else { + moveInfo.destination = ast->lbraceToken.end(); + } + + setDidRewriting(true); + } + } + + void appendToArray(UiArrayBinding *ast) + { + UiObjectMember *lastMember = 0; + + for (UiArrayMemberList *iter = ast->members; iter; iter = iter->next) { + if (iter->member) + lastMember = iter->member; + } + + if (!lastMember) + Q_ASSERT(!"Invalid QML: empty array found."); + + moveInfo.destination = lastMember->lastSourceLocation().end(); + moveInfo.suffixToInsert = QLatin1String(",\n"); + move(moveInfo); + } + +private: + quint32 targetParentObjectLocation; + QString targetPropertyName; + bool targetIsArrayBinding; + TextModifier::MoveInfo moveInfo; + QStringList propertyOrder; +}; + +MoveObjectVisitor::MoveObjectVisitor(QmlDesigner::TextModifier &modifier, + quint32 objectLocation, + const QString &targetPropertyName, + bool targetIsArrayBinding, + quint32 targetParentObjectLocation, + const QStringList &propertyOrder): + QMLRewriter(modifier), + objectLocation(objectLocation), + targetPropertyName(targetPropertyName), + targetIsArrayBinding(targetIsArrayBinding), + targetParentObjectLocation(targetParentObjectLocation), + propertyOrder(propertyOrder) +{ +} + +bool MoveObjectVisitor::operator()(UiProgram *ast) +{ + program = ast; + + return QMLRewriter::operator()(ast); +} + +bool MoveObjectVisitor::visit(UiArrayBinding *ast) +{ + if (didRewriting()) + return false; + + UiArrayMemberList *currentMember = 0; + + for (UiArrayMemberList *it = ast->members; it; it = it->next) { + if (it->member->firstSourceLocation().offset == objectLocation) { + currentMember = it; + break; + } + } + + if (currentMember) { + TextModifier::MoveInfo moveInfo; + moveInfo.objectEnd = currentMember->member->lastSourceLocation().end(); + + if (currentMember == ast->members && !currentMember->next) { + // array with 1 element, which is moved away, so also remove the array binding + int start = ast->firstSourceLocation().offset; + int end = ast->lastSourceLocation().end(); + includeSurroundingWhitespace(start, end); + moveInfo.leadingCharsToRemove = objectLocation - start; + moveInfo.trailingCharsToRemove = end - moveInfo.objectEnd; + } else if (currentMember->next) { + // we're not the last element, so remove the trailing comma too + Q_ASSERT(currentMember->next->commaToken.isValid()); + + int start = objectLocation; + int end = currentMember->next->commaToken.end(); + includeSurroundingWhitespace(start, end); + moveInfo.leadingCharsToRemove = objectLocation - start; + moveInfo.trailingCharsToRemove = end - moveInfo.objectEnd; + } else { + // we're the last member, but not the only member, so remove the preceeding comma too + Q_ASSERT(currentMember->commaToken.isValid()); + + int start = currentMember->commaToken.offset; + int end = moveInfo.objectEnd; + includeSurroundingWhitespace(start, end); + moveInfo.leadingCharsToRemove = objectLocation - start; + moveInfo.trailingCharsToRemove = end - moveInfo.objectEnd; + } + + doMove(moveInfo); + } + + return !didRewriting(); +} + +bool MoveObjectVisitor::visit(UiObjectBinding *ast) +{ + if (didRewriting()) + return false; + + if (ast->qualifiedTypeNameId->identifierToken.offset == objectLocation) { + TextModifier::MoveInfo moveInfo; + moveInfo.objectEnd = ast->lastSourceLocation().end(); + + // remove leading indentation and property name: + int start = ast->firstSourceLocation().offset; + int end = moveInfo.objectEnd; + includeSurroundingWhitespace(start, end); + includeLeadingEmptyLine(start); + moveInfo.leadingCharsToRemove = objectLocation - start; + + // remove trailing indentation + moveInfo.trailingCharsToRemove = end - moveInfo.objectEnd; + + doMove(moveInfo); + } + + return !didRewriting(); +} + +bool MoveObjectVisitor::visit(UiObjectDefinition *ast) +{ + if (didRewriting()) + return false; + + if (ast->qualifiedTypeNameId->identifierToken.offset == objectLocation) { + TextModifier::MoveInfo moveInfo; + moveInfo.objectStart = objectLocation; + moveInfo.objectEnd = ast->lastSourceLocation().end(); + + // remove leading indentation: + int start = objectLocation; + int end = moveInfo.objectEnd; + includeSurroundingWhitespace(start, end); + includeLeadingEmptyLine(start); + moveInfo.leadingCharsToRemove = objectLocation - start; + moveInfo.trailingCharsToRemove = end - moveInfo.objectEnd; + + doMove(moveInfo); + } + + return !didRewriting(); +} + +void MoveObjectVisitor::doMove(TextModifier::MoveInfo moveInfo) +{ + if (moveInfo.objectEnd > moveInfo.objectStart) { + Inserter findTargetAndInsert(*textModifier(), + targetParentObjectLocation, + targetPropertyName, + targetIsArrayBinding, + moveInfo, + propertyOrder); + setDidRewriting(findTargetAndInsert(program)); + } +} diff --git a/src/plugins/qmldesigner/core/filemanager/moveobjectvisitor.h b/src/plugins/qmldesigner/core/filemanager/moveobjectvisitor.h new file mode 100644 index 00000000000..5ec981cdb88 --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/moveobjectvisitor.h @@ -0,0 +1,72 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef MOVEOBJECTVISITOR_H +#define MOVEOBJECTVISITOR_H + +#include "qmlrewriter.h" + +namespace QmlEditor { +namespace Internal { + +class MoveObjectVisitor: public QMLRewriter +{ +public: + MoveObjectVisitor(QmlDesigner::TextModifier &modifier, + quint32 objectLocation, + const QString &targetPropertyName, + bool targetIsArrayBinding, + quint32 targetParentObjectLocation, + const QStringList &propertyOrder); + + bool operator ()(QmlJS::AST::UiProgram *ast); + +protected: + virtual bool visit(QmlJS::AST::UiArrayBinding *ast); + virtual bool visit(QmlJS::AST::UiObjectBinding *ast); + virtual bool visit(QmlJS::AST::UiObjectDefinition *ast); + +private: + void doMove(QmlDesigner::TextModifier::MoveInfo moveInfo); + +private: + QList<QmlJS::AST::Node *> parents; + quint32 objectLocation; + QString targetPropertyName; + bool targetIsArrayBinding; + quint32 targetParentObjectLocation; + QStringList propertyOrder; + + QmlJS::AST::UiProgram *program; +}; + +} // namespace Internal +} // namespace QmlEditor + +#endif // MOVEOBJECTVISITOR_H diff --git a/src/plugins/qmldesigner/core/filemanager/objectlengthcalculator.cpp b/src/plugins/qmldesigner/core/filemanager/objectlengthcalculator.cpp new file mode 100644 index 00000000000..cc9f229e147 --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/objectlengthcalculator.cpp @@ -0,0 +1,91 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "objectlengthcalculator.h" + +#include <qmljsast_p.h> + +using namespace QmlDesigner; +using namespace QmlEditor; +using namespace QmlJS::AST; + +ObjectLengthCalculator::ObjectLengthCalculator(const QString &text): + m_doc(QmlDocument::create("<internal>")) +{ + m_doc->setSource(text); + bool ok = m_doc->parse(); + + Q_ASSERT(ok); +} + +quint32 ObjectLengthCalculator::operator()(quint32 offset) +{ + m_offset = offset; + m_length = 0; + + Node::accept(m_doc->program(), this); + + return m_length; +} + +bool ObjectLengthCalculator::visit(QmlJS::AST::UiObjectBinding *ast) +{ + if (m_length > 0) + return false; + + if (ast->qualifiedTypeNameId && ast->qualifiedTypeNameId->identifierToken.isValid()) { + const quint32 start = ast->qualifiedTypeNameId->identifierToken.offset; + const quint32 end = ast->lastSourceLocation().end(); + + if (start == m_offset) { + m_length = end - start; + return false; + } + + return m_offset < end; + } else { + return true; + } +} + +bool ObjectLengthCalculator::visit(QmlJS::AST::UiObjectDefinition *ast) +{ + if (m_length > 0) + return false; + + const quint32 start = ast->firstSourceLocation().offset; + const quint32 end = ast->lastSourceLocation().end(); + + if (start == m_offset) { + m_length = end - start; + return false; + } + + return m_offset < end; +} diff --git a/src/plugins/qmldesigner/core/filemanager/objectlengthcalculator.h b/src/plugins/qmldesigner/core/filemanager/objectlengthcalculator.h new file mode 100644 index 00000000000..a49352c8b70 --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/objectlengthcalculator.h @@ -0,0 +1,60 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef OBJECTLENGTHCALCULATOR_H +#define OBJECTLENGTHCALCULATOR_H + +#include "qmldocument.h" + +#include <qmljsastvisitor_p.h> + +namespace QmlDesigner { + +class ObjectLengthCalculator: protected QmlJS::AST::Visitor +{ +public: + ObjectLengthCalculator(const QString &text); + + quint32 operator()(quint32 offset); + +protected: + using QmlJS::AST::Visitor::visit; + + virtual bool visit(QmlJS::AST::UiObjectBinding *ast); + virtual bool visit(QmlJS::AST::UiObjectDefinition *ast); + +private: + QmlEditor::QmlDocument::Ptr m_doc; + quint32 m_offset; + quint32 m_length; +}; + +} // namespace QmlDesigner + +#endif // OBJECTLENGTHCALCULATOR_H diff --git a/src/plugins/qmldesigner/core/filemanager/parser/parser.pri b/src/plugins/qmldesigner/core/filemanager/parser/parser.pri new file mode 100644 index 00000000000..986b14673c8 --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/parser/parser.pri @@ -0,0 +1,22 @@ + +INCLUDEPATH += $$PWD +DEPENDPATH += $$PWD + +HEADERS += $$PWD/qmljsast_p.h \ + $$PWD/qmljsastfwd_p.h \ + $$PWD/qmljsastvisitor_p.h \ + $$PWD/qmljsengine_p.h \ + $$PWD/qmljsgrammar_p.h \ + $$PWD/qmljslexer_p.h \ + $$PWD/qmljsmemorypool_p.h \ + $$PWD/qmljsnodepool_p.h \ + $$PWD/qmljsparser_p.h \ + $$PWD/qmljsglobal_p.h + +SOURCES += $$PWD/qmljsast.cpp \ + $$PWD/qmljsastvisitor.cpp \ + $$PWD/qmljsengine_p.cpp \ + $$PWD/qmljsgrammar.cpp \ + $$PWD/qmljslexer.cpp \ + $$PWD/qmljsparser.cpp + diff --git a/src/plugins/qmldesigner/core/filemanager/parser/qmljs.g b/src/plugins/qmldesigner/core/filemanager/parser/qmljs.g new file mode 100644 index 00000000000..8531ed51079 --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/parser/qmljs.g @@ -0,0 +1,3029 @@ +---------------------------------------------------------------------------- +-- +-- This file is part of Qt Creator +-- +-- Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +-- +-- Contact: Nokia Corporation (qt-info@nokia.com) +-- +-- Commercial Usage +-- +-- Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +-- +-- GNU Lesser General Public License Usage +-- +-- Alternatively, this file may be used under the terms of the GNU Lesser +-- General Public License version 2.1 as published by the Free Software +-- Foundation and appearing in the file LICENSE.LGPL included in the +-- packaging of this file. Please review the following information to +-- ensure the GNU Lesser General Public License version 2.1 requirements +-- will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +-- +-- If you are unsure which license is appropriate for your use, please +-- contact the sales department at http://qt.nokia.com/contact. +-- +---------------------------------------------------------------------------- + +%parser QmlJSGrammar +%decl qmljsparser_p.h +%impl qmljsparser.cpp +%expect 2 +%expect-rr 2 + +%token T_AND "&" T_AND_AND "&&" T_AND_EQ "&=" +%token T_BREAK "break" T_CASE "case" T_CATCH "catch" +%token T_COLON ":" T_COMMA ";" T_CONTINUE "continue" +%token T_DEFAULT "default" T_DELETE "delete" T_DIVIDE_ "/" +%token T_DIVIDE_EQ "/=" T_DO "do" T_DOT "." +%token T_ELSE "else" T_EQ "=" T_EQ_EQ "==" +%token T_EQ_EQ_EQ "===" T_FINALLY "finally" T_FOR "for" +%token T_FUNCTION "function" T_GE ">=" T_GT ">" +%token T_GT_GT ">>" T_GT_GT_EQ ">>=" T_GT_GT_GT ">>>" +%token T_GT_GT_GT_EQ ">>>=" T_IDENTIFIER "identifier" T_IF "if" +%token T_IN "in" T_INSTANCEOF "instanceof" T_LBRACE "{" +%token T_LBRACKET "[" T_LE "<=" T_LPAREN "(" +%token T_LT "<" T_LT_LT "<<" T_LT_LT_EQ "<<=" +%token T_MINUS "-" T_MINUS_EQ "-=" T_MINUS_MINUS "--" +%token T_NEW "new" T_NOT "!" T_NOT_EQ "!=" +%token T_NOT_EQ_EQ "!==" T_NUMERIC_LITERAL "numeric literal" T_OR "|" +%token T_OR_EQ "|=" T_OR_OR "||" T_PLUS "+" +%token T_PLUS_EQ "+=" T_PLUS_PLUS "++" T_QUESTION "?" +%token T_RBRACE "}" T_RBRACKET "]" T_REMAINDER "%" +%token T_REMAINDER_EQ "%=" T_RETURN "return" T_RPAREN ")" +%token T_SEMICOLON ";" T_AUTOMATIC_SEMICOLON T_STAR "*" +%token T_STAR_EQ "*=" T_STRING_LITERAL "string literal" +%token T_PROPERTY "property" T_SIGNAL "signal" T_READONLY "readonly" +%token T_SWITCH "switch" T_THIS "this" T_THROW "throw" +%token T_TILDE "~" T_TRY "try" T_TYPEOF "typeof" +%token T_VAR "var" T_VOID "void" T_WHILE "while" +%token T_WITH "with" T_XOR "^" T_XOR_EQ "^=" +%token T_NULL "null" T_TRUE "true" T_FALSE "false" +%token T_CONST "const" +%token T_DEBUGGER "debugger" +%token T_RESERVED_WORD "reserved word" +%token T_MULTILINE_STRING_LITERAL "multiline string literal" +%token T_COMMENT "comment" + +--- context keywords. +%token T_PUBLIC "public" +%token T_IMPORT "import" +%token T_AS "as" + +--- feed tokens +%token T_FEED_UI_PROGRAM +%token T_FEED_UI_OBJECT_MEMBER +%token T_FEED_JS_STATEMENT +%token T_FEED_JS_EXPRESSION + +%nonassoc SHIFT_THERE +%nonassoc T_IDENTIFIER T_COLON T_SIGNAL T_PROPERTY T_READONLY +%nonassoc REDUCE_HERE + +%start TopLevel + +/. +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include <QtCore/QtDebug> + +#include <string.h> + +#include "qmljsengine_p.h" +#include "qmljslexer_p.h" +#include "qmljsast_p.h" +#include "qmljsnodepool_p.h" + +./ + +/: +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +// +// This file is automatically generated from qmljs.g. +// Changes will be lost. +// + +#ifndef QMLJSPARSER_P_H +#define QMLJSPARSER_P_H + +#include "qmljsglobal_p.h" +#include "qmljsgrammar_p.h" +#include "qmljsast_p.h" +#include "qmljsengine_p.h" + +#include <QtCore/QList> +#include <QtCore/QString> + +QT_QML_BEGIN_NAMESPACE + +namespace QmlJS { + +class Engine; +class NameId; + +class Parser: protected $table +{ +public: + union Value { + int ival; + double dval; + NameId *sval; + AST::ArgumentList *ArgumentList; + AST::CaseBlock *CaseBlock; + AST::CaseClause *CaseClause; + AST::CaseClauses *CaseClauses; + AST::Catch *Catch; + AST::DefaultClause *DefaultClause; + AST::ElementList *ElementList; + AST::Elision *Elision; + AST::ExpressionNode *Expression; + AST::Finally *Finally; + AST::FormalParameterList *FormalParameterList; + AST::FunctionBody *FunctionBody; + AST::FunctionDeclaration *FunctionDeclaration; + AST::Node *Node; + AST::PropertyName *PropertyName; + AST::PropertyNameAndValueList *PropertyNameAndValueList; + AST::SourceElement *SourceElement; + AST::SourceElements *SourceElements; + AST::Statement *Statement; + AST::StatementList *StatementList; + AST::Block *Block; + AST::VariableDeclaration *VariableDeclaration; + AST::VariableDeclarationList *VariableDeclarationList; + + AST::UiProgram *UiProgram; + AST::UiImportList *UiImportList; + AST::UiImport *UiImport; + AST::UiParameterList *UiParameterList; + AST::UiPublicMember *UiPublicMember; + AST::UiObjectDefinition *UiObjectDefinition; + AST::UiObjectInitializer *UiObjectInitializer; + AST::UiObjectBinding *UiObjectBinding; + AST::UiScriptBinding *UiScriptBinding; + AST::UiArrayBinding *UiArrayBinding; + AST::UiObjectMember *UiObjectMember; + AST::UiObjectMemberList *UiObjectMemberList; + AST::UiArrayMemberList *UiArrayMemberList; + AST::UiQualifiedId *UiQualifiedId; + AST::UiSignature *UiSignature; + AST::UiFormalList *UiFormalList; + AST::UiFormal *UiFormal; + }; + +public: + Parser(Engine *engine); + ~Parser(); + + // parse a UI program + bool parse() { return parse(T_FEED_UI_PROGRAM); } + bool parseStatement() { return parse(T_FEED_JS_STATEMENT); } + bool parseExpression() { return parse(T_FEED_JS_EXPRESSION); } + bool parseUiObjectMember() { return parse(T_FEED_UI_OBJECT_MEMBER); } + + AST::UiProgram *ast() const + { return AST::cast<AST::UiProgram *>(program); } + + AST::Statement *statement() const + { + if (! program) + return 0; + + return program->statementCast(); + } + + AST::ExpressionNode *expression() const + { + if (! program) + return 0; + + return program->expressionCast(); + } + + AST::UiObjectMember *uiObjectMember() const + { + if (! program) + return 0; + + return program->uiObjectMemberCast(); + } + + QList<DiagnosticMessage> diagnosticMessages() const + { return diagnostic_messages; } + + inline DiagnosticMessage diagnosticMessage() const + { + foreach (const DiagnosticMessage &d, diagnostic_messages) { + if (! d.kind == DiagnosticMessage::Warning) + return d; + } + + return DiagnosticMessage(); + } + + inline QString errorMessage() const + { return diagnosticMessage().message; } + + inline int errorLineNumber() const + { return diagnosticMessage().loc.startLine; } + + inline int errorColumnNumber() const + { return diagnosticMessage().loc.startColumn; } + +protected: + bool parse(int startToken); + + void reallocateStack(); + + inline Value &sym(int index) + { return sym_stack [tos + index - 1]; } + + inline AST::SourceLocation &loc(int index) + { return location_stack [tos + index - 1]; } + + AST::UiQualifiedId *reparseAsQualifiedId(AST::ExpressionNode *expr); + +protected: + Engine *driver; + int tos; + int stack_size; + Value *sym_stack; + int *state_stack; + AST::SourceLocation *location_stack; + + AST::Node *program; + + // error recovery + enum { TOKEN_BUFFER_SIZE = 3 }; + + struct SavedToken { + int token; + double dval; + AST::SourceLocation loc; + }; + + double yylval; + AST::SourceLocation yylloc; + AST::SourceLocation yyprevlloc; + + SavedToken token_buffer[TOKEN_BUFFER_SIZE]; + SavedToken *first_token; + SavedToken *last_token; + + QList<DiagnosticMessage> diagnostic_messages; +}; + +} // end of namespace QmlJS + + +:/ + + +/. + +#include "qmljsparser_p.h" +#include <QVarLengthArray> + +// +// This file is automatically generated from qmljs.g. +// Changes will be lost. +// + +using namespace QmlJS; + +QT_QML_BEGIN_NAMESPACE + +void Parser::reallocateStack() +{ + if (! stack_size) + stack_size = 128; + else + stack_size <<= 1; + + sym_stack = reinterpret_cast<Value*> (qRealloc(sym_stack, stack_size * sizeof(Value))); + state_stack = reinterpret_cast<int*> (qRealloc(state_stack, stack_size * sizeof(int))); + location_stack = reinterpret_cast<AST::SourceLocation*> (qRealloc(location_stack, stack_size * sizeof(AST::SourceLocation))); +} + +inline static bool automatic(Engine *driver, int token) +{ + return token == $table::T_RBRACE + || token == 0 + || driver->lexer()->prevTerminator(); +} + + +Parser::Parser(Engine *engine): + driver(engine), + tos(0), + stack_size(0), + sym_stack(0), + state_stack(0), + location_stack(0), + first_token(0), + last_token(0) +{ +} + +Parser::~Parser() +{ + if (stack_size) { + qFree(sym_stack); + qFree(state_stack); + qFree(location_stack); + } +} + +static inline AST::SourceLocation location(Lexer *lexer) +{ + AST::SourceLocation loc; + loc.offset = lexer->tokenOffset(); + loc.length = lexer->tokenLength(); + loc.startLine = lexer->startLineNo(); + loc.startColumn = lexer->startColumnNo(); + return loc; +} + +AST::UiQualifiedId *Parser::reparseAsQualifiedId(AST::ExpressionNode *expr) +{ + QVarLengthArray<NameId *, 4> nameIds; + QVarLengthArray<AST::SourceLocation, 4> locations; + + AST::ExpressionNode *it = expr; + while (AST::FieldMemberExpression *m = AST::cast<AST::FieldMemberExpression *>(it)) { + nameIds.append(m->name); + locations.append(m->identifierToken); + it = m->base; + } + + if (AST::IdentifierExpression *idExpr = AST::cast<AST::IdentifierExpression *>(it)) { + AST::UiQualifiedId *q = makeAstNode<AST::UiQualifiedId>(driver->nodePool(), idExpr->name); + q->identifierToken = idExpr->identifierToken; + + AST::UiQualifiedId *currentId = q; + for (int i = nameIds.size() - 1; i != -1; --i) { + currentId = makeAstNode<AST::UiQualifiedId>(driver->nodePool(), currentId, nameIds[i]); + currentId->identifierToken = locations[i]; + } + + return currentId->finish(); + } + + return 0; +} + +bool Parser::parse(int startToken) +{ + Lexer *lexer = driver->lexer(); + bool hadErrors = false; + int yytoken = -1; + int action = 0; + + token_buffer[0].token = startToken; + first_token = &token_buffer[0]; + last_token = &token_buffer[1]; + + tos = -1; + program = 0; + + do { + if (++tos == stack_size) + reallocateStack(); + + state_stack[tos] = action; + + _Lcheck_token: + if (yytoken == -1 && -TERMINAL_COUNT != action_index[action]) { + yyprevlloc = yylloc; + + if (first_token == last_token) { + yytoken = lexer->lex(); + yylval = lexer->dval(); + yylloc = location(lexer); + } else { + yytoken = first_token->token; + yylval = first_token->dval; + yylloc = first_token->loc; + ++first_token; + } + } + + action = t_action(action, yytoken); + if (action > 0) { + if (action != ACCEPT_STATE) { + yytoken = -1; + sym(1).dval = yylval; + loc(1) = yylloc; + } else { + --tos; + return ! hadErrors; + } + } else if (action < 0) { + const int r = -action - 1; + tos -= rhs[r]; + + switch (r) { +./ + +-------------------------------------------------------------------------------------------------------- +-- Declarative UI +-------------------------------------------------------------------------------------------------------- + +TopLevel: T_FEED_UI_PROGRAM UiProgram ; +/. +case $rule_number: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; +./ + +TopLevel: T_FEED_JS_STATEMENT Statement ; +/. +case $rule_number: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; +./ + +TopLevel: T_FEED_JS_EXPRESSION Expression ; +/. +case $rule_number: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; +./ + +TopLevel: T_FEED_UI_OBJECT_MEMBER UiObjectMember ; +/. +case $rule_number: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; +./ + +UiProgram: UiImportListOpt UiRootMember ; +/. +case $rule_number: { + sym(1).UiProgram = makeAstNode<AST::UiProgram> (driver->nodePool(), sym(1).UiImportList, + sym(2).UiObjectMemberList->finish()); +} break; +./ + +UiImportListOpt: Empty ; +UiImportListOpt: UiImportList ; +/. +case $rule_number: { + sym(1).Node = sym(1).UiImportList->finish(); +} break; +./ + +UiImportList: UiImport ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::UiImportList> (driver->nodePool(), sym(1).UiImport); +} break; +./ + +UiImportList: UiImportList UiImport ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::UiImportList> (driver->nodePool(), + sym(1).UiImportList, sym(2).UiImport); +} break; +./ + +ImportId: MemberExpression ; + +UiImport: UiImportHead T_AUTOMATIC_SEMICOLON ; +UiImport: UiImportHead T_SEMICOLON ; +/. +case $rule_number: { + sym(1).UiImport->semicolonToken = loc(2); +} break; +./ + +UiImport: UiImportHead T_NUMERIC_LITERAL T_AUTOMATIC_SEMICOLON ; +UiImport: UiImportHead T_NUMERIC_LITERAL T_SEMICOLON ; +/. +case $rule_number: { + sym(1).UiImport->versionToken = loc(2); + sym(1).UiImport->semicolonToken = loc(3); +} break; +./ + +UiImport: UiImportHead T_NUMERIC_LITERAL T_AS JsIdentifier T_AUTOMATIC_SEMICOLON ; +UiImport: UiImportHead T_NUMERIC_LITERAL T_AS JsIdentifier T_SEMICOLON ; +/. +case $rule_number: { + sym(1).UiImport->versionToken = loc(2); + sym(1).UiImport->asToken = loc(3); + sym(1).UiImport->importIdToken = loc(4); + sym(1).UiImport->importId = sym(4).sval; + sym(1).UiImport->semicolonToken = loc(5); +} break; +./ + +UiImport: UiImportHead T_AS JsIdentifier T_AUTOMATIC_SEMICOLON ; +UiImport: UiImportHead T_AS JsIdentifier T_SEMICOLON ; +/. +case $rule_number: { + sym(1).UiImport->asToken = loc(2); + sym(1).UiImport->importIdToken = loc(3); + sym(1).UiImport->importId = sym(3).sval; + sym(1).UiImport->semicolonToken = loc(4); +} break; +./ + + +UiImportHead: T_IMPORT ImportId ; +/. +case $rule_number: { + AST::UiImport *node = 0; + + if (AST::StringLiteral *importIdLiteral = AST::cast<AST::StringLiteral *>(sym(2).Expression)) { + node = makeAstNode<AST::UiImport>(driver->nodePool(), importIdLiteral->value); + node->fileNameToken = loc(2); + } else if (AST::UiQualifiedId *qualifiedId = reparseAsQualifiedId(sym(2).Expression)) { + QString text; + for (AST::UiQualifiedId *q = qualifiedId; q; q = q->next) { + text += q->name->asString(); + if (q->next) text += QLatin1String("."); + } + node = makeAstNode<AST::UiImport>(driver->nodePool(), qualifiedId); + node->fileNameToken = loc(2); + } + + sym(1).Node = node; + + if (! node) { + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1), + QLatin1String("Expected a qualified name id or a string literal"))); + + return false; // ### remove me + } +} break; +./ + +Empty: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +UiRootMember: UiObjectDefinition ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::UiObjectMemberList> (driver->nodePool(), sym(1).UiObjectMember); +} break; +./ + +UiObjectMemberList: UiObjectMember ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::UiObjectMemberList> (driver->nodePool(), sym(1).UiObjectMember); +} break; +./ + +UiObjectMemberList: UiObjectMemberList UiObjectMember ; +/. +case $rule_number: { + AST::UiObjectMemberList *node = makeAstNode<AST:: UiObjectMemberList> (driver->nodePool(), + sym(1).UiObjectMemberList, sym(2).UiObjectMember); + sym(1).Node = node; +} break; +./ + +UiArrayMemberList: UiObjectDefinition ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::UiArrayMemberList> (driver->nodePool(), sym(1).UiObjectMember); +} break; +./ + +UiArrayMemberList: UiArrayMemberList T_COMMA UiObjectDefinition ; +/. +case $rule_number: { + AST::UiArrayMemberList *node = makeAstNode<AST::UiArrayMemberList> (driver->nodePool(), + sym(1).UiArrayMemberList, sym(3).UiObjectMember); + node->commaToken = loc(2); + sym(1).Node = node; +} break; +./ + +UiObjectInitializer: T_LBRACE T_RBRACE ; +/. +case $rule_number: { + AST::UiObjectInitializer *node = makeAstNode<AST::UiObjectInitializer> (driver->nodePool(), (AST::UiObjectMemberList*)0); + node->lbraceToken = loc(1); + node->rbraceToken = loc(2); + sym(1).Node = node; +} break; +./ + +UiObjectInitializer: T_LBRACE UiObjectMemberList T_RBRACE ; +/. +case $rule_number: { + AST::UiObjectInitializer *node = makeAstNode<AST::UiObjectInitializer> (driver->nodePool(), sym(2).UiObjectMemberList->finish()); + node->lbraceToken = loc(1); + node->rbraceToken = loc(3); + sym(1).Node = node; +} break; +./ + +UiObjectDefinition: UiQualifiedId UiObjectInitializer ; +/. +case $rule_number: { + AST::UiObjectDefinition *node = makeAstNode<AST::UiObjectDefinition> (driver->nodePool(), sym(1).UiQualifiedId, + sym(2).UiObjectInitializer); + sym(1).Node = node; +} break; +./ + +UiObjectMember: UiObjectDefinition ; + +UiObjectMember: UiQualifiedId T_COLON T_LBRACKET UiArrayMemberList T_RBRACKET ; +/. +case $rule_number: { + AST::UiArrayBinding *node = makeAstNode<AST::UiArrayBinding> (driver->nodePool(), + sym(1).UiQualifiedId, sym(4).UiArrayMemberList->finish()); + node->colonToken = loc(2); + node->lbracketToken = loc(3); + node->rbracketToken = loc(5); + sym(1).Node = node; +} break; +./ + +UiObjectMember: UiQualifiedId T_COLON UiQualifiedId UiObjectInitializer ; +/. +case $rule_number: { + AST::UiObjectBinding *node = makeAstNode<AST::UiObjectBinding> (driver->nodePool(), + sym(1).UiQualifiedId, sym(3).UiQualifiedId, sym(4).UiObjectInitializer); + node->colonToken = loc(2); + sym(1).Node = node; +} break; +./ + +UiObjectMember: UiQualifiedId T_COLON Block ; +/.case $rule_number:./ + +UiObjectMember: UiQualifiedId T_COLON EmptyStatement ; +/.case $rule_number:./ + +UiObjectMember: UiQualifiedId T_COLON ExpressionStatement ; +/.case $rule_number:./ + +UiObjectMember: UiQualifiedId T_COLON IfStatement ; --- ### do we really want if statement in a binding? +/.case $rule_number:./ + +/. +{ + AST::UiScriptBinding *node = makeAstNode<AST::UiScriptBinding> (driver->nodePool(), + sym(1).UiQualifiedId, sym(3).Statement); + node->colonToken = loc(2); + sym(1).Node = node; +} break; +./ + +UiPropertyType: T_VAR ; +/. +case $rule_number: +./ +UiPropertyType: T_RESERVED_WORD ; +/. +case $rule_number: { + sym(1).sval = driver->intern(lexer->characterBuffer(), lexer->characterCount()); + break; +} +./ + +UiPropertyType: T_IDENTIFIER ; + +UiParameterListOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +UiParameterListOpt: UiParameterList ; +/. +case $rule_number: { + sym(1).Node = sym(1).UiParameterList->finish (); +} break; +./ + +UiParameterList: UiPropertyType JsIdentifier ; +/. +case $rule_number: { + AST::UiParameterList *node = makeAstNode<AST::UiParameterList> (driver->nodePool(), sym(1).sval, sym(2).sval); + node->identifierToken = loc(2); + sym(1).Node = node; +} break; +./ + +UiParameterList: UiParameterList T_COMMA UiPropertyType JsIdentifier ; +/. +case $rule_number: { + AST::UiParameterList *node = makeAstNode<AST::UiParameterList> (driver->nodePool(), sym(1).UiParameterList, sym(3).sval, sym(4).sval); + node->commaToken = loc(2); + node->identifierToken = loc(4); + sym(1).Node = node; +} break; +./ + +UiObjectMember: T_SIGNAL T_IDENTIFIER T_LPAREN UiParameterListOpt T_RPAREN T_AUTOMATIC_SEMICOLON ; +UiObjectMember: T_SIGNAL T_IDENTIFIER T_LPAREN UiParameterListOpt T_RPAREN T_SEMICOLON ; +/. +case $rule_number: { + AST::UiPublicMember *node = makeAstNode<AST::UiPublicMember> (driver->nodePool(), (NameId *)0, sym(2).sval); + node->type = AST::UiPublicMember::Signal; + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(2); + node->parameters = sym(4).UiParameterList; + node->semicolonToken = loc(6); + sym(1).Node = node; +} break; +./ + +UiObjectMember: T_SIGNAL T_IDENTIFIER T_AUTOMATIC_SEMICOLON ; +UiObjectMember: T_SIGNAL T_IDENTIFIER T_SEMICOLON ; +/. +case $rule_number: { + AST::UiPublicMember *node = makeAstNode<AST::UiPublicMember> (driver->nodePool(), (NameId *)0, sym(2).sval); + node->type = AST::UiPublicMember::Signal; + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(2); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; +./ + +UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT T_IDENTIFIER T_AUTOMATIC_SEMICOLON ; +UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT T_IDENTIFIER T_SEMICOLON ; +/. +case $rule_number: { + AST::UiPublicMember *node = makeAstNode<AST::UiPublicMember> (driver->nodePool(), sym(4).sval, sym(6).sval); + node->typeModifier = sym(2).sval; + node->propertyToken = loc(1); + node->typeModifierToken = loc(2); + node->typeToken = loc(4); + node->identifierToken = loc(6); + node->semicolonToken = loc(7); + sym(1).Node = node; +} break; +./ + +UiObjectMember: T_PROPERTY UiPropertyType T_IDENTIFIER T_AUTOMATIC_SEMICOLON ; +UiObjectMember: T_PROPERTY UiPropertyType T_IDENTIFIER T_SEMICOLON ; +/. +case $rule_number: { + AST::UiPublicMember *node = makeAstNode<AST::UiPublicMember> (driver->nodePool(), sym(2).sval, sym(3).sval); + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(3); + node->semicolonToken = loc(4); + sym(1).Node = node; +} break; +./ + +UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType T_IDENTIFIER T_AUTOMATIC_SEMICOLON ; +UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType T_IDENTIFIER T_SEMICOLON ; +/. +case $rule_number: { + AST::UiPublicMember *node = makeAstNode<AST::UiPublicMember> (driver->nodePool(), sym(3).sval, sym(4).sval); + node->isDefaultMember = true; + node->defaultToken = loc(1); + node->propertyToken = loc(2); + node->typeToken = loc(3); + node->identifierToken = loc(4); + node->semicolonToken = loc(5); + sym(1).Node = node; +} break; +./ + +UiObjectMember: T_PROPERTY UiPropertyType T_IDENTIFIER T_COLON Expression T_AUTOMATIC_SEMICOLON ; +UiObjectMember: T_PROPERTY UiPropertyType T_IDENTIFIER T_COLON Expression T_SEMICOLON ; +/. +case $rule_number: { + AST::UiPublicMember *node = makeAstNode<AST::UiPublicMember> (driver->nodePool(), sym(2).sval, sym(3).sval, + sym(5).Expression); + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(3); + node->colonToken = loc(4); + node->semicolonToken = loc(6); + sym(1).Node = node; +} break; +./ + +UiObjectMember: T_READONLY T_PROPERTY UiPropertyType T_IDENTIFIER T_COLON Expression T_AUTOMATIC_SEMICOLON ; +UiObjectMember: T_READONLY T_PROPERTY UiPropertyType T_IDENTIFIER T_COLON Expression T_SEMICOLON ; +/. +case $rule_number: { + AST::UiPublicMember *node = makeAstNode<AST::UiPublicMember> (driver->nodePool(), sym(3).sval, sym(4).sval, + sym(6).Expression); + node->isReadonlyMember = true; + node->readonlyToken = loc(1); + node->propertyToken = loc(2); + node->typeToken = loc(3); + node->identifierToken = loc(4); + node->colonToken = loc(5); + node->semicolonToken = loc(7); + sym(1).Node = node; +} break; +./ + +UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType T_IDENTIFIER T_COLON Expression T_AUTOMATIC_SEMICOLON ; +UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType T_IDENTIFIER T_COLON Expression T_SEMICOLON ; +/. +case $rule_number: { + AST::UiPublicMember *node = makeAstNode<AST::UiPublicMember> (driver->nodePool(), sym(3).sval, sym(4).sval, + sym(6).Expression); + node->isDefaultMember = true; + node->defaultToken = loc(1); + node->propertyToken = loc(2); + node->typeToken = loc(3); + node->identifierToken = loc(4); + node->colonToken = loc(5); + node->semicolonToken = loc(7); + sym(1).Node = node; +} break; +./ + +UiObjectMember: FunctionDeclaration ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::UiSourceElement>(driver->nodePool(), sym(1).Node); +} break; +./ + +UiObjectMember: VariableStatement ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::UiSourceElement>(driver->nodePool(), sym(1).Node); +} break; +./ + +JsIdentifier: T_IDENTIFIER; + +JsIdentifier: T_PROPERTY ; +/. +case $rule_number: { + QString s = QLatin1String(QmlJSGrammar::spell[T_PROPERTY]); + sym(1).sval = driver->intern(s.constData(), s.length()); + break; +} +./ + +JsIdentifier: T_SIGNAL ; +/. +case $rule_number: { + QString s = QLatin1String(QmlJSGrammar::spell[T_SIGNAL]); + sym(1).sval = driver->intern(s.constData(), s.length()); + break; +} +./ + +JsIdentifier: T_READONLY ; +/. +case $rule_number: { + QString s = QLatin1String(QmlJSGrammar::spell[T_READONLY]); + sym(1).sval = driver->intern(s.constData(), s.length()); + break; +} +./ + +-------------------------------------------------------------------------------------------------------- +-- Expressions +-------------------------------------------------------------------------------------------------------- + +PrimaryExpression: T_THIS ; +/. +case $rule_number: { + AST::ThisExpression *node = makeAstNode<AST::ThisExpression> (driver->nodePool()); + node->thisToken = loc(1); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: JsIdentifier ; +/. +case $rule_number: { + AST::IdentifierExpression *node = makeAstNode<AST::IdentifierExpression> (driver->nodePool(), sym(1).sval); + node->identifierToken = loc(1); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_NULL ; +/. +case $rule_number: { + AST::NullExpression *node = makeAstNode<AST::NullExpression> (driver->nodePool()); + node->nullToken = loc(1); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_TRUE ; +/. +case $rule_number: { + AST::TrueLiteral *node = makeAstNode<AST::TrueLiteral> (driver->nodePool()); + node->trueToken = loc(1); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_FALSE ; +/. +case $rule_number: { + AST::FalseLiteral *node = makeAstNode<AST::FalseLiteral> (driver->nodePool()); + node->falseToken = loc(1); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_NUMERIC_LITERAL ; +/. +case $rule_number: { + AST::NumericLiteral *node = makeAstNode<AST::NumericLiteral> (driver->nodePool(), sym(1).dval); + node->literalToken = loc(1); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_MULTILINE_STRING_LITERAL ; +/.case $rule_number:./ + +PrimaryExpression: T_STRING_LITERAL ; +/. +case $rule_number: { + AST::StringLiteral *node = makeAstNode<AST::StringLiteral> (driver->nodePool(), sym(1).sval); + node->literalToken = loc(1); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_DIVIDE_ ; +/: +#define J_SCRIPT_REGEXPLITERAL_RULE1 $rule_number +:/ +/. +case $rule_number: { + bool rx = lexer->scanRegExp(Lexer::NoPrefix); + if (!rx) { + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage())); + return false; // ### remove me + } + AST::RegExpLiteral *node = makeAstNode<AST::RegExpLiteral> (driver->nodePool(), lexer->pattern, lexer->flags); + node->literalToken = loc(1); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_DIVIDE_EQ ; +/: +#define J_SCRIPT_REGEXPLITERAL_RULE2 $rule_number +:/ +/. +case $rule_number: { + bool rx = lexer->scanRegExp(Lexer::EqualPrefix); + if (!rx) { + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage())); + return false; + } + AST::RegExpLiteral *node = makeAstNode<AST::RegExpLiteral> (driver->nodePool(), lexer->pattern, lexer->flags); + node->literalToken = loc(1); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_LBRACKET T_RBRACKET ; +/. +case $rule_number: { + AST::ArrayLiteral *node = makeAstNode<AST::ArrayLiteral> (driver->nodePool(), (AST::Elision *) 0); + node->lbracketToken = loc(1); + node->rbracketToken = loc(2); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_LBRACKET Elision T_RBRACKET ; +/. +case $rule_number: { + AST::ArrayLiteral *node = makeAstNode<AST::ArrayLiteral> (driver->nodePool(), sym(2).Elision->finish()); + node->lbracketToken = loc(1); + node->rbracketToken = loc(3); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_LBRACKET ElementList T_RBRACKET ; +/. +case $rule_number: { + AST::ArrayLiteral *node = makeAstNode<AST::ArrayLiteral> (driver->nodePool(), sym(2).ElementList->finish ()); + node->lbracketToken = loc(1); + node->rbracketToken = loc(3); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_LBRACKET ElementList T_COMMA T_RBRACKET ; +/. +case $rule_number: { + AST::ArrayLiteral *node = makeAstNode<AST::ArrayLiteral> (driver->nodePool(), sym(2).ElementList->finish (), + (AST::Elision *) 0); + node->lbracketToken = loc(1); + node->commaToken = loc(3); + node->rbracketToken = loc(4); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_LBRACKET ElementList T_COMMA Elision T_RBRACKET ; +/. +case $rule_number: { + AST::ArrayLiteral *node = makeAstNode<AST::ArrayLiteral> (driver->nodePool(), sym(2).ElementList->finish (), + sym(4).Elision->finish()); + node->lbracketToken = loc(1); + node->commaToken = loc(3); + node->rbracketToken = loc(5); + sym(1).Node = node; +} break; +./ + +-- PrimaryExpression: T_LBRACE T_RBRACE ; +-- /. +-- case $rule_number: { +-- sym(1).Node = makeAstNode<AST::ObjectLiteral> (driver->nodePool()); +-- } break; +-- ./ + +PrimaryExpression: T_LBRACE PropertyNameAndValueListOpt T_RBRACE ; +/. +case $rule_number: { + AST::ObjectLiteral *node = 0; + if (sym(2).Node) + node = makeAstNode<AST::ObjectLiteral> (driver->nodePool(), + sym(2).PropertyNameAndValueList->finish ()); + else + node = makeAstNode<AST::ObjectLiteral> (driver->nodePool()); + node->lbraceToken = loc(1); + node->lbraceToken = loc(3); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_LBRACE PropertyNameAndValueList T_COMMA T_RBRACE ; +/. +case $rule_number: { + AST::ObjectLiteral *node = makeAstNode<AST::ObjectLiteral> (driver->nodePool(), + sym(2).PropertyNameAndValueList->finish ()); + node->lbraceToken = loc(1); + node->lbraceToken = loc(4); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_LPAREN Expression T_RPAREN ; +/. +case $rule_number: { + AST::NestedExpression *node = makeAstNode<AST::NestedExpression>(driver->nodePool(), sym(2).Expression); + node->lparenToken = loc(1); + node->rparenToken = loc(3); + sym(1).Node = node; +} break; +./ + +UiQualifiedId: MemberExpression ; +/. +case $rule_number: { + if (AST::ArrayMemberExpression *mem = AST::cast<AST::ArrayMemberExpression *>(sym(1).Expression)) { + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Warning, mem->lbracketToken, + QLatin1String("Ignored annotation"))); + + sym(1).Expression = mem->base; + } + + if (AST::UiQualifiedId *qualifiedId = reparseAsQualifiedId(sym(1).Expression)) { + sym(1).UiQualifiedId = qualifiedId; + } else { + sym(1).UiQualifiedId = 0; + + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1), + QLatin1String("Expected a qualified name id"))); + + return false; // ### recover + } +} break; +./ + +ElementList: AssignmentExpression ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::ElementList> (driver->nodePool(), (AST::Elision *) 0, sym(1).Expression); +} break; +./ + +ElementList: Elision AssignmentExpression ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::ElementList> (driver->nodePool(), sym(1).Elision->finish(), sym(2).Expression); +} break; +./ + +ElementList: ElementList T_COMMA AssignmentExpression ; +/. +case $rule_number: { + AST::ElementList *node = makeAstNode<AST::ElementList> (driver->nodePool(), sym(1).ElementList, + (AST::Elision *) 0, sym(3).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; +./ + +ElementList: ElementList T_COMMA Elision AssignmentExpression ; +/. +case $rule_number: { + AST::ElementList *node = makeAstNode<AST::ElementList> (driver->nodePool(), sym(1).ElementList, sym(3).Elision->finish(), + sym(4).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; +./ + +Elision: T_COMMA ; +/. +case $rule_number: { + AST::Elision *node = makeAstNode<AST::Elision> (driver->nodePool()); + node->commaToken = loc(1); + sym(1).Node = node; +} break; +./ + +Elision: Elision T_COMMA ; +/. +case $rule_number: { + AST::Elision *node = makeAstNode<AST::Elision> (driver->nodePool(), sym(1).Elision); + node->commaToken = loc(2); + sym(1).Node = node; +} break; +./ + +PropertyNameAndValueList: PropertyName T_COLON AssignmentExpression ; +/. +case $rule_number: { + AST::PropertyNameAndValueList *node = makeAstNode<AST::PropertyNameAndValueList> (driver->nodePool(), + sym(1).PropertyName, sym(3).Expression); + node->colonToken = loc(2); + sym(1).Node = node; +} break; +./ + +PropertyNameAndValueList: PropertyNameAndValueList T_COMMA PropertyName T_COLON AssignmentExpression ; +/. +case $rule_number: { + AST::PropertyNameAndValueList *node = makeAstNode<AST::PropertyNameAndValueList> (driver->nodePool(), + sym(1).PropertyNameAndValueList, sym(3).PropertyName, sym(5).Expression); + node->commaToken = loc(2); + node->colonToken = loc(4); + sym(1).Node = node; +} break; +./ + +PropertyName: T_IDENTIFIER %prec REDUCE_HERE ; +/. +case $rule_number: { + AST::IdentifierPropertyName *node = makeAstNode<AST::IdentifierPropertyName> (driver->nodePool(), sym(1).sval); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; +./ + +PropertyName: T_SIGNAL ; +/.case $rule_number:./ + +PropertyName: T_PROPERTY ; +/. +case $rule_number: { + AST::IdentifierPropertyName *node = makeAstNode<AST::IdentifierPropertyName> (driver->nodePool(), driver->intern(lexer->characterBuffer(), lexer->characterCount())); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; +./ + +PropertyName: T_STRING_LITERAL ; +/. +case $rule_number: { + AST::StringLiteralPropertyName *node = makeAstNode<AST::StringLiteralPropertyName> (driver->nodePool(), sym(1).sval); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; +./ + +PropertyName: T_NUMERIC_LITERAL ; +/. +case $rule_number: { + AST::NumericLiteralPropertyName *node = makeAstNode<AST::NumericLiteralPropertyName> (driver->nodePool(), sym(1).dval); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; +./ + +PropertyName: ReservedIdentifier ; +/. +case $rule_number: { + AST::IdentifierPropertyName *node = makeAstNode<AST::IdentifierPropertyName> (driver->nodePool(), sym(1).sval); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; +./ + +ReservedIdentifier: T_BREAK ; +/. +case $rule_number: +./ +ReservedIdentifier: T_CASE ; +/. +case $rule_number: +./ +ReservedIdentifier: T_CATCH ; +/. +case $rule_number: +./ +ReservedIdentifier: T_CONTINUE ; +/. +case $rule_number: +./ +ReservedIdentifier: T_DEFAULT ; +/. +case $rule_number: +./ +ReservedIdentifier: T_DELETE ; +/. +case $rule_number: +./ +ReservedIdentifier: T_DO ; +/. +case $rule_number: +./ +ReservedIdentifier: T_ELSE ; +/. +case $rule_number: +./ +ReservedIdentifier: T_FALSE ; +/. +case $rule_number: +./ +ReservedIdentifier: T_FINALLY ; +/. +case $rule_number: +./ +ReservedIdentifier: T_FOR ; +/. +case $rule_number: +./ +ReservedIdentifier: T_FUNCTION ; +/. +case $rule_number: +./ +ReservedIdentifier: T_IF ; +/. +case $rule_number: +./ +ReservedIdentifier: T_IN ; +/. +case $rule_number: +./ +ReservedIdentifier: T_INSTANCEOF ; +/. +case $rule_number: +./ +ReservedIdentifier: T_NEW ; +/. +case $rule_number: +./ +ReservedIdentifier: T_NULL ; +/. +case $rule_number: +./ +ReservedIdentifier: T_RETURN ; +/. +case $rule_number: +./ +ReservedIdentifier: T_SWITCH ; +/. +case $rule_number: +./ +ReservedIdentifier: T_THIS ; +/. +case $rule_number: +./ +ReservedIdentifier: T_THROW ; +/. +case $rule_number: +./ +ReservedIdentifier: T_TRUE ; +/. +case $rule_number: +./ +ReservedIdentifier: T_TRY ; +/. +case $rule_number: +./ +ReservedIdentifier: T_TYPEOF ; +/. +case $rule_number: +./ +ReservedIdentifier: T_VAR ; +/. +case $rule_number: +./ +ReservedIdentifier: T_VOID ; +/. +case $rule_number: +./ +ReservedIdentifier: T_WHILE ; +/. +case $rule_number: +./ +ReservedIdentifier: T_CONST ; +/. +case $rule_number: +./ +ReservedIdentifier: T_DEBUGGER ; +/. +case $rule_number: +./ +ReservedIdentifier: T_RESERVED_WORD ; +/. +case $rule_number: +./ +ReservedIdentifier: T_WITH ; +/. +case $rule_number: +{ + sym(1).sval = driver->intern(lexer->characterBuffer(), lexer->characterCount()); +} break; +./ + +PropertyIdentifier: JsIdentifier ; +PropertyIdentifier: ReservedIdentifier ; + +MemberExpression: PrimaryExpression ; +MemberExpression: FunctionExpression ; + +MemberExpression: MemberExpression T_LBRACKET Expression T_RBRACKET ; +/. +case $rule_number: { + AST::ArrayMemberExpression *node = makeAstNode<AST::ArrayMemberExpression> (driver->nodePool(), sym(1).Expression, sym(3).Expression); + node->lbracketToken = loc(2); + node->rbracketToken = loc(4); + sym(1).Node = node; +} break; +./ + +MemberExpression: MemberExpression T_DOT PropertyIdentifier ; +/. +case $rule_number: { + AST::FieldMemberExpression *node = makeAstNode<AST::FieldMemberExpression> (driver->nodePool(), sym(1).Expression, sym(3).sval); + node->dotToken = loc(2); + node->identifierToken = loc(3); + sym(1).Node = node; +} break; +./ + +MemberExpression: T_NEW MemberExpression T_LPAREN ArgumentListOpt T_RPAREN ; +/. +case $rule_number: { + AST::NewMemberExpression *node = makeAstNode<AST::NewMemberExpression> (driver->nodePool(), sym(2).Expression, sym(4).ArgumentList); + node->newToken = loc(1); + node->lparenToken = loc(3); + node->rparenToken = loc(5); + sym(1).Node = node; +} break; +./ + +NewExpression: MemberExpression ; + +NewExpression: T_NEW NewExpression ; +/. +case $rule_number: { + AST::NewExpression *node = makeAstNode<AST::NewExpression> (driver->nodePool(), sym(2).Expression); + node->newToken = loc(1); + sym(1).Node = node; +} break; +./ + +CallExpression: MemberExpression T_LPAREN ArgumentListOpt T_RPAREN ; +/. +case $rule_number: { + AST::CallExpression *node = makeAstNode<AST::CallExpression> (driver->nodePool(), sym(1).Expression, sym(3).ArgumentList); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; +./ + +CallExpression: CallExpression T_LPAREN ArgumentListOpt T_RPAREN ; +/. +case $rule_number: { + AST::CallExpression *node = makeAstNode<AST::CallExpression> (driver->nodePool(), sym(1).Expression, sym(3).ArgumentList); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; +./ + +CallExpression: CallExpression T_LBRACKET Expression T_RBRACKET ; +/. +case $rule_number: { + AST::ArrayMemberExpression *node = makeAstNode<AST::ArrayMemberExpression> (driver->nodePool(), sym(1).Expression, sym(3).Expression); + node->lbracketToken = loc(2); + node->rbracketToken = loc(4); + sym(1).Node = node; +} break; +./ + +CallExpression: CallExpression T_DOT PropertyIdentifier ; +/. +case $rule_number: { + AST::FieldMemberExpression *node = makeAstNode<AST::FieldMemberExpression> (driver->nodePool(), sym(1).Expression, sym(3).sval); + node->dotToken = loc(2); + node->identifierToken = loc(3); + sym(1).Node = node; +} break; +./ + +ArgumentListOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +ArgumentListOpt: ArgumentList ; +/. +case $rule_number: { + sym(1).Node = sym(1).ArgumentList->finish(); +} break; +./ + +ArgumentList: AssignmentExpression ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::ArgumentList> (driver->nodePool(), sym(1).Expression); +} break; +./ + +ArgumentList: ArgumentList T_COMMA AssignmentExpression ; +/. +case $rule_number: { + AST::ArgumentList *node = makeAstNode<AST::ArgumentList> (driver->nodePool(), sym(1).ArgumentList, sym(3).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; +./ + +LeftHandSideExpression: NewExpression ; +LeftHandSideExpression: CallExpression ; +PostfixExpression: LeftHandSideExpression ; + +PostfixExpression: LeftHandSideExpression T_PLUS_PLUS ; +/. +case $rule_number: { + AST::PostIncrementExpression *node = makeAstNode<AST::PostIncrementExpression> (driver->nodePool(), sym(1).Expression); + node->incrementToken = loc(2); + sym(1).Node = node; +} break; +./ + +PostfixExpression: LeftHandSideExpression T_MINUS_MINUS ; +/. +case $rule_number: { + AST::PostDecrementExpression *node = makeAstNode<AST::PostDecrementExpression> (driver->nodePool(), sym(1).Expression); + node->decrementToken = loc(2); + sym(1).Node = node; +} break; +./ + +UnaryExpression: PostfixExpression ; + +UnaryExpression: T_DELETE UnaryExpression ; +/. +case $rule_number: { + AST::DeleteExpression *node = makeAstNode<AST::DeleteExpression> (driver->nodePool(), sym(2).Expression); + node->deleteToken = loc(1); + sym(1).Node = node; +} break; +./ + +UnaryExpression: T_VOID UnaryExpression ; +/. +case $rule_number: { + AST::VoidExpression *node = makeAstNode<AST::VoidExpression> (driver->nodePool(), sym(2).Expression); + node->voidToken = loc(1); + sym(1).Node = node; +} break; +./ + +UnaryExpression: T_TYPEOF UnaryExpression ; +/. +case $rule_number: { + AST::TypeOfExpression *node = makeAstNode<AST::TypeOfExpression> (driver->nodePool(), sym(2).Expression); + node->typeofToken = loc(1); + sym(1).Node = node; +} break; +./ + +UnaryExpression: T_PLUS_PLUS UnaryExpression ; +/. +case $rule_number: { + AST::PreIncrementExpression *node = makeAstNode<AST::PreIncrementExpression> (driver->nodePool(), sym(2).Expression); + node->incrementToken = loc(1); + sym(1).Node = node; +} break; +./ + +UnaryExpression: T_MINUS_MINUS UnaryExpression ; +/. +case $rule_number: { + AST::PreDecrementExpression *node = makeAstNode<AST::PreDecrementExpression> (driver->nodePool(), sym(2).Expression); + node->decrementToken = loc(1); + sym(1).Node = node; +} break; +./ + +UnaryExpression: T_PLUS UnaryExpression ; +/. +case $rule_number: { + AST::UnaryPlusExpression *node = makeAstNode<AST::UnaryPlusExpression> (driver->nodePool(), sym(2).Expression); + node->plusToken = loc(1); + sym(1).Node = node; +} break; +./ + +UnaryExpression: T_MINUS UnaryExpression ; +/. +case $rule_number: { + AST::UnaryMinusExpression *node = makeAstNode<AST::UnaryMinusExpression> (driver->nodePool(), sym(2).Expression); + node->minusToken = loc(1); + sym(1).Node = node; +} break; +./ + +UnaryExpression: T_TILDE UnaryExpression ; +/. +case $rule_number: { + AST::TildeExpression *node = makeAstNode<AST::TildeExpression> (driver->nodePool(), sym(2).Expression); + node->tildeToken = loc(1); + sym(1).Node = node; +} break; +./ + +UnaryExpression: T_NOT UnaryExpression ; +/. +case $rule_number: { + AST::NotExpression *node = makeAstNode<AST::NotExpression> (driver->nodePool(), sym(2).Expression); + node->notToken = loc(1); + sym(1).Node = node; +} break; +./ + +MultiplicativeExpression: UnaryExpression ; + +MultiplicativeExpression: MultiplicativeExpression T_STAR UnaryExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Mul, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +MultiplicativeExpression: MultiplicativeExpression T_DIVIDE_ UnaryExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Div, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +MultiplicativeExpression: MultiplicativeExpression T_REMAINDER UnaryExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Mod, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +AdditiveExpression: MultiplicativeExpression ; + +AdditiveExpression: AdditiveExpression T_PLUS MultiplicativeExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Add, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +AdditiveExpression: AdditiveExpression T_MINUS MultiplicativeExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Sub, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +ShiftExpression: AdditiveExpression ; + +ShiftExpression: ShiftExpression T_LT_LT AdditiveExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::LShift, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +ShiftExpression: ShiftExpression T_GT_GT AdditiveExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::RShift, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +ShiftExpression: ShiftExpression T_GT_GT_GT AdditiveExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::URShift, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpression: ShiftExpression ; + +RelationalExpression: RelationalExpression T_LT ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Lt, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpression: RelationalExpression T_GT ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Gt, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpression: RelationalExpression T_LE ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Le, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpression: RelationalExpression T_GE ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Ge, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpression: RelationalExpression T_INSTANCEOF ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::InstanceOf, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpression: RelationalExpression T_IN ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::In, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpressionNotIn: ShiftExpression ; + +RelationalExpressionNotIn: RelationalExpressionNotIn T_LT ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Lt, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpressionNotIn: RelationalExpressionNotIn T_GT ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Gt, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpressionNotIn: RelationalExpressionNotIn T_LE ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Le, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpressionNotIn: RelationalExpressionNotIn T_GE ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Ge, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpressionNotIn: RelationalExpressionNotIn T_INSTANCEOF ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::InstanceOf, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +EqualityExpression: RelationalExpression ; + +EqualityExpression: EqualityExpression T_EQ_EQ RelationalExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Equal, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +EqualityExpression: EqualityExpression T_NOT_EQ RelationalExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::NotEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +EqualityExpression: EqualityExpression T_EQ_EQ_EQ RelationalExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::StrictEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +EqualityExpression: EqualityExpression T_NOT_EQ_EQ RelationalExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::StrictNotEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +EqualityExpressionNotIn: RelationalExpressionNotIn ; + +EqualityExpressionNotIn: EqualityExpressionNotIn T_EQ_EQ RelationalExpressionNotIn ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Equal, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +EqualityExpressionNotIn: EqualityExpressionNotIn T_NOT_EQ RelationalExpressionNotIn; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::NotEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +EqualityExpressionNotIn: EqualityExpressionNotIn T_EQ_EQ_EQ RelationalExpressionNotIn ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::StrictEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +EqualityExpressionNotIn: EqualityExpressionNotIn T_NOT_EQ_EQ RelationalExpressionNotIn ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::StrictNotEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +BitwiseANDExpression: EqualityExpression ; + +BitwiseANDExpression: BitwiseANDExpression T_AND EqualityExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::BitAnd, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +BitwiseANDExpressionNotIn: EqualityExpressionNotIn ; + +BitwiseANDExpressionNotIn: BitwiseANDExpressionNotIn T_AND EqualityExpressionNotIn ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::BitAnd, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +BitwiseXORExpression: BitwiseANDExpression ; + +BitwiseXORExpression: BitwiseXORExpression T_XOR BitwiseANDExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::BitXor, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +BitwiseXORExpressionNotIn: BitwiseANDExpressionNotIn ; + +BitwiseXORExpressionNotIn: BitwiseXORExpressionNotIn T_XOR BitwiseANDExpressionNotIn ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::BitXor, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +BitwiseORExpression: BitwiseXORExpression ; + +BitwiseORExpression: BitwiseORExpression T_OR BitwiseXORExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::BitOr, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +BitwiseORExpressionNotIn: BitwiseXORExpressionNotIn ; + +BitwiseORExpressionNotIn: BitwiseORExpressionNotIn T_OR BitwiseXORExpressionNotIn ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::BitOr, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +LogicalANDExpression: BitwiseORExpression ; + +LogicalANDExpression: LogicalANDExpression T_AND_AND BitwiseORExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::And, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +LogicalANDExpressionNotIn: BitwiseORExpressionNotIn ; + +LogicalANDExpressionNotIn: LogicalANDExpressionNotIn T_AND_AND BitwiseORExpressionNotIn ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::And, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +LogicalORExpression: LogicalANDExpression ; + +LogicalORExpression: LogicalORExpression T_OR_OR LogicalANDExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Or, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +LogicalORExpressionNotIn: LogicalANDExpressionNotIn ; + +LogicalORExpressionNotIn: LogicalORExpressionNotIn T_OR_OR LogicalANDExpressionNotIn ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Or, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +ConditionalExpression: LogicalORExpression ; + +ConditionalExpression: LogicalORExpression T_QUESTION AssignmentExpression T_COLON AssignmentExpression ; +/. +case $rule_number: { + AST::ConditionalExpression *node = makeAstNode<AST::ConditionalExpression> (driver->nodePool(), sym(1).Expression, + sym(3).Expression, sym(5).Expression); + node->questionToken = loc(2); + node->colonToken = loc(4); + sym(1).Node = node; +} break; +./ + +ConditionalExpressionNotIn: LogicalORExpressionNotIn ; + +ConditionalExpressionNotIn: LogicalORExpressionNotIn T_QUESTION AssignmentExpressionNotIn T_COLON AssignmentExpressionNotIn ; +/. +case $rule_number: { + AST::ConditionalExpression *node = makeAstNode<AST::ConditionalExpression> (driver->nodePool(), sym(1).Expression, + sym(3).Expression, sym(5).Expression); + node->questionToken = loc(2); + node->colonToken = loc(4); + sym(1).Node = node; +} break; +./ + +AssignmentExpression: ConditionalExpression ; + +AssignmentExpression: LeftHandSideExpression AssignmentOperator AssignmentExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + sym(2).ival, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +AssignmentExpressionNotIn: ConditionalExpressionNotIn ; + +AssignmentExpressionNotIn: LeftHandSideExpression AssignmentOperator AssignmentExpressionNotIn ; +/. +case $rule_number: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + sym(2).ival, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +AssignmentOperator: T_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::Assign; +} break; +./ + +AssignmentOperator: T_STAR_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceMul; +} break; +./ + +AssignmentOperator: T_DIVIDE_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceDiv; +} break; +./ + +AssignmentOperator: T_REMAINDER_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceMod; +} break; +./ + +AssignmentOperator: T_PLUS_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceAdd; +} break; +./ + +AssignmentOperator: T_MINUS_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceSub; +} break; +./ + +AssignmentOperator: T_LT_LT_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceLeftShift; +} break; +./ + +AssignmentOperator: T_GT_GT_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceRightShift; +} break; +./ + +AssignmentOperator: T_GT_GT_GT_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceURightShift; +} break; +./ + +AssignmentOperator: T_AND_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceAnd; +} break; +./ + +AssignmentOperator: T_XOR_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceXor; +} break; +./ + +AssignmentOperator: T_OR_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceOr; +} break; +./ + +Expression: AssignmentExpression ; + +Expression: Expression T_COMMA AssignmentExpression ; +/. +case $rule_number: { + AST::Expression *node = makeAstNode<AST::Expression> (driver->nodePool(), sym(1).Expression, sym(3).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; +./ + +ExpressionOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +ExpressionOpt: Expression ; + +ExpressionNotIn: AssignmentExpressionNotIn ; + +ExpressionNotIn: ExpressionNotIn T_COMMA AssignmentExpressionNotIn ; +/. +case $rule_number: { + AST::Expression *node = makeAstNode<AST::Expression> (driver->nodePool(), sym(1).Expression, sym(3).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; +./ + +ExpressionNotInOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +ExpressionNotInOpt: ExpressionNotIn ; + +Statement: Block ; +Statement: VariableStatement ; +Statement: EmptyStatement ; +Statement: ExpressionStatement ; +Statement: IfStatement ; +Statement: IterationStatement ; +Statement: ContinueStatement ; +Statement: BreakStatement ; +Statement: ReturnStatement ; +Statement: WithStatement ; +Statement: LabelledStatement ; +Statement: SwitchStatement ; +Statement: ThrowStatement ; +Statement: TryStatement ; +Statement: DebuggerStatement ; + + +Block: T_LBRACE StatementListOpt T_RBRACE ; +/. +case $rule_number: { + AST::Block *node = makeAstNode<AST::Block> (driver->nodePool(), sym(2).StatementList); + node->lbraceToken = loc(1); + node->rbraceToken = loc(3); + sym(1).Node = node; +} break; +./ + +StatementList: Statement ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::StatementList> (driver->nodePool(), sym(1).Statement); +} break; +./ + +StatementList: StatementList Statement ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::StatementList> (driver->nodePool(), sym(1).StatementList, sym(2).Statement); +} break; +./ + +StatementListOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +StatementListOpt: StatementList ; +/. +case $rule_number: { + sym(1).Node = sym(1).StatementList->finish (); +} break; +./ + +VariableStatement: VariableDeclarationKind VariableDeclarationList T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +VariableStatement: VariableDeclarationKind VariableDeclarationList T_SEMICOLON ; +/. +case $rule_number: { + AST::VariableStatement *node = makeAstNode<AST::VariableStatement> (driver->nodePool(), + sym(2).VariableDeclarationList->finish (/*readOnly=*/sym(1).ival == T_CONST)); + node->declarationKindToken = loc(1); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; +./ + +VariableDeclarationKind: T_CONST ; +/. +case $rule_number: { + sym(1).ival = T_CONST; +} break; +./ + +VariableDeclarationKind: T_VAR ; +/. +case $rule_number: { + sym(1).ival = T_VAR; +} break; +./ + +VariableDeclarationList: VariableDeclaration ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::VariableDeclarationList> (driver->nodePool(), sym(1).VariableDeclaration); +} break; +./ + +VariableDeclarationList: VariableDeclarationList T_COMMA VariableDeclaration ; +/. +case $rule_number: { + AST::VariableDeclarationList *node = makeAstNode<AST::VariableDeclarationList> (driver->nodePool(), + sym(1).VariableDeclarationList, sym(3).VariableDeclaration); + node->commaToken = loc(2); + sym(1).Node = node; +} break; +./ + +VariableDeclarationListNotIn: VariableDeclarationNotIn ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::VariableDeclarationList> (driver->nodePool(), sym(1).VariableDeclaration); +} break; +./ + +VariableDeclarationListNotIn: VariableDeclarationListNotIn T_COMMA VariableDeclarationNotIn ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::VariableDeclarationList> (driver->nodePool(), sym(1).VariableDeclarationList, sym(3).VariableDeclaration); +} break; +./ + +VariableDeclaration: JsIdentifier InitialiserOpt ; +/. +case $rule_number: { + AST::VariableDeclaration *node = makeAstNode<AST::VariableDeclaration> (driver->nodePool(), sym(1).sval, sym(2).Expression); + node->identifierToken = loc(1); + sym(1).Node = node; +} break; +./ + +VariableDeclarationNotIn: JsIdentifier InitialiserNotInOpt ; +/. +case $rule_number: { + AST::VariableDeclaration *node = makeAstNode<AST::VariableDeclaration> (driver->nodePool(), sym(1).sval, sym(2).Expression); + node->identifierToken = loc(1); + sym(1).Node = node; +} break; +./ + +Initialiser: T_EQ AssignmentExpression ; +/. +case $rule_number: { + // ### TODO: AST for initializer + sym(1) = sym(2); +} break; +./ + +InitialiserOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +InitialiserOpt: Initialiser ; + +InitialiserNotIn: T_EQ AssignmentExpressionNotIn ; +/. +case $rule_number: { + // ### TODO: AST for initializer + sym(1) = sym(2); +} break; +./ + +InitialiserNotInOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +InitialiserNotInOpt: InitialiserNotIn ; + +EmptyStatement: T_SEMICOLON ; +/. +case $rule_number: { + AST::EmptyStatement *node = makeAstNode<AST::EmptyStatement> (driver->nodePool()); + node->semicolonToken = loc(1); + sym(1).Node = node; +} break; +./ + +ExpressionStatement: Expression T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +ExpressionStatement: Expression T_SEMICOLON ; +/. +case $rule_number: { + AST::ExpressionStatement *node = makeAstNode<AST::ExpressionStatement> (driver->nodePool(), sym(1).Expression); + node->semicolonToken = loc(2); + sym(1).Node = node; +} break; +./ + +IfStatement: T_IF T_LPAREN Expression T_RPAREN Statement T_ELSE Statement ; +/. +case $rule_number: { + AST::IfStatement *node = makeAstNode<AST::IfStatement> (driver->nodePool(), sym(3).Expression, sym(5).Statement, sym(7).Statement); + node->ifToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + node->elseToken = loc(5); + sym(1).Node = node; +} break; +./ + +IfStatement: T_IF T_LPAREN Expression T_RPAREN Statement ; +/. +case $rule_number: { + AST::IfStatement *node = makeAstNode<AST::IfStatement> (driver->nodePool(), sym(3).Expression, sym(5).Statement); + node->ifToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; +./ + + +IterationStatement: T_DO Statement T_WHILE T_LPAREN Expression T_RPAREN T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +IterationStatement: T_DO Statement T_WHILE T_LPAREN Expression T_RPAREN T_SEMICOLON ; +/. +case $rule_number: { + AST::DoWhileStatement *node = makeAstNode<AST::DoWhileStatement> (driver->nodePool(), sym(2).Statement, sym(5).Expression); + node->doToken = loc(1); + node->whileToken = loc(3); + node->lparenToken = loc(4); + node->rparenToken = loc(6); + node->semicolonToken = loc(7); + sym(1).Node = node; +} break; +./ + +IterationStatement: T_WHILE T_LPAREN Expression T_RPAREN Statement ; +/. +case $rule_number: { + AST::WhileStatement *node = makeAstNode<AST::WhileStatement> (driver->nodePool(), sym(3).Expression, sym(5).Statement); + node->whileToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; +./ + +IterationStatement: T_FOR T_LPAREN ExpressionNotInOpt T_SEMICOLON ExpressionOpt T_SEMICOLON ExpressionOpt T_RPAREN Statement ; +/. +case $rule_number: { + AST::ForStatement *node = makeAstNode<AST::ForStatement> (driver->nodePool(), sym(3).Expression, + sym(5).Expression, sym(7).Expression, sym(9).Statement); + node->forToken = loc(1); + node->lparenToken = loc(2); + node->firstSemicolonToken = loc(4); + node->secondSemicolonToken = loc(6); + node->rparenToken = loc(8); + sym(1).Node = node; +} break; +./ + +IterationStatement: T_FOR T_LPAREN T_VAR VariableDeclarationListNotIn T_SEMICOLON ExpressionOpt T_SEMICOLON ExpressionOpt T_RPAREN Statement ; +/. +case $rule_number: { + AST::LocalForStatement *node = makeAstNode<AST::LocalForStatement> (driver->nodePool(), + sym(4).VariableDeclarationList->finish (/*readOnly=*/false), sym(6).Expression, + sym(8).Expression, sym(10).Statement); + node->forToken = loc(1); + node->lparenToken = loc(2); + node->varToken = loc(3); + node->firstSemicolonToken = loc(5); + node->secondSemicolonToken = loc(7); + node->rparenToken = loc(9); + sym(1).Node = node; +} break; +./ + +IterationStatement: T_FOR T_LPAREN LeftHandSideExpression T_IN Expression T_RPAREN Statement ; +/. +case $rule_number: { + AST:: ForEachStatement *node = makeAstNode<AST::ForEachStatement> (driver->nodePool(), sym(3).Expression, + sym(5).Expression, sym(7).Statement); + node->forToken = loc(1); + node->lparenToken = loc(2); + node->inToken = loc(4); + node->rparenToken = loc(6); + sym(1).Node = node; +} break; +./ + +IterationStatement: T_FOR T_LPAREN T_VAR VariableDeclarationNotIn T_IN Expression T_RPAREN Statement ; +/. +case $rule_number: { + AST::LocalForEachStatement *node = makeAstNode<AST::LocalForEachStatement> (driver->nodePool(), + sym(4).VariableDeclaration, sym(6).Expression, sym(8).Statement); + node->forToken = loc(1); + node->lparenToken = loc(2); + node->varToken = loc(3); + node->inToken = loc(5); + node->rparenToken = loc(7); + sym(1).Node = node; +} break; +./ + +ContinueStatement: T_CONTINUE T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +ContinueStatement: T_CONTINUE T_SEMICOLON ; +/. +case $rule_number: { + AST::ContinueStatement *node = makeAstNode<AST::ContinueStatement> (driver->nodePool()); + node->continueToken = loc(1); + node->semicolonToken = loc(2); + sym(1).Node = node; +} break; +./ + +ContinueStatement: T_CONTINUE JsIdentifier T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +ContinueStatement: T_CONTINUE JsIdentifier T_SEMICOLON ; +/. +case $rule_number: { + AST::ContinueStatement *node = makeAstNode<AST::ContinueStatement> (driver->nodePool(), sym(2).sval); + node->continueToken = loc(1); + node->identifierToken = loc(2); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; +./ + +BreakStatement: T_BREAK T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +BreakStatement: T_BREAK T_SEMICOLON ; +/. +case $rule_number: { + AST::BreakStatement *node = makeAstNode<AST::BreakStatement> (driver->nodePool()); + node->breakToken = loc(1); + node->semicolonToken = loc(2); + sym(1).Node = node; +} break; +./ + +BreakStatement: T_BREAK JsIdentifier T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +BreakStatement: T_BREAK JsIdentifier T_SEMICOLON ; +/. +case $rule_number: { + AST::BreakStatement *node = makeAstNode<AST::BreakStatement> (driver->nodePool(), sym(2).sval); + node->breakToken = loc(1); + node->identifierToken = loc(2); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; +./ + +ReturnStatement: T_RETURN ExpressionOpt T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +ReturnStatement: T_RETURN ExpressionOpt T_SEMICOLON ; +/. +case $rule_number: { + AST::ReturnStatement *node = makeAstNode<AST::ReturnStatement> (driver->nodePool(), sym(2).Expression); + node->returnToken = loc(1); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; +./ + +WithStatement: T_WITH T_LPAREN Expression T_RPAREN Statement ; +/. +case $rule_number: { + AST::WithStatement *node = makeAstNode<AST::WithStatement> (driver->nodePool(), sym(3).Expression, sym(5).Statement); + node->withToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; +./ + +SwitchStatement: T_SWITCH T_LPAREN Expression T_RPAREN CaseBlock ; +/. +case $rule_number: { + AST::SwitchStatement *node = makeAstNode<AST::SwitchStatement> (driver->nodePool(), sym(3).Expression, sym(5).CaseBlock); + node->switchToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; +./ + +CaseBlock: T_LBRACE CaseClausesOpt T_RBRACE ; +/. +case $rule_number: { + AST::CaseBlock *node = makeAstNode<AST::CaseBlock> (driver->nodePool(), sym(2).CaseClauses); + node->lbraceToken = loc(1); + node->rbraceToken = loc(3); + sym(1).Node = node; +} break; +./ + +CaseBlock: T_LBRACE CaseClausesOpt DefaultClause CaseClausesOpt T_RBRACE ; +/. +case $rule_number: { + AST::CaseBlock *node = makeAstNode<AST::CaseBlock> (driver->nodePool(), sym(2).CaseClauses, sym(3).DefaultClause, sym(4).CaseClauses); + node->lbraceToken = loc(1); + node->rbraceToken = loc(5); + sym(1).Node = node; +} break; +./ + +CaseClauses: CaseClause ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::CaseClauses> (driver->nodePool(), sym(1).CaseClause); +} break; +./ + +CaseClauses: CaseClauses CaseClause ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::CaseClauses> (driver->nodePool(), sym(1).CaseClauses, sym(2).CaseClause); +} break; +./ + +CaseClausesOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +CaseClausesOpt: CaseClauses ; +/. +case $rule_number: { + sym(1).Node = sym(1).CaseClauses->finish (); +} break; +./ + +CaseClause: T_CASE Expression T_COLON StatementListOpt ; +/. +case $rule_number: { + AST::CaseClause *node = makeAstNode<AST::CaseClause> (driver->nodePool(), sym(2).Expression, sym(4).StatementList); + node->caseToken = loc(1); + node->colonToken = loc(3); + sym(1).Node = node; +} break; +./ + +DefaultClause: T_DEFAULT T_COLON StatementListOpt ; +/. +case $rule_number: { + AST::DefaultClause *node = makeAstNode<AST::DefaultClause> (driver->nodePool(), sym(3).StatementList); + node->defaultToken = loc(1); + node->colonToken = loc(2); + sym(1).Node = node; +} break; +./ + +LabelledStatement: T_SIGNAL T_COLON Statement ; +/.case $rule_number:./ + +LabelledStatement: T_PROPERTY T_COLON Statement ; +/. +case $rule_number: { + AST::LabelledStatement *node = makeAstNode<AST::LabelledStatement> (driver->nodePool(), driver->intern(lexer->characterBuffer(), lexer->characterCount()), sym(3).Statement); + node->identifierToken = loc(1); + node->colonToken = loc(2); + sym(1).Node = node; +} break; +./ + +LabelledStatement: T_IDENTIFIER T_COLON Statement ; +/. +case $rule_number: { + AST::LabelledStatement *node = makeAstNode<AST::LabelledStatement> (driver->nodePool(), sym(1).sval, sym(3).Statement); + node->identifierToken = loc(1); + node->colonToken = loc(2); + sym(1).Node = node; +} break; +./ + +ThrowStatement: T_THROW Expression T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +ThrowStatement: T_THROW Expression T_SEMICOLON ; +/. +case $rule_number: { + AST::ThrowStatement *node = makeAstNode<AST::ThrowStatement> (driver->nodePool(), sym(2).Expression); + node->throwToken = loc(1); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; +./ + +TryStatement: T_TRY Block Catch ; +/. +case $rule_number: { + AST::TryStatement *node = makeAstNode<AST::TryStatement> (driver->nodePool(), sym(2).Statement, sym(3).Catch); + node->tryToken = loc(1); + sym(1).Node = node; +} break; +./ + +TryStatement: T_TRY Block Finally ; +/. +case $rule_number: { + AST::TryStatement *node = makeAstNode<AST::TryStatement> (driver->nodePool(), sym(2).Statement, sym(3).Finally); + node->tryToken = loc(1); + sym(1).Node = node; +} break; +./ + +TryStatement: T_TRY Block Catch Finally ; +/. +case $rule_number: { + AST::TryStatement *node = makeAstNode<AST::TryStatement> (driver->nodePool(), sym(2).Statement, sym(3).Catch, sym(4).Finally); + node->tryToken = loc(1); + sym(1).Node = node; +} break; +./ + +Catch: T_CATCH T_LPAREN JsIdentifier T_RPAREN Block ; +/. +case $rule_number: { + AST::Catch *node = makeAstNode<AST::Catch> (driver->nodePool(), sym(3).sval, sym(5).Block); + node->catchToken = loc(1); + node->lparenToken = loc(2); + node->identifierToken = loc(3); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; +./ + +Finally: T_FINALLY Block ; +/. +case $rule_number: { + AST::Finally *node = makeAstNode<AST::Finally> (driver->nodePool(), sym(2).Block); + node->finallyToken = loc(1); + sym(1).Node = node; +} break; +./ + +DebuggerStatement: T_DEBUGGER T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +DebuggerStatement: T_DEBUGGER T_SEMICOLON ; +/. +case $rule_number: { + AST::DebuggerStatement *node = makeAstNode<AST::DebuggerStatement> (driver->nodePool()); + node->debuggerToken = loc(1); + node->semicolonToken = loc(2); + sym(1).Node = node; +} break; +./ + +FunctionDeclaration: T_FUNCTION JsIdentifier T_LPAREN FormalParameterListOpt T_RPAREN T_LBRACE FunctionBodyOpt T_RBRACE ; +/. +case $rule_number: { + AST::FunctionDeclaration *node = makeAstNode<AST::FunctionDeclaration> (driver->nodePool(), sym(2).sval, sym(4).FormalParameterList, sym(7).FunctionBody); + node->functionToken = loc(1); + node->identifierToken = loc(2); + node->lparenToken = loc(3); + node->rparenToken = loc(5); + node->lbraceToken = loc(6); + node->rbraceToken = loc(8); + sym(1).Node = node; +} break; +./ + +FunctionExpression: T_FUNCTION IdentifierOpt T_LPAREN FormalParameterListOpt T_RPAREN T_LBRACE FunctionBodyOpt T_RBRACE ; +/. +case $rule_number: { + AST::FunctionExpression *node = makeAstNode<AST::FunctionExpression> (driver->nodePool(), sym(2).sval, sym(4).FormalParameterList, sym(7).FunctionBody); + node->functionToken = loc(1); + if (sym(2).sval) + node->identifierToken = loc(2); + node->lparenToken = loc(3); + node->rparenToken = loc(5); + node->lbraceToken = loc(6); + node->rbraceToken = loc(8); + sym(1).Node = node; +} break; +./ + +FormalParameterList: JsIdentifier ; +/. +case $rule_number: { + AST::FormalParameterList *node = makeAstNode<AST::FormalParameterList> (driver->nodePool(), sym(1).sval); + node->identifierToken = loc(1); + sym(1).Node = node; +} break; +./ + +FormalParameterList: FormalParameterList T_COMMA JsIdentifier ; +/. +case $rule_number: { + AST::FormalParameterList *node = makeAstNode<AST::FormalParameterList> (driver->nodePool(), sym(1).FormalParameterList, sym(3).sval); + node->commaToken = loc(2); + node->identifierToken = loc(3); + sym(1).Node = node; +} break; +./ + +FormalParameterListOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +FormalParameterListOpt: FormalParameterList ; +/. +case $rule_number: { + sym(1).Node = sym(1).FormalParameterList->finish (); +} break; +./ + +FunctionBodyOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +FunctionBodyOpt: FunctionBody ; + +FunctionBody: SourceElements ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::FunctionBody> (driver->nodePool(), sym(1).SourceElements->finish ()); +} break; +./ + +--QmlJSProgram: SourceElements ; +--/. +--case $rule_number: { +-- sym(1).Node = makeAstNode<AST::Program> (driver->nodePool(), sym(1).SourceElements->finish ()); +-- driver->changeAbstractSyntaxTree(sym(1).Node); +--} break; +--./ + +SourceElements: SourceElement ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::SourceElements> (driver->nodePool(), sym(1).SourceElement); +} break; +./ + +SourceElements: SourceElements SourceElement ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::SourceElements> (driver->nodePool(), sym(1).SourceElements, sym(2).SourceElement); +} break; +./ + +SourceElement: Statement ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::StatementSourceElement> (driver->nodePool(), sym(1).Statement); +} break; +./ + +SourceElement: FunctionDeclaration ; +/. +case $rule_number: { + sym(1).Node = makeAstNode<AST::FunctionSourceElement> (driver->nodePool(), sym(1).FunctionDeclaration); +} break; +./ + +IdentifierOpt: ; +/. +case $rule_number: { + sym(1).sval = 0; +} break; +./ + +IdentifierOpt: JsIdentifier ; + +PropertyNameAndValueListOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +PropertyNameAndValueListOpt: PropertyNameAndValueList ; + +/. + } // switch + action = nt_action(state_stack[tos], lhs[r] - TERMINAL_COUNT); + } // if + } while (action != 0); + + if (first_token == last_token) { + const int errorState = state_stack[tos]; + + // automatic insertion of `;' + if (t_action(errorState, T_AUTOMATIC_SEMICOLON) && automatic(driver, yytoken)) { + SavedToken &tk = token_buffer[0]; + tk.token = yytoken; + tk.dval = yylval; + tk.loc = yylloc; + + yylloc = yyprevlloc; + yylloc.offset += yylloc.length; + yylloc.startColumn += yylloc.length; + yylloc.length = 0; + + //const QString msg = QString::fromUtf8("Missing `;'"); + //diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Warning, yylloc, msg)); + + first_token = &token_buffer[0]; + last_token = &token_buffer[1]; + + yytoken = T_SEMICOLON; + yylval = 0; + + action = errorState; + + goto _Lcheck_token; + } + + hadErrors = true; + + token_buffer[0].token = yytoken; + token_buffer[0].dval = yylval; + token_buffer[0].loc = yylloc; + + token_buffer[1].token = yytoken = lexer->lex(); + token_buffer[1].dval = yylval = lexer->dval(); + token_buffer[1].loc = yylloc = location(lexer); + + if (t_action(errorState, yytoken)) { + const QString msg = QString::fromUtf8("Unexpected token `%1'").arg(QLatin1String(spell[token_buffer[0].token])); + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); + + action = errorState; + goto _Lcheck_token; + } + + static int tokens[] = { + T_PLUS, + T_EQ, + + T_COMMA, + T_COLON, + T_SEMICOLON, + + T_RPAREN, T_RBRACKET, T_RBRACE, + + T_NUMERIC_LITERAL, + T_IDENTIFIER, + + T_LPAREN, T_LBRACKET, T_LBRACE, + + EOF_SYMBOL + }; + + for (int *tk = tokens; *tk != EOF_SYMBOL; ++tk) { + int a = t_action(errorState, *tk); + if (a > 0 && t_action(a, yytoken)) { + const QString msg = QString::fromUtf8("Expected token `%1'").arg(QLatin1String(spell[*tk])); + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); + + yytoken = *tk; + yylval = 0; + yylloc = token_buffer[0].loc; + yylloc.length = 0; + + first_token = &token_buffer[0]; + last_token = &token_buffer[2]; + + action = errorState; + goto _Lcheck_token; + } + } + + for (int tk = 1; tk < TERMINAL_COUNT; ++tk) { + if (tk == T_AUTOMATIC_SEMICOLON || tk == T_FEED_UI_PROGRAM || + tk == T_FEED_JS_STATEMENT || tk == T_FEED_JS_EXPRESSION) + continue; + + int a = t_action(errorState, tk); + if (a > 0 && t_action(a, yytoken)) { + const QString msg = QString::fromUtf8("Expected token `%1'").arg(QLatin1String(spell[tk])); + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); + + yytoken = tk; + yylval = 0; + yylloc = token_buffer[0].loc; + yylloc.length = 0; + + action = errorState; + goto _Lcheck_token; + } + } + + const QString msg = QString::fromUtf8("Syntax error"); + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); + } + + return false; +} + +QT_QML_END_NAMESPACE + + +./ +/: +QT_QML_END_NAMESPACE + + + +#endif // QMLJSPARSER_P_H +:/ diff --git a/src/plugins/qmldesigner/core/filemanager/parser/qmljsast.cpp b/src/plugins/qmldesigner/core/filemanager/parser/qmljsast.cpp new file mode 100644 index 00000000000..ec199e80ae7 --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/parser/qmljsast.cpp @@ -0,0 +1,943 @@ +/**************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "qmljsast_p.h" +#include "qmljsastvisitor_p.h" + +QT_QML_BEGIN_NAMESPACE + +namespace QmlJS { namespace AST { + +void Node::accept(Visitor *visitor) +{ + if (visitor->preVisit(this)) { + accept0(visitor); + } + visitor->postVisit(this); +} + +void Node::accept(Node *node, Visitor *visitor) +{ + if (node) + node->accept(visitor); +} + +ExpressionNode *Node::expressionCast() +{ + return 0; +} + +BinaryExpression *Node::binaryExpressionCast() +{ + return 0; +} + +Statement *Node::statementCast() +{ + return 0; +} + +UiObjectMember *Node::uiObjectMemberCast() +{ + return 0; +} + +ExpressionNode *ExpressionNode::expressionCast() +{ + return this; +} + +BinaryExpression *BinaryExpression::binaryExpressionCast() +{ + return this; +} + +Statement *Statement::statementCast() +{ + return this; +} + +UiObjectMember *UiObjectMember::uiObjectMemberCast() +{ + return this; +} + +void NestedExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + visitor->endVisit(this); +} + +void ThisExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void IdentifierExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void NullExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void TrueLiteral::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void FalseLiteral::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void StringLiteral::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void NumericLiteral::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void RegExpLiteral::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void ArrayLiteral::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(elements, visitor); + accept(elision, visitor); + } + + visitor->endVisit(this); +} + +void ObjectLiteral::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(properties, visitor); + } + + visitor->endVisit(this); +} + +void ElementList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + for (ElementList *it = this; it; it = it->next) { + accept(it->elision, visitor); + accept(it->expression, visitor); + } + } + + visitor->endVisit(this); +} + +void Elision::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + // ### + } + + visitor->endVisit(this); +} + +void PropertyNameAndValueList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + for (PropertyNameAndValueList *it = this; it; it = it->next) { + accept(it->name, visitor); + accept(it->value, visitor); + } + } + + visitor->endVisit(this); +} + +void IdentifierPropertyName::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void StringLiteralPropertyName::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void NumericLiteralPropertyName::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void ArrayMemberExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(base, visitor); + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void FieldMemberExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(base, visitor); + } + + visitor->endVisit(this); +} + +void NewMemberExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(base, visitor); + accept(arguments, visitor); + } + + visitor->endVisit(this); +} + +void NewExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void CallExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(base, visitor); + accept(arguments, visitor); + } + + visitor->endVisit(this); +} + +void ArgumentList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + for (ArgumentList *it = this; it; it = it->next) { + accept(it->expression, visitor); + } + } + + visitor->endVisit(this); +} + +void PostIncrementExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(base, visitor); + } + + visitor->endVisit(this); +} + +void PostDecrementExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(base, visitor); + } + + visitor->endVisit(this); +} + +void DeleteExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void VoidExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void TypeOfExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void PreIncrementExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void PreDecrementExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void UnaryPlusExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void UnaryMinusExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void TildeExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void NotExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void BinaryExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(left, visitor); + accept(right, visitor); + } + + visitor->endVisit(this); +} + +void ConditionalExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + accept(ok, visitor); + accept(ko, visitor); + } + + visitor->endVisit(this); +} + +void Expression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(left, visitor); + accept(right, visitor); + } + + visitor->endVisit(this); +} + +void Block::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(statements, visitor); + } + + visitor->endVisit(this); +} + +void StatementList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + for (StatementList *it = this; it; it = it->next) { + accept(it->statement, visitor); + } + } + + visitor->endVisit(this); +} + +void VariableStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(declarations, visitor); + } + + visitor->endVisit(this); +} + +void VariableDeclarationList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + for (VariableDeclarationList *it = this; it; it = it->next) { + accept(it->declaration, visitor); + } + } + + visitor->endVisit(this); +} + +void VariableDeclaration::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void EmptyStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void ExpressionStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void IfStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + accept(ok, visitor); + accept(ko, visitor); + } + + visitor->endVisit(this); +} + +void DoWhileStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(statement, visitor); + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void WhileStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void ForStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(initialiser, visitor); + accept(condition, visitor); + accept(expression, visitor); + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void LocalForStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(declarations, visitor); + accept(condition, visitor); + accept(expression, visitor); + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void ForEachStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(initialiser, visitor); + accept(expression, visitor); + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void LocalForEachStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(declaration, visitor); + accept(expression, visitor); + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void ContinueStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void BreakStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void ReturnStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void WithStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void SwitchStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + accept(block, visitor); + } + + visitor->endVisit(this); +} + +void CaseBlock::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(clauses, visitor); + accept(defaultClause, visitor); + accept(moreClauses, visitor); + } + + visitor->endVisit(this); +} + +void CaseClauses::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + for (CaseClauses *it = this; it; it = it->next) { + accept(it->clause, visitor); + } + } + + visitor->endVisit(this); +} + +void CaseClause::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + accept(statements, visitor); + } + + visitor->endVisit(this); +} + +void DefaultClause::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(statements, visitor); + } + + visitor->endVisit(this); +} + +void LabelledStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void ThrowStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void TryStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(statement, visitor); + accept(catchExpression, visitor); + accept(finallyExpression, visitor); + } + + visitor->endVisit(this); +} + +void Catch::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void Finally::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void FunctionDeclaration::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(formals, visitor); + accept(body, visitor); + } + + visitor->endVisit(this); +} + +void FunctionExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(formals, visitor); + accept(body, visitor); + } + + visitor->endVisit(this); +} + +void FormalParameterList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + // ### + } + + visitor->endVisit(this); +} + +void FunctionBody::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(elements, visitor); + } + + visitor->endVisit(this); +} + +void Program::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(elements, visitor); + } + + visitor->endVisit(this); +} + +void SourceElements::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + for (SourceElements *it = this; it; it = it->next) { + accept(it->element, visitor); + } + } + + visitor->endVisit(this); +} + +void FunctionSourceElement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(declaration, visitor); + } + + visitor->endVisit(this); +} + +void StatementSourceElement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void DebuggerStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + + +void UiProgram::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(imports, visitor); + accept(members, visitor); + } + + visitor->endVisit(this); +} + +void UiSignature::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(formals, visitor); + } + visitor->endVisit(this); +} + +void UiFormalList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + for (UiFormalList *it = this; it; it = it->next) { + accept(it->formal, visitor); + } + } + visitor->endVisit(this); +} + +void UiFormal::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + visitor->endVisit(this); +} + +void UiPublicMember::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void UiObjectDefinition::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(qualifiedTypeNameId, visitor); + accept(initializer, visitor); + } + + visitor->endVisit(this); +} + +void UiObjectInitializer::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(members, visitor); + } + + visitor->endVisit(this); +} + +void UiObjectBinding::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(qualifiedId, visitor); + accept(qualifiedTypeNameId, visitor); + accept(initializer, visitor); + } + + visitor->endVisit(this); +} + +void UiScriptBinding::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(qualifiedId, visitor); + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void UiArrayBinding::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(qualifiedId, visitor); + accept(members, visitor); + } + + visitor->endVisit(this); +} + +void UiObjectMemberList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + for (UiObjectMemberList *it = this; it; it = it->next) + accept(it->member, visitor); + } + + visitor->endVisit(this); +} + +void UiArrayMemberList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + for (UiArrayMemberList *it = this; it; it = it->next) + accept(it->member, visitor); + } + + visitor->endVisit(this); +} + +void UiQualifiedId::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void UiImport::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(importUri, visitor); + } + + visitor->endVisit(this); +} + +void UiImportList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(import, visitor); + accept(next, visitor); + } + + visitor->endVisit(this); +} + +void UiSourceElement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(sourceElement, visitor); + } + + visitor->endVisit(this); +} + +} } // namespace QmlJS::AST + +QT_QML_END_NAMESPACE + + diff --git a/src/plugins/qmldesigner/core/filemanager/parser/qmljsast_p.h b/src/plugins/qmldesigner/core/filemanager/parser/qmljsast_p.h new file mode 100644 index 00000000000..3ad1f0de820 --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/parser/qmljsast_p.h @@ -0,0 +1,2665 @@ +/**************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef QMLJSAST_P_H +#define QMLJSAST_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qmljsastvisitor_p.h" +#include "qmljsglobal_p.h" +#include <QtCore/QString> + +QT_QML_BEGIN_NAMESPACE + +#define QMLJS_DECLARE_AST_NODE(name) \ + enum { K = Kind_##name }; + +namespace QSOperator // ### rename +{ + +enum Op { + Add, + And, + InplaceAnd, + Assign, + BitAnd, + BitOr, + BitXor, + InplaceSub, + Div, + InplaceDiv, + Equal, + Ge, + Gt, + In, + InplaceAdd, + InstanceOf, + Le, + LShift, + InplaceLeftShift, + Lt, + Mod, + InplaceMod, + Mul, + InplaceMul, + NotEqual, + Or, + InplaceOr, + RShift, + InplaceRightShift, + StrictEqual, + StrictNotEqual, + Sub, + URShift, + InplaceURightShift, + InplaceXor +}; + +} // namespace QSOperator + +namespace QmlJS { +class NameId; +namespace AST { + +template <typename _T1, typename _T2> +_T1 cast(_T2 *ast) +{ + if (ast && ast->kind == static_cast<_T1>(0)->K) + return static_cast<_T1>(ast); + + return 0; +} + +class Node +{ +public: + enum Kind { + Kind_Undefined, + + Kind_ArgumentList, + Kind_ArrayLiteral, + Kind_ArrayMemberExpression, + Kind_BinaryExpression, + Kind_Block, + Kind_BreakStatement, + Kind_CallExpression, + Kind_CaseBlock, + Kind_CaseClause, + Kind_CaseClauses, + Kind_Catch, + Kind_ConditionalExpression, + Kind_ContinueStatement, + Kind_DebuggerStatement, + Kind_DefaultClause, + Kind_DeleteExpression, + Kind_DoWhileStatement, + Kind_ElementList, + Kind_Elision, + Kind_EmptyStatement, + Kind_Expression, + Kind_ExpressionStatement, + Kind_FalseLiteral, + Kind_FieldMemberExpression, + Kind_Finally, + Kind_ForEachStatement, + Kind_ForStatement, + Kind_FormalParameterList, + Kind_FunctionBody, + Kind_FunctionDeclaration, + Kind_FunctionExpression, + Kind_FunctionSourceElement, + Kind_IdentifierExpression, + Kind_IdentifierPropertyName, + Kind_IfStatement, + Kind_LabelledStatement, + Kind_LocalForEachStatement, + Kind_LocalForStatement, + Kind_NewExpression, + Kind_NewMemberExpression, + Kind_NotExpression, + Kind_NullExpression, + Kind_NumericLiteral, + Kind_NumericLiteralPropertyName, + Kind_ObjectLiteral, + Kind_PostDecrementExpression, + Kind_PostIncrementExpression, + Kind_PreDecrementExpression, + Kind_PreIncrementExpression, + Kind_Program, + Kind_PropertyName, + Kind_PropertyNameAndValueList, + Kind_RegExpLiteral, + Kind_ReturnStatement, + Kind_SourceElement, + Kind_SourceElements, + Kind_StatementList, + Kind_StatementSourceElement, + Kind_StringLiteral, + Kind_StringLiteralPropertyName, + Kind_SwitchStatement, + Kind_ThisExpression, + Kind_ThrowStatement, + Kind_TildeExpression, + Kind_TrueLiteral, + Kind_TryStatement, + Kind_TypeOfExpression, + Kind_UnaryMinusExpression, + Kind_UnaryPlusExpression, + Kind_VariableDeclaration, + Kind_VariableDeclarationList, + Kind_VariableStatement, + Kind_VoidExpression, + Kind_WhileStatement, + Kind_WithStatement, + Kind_NestedExpression, + + Kind_UiArrayBinding, + Kind_UiImport, + Kind_UiImportList, + Kind_UiObjectBinding, + Kind_UiObjectDefinition, + Kind_UiObjectInitializer, + Kind_UiObjectMemberList, + Kind_UiArrayMemberList, + Kind_UiProgram, + Kind_UiParameterList, + Kind_UiPublicMember, + Kind_UiQualifiedId, + Kind_UiScriptBinding, + Kind_UiSourceElement, + Kind_UiFormal, + Kind_UiFormalList, + Kind_UiSignature + }; + + inline Node() + : kind(Kind_Undefined) {} + + virtual ~Node() {} + + virtual ExpressionNode *expressionCast(); + virtual BinaryExpression *binaryExpressionCast(); + virtual Statement *statementCast(); + virtual UiObjectMember *uiObjectMemberCast(); + + void accept(Visitor *visitor); + static void accept(Node *node, Visitor *visitor); + + inline static void acceptChild(Node *node, Visitor *visitor) + { return accept(node, visitor); } // ### remove + + virtual void accept0(Visitor *visitor) = 0; + +// attributes + int kind; +}; + +class ExpressionNode: public Node +{ +public: + ExpressionNode() {} + virtual ~ExpressionNode() {} + + virtual ExpressionNode *expressionCast(); + + virtual SourceLocation firstSourceLocation() const = 0; + virtual SourceLocation lastSourceLocation() const = 0; +}; + +class Statement: public Node +{ +public: + Statement() {} + virtual ~Statement() {} + + virtual Statement *statementCast(); + + virtual SourceLocation firstSourceLocation() const = 0; + virtual SourceLocation lastSourceLocation() const = 0; +}; + +class UiFormal: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(UiFormal) + + UiFormal(NameId *name, NameId *alias = 0) + : name(name), alias(alias) + { } + + virtual SourceLocation firstSourceLocation() const + { return SourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return SourceLocation(); } + + virtual void accept0(Visitor *visitor); + +// attributes + NameId *name; + NameId *alias; + SourceLocation identifierToken; + SourceLocation asToken; + SourceLocation aliasToken; +}; + +class UiFormalList: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(UiFormalList) + + UiFormalList(UiFormal *formal) + : formal(formal), next(this) {} + + UiFormalList(UiFormalList *previous, UiFormal *formal) + : formal(formal) + { + next = previous->next; + previous->next = this; + } + + UiFormalList *finish() + { + UiFormalList *head = next; + next = 0; + return head; + } + + virtual SourceLocation firstSourceLocation() const + { return SourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return SourceLocation(); } + + virtual void accept0(Visitor *visitor); + +// attributes + UiFormal *formal; + UiFormalList *next; +}; + +class UiSignature: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(UiSignature) + + UiSignature(UiFormalList *formals = 0) + : formals(formals) + { } + + virtual SourceLocation firstSourceLocation() const + { return SourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return SourceLocation(); } + + virtual void accept0(Visitor *visitor); + +// attributes + SourceLocation lparenToken; + UiFormalList *formals; + SourceLocation rparenToken; +}; + +class NestedExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(NestedExpression) + + NestedExpression(ExpressionNode *expression) + : expression(expression) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return lparenToken; } + + virtual SourceLocation lastSourceLocation() const + { return rparenToken; } + +// attributes + ExpressionNode *expression; + SourceLocation lparenToken; + SourceLocation rparenToken; +}; + +class ThisExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(ThisExpression) + + ThisExpression() { kind = K; } + virtual ~ThisExpression() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return thisToken; } + + virtual SourceLocation lastSourceLocation() const + { return thisToken; } + +// attributes + SourceLocation thisToken; +}; + +class IdentifierExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(IdentifierExpression) + + IdentifierExpression(NameId *n): + name (n) { kind = K; } + + virtual ~IdentifierExpression() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return identifierToken; } + + virtual SourceLocation lastSourceLocation() const + { return identifierToken; } + +// attributes + NameId *name; + SourceLocation identifierToken; +}; + +class NullExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(NullExpression) + + NullExpression() { kind = K; } + virtual ~NullExpression() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return nullToken; } + + virtual SourceLocation lastSourceLocation() const + { return nullToken; } + +// attributes + SourceLocation nullToken; +}; + +class TrueLiteral: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(TrueLiteral) + + TrueLiteral() { kind = K; } + virtual ~TrueLiteral() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return trueToken; } + + virtual SourceLocation lastSourceLocation() const + { return trueToken; } + +// attributes + SourceLocation trueToken; +}; + +class FalseLiteral: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(FalseLiteral) + + FalseLiteral() { kind = K; } + virtual ~FalseLiteral() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return falseToken; } + + virtual SourceLocation lastSourceLocation() const + { return falseToken; } + +// attributes + SourceLocation falseToken; +}; + +class NumericLiteral: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(NumericLiteral) + + NumericLiteral(double v): + value(v) { kind = K; } + virtual ~NumericLiteral() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return literalToken; } + + virtual SourceLocation lastSourceLocation() const + { return literalToken; } + +// attributes: + double value; + SourceLocation literalToken; +}; + +class StringLiteral: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(StringLiteral) + + StringLiteral(NameId *v): + value (v) { kind = K; } + + virtual ~StringLiteral() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return literalToken; } + + virtual SourceLocation lastSourceLocation() const + { return literalToken; } + +// attributes: + NameId *value; + SourceLocation literalToken; +}; + +class RegExpLiteral: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(RegExpLiteral) + + RegExpLiteral(NameId *p, int f): + pattern (p), flags (f) { kind = K; } + + virtual ~RegExpLiteral() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return literalToken; } + + virtual SourceLocation lastSourceLocation() const + { return literalToken; } + +// attributes: + NameId *pattern; + int flags; + SourceLocation literalToken; +}; + +class ArrayLiteral: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(ArrayLiteral) + + ArrayLiteral(Elision *e): + elements (0), elision (e) + { kind = K; } + + ArrayLiteral(ElementList *elts): + elements (elts), elision (0) + { kind = K; } + + ArrayLiteral(ElementList *elts, Elision *e): + elements (elts), elision (e) + { kind = K; } + + virtual ~ArrayLiteral() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return lbracketToken; } + + virtual SourceLocation lastSourceLocation() const + { return rbracketToken; } + +// attributes + ElementList *elements; + Elision *elision; + SourceLocation lbracketToken; + SourceLocation commaToken; + SourceLocation rbracketToken; +}; + +class ObjectLiteral: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(ObjectLiteral) + + ObjectLiteral(): + properties (0) { kind = K; } + + ObjectLiteral(PropertyNameAndValueList *plist): + properties (plist) { kind = K; } + + virtual ~ObjectLiteral() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return lbraceToken; } + + virtual SourceLocation lastSourceLocation() const + { return rbraceToken; } + +// attributes + PropertyNameAndValueList *properties; + SourceLocation lbraceToken; + SourceLocation rbraceToken; +}; + +class ElementList: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(ElementList) + + ElementList(Elision *e, ExpressionNode *expr): + elision (e), expression (expr), next (this) + { kind = K; } + + ElementList(ElementList *previous, Elision *e, ExpressionNode *expr): + elision (e), expression (expr) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual ~ElementList() {} + + inline ElementList *finish () + { + ElementList *front = next; + next = 0; + return front; + } + + virtual void accept0(Visitor *visitor); + +// attributes + Elision *elision; + ExpressionNode *expression; + ElementList *next; + SourceLocation commaToken; +}; + +class Elision: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(Elision) + + Elision(): + next (this) { kind = K; } + + Elision(Elision *previous) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual ~Elision() {} + + virtual void accept0(Visitor *visitor); + + inline Elision *finish () + { + Elision *front = next; + next = 0; + return front; + } + +// attributes + Elision *next; + SourceLocation commaToken; +}; + +class PropertyNameAndValueList: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(PropertyNameAndValueList) + + PropertyNameAndValueList(PropertyName *n, ExpressionNode *v): + name (n), value (v), next (this) + { kind = K; } + + PropertyNameAndValueList(PropertyNameAndValueList *previous, PropertyName *n, ExpressionNode *v): + name (n), value (v) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual ~PropertyNameAndValueList() {} + + virtual void accept0(Visitor *visitor); + + inline PropertyNameAndValueList *finish () + { + PropertyNameAndValueList *front = next; + next = 0; + return front; + } + +// attributes + PropertyName *name; + ExpressionNode *value; + PropertyNameAndValueList *next; + SourceLocation colonToken; + SourceLocation commaToken; +}; + +class PropertyName: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(PropertyName) + + PropertyName() { kind = K; } + virtual ~PropertyName() {} + +// attributes + SourceLocation propertyNameToken; +}; + +class IdentifierPropertyName: public PropertyName +{ +public: + QMLJS_DECLARE_AST_NODE(IdentifierPropertyName) + + IdentifierPropertyName(NameId *n): + id (n) { kind = K; } + + virtual ~IdentifierPropertyName() {} + + virtual void accept0(Visitor *visitor); + +// attributes + NameId *id; +}; + +class StringLiteralPropertyName: public PropertyName +{ +public: + QMLJS_DECLARE_AST_NODE(StringLiteralPropertyName) + + StringLiteralPropertyName(NameId *n): + id (n) { kind = K; } + virtual ~StringLiteralPropertyName() {} + + virtual void accept0(Visitor *visitor); + +// attributes + NameId *id; +}; + +class NumericLiteralPropertyName: public PropertyName +{ +public: + QMLJS_DECLARE_AST_NODE(NumericLiteralPropertyName) + + NumericLiteralPropertyName(double n): + id (n) { kind = K; } + virtual ~NumericLiteralPropertyName() {} + + virtual void accept0(Visitor *visitor); + +// attributes + double id; +}; + +class ArrayMemberExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(ArrayMemberExpression) + + ArrayMemberExpression(ExpressionNode *b, ExpressionNode *e): + base (b), expression (e) + { kind = K; } + + virtual ~ArrayMemberExpression() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return base->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return rbracketToken; } + +// attributes + ExpressionNode *base; + ExpressionNode *expression; + SourceLocation lbracketToken; + SourceLocation rbracketToken; +}; + +class FieldMemberExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(FieldMemberExpression) + + FieldMemberExpression(ExpressionNode *b, NameId *n): + base (b), name (n) + { kind = K; } + + virtual ~FieldMemberExpression() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return base->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return identifierToken; } + + // attributes + ExpressionNode *base; + NameId *name; + SourceLocation dotToken; + SourceLocation identifierToken; +}; + +class NewMemberExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(NewMemberExpression) + + NewMemberExpression(ExpressionNode *b, ArgumentList *a): + base (b), arguments (a) + { kind = K; } + + virtual ~NewMemberExpression() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return newToken; } + + virtual SourceLocation lastSourceLocation() const + { return rparenToken; } + + // attributes + ExpressionNode *base; + ArgumentList *arguments; + SourceLocation newToken; + SourceLocation lparenToken; + SourceLocation rparenToken; +}; + +class NewExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(NewExpression) + + NewExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual ~NewExpression() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return newToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation newToken; +}; + +class CallExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(CallExpression) + + CallExpression(ExpressionNode *b, ArgumentList *a): + base (b), arguments (a) + { kind = K; } + + virtual ~CallExpression() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return base->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return rparenToken; } + +// attributes + ExpressionNode *base; + ArgumentList *arguments; + SourceLocation lparenToken; + SourceLocation rparenToken; +}; + +class ArgumentList: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(ArgumentList) + + ArgumentList(ExpressionNode *e): + expression (e), next (this) + { kind = K; } + + ArgumentList(ArgumentList *previous, ExpressionNode *e): + expression (e) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual ~ArgumentList() {} + + virtual void accept0(Visitor *visitor); + + inline ArgumentList *finish () + { + ArgumentList *front = next; + next = 0; + return front; + } + +// attributes + ExpressionNode *expression; + ArgumentList *next; + SourceLocation commaToken; +}; + +class PostIncrementExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(PostIncrementExpression) + + PostIncrementExpression(ExpressionNode *b): + base (b) { kind = K; } + + virtual ~PostIncrementExpression() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return base->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return incrementToken; } + +// attributes + ExpressionNode *base; + SourceLocation incrementToken; +}; + +class PostDecrementExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(PostDecrementExpression) + + PostDecrementExpression(ExpressionNode *b): + base (b) { kind = K; } + + virtual ~PostDecrementExpression() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return base->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return decrementToken; } + +// attributes + ExpressionNode *base; + SourceLocation decrementToken; +}; + +class DeleteExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(DeleteExpression) + + DeleteExpression(ExpressionNode *e): + expression (e) { kind = K; } + virtual ~DeleteExpression() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return deleteToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation deleteToken; +}; + +class VoidExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(VoidExpression) + + VoidExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual ~VoidExpression() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return voidToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation voidToken; +}; + +class TypeOfExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(TypeOfExpression) + + TypeOfExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual ~TypeOfExpression() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return typeofToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation typeofToken; +}; + +class PreIncrementExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(PreIncrementExpression) + + PreIncrementExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual ~PreIncrementExpression() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return incrementToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation incrementToken; +}; + +class PreDecrementExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(PreDecrementExpression) + + PreDecrementExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual ~PreDecrementExpression() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return decrementToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation decrementToken; +}; + +class UnaryPlusExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(UnaryPlusExpression) + + UnaryPlusExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual ~UnaryPlusExpression() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return plusToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation plusToken; +}; + +class UnaryMinusExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(UnaryMinusExpression) + + UnaryMinusExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual ~UnaryMinusExpression() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return minusToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation minusToken; +}; + +class TildeExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(TildeExpression) + + TildeExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual ~TildeExpression() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return tildeToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation tildeToken; +}; + +class NotExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(NotExpression) + + NotExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual ~NotExpression() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return notToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation notToken; +}; + +class BinaryExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(BinaryExpression) + + BinaryExpression(ExpressionNode *l, int o, ExpressionNode *r): + left (l), op (o), right (r) + { kind = K; } + + virtual ~BinaryExpression() {} + + virtual BinaryExpression *binaryExpressionCast(); + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return left->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return right->lastSourceLocation(); } + +// attributes + ExpressionNode *left; + int op; + ExpressionNode *right; + SourceLocation operatorToken; +}; + +class ConditionalExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(ConditionalExpression) + + ConditionalExpression(ExpressionNode *e, ExpressionNode *t, ExpressionNode *f): + expression (e), ok (t), ko (f) + { kind = K; } + + virtual ~ConditionalExpression() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return expression->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return ko->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + ExpressionNode *ok; + ExpressionNode *ko; + SourceLocation questionToken; + SourceLocation colonToken; +}; + +class Expression: public ExpressionNode // ### rename +{ +public: + QMLJS_DECLARE_AST_NODE(Expression) + + Expression(ExpressionNode *l, ExpressionNode *r): + left (l), right (r) { kind = K; } + + virtual ~Expression() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return left->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return right->lastSourceLocation(); } + +// attributes + ExpressionNode *left; + ExpressionNode *right; + SourceLocation commaToken; +}; + +class Block: public Statement +{ +public: + QMLJS_DECLARE_AST_NODE(Block) + + Block(StatementList *slist): + statements (slist) { kind = K; } + + virtual ~Block() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return lbraceToken; } + + virtual SourceLocation lastSourceLocation() const + { return rbraceToken; } + + // attributes + StatementList *statements; + SourceLocation lbraceToken; + SourceLocation rbraceToken; +}; + +class StatementList: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(StatementList) + + StatementList(Statement *stmt): + statement (stmt), next (this) + { kind = K; } + + StatementList(StatementList *previous, Statement *stmt): + statement (stmt) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual ~StatementList() {} + + virtual void accept0(Visitor *visitor); + + inline StatementList *finish () + { + StatementList *front = next; + next = 0; + return front; + } + +// attributes + Statement *statement; + StatementList *next; +}; + +class VariableStatement: public Statement +{ +public: + QMLJS_DECLARE_AST_NODE(VariableStatement) + + VariableStatement(VariableDeclarationList *vlist): + declarations (vlist) + { kind = K; } + + virtual ~VariableStatement() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return declarationKindToken; } + + virtual SourceLocation lastSourceLocation() const + { return semicolonToken; } + +// attributes + VariableDeclarationList *declarations; + SourceLocation declarationKindToken; + SourceLocation semicolonToken; +}; + +class VariableDeclaration: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(VariableDeclaration) + + VariableDeclaration(NameId *n, ExpressionNode *e): + name (n), expression (e), readOnly(false) + { kind = K; } + + virtual ~VariableDeclaration() {} + + virtual void accept0(Visitor *visitor); + +// attributes + NameId *name; + ExpressionNode *expression; + bool readOnly; + SourceLocation identifierToken; +}; + +class VariableDeclarationList: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(VariableDeclarationList) + + VariableDeclarationList(VariableDeclaration *decl): + declaration (decl), next (this) + { kind = K; } + + VariableDeclarationList(VariableDeclarationList *previous, VariableDeclaration *decl): + declaration (decl) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual ~VariableDeclarationList() {} + + virtual void accept0(Visitor *visitor); + + inline VariableDeclarationList *finish (bool readOnly) + { + VariableDeclarationList *front = next; + next = 0; + if (readOnly) { + VariableDeclarationList *vdl; + for (vdl = front; vdl != 0; vdl = vdl->next) + vdl->declaration->readOnly = true; + } + return front; + } + +// attributes + VariableDeclaration *declaration; + VariableDeclarationList *next; + SourceLocation commaToken; +}; + +class EmptyStatement: public Statement +{ +public: + QMLJS_DECLARE_AST_NODE(EmptyStatement) + + EmptyStatement() { kind = K; } + virtual ~EmptyStatement() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return semicolonToken; } + + virtual SourceLocation lastSourceLocation() const + { return semicolonToken; } + +// attributes + SourceLocation semicolonToken; +}; + +class ExpressionStatement: public Statement +{ +public: + QMLJS_DECLARE_AST_NODE(ExpressionStatement) + + ExpressionStatement(ExpressionNode *e): + expression (e) { kind = K; } + + virtual ~ExpressionStatement() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return expression->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return semicolonToken; } + +// attributes + ExpressionNode *expression; + SourceLocation semicolonToken; +}; + +class IfStatement: public Statement +{ +public: + QMLJS_DECLARE_AST_NODE(IfStatement) + + IfStatement(ExpressionNode *e, Statement *t, Statement *f = 0): + expression (e), ok (t), ko (f) + { kind = K; } + + virtual ~IfStatement() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return ifToken; } + + virtual SourceLocation lastSourceLocation() const + { + if (ko) + return ko->lastSourceLocation(); + + return ok->lastSourceLocation(); + } + +// attributes + ExpressionNode *expression; + Statement *ok; + Statement *ko; + SourceLocation ifToken; + SourceLocation lparenToken; + SourceLocation rparenToken; + SourceLocation elseToken; +}; + +class DoWhileStatement: public Statement +{ +public: + QMLJS_DECLARE_AST_NODE(DoWhileStatement) + + DoWhileStatement(Statement *stmt, ExpressionNode *e): + statement (stmt), expression (e) + { kind = K; } + + virtual ~DoWhileStatement() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return doToken; } + + virtual SourceLocation lastSourceLocation() const + { return semicolonToken; } + +// attributes + Statement *statement; + ExpressionNode *expression; + SourceLocation doToken; + SourceLocation whileToken; + SourceLocation lparenToken; + SourceLocation rparenToken; + SourceLocation semicolonToken; +}; + +class WhileStatement: public Statement +{ +public: + QMLJS_DECLARE_AST_NODE(WhileStatement) + + WhileStatement(ExpressionNode *e, Statement *stmt): + expression (e), statement (stmt) + { kind = K; } + + virtual ~WhileStatement() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return whileToken; } + + virtual SourceLocation lastSourceLocation() const + { return statement->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + Statement *statement; + SourceLocation whileToken; + SourceLocation lparenToken; + SourceLocation rparenToken; +}; + +class ForStatement: public Statement +{ +public: + QMLJS_DECLARE_AST_NODE(ForStatement) + + ForStatement(ExpressionNode *i, ExpressionNode *c, ExpressionNode *e, Statement *stmt): + initialiser (i), condition (c), expression (e), statement (stmt) + { kind = K; } + + virtual ~ForStatement() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return forToken; } + + virtual SourceLocation lastSourceLocation() const + { return statement->lastSourceLocation(); } + +// attributes + ExpressionNode *initialiser; + ExpressionNode *condition; + ExpressionNode *expression; + Statement *statement; + SourceLocation forToken; + SourceLocation lparenToken; + SourceLocation firstSemicolonToken; + SourceLocation secondSemicolonToken; + SourceLocation rparenToken; +}; + +class LocalForStatement: public Statement +{ +public: + QMLJS_DECLARE_AST_NODE(LocalForStatement) + + LocalForStatement(VariableDeclarationList *vlist, ExpressionNode *c, ExpressionNode *e, Statement *stmt): + declarations (vlist), condition (c), expression (e), statement (stmt) + { kind = K; } + + virtual ~LocalForStatement() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return forToken; } + + virtual SourceLocation lastSourceLocation() const + { return statement->lastSourceLocation(); } + +// attributes + VariableDeclarationList *declarations; + ExpressionNode *condition; + ExpressionNode *expression; + Statement *statement; + SourceLocation forToken; + SourceLocation lparenToken; + SourceLocation varToken; + SourceLocation firstSemicolonToken; + SourceLocation secondSemicolonToken; + SourceLocation rparenToken; +}; + +class ForEachStatement: public Statement +{ +public: + QMLJS_DECLARE_AST_NODE(ForEachStatement) + + ForEachStatement(ExpressionNode *i, ExpressionNode *e, Statement *stmt): + initialiser (i), expression (e), statement (stmt) + { kind = K; } + + virtual ~ForEachStatement() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return forToken; } + + virtual SourceLocation lastSourceLocation() const + { return statement->lastSourceLocation(); } + +// attributes + ExpressionNode *initialiser; + ExpressionNode *expression; + Statement *statement; + SourceLocation forToken; + SourceLocation lparenToken; + SourceLocation inToken; + SourceLocation rparenToken; +}; + +class LocalForEachStatement: public Statement +{ +public: + QMLJS_DECLARE_AST_NODE(LocalForEachStatement) + + LocalForEachStatement(VariableDeclaration *v, ExpressionNode *e, Statement *stmt): + declaration (v), expression (e), statement (stmt) + { kind = K; } + + virtual ~LocalForEachStatement() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return forToken; } + + virtual SourceLocation lastSourceLocation() const + { return statement->lastSourceLocation(); } + +// attributes + VariableDeclaration *declaration; + ExpressionNode *expression; + Statement *statement; + SourceLocation forToken; + SourceLocation lparenToken; + SourceLocation varToken; + SourceLocation inToken; + SourceLocation rparenToken; +}; + +class ContinueStatement: public Statement +{ +public: + QMLJS_DECLARE_AST_NODE(ContinueStatement) + + ContinueStatement(NameId *l = 0): + label (l) { kind = K; } + + virtual ~ContinueStatement() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return continueToken; } + + virtual SourceLocation lastSourceLocation() const + { return semicolonToken; } + +// attributes + NameId *label; + SourceLocation continueToken; + SourceLocation identifierToken; + SourceLocation semicolonToken; +}; + +class BreakStatement: public Statement +{ +public: + QMLJS_DECLARE_AST_NODE(BreakStatement) + + BreakStatement(NameId *l = 0): + label (l) { kind = K; } + + virtual ~BreakStatement() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return breakToken; } + + virtual SourceLocation lastSourceLocation() const + { return semicolonToken; } + + // attributes + NameId *label; + SourceLocation breakToken; + SourceLocation identifierToken; + SourceLocation semicolonToken; +}; + +class ReturnStatement: public Statement +{ +public: + QMLJS_DECLARE_AST_NODE(ReturnStatement) + + ReturnStatement(ExpressionNode *e): + expression (e) { kind = K; } + + virtual ~ReturnStatement() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return returnToken; } + + virtual SourceLocation lastSourceLocation() const + { return semicolonToken; } + +// attributes + ExpressionNode *expression; + SourceLocation returnToken; + SourceLocation semicolonToken; +}; + +class WithStatement: public Statement +{ +public: + QMLJS_DECLARE_AST_NODE(WithStatement) + + WithStatement(ExpressionNode *e, Statement *stmt): + expression (e), statement (stmt) + { kind = K; } + + virtual ~WithStatement() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return withToken; } + + virtual SourceLocation lastSourceLocation() const + { return statement->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + Statement *statement; + SourceLocation withToken; + SourceLocation lparenToken; + SourceLocation rparenToken; +}; + +class CaseBlock: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(CaseBlock) + + CaseBlock(CaseClauses *c, DefaultClause *d = 0, CaseClauses *r = 0): + clauses (c), defaultClause (d), moreClauses (r) + { kind = K; } + + virtual ~CaseBlock() {} + + virtual void accept0(Visitor *visitor); + +// attributes + CaseClauses *clauses; + DefaultClause *defaultClause; + CaseClauses *moreClauses; + SourceLocation lbraceToken; + SourceLocation rbraceToken; +}; + +class SwitchStatement: public Statement +{ +public: + QMLJS_DECLARE_AST_NODE(SwitchStatement) + + SwitchStatement(ExpressionNode *e, CaseBlock *b): + expression (e), block (b) + { kind = K; } + + virtual ~SwitchStatement() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return switchToken; } + + virtual SourceLocation lastSourceLocation() const + { return block->rbraceToken; } + +// attributes + ExpressionNode *expression; + CaseBlock *block; + SourceLocation switchToken; + SourceLocation lparenToken; + SourceLocation rparenToken; +}; + +class CaseClauses: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(CaseClauses) + + CaseClauses(CaseClause *c): + clause (c), next (this) + { kind = K; } + + CaseClauses(CaseClauses *previous, CaseClause *c): + clause (c) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual ~CaseClauses() {} + + virtual void accept0(Visitor *visitor); + + inline CaseClauses *finish () + { + CaseClauses *front = next; + next = 0; + return front; + } + +//attributes + CaseClause *clause; + CaseClauses *next; +}; + +class CaseClause: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(CaseClause) + + CaseClause(ExpressionNode *e, StatementList *slist): + expression (e), statements (slist) + { kind = K; } + + virtual ~CaseClause() {} + + virtual void accept0(Visitor *visitor); + +// attributes + ExpressionNode *expression; + StatementList *statements; + SourceLocation caseToken; + SourceLocation colonToken; +}; + +class DefaultClause: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(DefaultClause) + + DefaultClause(StatementList *slist): + statements (slist) + { kind = K; } + + virtual ~DefaultClause() {} + + virtual void accept0(Visitor *visitor); + +// attributes + StatementList *statements; + SourceLocation defaultToken; + SourceLocation colonToken; +}; + +class LabelledStatement: public Statement +{ +public: + QMLJS_DECLARE_AST_NODE(LabelledStatement) + + LabelledStatement(NameId *l, Statement *stmt): + label (l), statement (stmt) + { kind = K; } + + virtual ~LabelledStatement() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return identifierToken; } + + virtual SourceLocation lastSourceLocation() const + { return statement->lastSourceLocation(); } + +// attributes + NameId *label; + Statement *statement; + SourceLocation identifierToken; + SourceLocation colonToken; +}; + +class ThrowStatement: public Statement +{ +public: + QMLJS_DECLARE_AST_NODE(ThrowStatement) + + ThrowStatement(ExpressionNode *e): + expression (e) { kind = K; } + + virtual ~ThrowStatement() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return throwToken; } + + virtual SourceLocation lastSourceLocation() const + { return semicolonToken; } + + // attributes + ExpressionNode *expression; + SourceLocation throwToken; + SourceLocation semicolonToken; +}; + +class Catch: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(Catch) + + Catch(NameId *n, Block *stmt): + name (n), statement (stmt) + { kind = K; } + + virtual ~Catch() {} + + virtual void accept0(Visitor *visitor); + +// attributes + NameId *name; + Block *statement; + SourceLocation catchToken; + SourceLocation lparenToken; + SourceLocation identifierToken; + SourceLocation rparenToken; +}; + +class Finally: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(Finally) + + Finally(Block *stmt): + statement (stmt) + { kind = K; } + + virtual ~Finally() {} + + virtual void accept0(Visitor *visitor); + +// attributes + Block *statement; + SourceLocation finallyToken; +}; + +class TryStatement: public Statement +{ +public: + QMLJS_DECLARE_AST_NODE(TryStatement) + + TryStatement(Statement *stmt, Catch *c, Finally *f): + statement (stmt), catchExpression (c), finallyExpression (f) + { kind = K; } + + TryStatement(Statement *stmt, Finally *f): + statement (stmt), catchExpression (0), finallyExpression (f) + { kind = K; } + + TryStatement(Statement *stmt, Catch *c): + statement (stmt), catchExpression (c), finallyExpression (0) + { kind = K; } + + virtual ~TryStatement() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return tryToken; } + + virtual SourceLocation lastSourceLocation() const + { + if (finallyExpression) + return finallyExpression->statement->rbraceToken; + else if (catchExpression) + return catchExpression->statement->rbraceToken; + + return statement->lastSourceLocation(); + } + +// attributes + Statement *statement; + Catch *catchExpression; + Finally *finallyExpression; + SourceLocation tryToken; +}; + +class FunctionExpression: public ExpressionNode +{ +public: + QMLJS_DECLARE_AST_NODE(FunctionExpression) + + FunctionExpression(NameId *n, FormalParameterList *f, FunctionBody *b): + name (n), formals (f), body (b) + { kind = K; } + + virtual ~FunctionExpression() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return functionToken; } + + virtual SourceLocation lastSourceLocation() const + { return rbraceToken; } + +// attributes + NameId *name; + FormalParameterList *formals; + FunctionBody *body; + SourceLocation functionToken; + SourceLocation identifierToken; + SourceLocation lparenToken; + SourceLocation rparenToken; + SourceLocation lbraceToken; + SourceLocation rbraceToken; +}; + +class FunctionDeclaration: public FunctionExpression +{ +public: + QMLJS_DECLARE_AST_NODE(FunctionDeclaration) + + FunctionDeclaration(NameId *n, FormalParameterList *f, FunctionBody *b): + FunctionExpression(n, f, b) + { kind = K; } + + virtual ~FunctionDeclaration() {} + + virtual void accept0(Visitor *visitor); +}; + +class FormalParameterList: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(FormalParameterList) + + FormalParameterList(NameId *n): + name (n), next (this) + { kind = K; } + + FormalParameterList(FormalParameterList *previous, NameId *n): + name (n) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual ~FormalParameterList() {} + + virtual void accept0(Visitor *visitor); + + inline FormalParameterList *finish () + { + FormalParameterList *front = next; + next = 0; + return front; + } + +// attributes + NameId *name; + FormalParameterList *next; + SourceLocation commaToken; + SourceLocation identifierToken; +}; + +class FunctionBody: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(FunctionBody) + + FunctionBody(SourceElements *elts): + elements (elts) + { kind = K; } + + virtual ~FunctionBody() {} + + virtual void accept0(Visitor *visitor); + +// attributes + SourceElements *elements; +}; + +class Program: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(Program) + + Program(SourceElements *elts): + elements (elts) + { kind = K; } + + virtual ~Program() {} + + virtual void accept0(Visitor *visitor); + +// attributes + SourceElements *elements; +}; + +class SourceElements: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(SourceElements) + + SourceElements(SourceElement *elt): + element (elt), next (this) + { kind = K; } + + SourceElements(SourceElements *previous, SourceElement *elt): + element (elt) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual ~SourceElements() {} + + virtual void accept0(Visitor *visitor); + + inline SourceElements *finish () + { + SourceElements *front = next; + next = 0; + return front; + } + +// attributes + SourceElement *element; + SourceElements *next; +}; + +class SourceElement: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(SourceElement) + + inline SourceElement() + { kind = K; } + + virtual ~SourceElement() {} +}; + +class FunctionSourceElement: public SourceElement +{ +public: + QMLJS_DECLARE_AST_NODE(FunctionSourceElement) + + FunctionSourceElement(FunctionDeclaration *f): + declaration (f) + { kind = K; } + + virtual ~FunctionSourceElement() {} + + virtual void accept0(Visitor *visitor); + +// attributes + FunctionDeclaration *declaration; +}; + +class StatementSourceElement: public SourceElement +{ +public: + QMLJS_DECLARE_AST_NODE(StatementSourceElement) + + StatementSourceElement(Statement *stmt): + statement (stmt) + { kind = K; } + + virtual ~StatementSourceElement() {} + + virtual void accept0(Visitor *visitor); + +// attributes + Statement *statement; +}; + +class DebuggerStatement: public Statement +{ +public: + QMLJS_DECLARE_AST_NODE(DebuggerStatement) + + DebuggerStatement() + { kind = K; } + + virtual ~DebuggerStatement() {} + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return debuggerToken; } + + virtual SourceLocation lastSourceLocation() const + { return semicolonToken; } + +// attributes + SourceLocation debuggerToken; + SourceLocation semicolonToken; +}; + +class UiProgram: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(UiProgram) + + UiProgram(UiImportList *imports, UiObjectMemberList *members) + : imports(imports), members(members) + { kind = K; } + + virtual void accept0(Visitor *visitor); + +// attributes + UiImportList *imports; + UiObjectMemberList *members; +}; + +class UiQualifiedId: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(UiQualifiedId) + + UiQualifiedId(NameId *name) + : next(this), name(name) + { kind = K; } + + UiQualifiedId(UiQualifiedId *previous, NameId *name) + : name(name) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual ~UiQualifiedId() {} + + UiQualifiedId *finish() + { + UiQualifiedId *head = next; + next = 0; + return head; + } + + virtual void accept0(Visitor *visitor); + +// attributes + UiQualifiedId *next; + NameId *name; + SourceLocation identifierToken; +}; + +class UiImport: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(UiImport) + + UiImport(NameId *fileName) + : fileName(fileName), importUri(0), importId(0) + { kind = K; } + + UiImport(UiQualifiedId *uri) + : fileName(0), importUri(uri), importId(0) + { kind = K; } + + virtual SourceLocation firstSourceLocation() const + { return importToken; } + + virtual SourceLocation lastSourceLocation() const + { return semicolonToken; } + + virtual void accept0(Visitor *visitor); + +// attributes + NameId *fileName; + UiQualifiedId *importUri; + NameId *importId; + SourceLocation importToken; + SourceLocation fileNameToken; + SourceLocation versionToken; + SourceLocation asToken; + SourceLocation importIdToken; + SourceLocation semicolonToken; +}; + +class UiImportList: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(UiImportList) + + UiImportList(UiImport *import) + : import(import), + next(this) + { kind = K; } + + UiImportList(UiImportList *previous, UiImport *import) + : import(import) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual SourceLocation firstSourceLocation() const + { + if (import) return import->firstSourceLocation(); + else return SourceLocation(); + } + + virtual SourceLocation lastSourceLocation() const + { + for (const UiImportList *it = this; it; it = it->next) + if (!it->next && it->import) + return it->import->lastSourceLocation(); + + return SourceLocation(); + } + + UiImportList *finish() + { + UiImportList *head = next; + next = 0; + return head; + } + + virtual void accept0(Visitor *visitor); + +// attributes + UiImport *import; + UiImportList *next; +}; + +class UiObjectMember: public Node +{ +public: + virtual SourceLocation firstSourceLocation() const = 0; + virtual SourceLocation lastSourceLocation() const = 0; + + virtual UiObjectMember *uiObjectMemberCast(); +}; + +class UiObjectMemberList: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(UiObjectMemberList) + + UiObjectMemberList(UiObjectMember *member) + : next(this), member(member) + { kind = K; } + + UiObjectMemberList(UiObjectMemberList *previous, UiObjectMember *member) + : member(member) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual void accept0(Visitor *visitor); + + UiObjectMemberList *finish() + { + UiObjectMemberList *head = next; + next = 0; + return head; + } + +// attributes + UiObjectMemberList *next; + UiObjectMember *member; +}; + +class UiArrayMemberList: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(UiArrayMemberList) + + UiArrayMemberList(UiObjectMember *member) + : next(this), member(member) + { kind = K; } + + UiArrayMemberList(UiArrayMemberList *previous, UiObjectMember *member) + : member(member) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual void accept0(Visitor *visitor); + + UiArrayMemberList *finish() + { + UiArrayMemberList *head = next; + next = 0; + return head; + } + +// attributes + UiArrayMemberList *next; + UiObjectMember *member; + SourceLocation commaToken; +}; + +class UiObjectInitializer: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(UiObjectInitializer) + + UiObjectInitializer(UiObjectMemberList *members) + : members(members) + { kind = K; } + + virtual void accept0(Visitor *visitor); + +// attributes + SourceLocation lbraceToken; + UiObjectMemberList *members; + SourceLocation rbraceToken; +}; + +class UiParameterList: public Node +{ +public: + QMLJS_DECLARE_AST_NODE(UiParameterList) + + UiParameterList(NameId *t, NameId *n): + type (t), name (n), next (this) + { kind = K; } + + UiParameterList(UiParameterList *previous, NameId *t, NameId *n): + type (t), name (n) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual ~UiParameterList() {} + + virtual void accept0(Visitor *) {} + + inline UiParameterList *finish () + { + UiParameterList *front = next; + next = 0; + return front; + } + +// attributes + NameId *type; + NameId *name; + UiParameterList *next; + SourceLocation commaToken; + SourceLocation identifierToken; +}; + +class UiPublicMember: public UiObjectMember +{ +public: + QMLJS_DECLARE_AST_NODE(UiPublicMember) + + UiPublicMember(NameId *memberType, + NameId *name) + : type(Property), typeModifier(0), memberType(memberType), name(name), expression(0), isDefaultMember(false), isReadonlyMember(false), parameters(0) + { kind = K; } + + UiPublicMember(NameId *memberType, + NameId *name, + ExpressionNode *expression) + : type(Property), typeModifier(0), memberType(memberType), name(name), expression(expression), isDefaultMember(false), isReadonlyMember(false), parameters(0) + { kind = K; } + + virtual SourceLocation firstSourceLocation() const + { + if (defaultToken.isValid()) + return defaultToken; + else if (readonlyToken.isValid()) + return readonlyToken; + + return propertyToken; + } + + virtual SourceLocation lastSourceLocation() const + { + return semicolonToken; + } + + virtual void accept0(Visitor *visitor); + +// attributes + enum { Signal, Property } type; + NameId *typeModifier; + NameId *memberType; + NameId *name; + ExpressionNode *expression; + bool isDefaultMember; + bool isReadonlyMember; + UiParameterList *parameters; + SourceLocation defaultToken; + SourceLocation readonlyToken; + SourceLocation propertyToken; + SourceLocation typeModifierToken; + SourceLocation typeToken; + SourceLocation identifierToken; + SourceLocation colonToken; + SourceLocation semicolonToken; +}; + +class UiObjectDefinition: public UiObjectMember +{ +public: + QMLJS_DECLARE_AST_NODE(UiObjectDefinition) + + UiObjectDefinition(UiQualifiedId *qualifiedTypeNameId, + UiObjectInitializer *initializer) + : qualifiedTypeNameId(qualifiedTypeNameId), initializer(initializer) + { kind = K; } + + virtual SourceLocation firstSourceLocation() const + { return qualifiedTypeNameId->identifierToken; } + + virtual SourceLocation lastSourceLocation() const + { return initializer->rbraceToken; } + + virtual void accept0(Visitor *visitor); + +// attributes + UiQualifiedId *qualifiedTypeNameId; + UiObjectInitializer *initializer; +}; + +class UiSourceElement: public UiObjectMember +{ +public: + QMLJS_DECLARE_AST_NODE(UiSourceElement) + + UiSourceElement(Node *sourceElement) + : sourceElement(sourceElement) + { kind = K; } + + virtual SourceLocation firstSourceLocation() const + { + if (FunctionDeclaration *funDecl = cast<FunctionDeclaration *>(sourceElement)) + return funDecl->firstSourceLocation(); + else if (VariableStatement *varStmt = cast<VariableStatement *>(sourceElement)) + return varStmt->firstSourceLocation(); + + return SourceLocation(); + } + + virtual SourceLocation lastSourceLocation() const + { + if (FunctionDeclaration *funDecl = cast<FunctionDeclaration *>(sourceElement)) + return funDecl->lastSourceLocation(); + else if (VariableStatement *varStmt = cast<VariableStatement *>(sourceElement)) + return varStmt->lastSourceLocation(); + + return SourceLocation(); + } + + + virtual void accept0(Visitor *visitor); + +// attributes + Node *sourceElement; +}; + +class UiObjectBinding: public UiObjectMember +{ +public: + QMLJS_DECLARE_AST_NODE(UiObjectBinding) + + UiObjectBinding(UiQualifiedId *qualifiedId, + UiQualifiedId *qualifiedTypeNameId, + UiObjectInitializer *initializer) + : qualifiedId(qualifiedId), + qualifiedTypeNameId(qualifiedTypeNameId), + initializer(initializer) + { kind = K; } + + virtual SourceLocation firstSourceLocation() const + { return qualifiedId->identifierToken; } + + virtual SourceLocation lastSourceLocation() const + { return initializer->rbraceToken; } + + virtual void accept0(Visitor *visitor); + +// attributes + UiQualifiedId *qualifiedId; + UiQualifiedId *qualifiedTypeNameId; + UiObjectInitializer *initializer; + SourceLocation colonToken; +}; + +class UiScriptBinding: public UiObjectMember +{ +public: + QMLJS_DECLARE_AST_NODE(UiScriptBinding) + + UiScriptBinding(UiQualifiedId *qualifiedId, + Statement *statement) + : qualifiedId(qualifiedId), + statement(statement) + { kind = K; } + + virtual SourceLocation firstSourceLocation() const + { return qualifiedId->identifierToken; } + + virtual SourceLocation lastSourceLocation() const + { return statement->lastSourceLocation(); } + + virtual void accept0(Visitor *visitor); + +// attributes + UiQualifiedId *qualifiedId; + Statement *statement; + SourceLocation colonToken; +}; + +class UiArrayBinding: public UiObjectMember +{ +public: + QMLJS_DECLARE_AST_NODE(UiArrayBinding) + + UiArrayBinding(UiQualifiedId *qualifiedId, + UiArrayMemberList *members) + : qualifiedId(qualifiedId), + members(members) + { kind = K; } + + virtual SourceLocation firstSourceLocation() const + { return qualifiedId->identifierToken; } + + virtual SourceLocation lastSourceLocation() const + { return rbracketToken; } + + virtual void accept0(Visitor *visitor); + +// attributes + UiQualifiedId *qualifiedId; + UiArrayMemberList *members; + SourceLocation colonToken; + SourceLocation lbracketToken; + SourceLocation rbracketToken; +}; + +} } // namespace AST + + + +QT_QML_END_NAMESPACE + +#endif diff --git a/src/plugins/qmldesigner/core/filemanager/parser/qmljsastfwd_p.h b/src/plugins/qmldesigner/core/filemanager/parser/qmljsastfwd_p.h new file mode 100644 index 00000000000..dd164e9e3c8 --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/parser/qmljsastfwd_p.h @@ -0,0 +1,176 @@ +/**************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef QMLJSAST_FWD_P_H +#define QMLJSAST_FWD_P_H + +#include <QtCore/qglobal.h> +#include "qmljsglobal_p.h" + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_QML_BEGIN_NAMESPACE + +namespace QmlJS { namespace AST { + +class SourceLocation +{ +public: + SourceLocation(quint32 offset = 0, quint32 length = 0, quint32 line = 0, quint32 column = 0) + : offset(offset), length(length), + startLine(line), startColumn(column) + { } + + bool isValid() const { return length != 0; } + + quint32 begin() const { return offset; } + quint32 end() const { return offset + length; } + +// attributes + // ### encode + quint32 offset; + quint32 length; + quint32 startLine; + quint32 startColumn; +}; + +class Visitor; +class Node; +class ExpressionNode; +class Statement; +class ThisExpression; +class IdentifierExpression; +class NullExpression; +class TrueLiteral; +class FalseLiteral; +class NumericLiteral; +class StringLiteral; +class RegExpLiteral; +class ArrayLiteral; +class ObjectLiteral; +class ElementList; +class Elision; +class PropertyNameAndValueList; +class PropertyName; +class IdentifierPropertyName; +class StringLiteralPropertyName; +class NumericLiteralPropertyName; +class ArrayMemberExpression; +class FieldMemberExpression; +class NewMemberExpression; +class NewExpression; +class CallExpression; +class ArgumentList; +class PostIncrementExpression; +class PostDecrementExpression; +class DeleteExpression; +class VoidExpression; +class TypeOfExpression; +class PreIncrementExpression; +class PreDecrementExpression; +class UnaryPlusExpression; +class UnaryMinusExpression; +class TildeExpression; +class NotExpression; +class BinaryExpression; +class ConditionalExpression; +class Expression; // ### rename +class Block; +class StatementList; +class VariableStatement; +class VariableDeclarationList; +class VariableDeclaration; +class EmptyStatement; +class ExpressionStatement; +class IfStatement; +class DoWhileStatement; +class WhileStatement; +class ForStatement; +class LocalForStatement; +class ForEachStatement; +class LocalForEachStatement; +class ContinueStatement; +class BreakStatement; +class ReturnStatement; +class WithStatement; +class SwitchStatement; +class CaseBlock; +class CaseClauses; +class CaseClause; +class DefaultClause; +class LabelledStatement; +class ThrowStatement; +class TryStatement; +class Catch; +class Finally; +class FunctionDeclaration; +class FunctionExpression; +class FormalParameterList; +class FunctionBody; +class Program; +class SourceElements; +class SourceElement; +class FunctionSourceElement; +class StatementSourceElement; +class DebuggerStatement; +class NestedExpression; + +// ui elements +class UiProgram; +class UiImportList; +class UiImport; +class UiPublicMember; +class UiObjectDefinition; +class UiObjectInitializer; +class UiObjectBinding; +class UiScriptBinding; +class UiSourceElement; +class UiArrayBinding; +class UiObjectMember; +class UiObjectMemberList; +class UiArrayMemberList; +class UiQualifiedId; +class UiFormalList; +class UiFormal; +class UiSignature; + +} } // namespace AST + +QT_QML_END_NAMESPACE + +#endif diff --git a/src/plugins/qmldesigner/core/filemanager/parser/qmljsastvisitor.cpp b/src/plugins/qmldesigner/core/filemanager/parser/qmljsastvisitor.cpp new file mode 100644 index 00000000000..6a0d55a4f16 --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/parser/qmljsastvisitor.cpp @@ -0,0 +1,46 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "qmljsastvisitor_p.h" + +QT_QML_BEGIN_NAMESPACE + +namespace QmlJS { namespace AST { + +Visitor::Visitor() +{ +} + +Visitor::~Visitor() +{ +} + +} } // namespace QmlJS::AST + +QT_QML_END_NAMESPACE diff --git a/src/plugins/qmldesigner/core/filemanager/parser/qmljsastvisitor_p.h b/src/plugins/qmldesigner/core/filemanager/parser/qmljsastvisitor_p.h new file mode 100644 index 00000000000..09714ee0a0a --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/parser/qmljsastvisitor_p.h @@ -0,0 +1,323 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef QMLJSASTVISITOR_P_H +#define QMLJSASTVISITOR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qmljsastfwd_p.h" +#include "qmljsglobal_p.h" + +QT_QML_BEGIN_NAMESPACE + +namespace QmlJS { namespace AST { + +class Visitor +{ +public: + Visitor(); + virtual ~Visitor(); + + virtual bool preVisit(Node *) { return true; } + virtual void postVisit(Node *) {} + + // Ui + virtual bool visit(UiProgram *) { return true; } + virtual bool visit(UiImportList *) { return true; } + virtual bool visit(UiImport *) { return true; } + virtual bool visit(UiPublicMember *) { return true; } + virtual bool visit(UiSourceElement *) { return true; } + virtual bool visit(UiObjectDefinition *) { return true; } + virtual bool visit(UiObjectInitializer *) { return true; } + virtual bool visit(UiObjectBinding *) { return true; } + virtual bool visit(UiScriptBinding *) { return true; } + virtual bool visit(UiArrayBinding *) { return true; } + virtual bool visit(UiObjectMemberList *) { return true; } + virtual bool visit(UiArrayMemberList *) { return true; } + virtual bool visit(UiQualifiedId *) { return true; } + virtual bool visit(UiSignature *) { return true; } + virtual bool visit(UiFormalList *) { return true; } + virtual bool visit(UiFormal *) { return true; } + + virtual void endVisit(UiProgram *) {} + virtual void endVisit(UiImportList *) {} + virtual void endVisit(UiImport *) {} + virtual void endVisit(UiPublicMember *) {} + virtual void endVisit(UiSourceElement *) {} + virtual void endVisit(UiObjectDefinition *) {} + virtual void endVisit(UiObjectInitializer *) {} + virtual void endVisit(UiObjectBinding *) {} + virtual void endVisit(UiScriptBinding *) {} + virtual void endVisit(UiArrayBinding *) {} + virtual void endVisit(UiObjectMemberList *) {} + virtual void endVisit(UiArrayMemberList *) {} + virtual void endVisit(UiQualifiedId *) {} + virtual void endVisit(UiSignature *) {} + virtual void endVisit(UiFormalList *) {} + virtual void endVisit(UiFormal *) {} + + // QmlJS + virtual bool visit(ThisExpression *) { return true; } + virtual void endVisit(ThisExpression *) {} + + virtual bool visit(IdentifierExpression *) { return true; } + virtual void endVisit(IdentifierExpression *) {} + + virtual bool visit(NullExpression *) { return true; } + virtual void endVisit(NullExpression *) {} + + virtual bool visit(TrueLiteral *) { return true; } + virtual void endVisit(TrueLiteral *) {} + + virtual bool visit(FalseLiteral *) { return true; } + virtual void endVisit(FalseLiteral *) {} + + virtual bool visit(StringLiteral *) { return true; } + virtual void endVisit(StringLiteral *) {} + + virtual bool visit(NumericLiteral *) { return true; } + virtual void endVisit(NumericLiteral *) {} + + virtual bool visit(RegExpLiteral *) { return true; } + virtual void endVisit(RegExpLiteral *) {} + + virtual bool visit(ArrayLiteral *) { return true; } + virtual void endVisit(ArrayLiteral *) {} + + virtual bool visit(ObjectLiteral *) { return true; } + virtual void endVisit(ObjectLiteral *) {} + + virtual bool visit(ElementList *) { return true; } + virtual void endVisit(ElementList *) {} + + virtual bool visit(Elision *) { return true; } + virtual void endVisit(Elision *) {} + + virtual bool visit(PropertyNameAndValueList *) { return true; } + virtual void endVisit(PropertyNameAndValueList *) {} + + virtual bool visit(NestedExpression *) { return true; } + virtual void endVisit(NestedExpression *) {} + + virtual bool visit(IdentifierPropertyName *) { return true; } + virtual void endVisit(IdentifierPropertyName *) {} + + virtual bool visit(StringLiteralPropertyName *) { return true; } + virtual void endVisit(StringLiteralPropertyName *) {} + + virtual bool visit(NumericLiteralPropertyName *) { return true; } + virtual void endVisit(NumericLiteralPropertyName *) {} + + virtual bool visit(ArrayMemberExpression *) { return true; } + virtual void endVisit(ArrayMemberExpression *) {} + + virtual bool visit(FieldMemberExpression *) { return true; } + virtual void endVisit(FieldMemberExpression *) {} + + virtual bool visit(NewMemberExpression *) { return true; } + virtual void endVisit(NewMemberExpression *) {} + + virtual bool visit(NewExpression *) { return true; } + virtual void endVisit(NewExpression *) {} + + virtual bool visit(CallExpression *) { return true; } + virtual void endVisit(CallExpression *) {} + + virtual bool visit(ArgumentList *) { return true; } + virtual void endVisit(ArgumentList *) {} + + virtual bool visit(PostIncrementExpression *) { return true; } + virtual void endVisit(PostIncrementExpression *) {} + + virtual bool visit(PostDecrementExpression *) { return true; } + virtual void endVisit(PostDecrementExpression *) {} + + virtual bool visit(DeleteExpression *) { return true; } + virtual void endVisit(DeleteExpression *) {} + + virtual bool visit(VoidExpression *) { return true; } + virtual void endVisit(VoidExpression *) {} + + virtual bool visit(TypeOfExpression *) { return true; } + virtual void endVisit(TypeOfExpression *) {} + + virtual bool visit(PreIncrementExpression *) { return true; } + virtual void endVisit(PreIncrementExpression *) {} + + virtual bool visit(PreDecrementExpression *) { return true; } + virtual void endVisit(PreDecrementExpression *) {} + + virtual bool visit(UnaryPlusExpression *) { return true; } + virtual void endVisit(UnaryPlusExpression *) {} + + virtual bool visit(UnaryMinusExpression *) { return true; } + virtual void endVisit(UnaryMinusExpression *) {} + + virtual bool visit(TildeExpression *) { return true; } + virtual void endVisit(TildeExpression *) {} + + virtual bool visit(NotExpression *) { return true; } + virtual void endVisit(NotExpression *) {} + + virtual bool visit(BinaryExpression *) { return true; } + virtual void endVisit(BinaryExpression *) {} + + virtual bool visit(ConditionalExpression *) { return true; } + virtual void endVisit(ConditionalExpression *) {} + + virtual bool visit(Expression *) { return true; } + virtual void endVisit(Expression *) {} + + virtual bool visit(Block *) { return true; } + virtual void endVisit(Block *) {} + + virtual bool visit(StatementList *) { return true; } + virtual void endVisit(StatementList *) {} + + virtual bool visit(VariableStatement *) { return true; } + virtual void endVisit(VariableStatement *) {} + + virtual bool visit(VariableDeclarationList *) { return true; } + virtual void endVisit(VariableDeclarationList *) {} + + virtual bool visit(VariableDeclaration *) { return true; } + virtual void endVisit(VariableDeclaration *) {} + + virtual bool visit(EmptyStatement *) { return true; } + virtual void endVisit(EmptyStatement *) {} + + virtual bool visit(ExpressionStatement *) { return true; } + virtual void endVisit(ExpressionStatement *) {} + + virtual bool visit(IfStatement *) { return true; } + virtual void endVisit(IfStatement *) {} + + virtual bool visit(DoWhileStatement *) { return true; } + virtual void endVisit(DoWhileStatement *) {} + + virtual bool visit(WhileStatement *) { return true; } + virtual void endVisit(WhileStatement *) {} + + virtual bool visit(ForStatement *) { return true; } + virtual void endVisit(ForStatement *) {} + + virtual bool visit(LocalForStatement *) { return true; } + virtual void endVisit(LocalForStatement *) {} + + virtual bool visit(ForEachStatement *) { return true; } + virtual void endVisit(ForEachStatement *) {} + + virtual bool visit(LocalForEachStatement *) { return true; } + virtual void endVisit(LocalForEachStatement *) {} + + virtual bool visit(ContinueStatement *) { return true; } + virtual void endVisit(ContinueStatement *) {} + + virtual bool visit(BreakStatement *) { return true; } + virtual void endVisit(BreakStatement *) {} + + virtual bool visit(ReturnStatement *) { return true; } + virtual void endVisit(ReturnStatement *) {} + + virtual bool visit(WithStatement *) { return true; } + virtual void endVisit(WithStatement *) {} + + virtual bool visit(SwitchStatement *) { return true; } + virtual void endVisit(SwitchStatement *) {} + + virtual bool visit(CaseBlock *) { return true; } + virtual void endVisit(CaseBlock *) {} + + virtual bool visit(CaseClauses *) { return true; } + virtual void endVisit(CaseClauses *) {} + + virtual bool visit(CaseClause *) { return true; } + virtual void endVisit(CaseClause *) {} + + virtual bool visit(DefaultClause *) { return true; } + virtual void endVisit(DefaultClause *) {} + + virtual bool visit(LabelledStatement *) { return true; } + virtual void endVisit(LabelledStatement *) {} + + virtual bool visit(ThrowStatement *) { return true; } + virtual void endVisit(ThrowStatement *) {} + + virtual bool visit(TryStatement *) { return true; } + virtual void endVisit(TryStatement *) {} + + virtual bool visit(Catch *) { return true; } + virtual void endVisit(Catch *) {} + + virtual bool visit(Finally *) { return true; } + virtual void endVisit(Finally *) {} + + virtual bool visit(FunctionDeclaration *) { return true; } + virtual void endVisit(FunctionDeclaration *) {} + + virtual bool visit(FunctionExpression *) { return true; } + virtual void endVisit(FunctionExpression *) {} + + virtual bool visit(FormalParameterList *) { return true; } + virtual void endVisit(FormalParameterList *) {} + + virtual bool visit(FunctionBody *) { return true; } + virtual void endVisit(FunctionBody *) {} + + virtual bool visit(Program *) { return true; } + virtual void endVisit(Program *) {} + + virtual bool visit(SourceElements *) { return true; } + virtual void endVisit(SourceElements *) {} + + virtual bool visit(FunctionSourceElement *) { return true; } + virtual void endVisit(FunctionSourceElement *) {} + + virtual bool visit(StatementSourceElement *) { return true; } + virtual void endVisit(StatementSourceElement *) {} + + virtual bool visit(DebuggerStatement *) { return true; } + virtual void endVisit(DebuggerStatement *) {} +}; + +} } // namespace AST + +QT_QML_END_NAMESPACE + +#endif // QMLJSASTVISITOR_P_H diff --git a/src/plugins/qmldesigner/core/filemanager/parser/qmljsengine_p.cpp b/src/plugins/qmldesigner/core/filemanager/parser/qmljsengine_p.cpp new file mode 100644 index 00000000000..6e7a45a5065 --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/parser/qmljsengine_p.cpp @@ -0,0 +1,198 @@ +/**************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "qmljsglobal_p.h" +#include "qmljsengine_p.h" +#include "qmljsnodepool_p.h" +#include <qnumeric.h> +#include <QHash> + +QT_QML_BEGIN_NAMESPACE + +namespace QmlJS { + +uint qHash(const QmlJS::NameId &id) +{ return qHash(id.asString()); } + +QString numberToString(double value) +{ return QString::number(value); } + +int Ecma::RegExp::flagFromChar(const QChar &ch) +{ + static QHash<QChar, int> flagsHash; + if (flagsHash.isEmpty()) { + flagsHash[QLatin1Char('g')] = Global; + flagsHash[QLatin1Char('i')] = IgnoreCase; + flagsHash[QLatin1Char('m')] = Multiline; + } + QHash<QChar, int>::const_iterator it; + it = flagsHash.constFind(ch); + if (it == flagsHash.constEnd()) + return 0; + return it.value(); +} + +QString Ecma::RegExp::flagsToString(int flags) +{ + QString result; + if (flags & Global) + result += QLatin1Char('g'); + if (flags & IgnoreCase) + result += QLatin1Char('i'); + if (flags & Multiline) + result += QLatin1Char('m'); + return result; +} + +NodePool::NodePool(const QString &fileName, Engine *engine) + : m_fileName(fileName), m_engine(engine) +{ + m_engine->setNodePool(this); +} + +NodePool::~NodePool() +{ +} + +Code *NodePool::createCompiledCode(AST::Node *, CompilationUnit &) +{ + Q_ASSERT(0); + return 0; +} + +static int toDigit(char c) +{ + if ((c >= '0') && (c <= '9')) + return c - '0'; + else if ((c >= 'a') && (c <= 'z')) + return 10 + c - 'a'; + else if ((c >= 'A') && (c <= 'Z')) + return 10 + c - 'A'; + return -1; +} + +double integerFromString(const char *buf, int size, int radix) +{ + if (size == 0) + return qSNaN(); + + double sign = 1.0; + int i = 0; + if (buf[0] == '+') { + ++i; + } else if (buf[0] == '-') { + sign = -1.0; + ++i; + } + + if (((size-i) >= 2) && (buf[i] == '0')) { + if (((buf[i+1] == 'x') || (buf[i+1] == 'X')) + && (radix < 34)) { + if ((radix != 0) && (radix != 16)) + return 0; + radix = 16; + i += 2; + } else { + if (radix == 0) { + radix = 8; + ++i; + } + } + } else if (radix == 0) { + radix = 10; + } + + int j = i; + for ( ; i < size; ++i) { + int d = toDigit(buf[i]); + if ((d == -1) || (d >= radix)) + break; + } + double result; + if (j == i) { + if (!qstrcmp(buf, "Infinity")) + result = qInf(); + else + result = qSNaN(); + } else { + result = 0; + double multiplier = 1; + for (--i ; i >= j; --i, multiplier *= radix) + result += toDigit(buf[i]) * multiplier; + } + result *= sign; + return result; +} + +double integerFromString(const QString &str, int radix) +{ + QByteArray ba = str.trimmed().toUtf8(); + return integerFromString(ba.constData(), ba.size(), radix); +} + + +Engine::Engine() + : _lexer(0), _nodePool(0) +{ } + +Engine::~Engine() +{ } + +QSet<NameId> Engine::literals() const +{ return _literals; } + +void Engine::addComment(int pos, int len, int line, int col) +{ if (len > 0) _comments.append(QmlJS::AST::SourceLocation(pos, len, line, col)); } + +QList<QmlJS::AST::SourceLocation> Engine::comments() const +{ return _comments; } + +NameId *Engine::intern(const QChar *u, int s) +{ return const_cast<NameId *>(&*_literals.insert(NameId(u, s))); } + +QString Engine::toString(NameId *id) +{ return id->asString(); } + +Lexer *Engine::lexer() const +{ return _lexer; } + +void Engine::setLexer(Lexer *lexer) +{ _lexer = lexer; } + +NodePool *Engine::nodePool() const +{ return _nodePool; } + +void Engine::setNodePool(NodePool *nodePool) +{ _nodePool = nodePool; } + + + +} // end of namespace QmlJS + +QT_QML_END_NAMESPACE diff --git a/src/plugins/qmldesigner/core/filemanager/parser/qmljsengine_p.h b/src/plugins/qmldesigner/core/filemanager/parser/qmljsengine_p.h new file mode 100644 index 00000000000..98e71c88790 --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/parser/qmljsengine_p.h @@ -0,0 +1,161 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef QMLJSENGINE_P_H +#define QMLJSENGINE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QString> +#include <QSet> + +#include "qmljsglobal_p.h" +#include "qmljsastfwd_p.h" + +QT_QML_BEGIN_NAMESPACE + +namespace QmlJS { +class NameId +{ + QString _text; + +public: + NameId(const QChar *u, int s) + : _text(u, s) + { } + + const QString asString() const + { return _text; } + + bool operator == (const NameId &other) const + { return _text == other._text; } + + bool operator != (const NameId &other) const + { return _text != other._text; } + + bool operator < (const NameId &other) const + { return _text < other._text; } +}; + +uint qHash(const QmlJS::NameId &id); + +} // end of namespace QmlJS + +#if defined(Q_CC_MSVC) && _MSC_VER <= 1300 +//this ensures that code outside QmlJS can use the hash function +//it also a workaround for some compilers +inline uint qHash(const QmlJS::NameId &nameId) { return QmlJS::qHash(nameId); } +#endif + +namespace QmlJS { + +class Lexer; +class NodePool; + +namespace Ecma { + +class RegExp +{ +public: + enum RegExpFlag { + Global = 0x01, + IgnoreCase = 0x02, + Multiline = 0x04 + }; + +public: + static int flagFromChar(const QChar &); + static QString flagsToString(int flags); +}; + +} // end of namespace Ecma + +class DiagnosticMessage +{ +public: + enum Kind { Warning, Error }; + + DiagnosticMessage() + : kind(Error) {} + + DiagnosticMessage(Kind kind, const AST::SourceLocation &loc, const QString &message) + : kind(kind), loc(loc), message(message) {} + + bool isWarning() const + { return kind == Warning; } + + bool isError() const + { return kind == Error; } + + Kind kind; + AST::SourceLocation loc; + QString message; +}; + +class Engine +{ + Lexer *_lexer; + NodePool *_nodePool; + QSet<NameId> _literals; + QList<QmlJS::AST::SourceLocation> _comments; + +public: + Engine(); + ~Engine(); + + QSet<NameId> literals() const; + + void addComment(int pos, int len, int line, int col); + QList<QmlJS::AST::SourceLocation> comments() const; + + NameId *intern(const QChar *u, int s); + + static QString toString(NameId *id); + + Lexer *lexer() const; + void setLexer(Lexer *lexer); + + NodePool *nodePool() const; + void setNodePool(NodePool *nodePool); +}; + +} // end of namespace QmlJS + +QT_QML_END_NAMESPACE + +#endif // QMLJSENGINE_P_H diff --git a/src/plugins/qmldesigner/core/filemanager/parser/qmljsglobal_p.h b/src/plugins/qmldesigner/core/filemanager/parser/qmljsglobal_p.h new file mode 100644 index 00000000000..96f1ced2664 --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/parser/qmljsglobal_p.h @@ -0,0 +1,43 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef QMLJSGLOBAL_P_H +#define QMLJSGLOBAL_P_H + +#include <QtCore/qglobal.h> + +#ifdef QT_CREATOR +#define QT_QML_BEGIN_NAMESPACE +#define QT_QML_END_NAMESPACE +#else // !QT_CREATOR +#define QT_QML_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE +#define QT_QML_END_NAMESPACE QT_END_NAMESPACE +#endif // QT_CREATOR + +#endif // QMLJSGLOBAL_P_H diff --git a/src/plugins/qmldesigner/core/filemanager/parser/qmljsgrammar.cpp b/src/plugins/qmldesigner/core/filemanager/parser/qmljsgrammar.cpp new file mode 100644 index 00000000000..c71b759a355 --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/parser/qmljsgrammar.cpp @@ -0,0 +1,960 @@ +// This file was generated by qlalr - DO NOT EDIT! +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmljsgrammar_p.h" + +const char *const QmlJSGrammar::spell [] = { + "end of file", "&", "&&", "&=", "break", "case", "catch", ":", ";", "continue", + "default", "delete", "/", "/=", "do", ".", "else", "=", "==", "===", + "finally", "for", "function", ">=", ">", ">>", ">>=", ">>>", ">>>=", "identifier", + "if", "in", "instanceof", "{", "[", "<=", "(", "<", "<<", "<<=", + "-", "-=", "--", "new", "!", "!=", "!==", "numeric literal", "|", "|=", + "||", "+", "+=", "++", "?", "}", "]", "%", "%=", "return", + ")", ";", 0, "*", "*=", "string literal", "property", "signal", "readonly", "switch", + "this", "throw", "~", "try", "typeof", "var", "void", "while", "with", "^", + "^=", "null", "true", "false", "const", "debugger", "reserved word", "multiline string literal", "comment", "public", + "import", "as", 0, 0, 0, 0, 0, 0}; + +const int QmlJSGrammar::lhs [] = { + 98, 98, 98, 98, 99, 103, 103, 106, 106, 108, + 107, 107, 107, 107, 107, 107, 107, 107, 110, 105, + 104, 113, 113, 114, 114, 115, 115, 112, 102, 102, + 102, 102, 102, 102, 102, 121, 121, 121, 122, 122, + 123, 123, 102, 102, 102, 102, 102, 102, 102, 102, + 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, + 111, 111, 111, 111, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 116, 128, 128, 128, 128, 127, 127, 130, + 130, 132, 132, 132, 132, 132, 132, 133, 133, 133, + 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, + 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, + 133, 133, 133, 133, 133, 133, 133, 133, 134, 134, + 109, 109, 109, 109, 109, 137, 137, 138, 138, 138, + 138, 136, 136, 139, 139, 140, 140, 141, 141, 141, + 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, + 143, 143, 143, 143, 144, 144, 144, 145, 145, 145, + 145, 146, 146, 146, 146, 146, 146, 146, 147, 147, + 147, 147, 147, 147, 148, 148, 148, 148, 148, 149, + 149, 149, 149, 149, 150, 150, 151, 151, 152, 152, + 153, 153, 154, 154, 155, 155, 156, 156, 157, 157, + 158, 158, 159, 159, 160, 160, 161, 161, 131, 131, + 162, 162, 163, 163, 163, 163, 163, 163, 163, 163, + 163, 163, 163, 163, 101, 101, 164, 164, 165, 165, + 166, 166, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 117, 178, 178, + 177, 177, 125, 125, 179, 179, 180, 180, 182, 182, + 181, 183, 186, 184, 184, 187, 185, 185, 118, 119, + 119, 120, 120, 167, 167, 167, 167, 167, 167, 167, + 168, 168, 168, 168, 169, 169, 169, 169, 170, 170, + 171, 173, 188, 188, 191, 191, 189, 189, 192, 190, + 172, 172, 172, 174, 174, 175, 175, 175, 193, 194, + 176, 176, 124, 135, 198, 198, 195, 195, 196, 196, + 199, 200, 200, 201, 201, 197, 197, 129, 129, 202}; + +const int QmlJSGrammar:: rhs[] = { + 2, 2, 2, 2, 2, 1, 1, 1, 2, 1, + 2, 2, 3, 3, 5, 5, 4, 4, 2, 0, + 1, 1, 2, 1, 3, 2, 3, 2, 1, 5, + 4, 3, 3, 3, 3, 1, 1, 1, 0, 1, + 2, 4, 6, 6, 3, 3, 7, 7, 4, 4, + 5, 5, 6, 6, 7, 7, 7, 7, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 2, 3, 3, 4, 5, 3, + 4, 3, 1, 1, 2, 3, 4, 1, 2, 3, + 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 4, 3, 5, 1, 2, 4, 4, 4, + 3, 0, 1, 1, 3, 1, 1, 1, 2, 2, + 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 3, 3, 3, 1, 3, 3, 1, 3, 3, + 3, 1, 3, 3, 3, 3, 3, 3, 1, 3, + 3, 3, 3, 3, 1, 3, 3, 3, 3, 1, + 3, 3, 3, 3, 1, 3, 1, 3, 1, 3, + 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, + 1, 3, 1, 3, 1, 5, 1, 5, 1, 3, + 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 3, 0, 1, 1, 3, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 3, 1, 2, + 0, 1, 3, 3, 1, 1, 1, 3, 1, 3, + 2, 2, 2, 0, 1, 2, 0, 1, 1, 2, + 2, 7, 5, 7, 7, 5, 9, 10, 7, 8, + 2, 2, 3, 3, 2, 2, 3, 3, 3, 3, + 5, 5, 3, 5, 1, 2, 0, 1, 4, 3, + 3, 3, 3, 3, 3, 3, 3, 4, 5, 2, + 2, 2, 8, 8, 1, 3, 0, 1, 0, 1, + 1, 1, 2, 1, 1, 0, 1, 0, 1, 2}; + +const int QmlJSGrammar::action_default [] = { + 0, 0, 0, 0, 20, 0, 168, 235, 199, 207, + 203, 147, 219, 195, 3, 132, 66, 148, 211, 215, + 136, 165, 146, 151, 131, 185, 172, 0, 73, 74, + 69, 336, 61, 338, 0, 0, 0, 0, 71, 0, + 0, 67, 70, 0, 0, 62, 64, 63, 72, 65, + 0, 68, 0, 0, 161, 0, 0, 148, 167, 150, + 149, 0, 0, 0, 163, 164, 162, 166, 0, 196, + 0, 0, 0, 0, 186, 0, 0, 0, 0, 0, + 0, 176, 0, 0, 0, 170, 171, 169, 174, 178, + 177, 175, 173, 188, 187, 189, 0, 204, 0, 200, + 0, 0, 142, 129, 141, 130, 98, 99, 100, 125, + 101, 126, 102, 103, 104, 105, 106, 107, 108, 109, + 110, 111, 112, 113, 114, 127, 115, 116, 117, 118, + 119, 120, 121, 122, 123, 124, 128, 0, 0, 140, + 236, 143, 0, 144, 0, 145, 139, 0, 232, 225, + 223, 230, 231, 229, 228, 234, 227, 226, 224, 233, + 220, 0, 208, 0, 0, 212, 0, 0, 216, 0, + 0, 142, 134, 0, 133, 0, 138, 152, 0, 337, + 327, 328, 0, 325, 0, 326, 0, 329, 243, 250, + 249, 257, 245, 0, 246, 330, 0, 335, 247, 248, + 253, 251, 332, 331, 334, 254, 0, 265, 0, 0, + 0, 0, 336, 61, 0, 338, 62, 237, 279, 63, + 0, 0, 0, 266, 0, 0, 255, 256, 0, 244, + 252, 280, 281, 324, 333, 0, 295, 296, 297, 298, + 0, 291, 292, 293, 294, 321, 322, 0, 0, 0, + 0, 0, 284, 285, 241, 239, 201, 209, 205, 221, + 197, 242, 0, 148, 213, 217, 190, 179, 0, 0, + 198, 0, 0, 0, 0, 191, 0, 0, 0, 0, + 0, 183, 181, 184, 182, 180, 193, 192, 194, 0, + 206, 0, 202, 0, 240, 148, 0, 222, 237, 238, + 0, 237, 0, 0, 287, 0, 0, 0, 289, 0, + 210, 0, 0, 214, 0, 0, 218, 277, 0, 269, + 278, 272, 0, 276, 0, 237, 270, 0, 237, 0, + 0, 288, 0, 0, 0, 290, 337, 327, 0, 0, + 329, 0, 323, 0, 313, 0, 0, 0, 283, 0, + 282, 0, 339, 0, 97, 259, 262, 0, 98, 265, + 101, 126, 103, 104, 69, 108, 109, 61, 110, 113, + 67, 70, 62, 237, 63, 72, 116, 65, 118, 68, + 120, 121, 266, 123, 124, 128, 0, 90, 0, 0, + 92, 96, 94, 81, 93, 95, 0, 91, 80, 260, + 258, 136, 137, 142, 0, 135, 0, 312, 0, 299, + 300, 0, 311, 0, 0, 0, 302, 307, 305, 308, + 0, 0, 306, 307, 0, 303, 0, 304, 261, 310, + 0, 261, 309, 0, 314, 315, 0, 261, 316, 317, + 0, 0, 318, 0, 0, 0, 319, 320, 154, 153, + 0, 0, 0, 286, 0, 0, 0, 301, 274, 267, + 0, 275, 271, 0, 273, 263, 0, 264, 268, 84, + 0, 0, 88, 75, 0, 77, 86, 0, 78, 87, + 89, 79, 85, 76, 0, 82, 158, 156, 160, 157, + 155, 159, 2, 59, 83, 0, 0, 62, 64, 63, + 29, 4, 0, 60, 0, 38, 37, 36, 0, 0, + 51, 0, 52, 0, 57, 58, 0, 38, 0, 0, + 0, 0, 0, 47, 48, 0, 49, 0, 50, 0, + 53, 54, 0, 0, 0, 0, 0, 55, 56, 0, + 45, 39, 46, 40, 0, 0, 0, 0, 42, 0, + 43, 44, 41, 0, 0, 28, 32, 33, 34, 35, + 136, 261, 0, 0, 98, 265, 101, 126, 103, 104, + 69, 108, 109, 61, 110, 113, 67, 70, 62, 237, + 63, 72, 116, 65, 118, 68, 120, 121, 266, 123, + 124, 128, 136, 0, 24, 0, 0, 30, 25, 31, + 26, 22, 0, 27, 23, 6, 0, 8, 0, 7, + 0, 1, 19, 10, 0, 11, 0, 12, 0, 17, + 18, 0, 13, 14, 0, 15, 16, 9, 21, 5, + 340}; + +const int QmlJSGrammar::goto_default [] = { + 5, 611, 355, 193, 501, 610, 629, 605, 609, 607, + 612, 20, 608, 16, 500, 602, 593, 555, 502, 188, + 192, 194, 198, 518, 544, 543, 197, 229, 24, 471, + 470, 353, 352, 7, 351, 354, 104, 15, 142, 22, + 11, 141, 17, 23, 54, 21, 6, 26, 25, 266, + 13, 260, 8, 256, 10, 258, 9, 257, 18, 264, + 19, 265, 12, 259, 255, 296, 408, 261, 262, 199, + 190, 189, 201, 230, 200, 205, 226, 227, 191, 357, + 356, 228, 460, 459, 318, 319, 462, 321, 461, 320, + 416, 420, 423, 419, 418, 438, 439, 182, 196, 178, + 181, 195, 203, 202, 0}; + +const int QmlJSGrammar::action_index [] = { + 251, 1237, 2238, 974, -45, 51, 122, -98, 52, -11, + -39, 182, -98, 389, 36, -98, -98, 628, 58, 75, + 246, 228, -98, -98, -98, 438, 309, 1237, -98, -98, + -98, 348, -98, 2050, 1772, 1237, 1237, 1237, -98, 800, + 1237, -98, -98, 1237, 1237, -98, -98, -98, -98, -98, + 1237, -98, 1237, 1237, -98, 1237, 1237, 97, 226, -98, + -98, 1237, 1237, 1237, -98, -98, -98, 200, 1237, 389, + 1237, 1237, 1237, 1237, 418, 1237, 1237, 1237, 1237, 1237, + 1237, 212, 1237, 1237, 1237, 144, 121, 71, 309, 309, + 164, 156, 179, 448, 428, 408, 1237, -51, 1237, 101, + 1956, 1237, 1237, -98, -98, -98, -98, -98, -98, -98, + -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, + -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, + -98, -98, -98, -98, -98, -98, -98, 236, 1237, -98, + -98, 31, 16, -98, 1237, -98, -98, 1237, -98, -98, + -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, + -98, 1237, 2, 1237, 1237, 41, 35, 1237, -98, 1956, + 1237, 1237, -98, 102, -98, 6, -98, -98, 20, -98, + 299, 53, 14, -98, 321, -98, -22, 2332, -98, -98, + -98, -98, -98, 216, -98, -98, -28, -98, -98, -98, + -98, -98, -98, 2332, -98, -98, 469, -98, 533, 115, + 2238, -2, 335, -3, -36, 2520, 50, 1237, -98, 64, + 33, 1237, 21, -98, 5, -14, -98, -98, 306, -98, + -98, -98, -98, -98, -98, 74, -98, -98, -98, -98, + 117, -98, -98, -98, -98, -98, -98, -57, -18, 1237, + 140, 109, -98, -98, 1324, -98, 104, 67, 27, -98, + 330, 84, 29, 585, 80, 92, 464, 171, 295, 1237, + 314, 1237, 1237, 1237, 1237, 346, 1237, 1237, 1237, 1237, + 1237, 309, 309, 309, 309, 309, 386, 464, 464, 1237, + 27, 1237, 87, 1237, -98, 529, 1237, -98, 1237, 83, + 38, 1237, 48, 2238, -98, 1237, 143, 2238, -98, 1237, + 30, 1237, 1237, 81, 77, 1237, -98, 56, 126, 90, + -98, -98, 1237, -98, 290, 1237, -98, 42, 1237, 44, + 2238, -98, 1237, 133, 2238, -98, 22, 285, -37, -9, + 2332, -30, -98, 2238, -98, 1237, 141, 2238, 10, 2238, + -98, 26, 24, -24, -98, -98, 2238, 25, 453, 23, + 533, 106, 1237, 2238, 9, -29, 361, -4, -31, 800, + -6, 55, -98, 1415, -98, -1, -23, 8, 1237, 7, + -21, 1237, 1, 1237, -27, -19, 1237, -98, 2144, 28, + -98, -98, -98, -98, -98, -98, 1237, -98, -98, -98, + -98, 211, -98, 1237, 17, -98, 2238, -98, 98, -98, + -98, 2238, -98, 1237, 105, 37, -98, 63, -98, 62, + 99, 1237, -98, 60, 57, -98, 4, -98, 2238, -98, + 116, 2238, -98, 180, -98, -98, 112, 2238, 18, -98, + 11, 13, -98, 325, 3, 19, -98, -98, -98, -98, + 1237, 132, 2238, -98, 1237, 139, 2238, -98, 32, -98, + 174, -98, -98, 1237, -98, -98, 229, -98, -98, -98, + 108, 1682, -98, -98, 1862, -98, -98, 1505, -98, -98, + -98, -98, -98, -98, 125, -98, -98, -98, -98, -98, + -98, -98, -98, -98, 111, 46, 792, 184, 34, 85, + -98, -98, 136, -98, 185, -98, -98, -98, 65, 204, + -98, 1237, -98, 207, -98, -98, 193, 61, 43, 225, + 73, 88, 66, -98, -98, 214, -98, 1237, -98, 158, + -98, -98, 181, 91, 72, 1237, 172, -98, -98, 187, + -98, 157, -98, 47, -50, 274, 178, 250, -98, 113, + -98, -98, -98, 1592, 1062, -98, -98, -98, -98, -98, + 271, 2426, 1772, 15, 461, 146, 445, 76, 1237, 2238, + -5, 0, 370, 12, -15, 706, 89, 78, -98, 1415, + -98, 100, 59, 82, 1237, 86, 54, 1237, 79, 1237, + 45, 39, 279, 149, -98, 68, 617, -98, -98, -98, + -98, -98, 1150, -98, -98, -98, 886, -98, 240, -61, + 714, -98, -98, 129, 259, -98, 190, -98, 69, -98, + -98, 255, -98, -98, 94, -98, -98, -98, -98, -98, + -98, + + -105, 23, 25, 216, -105, -105, -105, -105, -105, -105, + -105, -105, -105, -105, -105, -105, -105, -44, -105, -105, + -105, -105, -105, -105, -105, -105, -105, 70, -105, -105, + -105, 28, -105, -105, 20, 39, 97, 163, -105, 178, + 159, -105, -105, 156, 164, -105, -105, -105, -105, -105, + 132, -105, 153, 135, -105, 139, 140, -105, -105, -105, + -105, 103, 92, 144, -105, -105, -105, -105, 125, -105, + 154, 162, 83, 84, -105, 88, 82, 81, 75, 66, + 122, -105, 116, 98, 104, -105, -105, -105, -105, -105, + -105, -105, -105, -105, -105, -105, 106, -105, 113, -105, + 86, 80, 52, -105, -105, -105, -105, -105, -105, -105, + -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, + -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, + -105, -105, -105, -105, -105, -105, -105, -105, 34, -105, + -105, -105, -105, -105, 38, -105, -105, 43, -105, -105, + -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, + -105, 136, -105, 91, -29, -105, -105, 3, -105, 225, + 37, 71, -105, -105, -105, -105, -105, -105, -105, -105, + -3, -105, -105, -105, 18, -105, -105, 27, -105, -105, + -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, + -105, -105, -105, 87, -105, -105, 62, -105, 56, -105, + 50, -105, 31, -105, -105, -105, -105, 59, -105, -105, + -105, 47, 69, -105, -105, -105, -105, -105, 67, -105, + -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, + -105, -105, -105, -105, -105, -105, -105, -105, -105, 30, + -105, -105, -105, -105, 96, -105, -105, -105, -105, -105, + -105, -105, -105, -105, -105, -105, -105, -105, 10, 184, + -105, 222, 226, 232, 202, -105, 124, 114, 112, 110, + 95, -105, -105, -105, -105, -105, -105, -105, -105, 212, + -105, 192, -105, 177, -105, -105, 208, -105, 107, -105, + -105, 255, -105, 11, -105, 13, -105, 44, -105, 206, + -105, 169, 176, -105, -105, 183, -105, -105, -105, -105, + -105, -105, 173, -105, 167, 117, -105, -105, 191, -105, + 54, -105, 55, -105, 58, -105, -105, 102, -105, -105, + 89, -105, -105, 57, -105, 46, -105, 45, -105, 49, + -105, -105, -105, -105, -105, -105, 77, -105, 64, -105, + 51, -105, 109, 61, -105, -105, 48, -105, -105, 152, + -105, -105, -105, 79, -105, -105, -105, -105, 4, -105, + -16, 153, -105, 74, -105, -105, -11, -105, 0, -105, + -105, -105, -105, -105, -105, -105, -5, -105, -105, -105, + -105, -105, -105, 68, -105, -105, 41, -105, -105, -105, + -105, 90, -105, -1, -105, -105, -105, -105, -105, -93, + -105, 32, -105, -4, -105, -105, -105, -105, -22, -105, + -105, -14, -105, -105, -105, -105, -105, -105, -26, -105, + -105, -2, -105, 60, -105, 53, -105, -105, -105, -105, + 65, -105, 76, -105, 78, -105, 72, -105, -105, -105, + -105, -105, -105, 21, -105, -105, 85, -105, -105, -105, + -105, 22, -105, -105, 180, -105, -105, 33, -105, -105, + -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, + -105, -105, -105, -105, -105, -105, 73, -105, -105, -105, + -105, -105, -105, -105, 63, -105, -105, -105, -105, -105, + -105, 12, -105, -105, -105, -105, -105, -105, -105, -12, + -105, -105, -105, -105, -105, -105, -105, 6, -105, -105, + -105, -105, -15, -105, -105, 9, -105, -105, -105, -105, + -105, -9, -105, -105, -105, -7, -18, -13, -105, -105, + -105, -105, -105, 277, 266, -105, -105, -105, -105, -105, + -105, -105, 269, 2, 26, -105, 24, -105, 170, 16, + -105, -105, 19, -105, -105, 182, -105, -105, -105, 35, + -105, -105, -105, -105, 42, -105, 29, 153, -105, 128, + -105, -105, -105, -105, -105, -105, 349, -105, -105, -105, + -105, -105, 274, -105, -105, -105, 14, -105, -105, 15, + 218, -105, -105, -105, 7, -105, -105, -105, -105, -105, + -105, 17, -105, -105, -105, -105, -105, -105, -105, -105, + -105}; + +const int QmlJSGrammar::action_info [] = { + 345, -114, -106, -92, 343, 345, -95, 254, -122, 450, + 549, 187, 437, 413, -119, -117, -106, 454, 249, -92, + 248, 345, 454, 339, 340, 342, 349, 233, 98, 606, + -125, 398, 388, 386, 254, 396, 254, 96, 441, 144, + 98, 450, 167, 161, 138, 606, 437, 443, 554, 463, + 96, 630, 437, 68, 437, 546, 180, 406, 337, 427, + 161, 184, -96, 445, 428, 421, 176, 421, 421, 413, + 417, 411, 525, 322, 186, 454, 146, 405, 289, 535, + 400, 450, 309, 309, 315, -96, -122, 437, 269, -117, + 298, 138, 293, -119, 509, 413, -114, 521, 519, 301, + 532, 554, 68, 328, 330, 269, 291, -95, 303, 424, + 138, 55, 504, 138, 539, 289, 474, 522, 440, 0, + 534, 332, 56, 431, 138, 163, 169, 524, 523, 164, + 620, 619, 441, 138, 324, 239, 238, 246, 245, 59, + 138, 138, 311, 553, 169, 170, 312, 138, 138, 138, + 60, 138, 0, -125, 425, 626, 625, 596, 174, 410, + 409, 55, 55, 170, 475, 415, 138, 246, 245, 554, + 253, 252, 56, 56, 551, 550, 246, 245, 244, 243, + 138, 82, 466, 83, 55, 485, 505, 325, 138, 82, + 0, 83, 452, 334, 84, 56, 82, 100, 83, 456, + 251, 347, 84, 307, 82, 597, 83, 505, 169, 84, + 505, 511, 61, 517, 505, 138, 101, 84, 102, 531, + 530, 527, 0, 541, 138, 0, 169, 170, 0, 403, + 0, 0, 507, 538, 537, 467, 465, 82, 61, 83, + 61, 435, 434, 506, 138, 170, 0, 403, 542, 540, + 84, 623, 622, 507, 505, 0, 507, 62, 32, 507, + 507, 169, 0, 63, 506, 512, 510, 506, 515, 514, + 506, 506, 0, 0, 0, 528, 526, 232, 231, 32, + 170, 621, 171, 62, 32, 62, 169, 616, 32, 63, + 0, 63, 139, 0, 169, 45, 47, 46, 0, 0, + 507, 617, 615, 32, -83, 170, 0, 171, 0, 0, + 0, 506, -83, 170, 32, 171, 45, 47, 46, 32, + 0, 45, 47, 46, 32, 45, 47, 46, 32, 0, + 0, 614, 271, 272, 82, 32, 83, 0, 0, 0, + 45, 47, 46, 4, 3, 2, 1, 84, 271, 272, + 32, 45, 47, 46, 32, 0, 45, 47, 46, 273, + 274, 45, 47, 46, 32, 45, 47, 46, 0, 276, + 277, 0, 45, 47, 46, 273, 274, 32, 278, 0, + 0, 279, 0, 280, 0, 0, 0, 45, 47, 46, + 32, 45, 47, 46, 0, 0, 0, -336, 0, 32, + 0, 45, 47, 46, 0, 0, -336, 70, 71, 276, + 277, 0, 0, 0, 45, 47, 46, 0, 278, 0, + 0, 279, 0, 280, 0, 0, 0, 45, 47, 46, + 0, 75, 76, 0, 72, 73, 45, 47, 46, 77, + 78, 75, 76, 79, 0, 80, 0, 0, 0, 77, + 78, 75, 76, 79, 0, 80, 0, 0, 0, 77, + 78, 75, 76, 79, 0, 80, 0, 0, 0, 77, + 78, 75, 76, 79, 32, 80, 0, 0, 0, 77, + 78, 0, 32, 79, 0, 80, 0, 276, 277, 0, + 32, 0, 0, 0, 0, 0, 278, 0, 32, 279, + 0, 280, 0, 0, 0, 0, 242, 241, 0, 0, + 0, 45, 47, 46, 237, 236, 0, 0, 0, 45, + 47, 46, 237, 236, 0, 0, 0, 45, 47, 46, + 237, 236, 148, 0, 0, 45, 47, 46, 0, 0, + 0, 0, 149, 0, 0, 0, 150, 0, 0, 0, + 0, 0, 0, 0, 0, 151, 0, 152, 0, 0, + 0, 0, 32, 0, 0, 0, 0, 0, 153, 0, + 154, 59, 0, 0, 0, 0, 0, 0, 155, 0, + 0, 156, 60, 0, 0, 0, 0, 157, 148, 0, + 0, 0, 0, 158, 242, 241, 0, 0, 149, 45, + 47, 46, 150, 0, 0, 0, 0, 0, 0, 159, + 0, 151, 0, 152, 0, 0, 305, 0, 0, 0, + 0, 0, 0, 0, 153, 0, 154, 59, 0, 28, + 29, 148, 0, 0, 155, 0, 0, 156, 60, 31, + 0, 149, 0, 157, 0, 150, 32, 0, 0, 158, + 33, 34, 0, 35, 151, 0, 152, 0, 0, 0, + 496, 0, 0, 0, 42, 159, 0, 153, 0, 154, + 59, 0, 0, 0, 0, 0, 0, 155, 0, 0, + 156, 60, 48, 45, 47, 46, 157, 49, 0, 0, + 0, 0, 158, 0, 0, 0, 0, 0, 41, 51, + 30, 0, 0, 0, 38, 0, 0, 0, 159, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 28, 29, + 0, 0, 0, 0, 0, 0, 28, 29, 31, 0, + 0, 0, 0, 0, 0, 32, 31, 0, 0, 33, + 34, 0, 35, 32, 0, 0, 0, 33, 34, 39, + 35, 0, 0, 42, 0, 0, 0, 496, 0, 0, + 0, 42, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 48, 45, 47, 46, 0, 49, 0, 0, 48, + 45, 47, 46, 0, 49, 0, 0, 41, 51, 30, + 0, 0, 0, 38, 0, 41, 51, 30, 0, 0, + 0, 38, 0, 0, 28, 29, 0, 0, 0, 0, + 0, 0, 28, 29, 31, 0, 0, 0, 0, 0, + 0, 32, 31, 0, 0, 33, 34, 0, 35, 32, + 0, 0, 0, 33, 34, 496, 35, 0, 0, 42, + 0, 0, 0, 39, 0, 0, 0, 42, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 48, 45, 47, + 46, 0, 49, 0, 0, 48, 45, 47, 46, 0, + 49, 0, 0, 41, 51, 30, 0, 0, 0, 38, + 0, 41, 51, 30, 0, 0, 0, 38, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 28, 29, + 0, 0, 0, 0, 0, 0, 0, 0, 31, 0, + 0, 0, 0, 0, 0, 32, 0, 0, 0, 33, + 34, 0, 35, 0, 0, 0, 0, 0, 0, 496, + 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 48, 45, 47, 46, 0, 49, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 41, 51, 30, + 0, 0, 0, 38, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 495, 0, 28, 29, 0, 0, + 0, 0, 0, 0, 0, 0, 212, 0, 0, 0, + 0, 0, 0, 32, 0, 0, 0, 33, 34, 0, + 35, 0, 0, 0, 0, 0, 0, 496, 0, 0, + 0, 42, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, + 497, 499, 498, 0, 49, 0, 0, 0, 0, 223, + 0, 0, 0, 0, 0, 41, 51, 30, 207, 0, + 0, 38, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 495, 0, 28, 29, 0, 0, 0, 0, + 0, 0, 0, 0, 212, 0, 0, 0, 0, 0, + 0, 32, 0, 0, 0, 33, 34, 0, 35, 0, + 0, 0, 0, 0, 0, 496, 0, 0, 0, 42, + 0, 0, 0, 0, 0, 0, 0, 600, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 48, 497, 499, + 498, 0, 49, 0, 0, 0, 0, 223, 0, 0, + 0, 0, 0, 41, 51, 30, 207, 0, 0, 38, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 495, 0, 28, 29, 0, 0, 0, 0, 0, 0, + 0, 0, 212, 0, 0, 0, 0, 0, 0, 32, + 0, 0, 0, 33, 34, 0, 35, 0, 0, 0, + 0, 0, 0, 496, 0, 0, 0, 42, 0, 0, + 0, 0, 0, 0, 0, 603, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 48, 497, 499, 498, 0, + 49, 0, 0, 0, 0, 223, 0, 0, 0, 0, + 0, 41, 51, 30, 207, 0, 0, 38, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 27, 28, + 29, 0, 0, 0, 0, 0, 0, 0, 0, 31, + 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, + 33, 34, 0, 35, 0, 0, 0, 36, 0, 37, + 39, 40, 0, 0, 42, 0, 0, 0, 43, 0, + 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 48, 45, 47, 46, 0, 49, 0, 50, + 0, 52, 0, 53, 0, 0, 0, 0, 41, 51, + 30, 0, 0, 0, 38, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 27, 28, 29, 0, 0, + 0, 0, 0, 0, 0, 0, 31, 0, 0, 0, + 0, 0, 0, 32, 0, 0, 0, 33, 34, 0, + 35, 0, 0, 0, 36, 0, 37, 39, 40, 0, + 0, 42, 0, 0, 0, 43, 0, 44, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, + 45, 47, 46, 0, 49, 0, 50, 0, 52, 268, + 53, 0, 0, 0, 0, 41, 51, 30, 0, 0, + 0, 38, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, -115, 0, 0, 0, 27, 28, 29, 0, + 0, 0, 0, 0, 0, 0, 0, 31, 0, 0, + 0, 0, 0, 0, 32, 0, 0, 0, 33, 34, + 0, 35, 0, 0, 0, 36, 0, 37, 39, 40, + 0, 0, 42, 0, 0, 0, 43, 0, 44, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 48, 45, 47, 46, 0, 49, 0, 50, 0, 52, + 0, 53, 0, 0, 0, 0, 41, 51, 30, 0, + 0, 0, 38, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 480, 0, 0, 27, 28, 29, 0, + 0, 0, 0, 0, 0, 0, 0, 31, 0, 0, + 0, 0, 0, 0, 32, 0, 0, 0, 33, 34, + 0, 35, 0, 0, 0, 36, 0, 37, 39, 40, + 0, 0, 42, 0, 0, 0, 43, 0, 44, 0, + 0, 481, 0, 0, 0, 0, 0, 0, 0, 0, + 48, 45, 47, 46, 0, 49, 0, 50, 0, 52, + 0, 53, 0, 0, 0, 0, 41, 51, 30, 0, + 0, 0, 38, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 27, 28, 29, 0, 0, 0, 0, + 0, 0, 0, 0, 31, 0, 0, 0, 0, 0, + 0, 32, 214, 0, 0, 561, 562, 0, 35, 0, + 0, 0, 36, 0, 37, 39, 40, 0, 0, 42, + 0, 0, 0, 43, 0, 44, 0, 0, 0, 0, + 0, 0, 0, 218, 0, 0, 0, 48, 45, 47, + 46, 0, 49, 0, 50, 0, 52, 0, 53, 0, + 0, 0, 0, 41, 51, 30, 0, 0, 0, 38, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 480, 0, 0, 27, 28, 29, 0, 0, 0, 0, + 0, 0, 0, 0, 31, 0, 0, 0, 0, 0, + 0, 32, 0, 0, 0, 33, 34, 0, 35, 0, + 0, 0, 36, 0, 37, 39, 40, 0, 0, 42, + 0, 0, 0, 43, 0, 44, 0, 0, 483, 0, + 0, 0, 0, 0, 0, 0, 0, 48, 45, 47, + 46, 0, 49, 0, 50, 0, 52, 0, 53, 0, + 0, 0, 0, 41, 51, 30, 0, 0, 0, 38, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 472, 0, 0, 27, 28, 29, 0, 0, 0, 0, + 0, 0, 0, 0, 31, 0, 0, 0, 0, 0, + 0, 32, 0, 0, 0, 33, 34, 0, 35, 0, + 0, 0, 36, 0, 37, 39, 40, 0, 0, 42, + 0, 0, 0, 43, 0, 44, 0, 0, 473, 0, + 0, 0, 0, 0, 0, 0, 0, 48, 45, 47, + 46, 0, 49, 0, 50, 0, 52, 0, 53, 0, + 0, 0, 0, 41, 51, 30, 0, 0, 0, 38, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 472, 0, 0, 27, 28, 29, 0, 0, 0, 0, + 0, 0, 0, 0, 31, 0, 0, 0, 0, 0, + 0, 32, 0, 0, 0, 33, 34, 0, 35, 0, + 0, 0, 36, 0, 37, 39, 40, 0, 0, 42, + 0, 0, 0, 43, 0, 44, 0, 0, 478, 0, + 0, 0, 0, 0, 0, 0, 0, 48, 45, 47, + 46, 0, 49, 0, 50, 0, 52, 0, 53, 0, + 0, 0, 0, 41, 51, 30, 0, 0, 0, 38, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 106, 107, 108, 0, 0, 110, 112, 113, 0, 0, + 114, 0, 115, 0, 0, 0, 117, 118, 119, 0, + 0, 0, 0, 0, 0, 32, 120, 121, 122, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 123, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 126, 0, 0, 0, 0, + 0, 0, 45, 47, 46, 127, 128, 129, 0, 131, + 132, 133, 134, 135, 136, 0, 0, 124, 130, 116, + 109, 111, 125, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 106, 107, 108, 0, 0, 110, + 112, 113, 0, 0, 114, 0, 115, 0, 0, 0, + 117, 118, 119, 0, 0, 0, 0, 0, 0, 390, + 120, 121, 122, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 123, 0, 0, 0, 391, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 126, + 0, 0, 0, 0, 0, 395, 392, 394, 0, 127, + 128, 129, 0, 131, 132, 133, 134, 135, 136, 0, + 0, 124, 130, 116, 109, 111, 125, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 106, 107, + 108, 0, 0, 110, 112, 113, 0, 0, 114, 0, + 115, 0, 0, 0, 117, 118, 119, 0, 0, 0, + 0, 0, 0, 390, 120, 121, 122, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 123, 0, 0, + 0, 391, 0, 0, 0, 0, 0, 0, 0, 393, + 0, 0, 0, 126, 0, 0, 0, 0, 0, 395, + 392, 394, 0, 127, 128, 129, 0, 131, 132, 133, + 134, 135, 136, 0, 0, 124, 130, 116, 109, 111, + 125, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 206, 0, 0, 0, 0, 208, 0, 27, + 28, 29, 210, 0, 0, 0, 0, 0, 0, 211, + 31, 0, 0, 0, 0, 0, 0, 213, 214, 0, + 0, 215, 34, 0, 35, 0, 0, 0, 36, 0, + 37, 39, 40, 0, 0, 42, 0, 0, 0, 43, + 0, 44, 0, 0, 0, 0, 0, 217, 0, 218, + 0, 0, 0, 48, 216, 219, 46, 220, 49, 221, + 50, 222, 52, 223, 53, 224, 225, 0, 0, 41, + 51, 30, 207, 209, 0, 38, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 206, 0, 0, 0, + 0, 208, 0, 27, 28, 29, 210, 0, 0, 0, + 0, 0, 0, 211, 212, 0, 0, 0, 0, 0, + 0, 213, 214, 0, 0, 215, 34, 0, 35, 0, + 0, 0, 36, 0, 37, 39, 40, 0, 0, 42, + 0, 0, 0, 43, 0, 44, 0, 0, 0, 0, + 0, 217, 0, 218, 0, 0, 0, 48, 216, 219, + 46, 220, 49, 221, 50, 222, 52, 223, 53, 224, + 225, 0, 0, 41, 51, 30, 207, 209, 0, 38, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 564, 107, 108, 0, 0, 566, 112, 568, 28, 29, + 569, 0, 115, 0, 0, 0, 117, 571, 572, 0, + 0, 0, 0, 0, 0, 573, 574, 121, 122, 215, + 34, 0, 35, 0, 0, 0, 36, 0, 37, 575, + 40, 0, 0, 577, 0, 0, 0, 43, 0, 44, + 0, 0, 0, 0, 0, 579, 0, 218, 0, 0, + 0, 581, 578, 580, 46, 582, 583, 584, 50, 586, + 587, 588, 589, 590, 591, 0, 0, 576, 585, 570, + 565, 567, 125, 38, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 358, 107, 108, 0, 0, 360, + 112, 362, 28, 29, 363, 0, 115, 0, 0, 0, + 117, 365, 366, 0, 0, 0, 0, 0, 0, 367, + 368, 121, 122, 215, 34, 0, 35, 0, 0, 0, + 36, 0, 37, 369, 40, 0, 0, 371, 0, 0, + 0, 43, 0, 44, 0, -261, 0, 0, 0, 373, + 0, 218, 0, 0, 0, 375, 372, 374, 46, 376, + 377, 378, 50, 380, 381, 382, 383, 384, 385, 0, + 0, 370, 379, 364, 359, 361, 125, 38, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + + 548, 422, 414, 436, 166, 547, 552, 433, 533, 529, + 183, 520, 536, 304, 545, 513, 306, 447, 247, 599, + 618, 147, 387, 317, 627, 613, 14, 492, 397, 204, + 624, 185, 179, 250, 389, 430, 168, 240, 299, 235, + 173, 179, 484, 407, 336, 433, 308, 348, 436, 346, + 433, 350, 247, 469, 464, 482, 331, 429, 333, 344, + 335, 179, 299, 247, 240, 432, 479, 140, 451, 240, + 442, 145, 446, 444, 457, 235, 160, 235, 453, 399, + 458, 455, 299, 137, 516, 143, 508, 426, 436, 204, + 0, 204, 412, 0, 0, 0, 0, 0, 458, 103, + 0, 143, 0, 0, 143, 0, 404, 0, 57, 175, + 299, 0, 57, 91, 177, 183, 57, 57, 449, 0, + 299, 105, 90, 57, 57, 57, 57, 0, 89, 88, + 57, 94, 95, 57, 57, 81, 65, 57, 263, 57, + 57, 486, 285, 267, 86, 57, 57, 64, 57, 165, + 87, 57, 57, 177, 57, 57, 57, 284, 57, 283, + 97, 282, 85, 401, 57, 99, 57, 57, 468, 92, + 57, 281, 449, 300, 57, 69, 491, 57, 57, 449, + 317, 57, 57, 327, 58, 67, 57, 341, 66, 401, + 234, 402, 162, 401, 299, 57, 57, 448, 57, 338, + 489, 57, 74, 488, 57, 57, 57, 487, 490, 477, + 93, 57, 57, 476, 177, 295, 267, 402, 295, 295, + 267, 402, 0, 267, 267, 295, 57, 494, 313, 494, + 267, 267, 628, 0, 57, 270, 595, 323, 103, 267, + 314, 294, 493, 503, 57, 292, 0, 316, 57, 267, + 295, 288, 326, 267, 57, 267, 0, 329, 299, 267, + 105, 172, 0, 310, 57, 0, 0, 290, 57, 267, + 601, 275, 297, 267, 57, 286, 0, 494, 604, 267, + 592, 287, 0, 594, 0, 494, 0, 595, 560, 0, + 0, 0, 493, 503, 0, 563, 556, 557, 558, 559, + 493, 503, 469, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 302, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 494, 0, 0, 598, 0, 0, 0, 595, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0}; + +const int QmlJSGrammar::action_check [] = { + 36, 7, 7, 7, 7, 36, 7, 36, 7, 36, + 60, 33, 33, 36, 7, 7, 7, 36, 36, 7, + 77, 36, 36, 60, 33, 55, 16, 55, 79, 90, + 7, 55, 8, 7, 36, 7, 36, 48, 20, 8, + 79, 36, 7, 2, 8, 90, 33, 36, 33, 17, + 48, 0, 33, 1, 33, 8, 36, 7, 36, 55, + 2, 8, 7, 60, 7, 5, 60, 5, 5, 36, + 33, 7, 29, 17, 60, 36, 60, 60, 48, 7, + 55, 36, 2, 2, 7, 7, 7, 33, 1, 7, + 61, 8, 8, 7, 29, 36, 7, 24, 37, 61, + 66, 33, 1, 61, 60, 1, 79, 7, 60, 10, + 8, 40, 66, 8, 29, 48, 8, 29, 6, -1, + 29, 31, 51, 7, 8, 50, 15, 61, 62, 54, + 61, 62, 20, 8, 8, 61, 62, 61, 62, 42, + 8, 8, 50, 7, 15, 34, 54, 8, 8, 8, + 53, 8, -1, 7, 55, 61, 62, 8, 56, 61, + 62, 40, 40, 34, 56, 60, 8, 61, 62, 33, + 61, 62, 51, 51, 61, 62, 61, 62, 61, 62, + 8, 25, 8, 27, 40, 60, 29, 61, 8, 25, + -1, 27, 60, 60, 38, 51, 25, 15, 27, 60, + 60, 60, 38, 60, 25, 56, 27, 29, 15, 38, + 29, 7, 12, 29, 29, 8, 34, 38, 36, 61, + 62, 7, -1, 36, 8, -1, 15, 34, -1, 36, + -1, -1, 75, 61, 62, 61, 62, 25, 12, 27, + 12, 61, 62, 86, 8, 34, -1, 36, 61, 62, + 38, 61, 62, 75, 29, -1, 75, 57, 29, 75, + 75, 15, -1, 63, 86, 61, 62, 86, 61, 62, + 86, 86, -1, -1, -1, 61, 62, 61, 62, 29, + 34, 91, 36, 57, 29, 57, 15, 47, 29, 63, + -1, 63, 56, -1, 15, 66, 67, 68, -1, -1, + 75, 61, 62, 29, 33, 34, -1, 36, -1, -1, + -1, 86, 33, 34, 29, 36, 66, 67, 68, 29, + -1, 66, 67, 68, 29, 66, 67, 68, 29, -1, + -1, 91, 18, 19, 25, 29, 27, -1, -1, -1, + 66, 67, 68, 92, 93, 94, 95, 38, 18, 19, + 29, 66, 67, 68, 29, -1, 66, 67, 68, 45, + 46, 66, 67, 68, 29, 66, 67, 68, -1, 23, + 24, -1, 66, 67, 68, 45, 46, 29, 32, -1, + -1, 35, -1, 37, -1, -1, -1, 66, 67, 68, + 29, 66, 67, 68, -1, -1, -1, 36, -1, 29, + -1, 66, 67, 68, -1, -1, 36, 18, 19, 23, + 24, -1, -1, -1, 66, 67, 68, -1, 32, -1, + -1, 35, -1, 37, -1, -1, -1, 66, 67, 68, + -1, 23, 24, -1, 45, 46, 66, 67, 68, 31, + 32, 23, 24, 35, -1, 37, -1, -1, -1, 31, + 32, 23, 24, 35, -1, 37, -1, -1, -1, 31, + 32, 23, 24, 35, -1, 37, -1, -1, -1, 31, + 32, 23, 24, 35, 29, 37, -1, -1, -1, 31, + 32, -1, 29, 35, -1, 37, -1, 23, 24, -1, + 29, -1, -1, -1, -1, -1, 32, -1, 29, 35, + -1, 37, -1, -1, -1, -1, 61, 62, -1, -1, + -1, 66, 67, 68, 61, 62, -1, -1, -1, 66, + 67, 68, 61, 62, -1, -1, -1, 66, 67, 68, + 61, 62, 3, -1, -1, 66, 67, 68, -1, -1, + -1, -1, 13, -1, -1, -1, 17, -1, -1, -1, + -1, -1, -1, -1, -1, 26, -1, 28, -1, -1, + -1, -1, 29, -1, -1, -1, -1, -1, 39, -1, + 41, 42, -1, -1, -1, -1, -1, -1, 49, -1, + -1, 52, 53, -1, -1, -1, -1, 58, 3, -1, + -1, -1, -1, 64, 61, 62, -1, -1, 13, 66, + 67, 68, 17, -1, -1, -1, -1, -1, -1, 80, + -1, 26, -1, 28, -1, -1, 31, -1, -1, -1, + -1, -1, -1, -1, 39, -1, 41, 42, -1, 12, + 13, 3, -1, -1, 49, -1, -1, 52, 53, 22, + -1, 13, -1, 58, -1, 17, 29, -1, -1, 64, + 33, 34, -1, 36, 26, -1, 28, -1, -1, -1, + 43, -1, -1, -1, 47, 80, -1, 39, -1, 41, + 42, -1, -1, -1, -1, -1, -1, 49, -1, -1, + 52, 53, 65, 66, 67, 68, 58, 70, -1, -1, + -1, -1, 64, -1, -1, -1, -1, -1, 81, 82, + 83, -1, -1, -1, 87, -1, -1, -1, 80, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 12, 13, + -1, -1, -1, -1, -1, -1, 12, 13, 22, -1, + -1, -1, -1, -1, -1, 29, 22, -1, -1, 33, + 34, -1, 36, 29, -1, -1, -1, 33, 34, 43, + 36, -1, -1, 47, -1, -1, -1, 43, -1, -1, + -1, 47, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 65, 66, 67, 68, -1, 70, -1, -1, 65, + 66, 67, 68, -1, 70, -1, -1, 81, 82, 83, + -1, -1, -1, 87, -1, 81, 82, 83, -1, -1, + -1, 87, -1, -1, 12, 13, -1, -1, -1, -1, + -1, -1, 12, 13, 22, -1, -1, -1, -1, -1, + -1, 29, 22, -1, -1, 33, 34, -1, 36, 29, + -1, -1, -1, 33, 34, 43, 36, -1, -1, 47, + -1, -1, -1, 43, -1, -1, -1, 47, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 65, 66, 67, + 68, -1, 70, -1, -1, 65, 66, 67, 68, -1, + 70, -1, -1, 81, 82, 83, -1, -1, -1, 87, + -1, 81, 82, 83, -1, -1, -1, 87, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 12, 13, + -1, -1, -1, -1, -1, -1, -1, -1, 22, -1, + -1, -1, -1, -1, -1, 29, -1, -1, -1, 33, + 34, -1, 36, -1, -1, -1, -1, -1, -1, 43, + -1, -1, -1, 47, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 65, 66, 67, 68, -1, 70, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 81, 82, 83, + -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 10, -1, 12, 13, -1, -1, + -1, -1, -1, -1, -1, -1, 22, -1, -1, -1, + -1, -1, -1, 29, -1, -1, -1, 33, 34, -1, + 36, -1, -1, -1, -1, -1, -1, 43, -1, -1, + -1, 47, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 65, + 66, 67, 68, -1, 70, -1, -1, -1, -1, 75, + -1, -1, -1, -1, -1, 81, 82, 83, 84, -1, + -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 10, -1, 12, 13, -1, -1, -1, -1, + -1, -1, -1, -1, 22, -1, -1, -1, -1, -1, + -1, 29, -1, -1, -1, 33, 34, -1, 36, -1, + -1, -1, -1, -1, -1, 43, -1, -1, -1, 47, + -1, -1, -1, -1, -1, -1, -1, 55, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 65, 66, 67, + 68, -1, 70, -1, -1, -1, -1, 75, -1, -1, + -1, -1, -1, 81, 82, 83, 84, -1, -1, 87, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 10, -1, 12, 13, -1, -1, -1, -1, -1, -1, + -1, -1, 22, -1, -1, -1, -1, -1, -1, 29, + -1, -1, -1, 33, 34, -1, 36, -1, -1, -1, + -1, -1, -1, 43, -1, -1, -1, 47, -1, -1, + -1, -1, -1, -1, -1, 55, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 65, 66, 67, 68, -1, + 70, -1, -1, -1, -1, 75, -1, -1, -1, -1, + -1, 81, 82, 83, 84, -1, -1, 87, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 11, 12, + 13, -1, -1, -1, -1, -1, -1, -1, -1, 22, + -1, -1, -1, -1, -1, -1, 29, -1, -1, -1, + 33, 34, -1, 36, -1, -1, -1, 40, -1, 42, + 43, 44, -1, -1, 47, -1, -1, -1, 51, -1, + 53, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 65, 66, 67, 68, -1, 70, -1, 72, + -1, 74, -1, 76, -1, -1, -1, -1, 81, 82, + 83, -1, -1, -1, 87, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 11, 12, 13, -1, -1, + -1, -1, -1, -1, -1, -1, 22, -1, -1, -1, + -1, -1, -1, 29, -1, -1, -1, 33, 34, -1, + 36, -1, -1, -1, 40, -1, 42, 43, 44, -1, + -1, 47, -1, -1, -1, 51, -1, 53, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 65, + 66, 67, 68, -1, 70, -1, 72, -1, 74, 75, + 76, -1, -1, -1, -1, 81, 82, 83, -1, -1, + -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 7, -1, -1, -1, 11, 12, 13, -1, + -1, -1, -1, -1, -1, -1, -1, 22, -1, -1, + -1, -1, -1, -1, 29, -1, -1, -1, 33, 34, + -1, 36, -1, -1, -1, 40, -1, 42, 43, 44, + -1, -1, 47, -1, -1, -1, 51, -1, 53, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 65, 66, 67, 68, -1, 70, -1, 72, -1, 74, + -1, 76, -1, -1, -1, -1, 81, 82, 83, -1, + -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 8, -1, -1, 11, 12, 13, -1, + -1, -1, -1, -1, -1, -1, -1, 22, -1, -1, + -1, -1, -1, -1, 29, -1, -1, -1, 33, 34, + -1, 36, -1, -1, -1, 40, -1, 42, 43, 44, + -1, -1, 47, -1, -1, -1, 51, -1, 53, -1, + -1, 56, -1, -1, -1, -1, -1, -1, -1, -1, + 65, 66, 67, 68, -1, 70, -1, 72, -1, 74, + -1, 76, -1, -1, -1, -1, 81, 82, 83, -1, + -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 11, 12, 13, -1, -1, -1, -1, + -1, -1, -1, -1, 22, -1, -1, -1, -1, -1, + -1, 29, 30, -1, -1, 33, 34, -1, 36, -1, + -1, -1, 40, -1, 42, 43, 44, -1, -1, 47, + -1, -1, -1, 51, -1, 53, -1, -1, -1, -1, + -1, -1, -1, 61, -1, -1, -1, 65, 66, 67, + 68, -1, 70, -1, 72, -1, 74, -1, 76, -1, + -1, -1, -1, 81, 82, 83, -1, -1, -1, 87, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 8, -1, -1, 11, 12, 13, -1, -1, -1, -1, + -1, -1, -1, -1, 22, -1, -1, -1, -1, -1, + -1, 29, -1, -1, -1, 33, 34, -1, 36, -1, + -1, -1, 40, -1, 42, 43, 44, -1, -1, 47, + -1, -1, -1, 51, -1, 53, -1, -1, 56, -1, + -1, -1, -1, -1, -1, -1, -1, 65, 66, 67, + 68, -1, 70, -1, 72, -1, 74, -1, 76, -1, + -1, -1, -1, 81, 82, 83, -1, -1, -1, 87, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 8, -1, -1, 11, 12, 13, -1, -1, -1, -1, + -1, -1, -1, -1, 22, -1, -1, -1, -1, -1, + -1, 29, -1, -1, -1, 33, 34, -1, 36, -1, + -1, -1, 40, -1, 42, 43, 44, -1, -1, 47, + -1, -1, -1, 51, -1, 53, -1, -1, 56, -1, + -1, -1, -1, -1, -1, -1, -1, 65, 66, 67, + 68, -1, 70, -1, 72, -1, 74, -1, 76, -1, + -1, -1, -1, 81, 82, 83, -1, -1, -1, 87, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 8, -1, -1, 11, 12, 13, -1, -1, -1, -1, + -1, -1, -1, -1, 22, -1, -1, -1, -1, -1, + -1, 29, -1, -1, -1, 33, 34, -1, 36, -1, + -1, -1, 40, -1, 42, 43, 44, -1, -1, 47, + -1, -1, -1, 51, -1, 53, -1, -1, 56, -1, + -1, -1, -1, -1, -1, -1, -1, 65, 66, 67, + 68, -1, 70, -1, 72, -1, 74, -1, 76, -1, + -1, -1, -1, 81, 82, 83, -1, -1, -1, 87, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 4, 5, 6, -1, -1, 9, 10, 11, -1, -1, + 14, -1, 16, -1, -1, -1, 20, 21, 22, -1, + -1, -1, -1, -1, -1, 29, 30, 31, 32, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 43, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 59, -1, -1, -1, -1, + -1, -1, 66, 67, 68, 69, 70, 71, -1, 73, + 74, 75, 76, 77, 78, -1, -1, 81, 82, 83, + 84, 85, 86, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 4, 5, 6, -1, -1, 9, + 10, 11, -1, -1, 14, -1, 16, -1, -1, -1, + 20, 21, 22, -1, -1, -1, -1, -1, -1, 29, + 30, 31, 32, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 43, -1, -1, -1, 47, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 59, + -1, -1, -1, -1, -1, 65, 66, 67, -1, 69, + 70, 71, -1, 73, 74, 75, 76, 77, 78, -1, + -1, 81, 82, 83, 84, 85, 86, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 4, 5, + 6, -1, -1, 9, 10, 11, -1, -1, 14, -1, + 16, -1, -1, -1, 20, 21, 22, -1, -1, -1, + -1, -1, -1, 29, 30, 31, 32, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 43, -1, -1, + -1, 47, -1, -1, -1, -1, -1, -1, -1, 55, + -1, -1, -1, 59, -1, -1, -1, -1, -1, 65, + 66, 67, -1, 69, 70, 71, -1, 73, 74, 75, + 76, 77, 78, -1, -1, 81, 82, 83, 84, 85, + 86, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 4, -1, -1, -1, -1, 9, -1, 11, + 12, 13, 14, -1, -1, -1, -1, -1, -1, 21, + 22, -1, -1, -1, -1, -1, -1, 29, 30, -1, + -1, 33, 34, -1, 36, -1, -1, -1, 40, -1, + 42, 43, 44, -1, -1, 47, -1, -1, -1, 51, + -1, 53, -1, -1, -1, -1, -1, 59, -1, 61, + -1, -1, -1, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, -1, -1, 81, + 82, 83, 84, 85, -1, 87, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 4, -1, -1, -1, + -1, 9, -1, 11, 12, 13, 14, -1, -1, -1, + -1, -1, -1, 21, 22, -1, -1, -1, -1, -1, + -1, 29, 30, -1, -1, 33, 34, -1, 36, -1, + -1, -1, 40, -1, 42, 43, 44, -1, -1, 47, + -1, -1, -1, 51, -1, 53, -1, -1, -1, -1, + -1, 59, -1, 61, -1, -1, -1, 65, 66, 67, + 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, + 78, -1, -1, 81, 82, 83, 84, 85, -1, 87, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 4, 5, 6, -1, -1, 9, 10, 11, 12, 13, + 14, -1, 16, -1, -1, -1, 20, 21, 22, -1, + -1, -1, -1, -1, -1, 29, 30, 31, 32, 33, + 34, -1, 36, -1, -1, -1, 40, -1, 42, 43, + 44, -1, -1, 47, -1, -1, -1, 51, -1, 53, + -1, -1, -1, -1, -1, 59, -1, 61, -1, -1, + -1, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, -1, -1, 81, 82, 83, + 84, 85, 86, 87, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 4, 5, 6, -1, -1, 9, + 10, 11, 12, 13, 14, -1, 16, -1, -1, -1, + 20, 21, 22, -1, -1, -1, -1, -1, -1, 29, + 30, 31, 32, 33, 34, -1, 36, -1, -1, -1, + 40, -1, 42, 43, 44, -1, -1, 47, -1, -1, + -1, 51, -1, 53, -1, 55, -1, -1, -1, 59, + -1, 61, -1, -1, -1, 65, 66, 67, 68, 69, + 70, 71, 72, 73, 74, 75, 76, 77, 78, -1, + -1, 81, 82, 83, 84, 85, 86, 87, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + + 13, 94, 3, 19, 33, 23, 13, 3, 23, 3, + 13, 23, 3, 2, 23, 3, 3, 19, 2, 17, + 13, 65, 33, 13, 9, 11, 3, 2, 33, 2, + 13, 13, 13, 3, 34, 3, 33, 13, 3, 13, + 3, 13, 3, 2, 13, 3, 2, 2, 19, 3, + 3, 2, 2, 33, 33, 33, 2, 79, 3, 2, + 2, 13, 3, 2, 13, 79, 33, 33, 3, 13, + 96, 33, 19, 13, 2, 13, 33, 13, 2, 2, + 13, 3, 3, 3, 11, 33, 23, 91, 19, 2, + -1, 2, 2, -1, -1, -1, -1, -1, 13, 13, + -1, 33, -1, -1, 33, -1, 38, -1, 42, 38, + 3, -1, 42, 47, 44, 13, 42, 42, 44, -1, + 3, 35, 47, 42, 42, 42, 42, -1, 47, 47, + 42, 48, 48, 42, 42, 47, 44, 42, 42, 42, + 42, 44, 47, 47, 46, 42, 42, 44, 42, 58, + 46, 42, 42, 44, 42, 42, 42, 47, 42, 47, + 54, 47, 46, 11, 42, 52, 42, 42, 83, 47, + 42, 47, 44, 66, 42, 50, 44, 42, 42, 44, + 13, 42, 42, 66, 45, 45, 42, 98, 44, 11, + 103, 39, 56, 11, 3, 42, 42, 44, 42, 97, + 44, 42, 48, 44, 42, 42, 42, 44, 44, 29, + 48, 42, 42, 33, 44, 42, 47, 39, 42, 42, + 47, 39, -1, 47, 47, 42, 42, 11, 59, 11, + 47, 47, 14, -1, 42, 51, 18, 64, 13, 47, + 64, 64, 26, 27, 42, 53, -1, 64, 42, 47, + 42, 49, 85, 47, 42, 47, -1, 66, 3, 47, + 35, 36, -1, 57, 42, -1, -1, 55, 42, 47, + 4, 49, 64, 47, 42, 49, -1, 11, 4, 47, + 11, 49, -1, 14, -1, 11, -1, 18, 11, -1, + -1, -1, 26, 27, -1, 18, 19, 20, 21, 22, + 26, 27, 33, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 11, -1, -1, 14, -1, -1, -1, 18, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1}; + diff --git a/src/plugins/qmldesigner/core/filemanager/parser/qmljsgrammar_p.h b/src/plugins/qmldesigner/core/filemanager/parser/qmljsgrammar_p.h new file mode 100644 index 00000000000..7de77c03a09 --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/parser/qmljsgrammar_p.h @@ -0,0 +1,207 @@ +// This file was generated by qlalr - DO NOT EDIT! +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QMLJSGRAMMAR_P_H +#define QMLJSGRAMMAR_P_H + +class QmlJSGrammar +{ +public: + enum { + EOF_SYMBOL = 0, + REDUCE_HERE = 97, + SHIFT_THERE = 96, + T_AND = 1, + T_AND_AND = 2, + T_AND_EQ = 3, + T_AS = 91, + T_AUTOMATIC_SEMICOLON = 62, + T_BREAK = 4, + T_CASE = 5, + T_CATCH = 6, + T_COLON = 7, + T_COMMA = 8, + T_COMMENT = 88, + T_CONST = 84, + T_CONTINUE = 9, + T_DEBUGGER = 85, + T_DEFAULT = 10, + T_DELETE = 11, + T_DIVIDE_ = 12, + T_DIVIDE_EQ = 13, + T_DO = 14, + T_DOT = 15, + T_ELSE = 16, + T_EQ = 17, + T_EQ_EQ = 18, + T_EQ_EQ_EQ = 19, + T_FALSE = 83, + T_FEED_JS_EXPRESSION = 95, + T_FEED_JS_STATEMENT = 94, + T_FEED_UI_OBJECT_MEMBER = 93, + T_FEED_UI_PROGRAM = 92, + T_FINALLY = 20, + T_FOR = 21, + T_FUNCTION = 22, + T_GE = 23, + T_GT = 24, + T_GT_GT = 25, + T_GT_GT_EQ = 26, + T_GT_GT_GT = 27, + T_GT_GT_GT_EQ = 28, + T_IDENTIFIER = 29, + T_IF = 30, + T_IMPORT = 90, + T_IN = 31, + T_INSTANCEOF = 32, + T_LBRACE = 33, + T_LBRACKET = 34, + T_LE = 35, + T_LPAREN = 36, + T_LT = 37, + T_LT_LT = 38, + T_LT_LT_EQ = 39, + T_MINUS = 40, + T_MINUS_EQ = 41, + T_MINUS_MINUS = 42, + T_MULTILINE_STRING_LITERAL = 87, + T_NEW = 43, + T_NOT = 44, + T_NOT_EQ = 45, + T_NOT_EQ_EQ = 46, + T_NULL = 81, + T_NUMERIC_LITERAL = 47, + T_OR = 48, + T_OR_EQ = 49, + T_OR_OR = 50, + T_PLUS = 51, + T_PLUS_EQ = 52, + T_PLUS_PLUS = 53, + T_PROPERTY = 66, + T_PUBLIC = 89, + T_QUESTION = 54, + T_RBRACE = 55, + T_RBRACKET = 56, + T_READONLY = 68, + T_REMAINDER = 57, + T_REMAINDER_EQ = 58, + T_RESERVED_WORD = 86, + T_RETURN = 59, + T_RPAREN = 60, + T_SEMICOLON = 61, + T_SIGNAL = 67, + T_STAR = 63, + T_STAR_EQ = 64, + T_STRING_LITERAL = 65, + T_SWITCH = 69, + T_THIS = 70, + T_THROW = 71, + T_TILDE = 72, + T_TRUE = 82, + T_TRY = 73, + T_TYPEOF = 74, + T_VAR = 75, + T_VOID = 76, + T_WHILE = 77, + T_WITH = 78, + T_XOR = 79, + T_XOR_EQ = 80, + + ACCEPT_STATE = 630, + RULE_COUNT = 340, + STATE_COUNT = 631, + TERMINAL_COUNT = 98, + NON_TERMINAL_COUNT = 105, + + GOTO_INDEX_OFFSET = 631, + GOTO_INFO_OFFSET = 2618, + GOTO_CHECK_OFFSET = 2618 + }; + + static const char *const spell []; + static const int lhs []; + static const int rhs []; + static const int goto_default []; + static const int action_default []; + static const int action_index []; + static const int action_info []; + static const int action_check []; + + static inline int nt_action (int state, int nt) + { + const int *const goto_index = &action_index [GOTO_INDEX_OFFSET]; + const int *const goto_check = &action_check [GOTO_CHECK_OFFSET]; + + const int yyn = goto_index [state] + nt; + + if (yyn < 0 || goto_check [yyn] != nt) + return goto_default [nt]; + + const int *const goto_info = &action_info [GOTO_INFO_OFFSET]; + return goto_info [yyn]; + } + + static inline int t_action (int state, int token) + { + const int yyn = action_index [state] + token; + + if (yyn < 0 || action_check [yyn] != token) + return - action_default [state]; + + return action_info [yyn]; + } +}; + + +#endif // QMLJSGRAMMAR_P_H + diff --git a/src/plugins/qmldesigner/core/filemanager/parser/qmljslexer.cpp b/src/plugins/qmldesigner/core/filemanager/parser/qmljslexer.cpp new file mode 100644 index 00000000000..a52ea7c0896 --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/parser/qmljslexer.cpp @@ -0,0 +1,1141 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "qmljsglobal_p.h" +#include "qmljsengine_p.h" +#include "qmljslexer_p.h" +#include "qmljsgrammar_p.h" + +#include <ctype.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +QT_QML_BEGIN_NAMESPACE + +extern double qstrtod(const char *s00, char const **se, bool *ok); + +#define shiftWindowsLineBreak() \ + do { \ + if (((current == '\r') && (next1 == '\n')) \ + || ((current == '\n') && (next1 == '\r'))) { \ + shift(1); \ + } \ + } \ + while (0) + +namespace QmlJS { +extern double integerFromString(const char *buf, int size, int radix); +} + +using namespace QmlJS; + +Lexer::Lexer(Engine *eng, bool tokenizeComments) + : driver(eng), + yylineno(0), + done(false), + size8(128), size16(128), + pos8(0), pos16(0), + terminator(false), + restrKeyword(false), + delimited(false), + stackToken(-1), + state(Start), + pos(0), + code(0), length(0), + yycolumn(0), + startpos(0), + startlineno(0), startcolumn(0), + bol(true), + current(0), next1(0), next2(0), next3(0), + err(NoError), + wantRx(false), + check_reserved(true), + parenthesesState(IgnoreParentheses), + parenthesesCount(0), + prohibitAutomaticSemicolon(false), + tokenizeComments(tokenizeComments) +{ + driver->setLexer(this); + // allocate space for read buffers + buffer8 = new char[size8]; + buffer16 = new QChar[size16]; + pattern = 0; + flags = 0; + +} + +Lexer::~Lexer() +{ + delete [] buffer8; + delete [] buffer16; +} + +void Lexer::setCode(const QString &c, int lineno) +{ + errmsg = QString(); + yylineno = lineno; + yycolumn = 1; + restrKeyword = false; + delimited = false; + stackToken = -1; + pos = 0; + code = c.unicode(); + length = c.length(); + bol = true; + + // read first characters + current = (length > 0) ? code[0].unicode() : 0; + next1 = (length > 1) ? code[1].unicode() : 0; + next2 = (length > 2) ? code[2].unicode() : 0; + next3 = (length > 3) ? code[3].unicode() : 0; +} + +void Lexer::shift(uint p) +{ + while (p--) { + ++pos; + ++yycolumn; + current = next1; + next1 = next2; + next2 = next3; + next3 = (pos + 3 < length) ? code[pos+3].unicode() : 0; + } +} + +void Lexer::setDone(State s) +{ + state = s; + done = true; +} + +int Lexer::findReservedWord(const QChar *c, int size) const +{ + switch (size) { + case 2: { + if (c[0] == QLatin1Char('d') && c[1] == QLatin1Char('o')) + return QmlJSGrammar::T_DO; + else if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('f')) + return QmlJSGrammar::T_IF; + else if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('n')) + return QmlJSGrammar::T_IN; + else if (c[0] == QLatin1Char('a') && c[1] == QLatin1Char('s')) + return QmlJSGrammar::T_AS; + } break; + + case 3: { + if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('o') && c[2] == QLatin1Char('r')) + return QmlJSGrammar::T_FOR; + else if (c[0] == QLatin1Char('n') && c[1] == QLatin1Char('e') && c[2] == QLatin1Char('w')) + return QmlJSGrammar::T_NEW; + else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('r') && c[2] == QLatin1Char('y')) + return QmlJSGrammar::T_TRY; + else if (c[0] == QLatin1Char('v') && c[1] == QLatin1Char('a') && c[2] == QLatin1Char('r')) + return QmlJSGrammar::T_VAR; + else if (check_reserved) { + if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('n') && c[2] == QLatin1Char('t')) + return QmlJSGrammar::T_RESERVED_WORD; + } + } break; + + case 4: { + if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('a') + && c[2] == QLatin1Char('s') && c[3] == QLatin1Char('e')) + return QmlJSGrammar::T_CASE; + else if (c[0] == QLatin1Char('e') && c[1] == QLatin1Char('l') + && c[2] == QLatin1Char('s') && c[3] == QLatin1Char('e')) + return QmlJSGrammar::T_ELSE; + else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('h') + && c[2] == QLatin1Char('i') && c[3] == QLatin1Char('s')) + return QmlJSGrammar::T_THIS; + else if (c[0] == QLatin1Char('v') && c[1] == QLatin1Char('o') + && c[2] == QLatin1Char('i') && c[3] == QLatin1Char('d')) + return QmlJSGrammar::T_VOID; + else if (c[0] == QLatin1Char('w') && c[1] == QLatin1Char('i') + && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('h')) + return QmlJSGrammar::T_WITH; + else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('r') + && c[2] == QLatin1Char('u') && c[3] == QLatin1Char('e')) + return QmlJSGrammar::T_TRUE; + else if (c[0] == QLatin1Char('n') && c[1] == QLatin1Char('u') + && c[2] == QLatin1Char('l') && c[3] == QLatin1Char('l')) + return QmlJSGrammar::T_NULL; + else if (check_reserved) { + if (c[0] == QLatin1Char('e') && c[1] == QLatin1Char('n') + && c[2] == QLatin1Char('u') && c[3] == QLatin1Char('m')) + return QmlJSGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('b') && c[1] == QLatin1Char('y') + && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('e')) + return QmlJSGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('l') && c[1] == QLatin1Char('o') + && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('g')) + return QmlJSGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('h') + && c[2] == QLatin1Char('a') && c[3] == QLatin1Char('r')) + return QmlJSGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('g') && c[1] == QLatin1Char('o') + && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('o')) + return QmlJSGrammar::T_RESERVED_WORD; + } + } break; + + case 5: { + if (c[0] == QLatin1Char('b') && c[1] == QLatin1Char('r') + && c[2] == QLatin1Char('e') && c[3] == QLatin1Char('a') + && c[4] == QLatin1Char('k')) + return QmlJSGrammar::T_BREAK; + else if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('a') + && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('c') + && c[4] == QLatin1Char('h')) + return QmlJSGrammar::T_CATCH; + else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('h') + && c[2] == QLatin1Char('r') && c[3] == QLatin1Char('o') + && c[4] == QLatin1Char('w')) + return QmlJSGrammar::T_THROW; + else if (c[0] == QLatin1Char('w') && c[1] == QLatin1Char('h') + && c[2] == QLatin1Char('i') && c[3] == QLatin1Char('l') + && c[4] == QLatin1Char('e')) + return QmlJSGrammar::T_WHILE; + else if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('o') + && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('s') + && c[4] == QLatin1Char('t')) + return QmlJSGrammar::T_CONST; + else if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('a') + && c[2] == QLatin1Char('l') && c[3] == QLatin1Char('s') + && c[4] == QLatin1Char('e')) + return QmlJSGrammar::T_FALSE; + else if (check_reserved) { + if (c[0] == QLatin1Char('s') && c[1] == QLatin1Char('h') + && c[2] == QLatin1Char('o') && c[3] == QLatin1Char('r') + && c[4] == QLatin1Char('t')) + return QmlJSGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('s') && c[1] == QLatin1Char('u') + && c[2] == QLatin1Char('p') && c[3] == QLatin1Char('e') + && c[4] == QLatin1Char('r')) + return QmlJSGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('i') + && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('a') + && c[4] == QLatin1Char('l')) + return QmlJSGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('l') + && c[2] == QLatin1Char('a') && c[3] == QLatin1Char('s') + && c[4] == QLatin1Char('s')) + return QmlJSGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('l') + && c[2] == QLatin1Char('o') && c[3] == QLatin1Char('a') + && c[4] == QLatin1Char('t')) + return QmlJSGrammar::T_RESERVED_WORD; + } + } break; + + case 6: { + if (c[0] == QLatin1Char('d') && c[1] == QLatin1Char('e') + && c[2] == QLatin1Char('l') && c[3] == QLatin1Char('e') + && c[4] == QLatin1Char('t') && c[5] == QLatin1Char('e')) + return QmlJSGrammar::T_DELETE; + else if (c[0] == QLatin1Char('r') && c[1] == QLatin1Char('e') + && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('u') + && c[4] == QLatin1Char('r') && c[5] == QLatin1Char('n')) + return QmlJSGrammar::T_RETURN; + else if (c[0] == QLatin1Char('s') && c[1] == QLatin1Char('w') + && c[2] == QLatin1Char('i') && c[3] == QLatin1Char('t') + && c[4] == QLatin1Char('c') && c[5] == QLatin1Char('h')) + return QmlJSGrammar::T_SWITCH; + else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('y') + && c[2] == QLatin1Char('p') && c[3] == QLatin1Char('e') + && c[4] == QLatin1Char('o') && c[5] == QLatin1Char('f')) + return QmlJSGrammar::T_TYPEOF; + else if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('m') + && c[2] == QLatin1Char('p') && c[3] == QLatin1Char('o') + && c[4] == QLatin1Char('r') && c[5] == QLatin1Char('t')) + return QmlJSGrammar::T_IMPORT; + else if (c[0] == QLatin1Char('s') && c[1] == QLatin1Char('i') + && c[2] == QLatin1Char('g') && c[3] == QLatin1Char('n') + && c[4] == QLatin1Char('a') && c[5] == QLatin1Char('l')) + return QmlJSGrammar::T_SIGNAL; + else if (check_reserved) { + if (c[0] == QLatin1Char('e') && c[1] == QLatin1Char('x') + && c[2] == QLatin1Char('p') && c[3] == QLatin1Char('o') + && c[4] == QLatin1Char('r') && c[5] == QLatin1Char('t')) + return QmlJSGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('s') && c[1] == QLatin1Char('t') + && c[2] == QLatin1Char('a') && c[3] == QLatin1Char('t') + && c[4] == QLatin1Char('i') && c[5] == QLatin1Char('c')) + return QmlJSGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('d') && c[1] == QLatin1Char('o') + && c[2] == QLatin1Char('u') && c[3] == QLatin1Char('b') + && c[4] == QLatin1Char('l') && c[5] == QLatin1Char('e')) + return QmlJSGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('m') + && c[2] == QLatin1Char('p') && c[3] == QLatin1Char('o') + && c[4] == QLatin1Char('r') && c[5] == QLatin1Char('t')) + return QmlJSGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('p') && c[1] == QLatin1Char('u') + && c[2] == QLatin1Char('b') && c[3] == QLatin1Char('l') + && c[4] == QLatin1Char('i') && c[5] == QLatin1Char('c')) + return QmlJSGrammar::T_PUBLIC; + else if (c[0] == QLatin1Char('n') && c[1] == QLatin1Char('a') + && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('i') + && c[4] == QLatin1Char('v') && c[5] == QLatin1Char('e')) + return QmlJSGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('h') + && c[2] == QLatin1Char('r') && c[3] == QLatin1Char('o') + && c[4] == QLatin1Char('w') && c[5] == QLatin1Char('s')) + return QmlJSGrammar::T_RESERVED_WORD; + } + } break; + + case 7: { + if (c[0] == QLatin1Char('d') && c[1] == QLatin1Char('e') + && c[2] == QLatin1Char('f') && c[3] == QLatin1Char('a') + && c[4] == QLatin1Char('u') && c[5] == QLatin1Char('l') + && c[6] == QLatin1Char('t')) + return QmlJSGrammar::T_DEFAULT; + else if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('i') + && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('a') + && c[4] == QLatin1Char('l') && c[5] == QLatin1Char('l') + && c[6] == QLatin1Char('y')) + return QmlJSGrammar::T_FINALLY; + else if (check_reserved) { + if (c[0] == QLatin1Char('b') && c[1] == QLatin1Char('o') + && c[2] == QLatin1Char('o') && c[3] == QLatin1Char('l') + && c[4] == QLatin1Char('e') && c[5] == QLatin1Char('a') + && c[6] == QLatin1Char('n')) + return QmlJSGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('e') && c[1] == QLatin1Char('x') + && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('e') + && c[4] == QLatin1Char('n') && c[5] == QLatin1Char('d') + && c[6] == QLatin1Char('s')) + return QmlJSGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('p') && c[1] == QLatin1Char('a') + && c[2] == QLatin1Char('c') && c[3] == QLatin1Char('k') + && c[4] == QLatin1Char('a') && c[5] == QLatin1Char('g') + && c[6] == QLatin1Char('e')) + return QmlJSGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('p') && c[1] == QLatin1Char('r') + && c[2] == QLatin1Char('i') && c[3] == QLatin1Char('v') + && c[4] == QLatin1Char('a') && c[5] == QLatin1Char('t') + && c[6] == QLatin1Char('e')) + return QmlJSGrammar::T_RESERVED_WORD; + } + } break; + + case 8: { + if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('o') + && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('t') + && c[4] == QLatin1Char('i') && c[5] == QLatin1Char('n') + && c[6] == QLatin1Char('u') && c[7] == QLatin1Char('e')) + return QmlJSGrammar::T_CONTINUE; + else if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('u') + && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('c') + && c[4] == QLatin1Char('t') && c[5] == QLatin1Char('i') + && c[6] == QLatin1Char('o') && c[7] == QLatin1Char('n')) + return QmlJSGrammar::T_FUNCTION; + else if (c[0] == QLatin1Char('d') && c[1] == QLatin1Char('e') + && c[2] == QLatin1Char('b') && c[3] == QLatin1Char('u') + && c[4] == QLatin1Char('g') && c[5] == QLatin1Char('g') + && c[6] == QLatin1Char('e') && c[7] == QLatin1Char('r')) + return QmlJSGrammar::T_DEBUGGER; + else if (c[0] == QLatin1Char('p') && c[1] == QLatin1Char('r') + && c[2] == QLatin1Char('o') && c[3] == QLatin1Char('p') + && c[4] == QLatin1Char('e') && c[5] == QLatin1Char('r') + && c[6] == QLatin1Char('t') && c[7] == QLatin1Char('y')) + return QmlJSGrammar::T_PROPERTY; + else if (c[0] == QLatin1Char('r') && c[1] == QLatin1Char('e') + && c[2] == QLatin1Char('a') && c[3] == QLatin1Char('d') + && c[4] == QLatin1Char('o') && c[5] == QLatin1Char('n') + && c[6] == QLatin1Char('l') && c[7] == QLatin1Char('y')) + return QmlJSGrammar::T_READONLY; + else if (check_reserved) { + if (c[0] == QLatin1Char('a') && c[1] == QLatin1Char('b') + && c[2] == QLatin1Char('s') && c[3] == QLatin1Char('t') + && c[4] == QLatin1Char('r') && c[5] == QLatin1Char('a') + && c[6] == QLatin1Char('c') && c[7] == QLatin1Char('t')) + return QmlJSGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('v') && c[1] == QLatin1Char('o') + && c[2] == QLatin1Char('l') && c[3] == QLatin1Char('a') + && c[4] == QLatin1Char('t') && c[5] == QLatin1Char('i') + && c[6] == QLatin1Char('l') && c[7] == QLatin1Char('e')) + return QmlJSGrammar::T_RESERVED_WORD; + } + } break; + + case 9: { + if (check_reserved) { + if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('n') + && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('e') + && c[4] == QLatin1Char('r') && c[5] == QLatin1Char('f') + && c[6] == QLatin1Char('a') && c[7] == QLatin1Char('c') + && c[8] == QLatin1Char('e')) + return QmlJSGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('r') + && c[2] == QLatin1Char('a') && c[3] == QLatin1Char('n') + && c[4] == QLatin1Char('s') && c[5] == QLatin1Char('i') + && c[6] == QLatin1Char('e') && c[7] == QLatin1Char('n') + && c[8] == QLatin1Char('t')) + return QmlJSGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('p') && c[1] == QLatin1Char('r') + && c[2] == QLatin1Char('o') && c[3] == QLatin1Char('t') + && c[4] == QLatin1Char('e') && c[5] == QLatin1Char('c') + && c[6] == QLatin1Char('t') && c[7] == QLatin1Char('e') + && c[8] == QLatin1Char('d')) + return QmlJSGrammar::T_RESERVED_WORD; + } + } break; + + case 10: { + if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('n') + && c[2] == QLatin1Char('s') && c[3] == QLatin1Char('t') + && c[4] == QLatin1Char('a') && c[5] == QLatin1Char('n') + && c[6] == QLatin1Char('c') && c[7] == QLatin1Char('e') + && c[8] == QLatin1Char('o') && c[9] == QLatin1Char('f')) + return QmlJSGrammar::T_INSTANCEOF; + else if (check_reserved) { + if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('m') + && c[2] == QLatin1Char('p') && c[3] == QLatin1Char('l') + && c[4] == QLatin1Char('e') && c[5] == QLatin1Char('m') + && c[6] == QLatin1Char('e') && c[7] == QLatin1Char('n') + && c[8] == QLatin1Char('t') && c[9] == QLatin1Char('s')) + return QmlJSGrammar::T_RESERVED_WORD; + } + } break; + + case 12: { + if (check_reserved) { + if (c[0] == QLatin1Char('s') && c[1] == QLatin1Char('y') + && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('c') + && c[4] == QLatin1Char('h') && c[5] == QLatin1Char('r') + && c[6] == QLatin1Char('o') && c[7] == QLatin1Char('n') + && c[8] == QLatin1Char('i') && c[9] == QLatin1Char('z') + && c[10] == QLatin1Char('e') && c[11] == QLatin1Char('d')) + return QmlJSGrammar::T_RESERVED_WORD; + } + } break; + + } // switch + + return -1; +} + +int Lexer::lex() +{ + int token = 0; + state = Start; + ushort stringType = 0; // either single or double quotes + bool multiLineString = false; + pos8 = pos16 = 0; + done = false; + terminator = false; + + // did we push a token on the stack previously ? + // (after an automatic semicolon insertion) + if (stackToken >= 0) { + setDone(Other); + token = stackToken; + stackToken = -1; + } + + while (!done) { + switch (state) { + case Start: + if (isWhiteSpace()) { + // do nothing + } else if (current == '/' && next1 == '/') { + recordStartPos(); + shift(1); + state = InSingleLineComment; + } else if (current == '/' && next1 == '*') { + recordStartPos(); + shift(1); + state = InMultiLineComment; + } else if (current == 0) { + syncProhibitAutomaticSemicolon(); + if (!terminator && !delimited && !prohibitAutomaticSemicolon) { + // automatic semicolon insertion if program incomplete + token = QmlJSGrammar::T_SEMICOLON; + stackToken = 0; + setDone(Other); + } else { + setDone(Eof); + } + } else if (isLineTerminator()) { + shiftWindowsLineBreak(); + yylineno++; + yycolumn = 0; + bol = true; + terminator = true; + syncProhibitAutomaticSemicolon(); + if (restrKeyword) { + token = QmlJSGrammar::T_SEMICOLON; + setDone(Other); + } + } else if (current == '"' || current == '\'') { + recordStartPos(); + state = InString; + multiLineString = false; + stringType = current; + } else if (isIdentLetter(current)) { + recordStartPos(); + record16(current); + state = InIdentifier; + } else if (current == '0') { + recordStartPos(); + record8(current); + state = InNum0; + } else if (isDecimalDigit(current)) { + recordStartPos(); + record8(current); + state = InNum; + } else if (current == '.' && isDecimalDigit(next1)) { + recordStartPos(); + record8(current); + state = InDecimal; + } else { + recordStartPos(); + token = matchPunctuator(current, next1, next2, next3); + if (token != -1) { + if (terminator && !delimited && !prohibitAutomaticSemicolon + && (token == QmlJSGrammar::T_PLUS_PLUS + || token == QmlJSGrammar::T_MINUS_MINUS)) { + // automatic semicolon insertion + stackToken = token; + token = QmlJSGrammar::T_SEMICOLON; + } + setDone(Other); + } + else { + setDone(Bad); + err = IllegalCharacter; + errmsg = QLatin1String("Illegal character"); + } + } + break; + case InString: + if (current == stringType) { + shift(1); + setDone(String); + } else if (isLineTerminator()) { + multiLineString = true; + record16(current); + } else if (current == 0 || isLineTerminator()) { + setDone(Bad); + err = UnclosedStringLiteral; + errmsg = QLatin1String("Unclosed string at end of line"); + } else if (current == '\\') { + state = InEscapeSequence; + } else { + record16(current); + } + break; + // Escape Sequences inside of strings + case InEscapeSequence: + if (isOctalDigit(current)) { + if (current >= '0' && current <= '3' && + isOctalDigit(next1) && isOctalDigit(next2)) { + record16(convertOctal(current, next1, next2)); + shift(2); + state = InString; + } else if (isOctalDigit(current) && + isOctalDigit(next1)) { + record16(convertOctal('0', current, next1)); + shift(1); + state = InString; + } else if (isOctalDigit(current)) { + record16(convertOctal('0', '0', current)); + state = InString; + } else { + setDone(Bad); + err = IllegalEscapeSequence; + errmsg = QLatin1String("Illegal escape squence"); + } + } else if (current == 'x') + state = InHexEscape; + else if (current == 'u') + state = InUnicodeEscape; + else { + if (isLineTerminator()) { + shiftWindowsLineBreak(); + yylineno++; + yycolumn = 0; + bol = true; + } else { + record16(singleEscape(current)); + } + state = InString; + } + break; + case InHexEscape: + if (isHexDigit(current) && isHexDigit(next1)) { + state = InString; + record16(QLatin1Char(convertHex(current, next1))); + shift(1); + } else if (current == stringType) { + record16(QLatin1Char('x')); + shift(1); + setDone(String); + } else { + record16(QLatin1Char('x')); + record16(current); + state = InString; + } + break; + case InUnicodeEscape: + if (isHexDigit(current) && isHexDigit(next1) && + isHexDigit(next2) && isHexDigit(next3)) { + record16(convertUnicode(current, next1, next2, next3)); + shift(3); + state = InString; + } else if (current == stringType) { + record16(QLatin1Char('u')); + shift(1); + setDone(String); + } else { + setDone(Bad); + err = IllegalUnicodeEscapeSequence; + errmsg = QLatin1String("Illegal unicode escape sequence"); + } + break; + case InSingleLineComment: + if (isLineTerminator()) { + shiftWindowsLineBreak(); + yylineno++; + yycolumn = 0; + terminator = true; + bol = true; + if (restrKeyword) { + token = QmlJSGrammar::T_SEMICOLON; + setDone(Other); + } else + state = Start; + driver->addComment(startpos, tokenLength(), startlineno, startcolumn); + } else if (current == 0) { + driver->addComment(startpos, tokenLength(), startlineno, startcolumn); + setDone(Eof); + } + + break; + case InMultiLineComment: + if (current == 0) { + setDone(Bad); + err = UnclosedComment; + errmsg = QLatin1String("Unclosed comment at end of file"); + driver->addComment(startpos, tokenLength(), startlineno, startcolumn); + } else if (isLineTerminator()) { + shiftWindowsLineBreak(); + yylineno++; + } else if (current == '*' && next1 == '/') { + state = Start; + shift(1); + driver->addComment(startpos, tokenLength(), startlineno, startcolumn); + } + + break; + case InIdentifier: + if (isIdentLetter(current) || isDecimalDigit(current)) { + record16(current); + break; + } + setDone(Identifier); + break; + case InNum0: + if (current == 'x' || current == 'X') { + record8(current); + state = InHex; + } else if (current == '.') { + record8(current); + state = InDecimal; + } else if (current == 'e' || current == 'E') { + record8(current); + state = InExponentIndicator; + } else if (isOctalDigit(current)) { + record8(current); + state = InOctal; + } else if (isDecimalDigit(current)) { + record8(current); + state = InDecimal; + } else { + setDone(Number); + } + break; + case InHex: + if (isHexDigit(current)) + record8(current); + else + setDone(Hex); + break; + case InOctal: + if (isOctalDigit(current)) { + record8(current); + } else if (isDecimalDigit(current)) { + record8(current); + state = InDecimal; + } else { + setDone(Octal); + } + break; + case InNum: + if (isDecimalDigit(current)) { + record8(current); + } else if (current == '.') { + record8(current); + state = InDecimal; + } else if (current == 'e' || current == 'E') { + record8(current); + state = InExponentIndicator; + } else { + setDone(Number); + } + break; + case InDecimal: + if (isDecimalDigit(current)) { + record8(current); + } else if (current == 'e' || current == 'E') { + record8(current); + state = InExponentIndicator; + } else { + setDone(Number); + } + break; + case InExponentIndicator: + if (current == '+' || current == '-') { + record8(current); + } else if (isDecimalDigit(current)) { + record8(current); + state = InExponent; + } else { + setDone(Bad); + err = IllegalExponentIndicator; + errmsg = QLatin1String("Illegal syntax for exponential number"); + } + break; + case InExponent: + if (isDecimalDigit(current)) { + record8(current); + } else { + setDone(Number); + } + break; + default: + Q_ASSERT_X(0, "Lexer::lex", "Unhandled state in switch statement"); + } + + // move on to the next character + if (!done) + shift(1); + if (state != Start && state != InSingleLineComment) + bol = false; + } + + // no identifiers allowed directly after numeric literal, e.g. "3in" is bad + if ((state == Number || state == Octal || state == Hex) + && isIdentLetter(current)) { + state = Bad; + err = IllegalIdentifier; + errmsg = QLatin1String("Identifier cannot start with numeric literal"); + } + + // terminate string + buffer8[pos8] = '\0'; + + double dval = 0; + if (state == Number) { + dval = qstrtod(buffer8, 0, 0); + } else if (state == Hex) { // scan hex numbers + dval = integerFromString(buffer8, pos8, 16); + state = Number; + } else if (state == Octal) { // scan octal number + dval = integerFromString(buffer8, pos8, 8); + state = Number; + } + + restrKeyword = false; + delimited = false; + + switch (parenthesesState) { + case IgnoreParentheses: + break; + case CountParentheses: + if (token == QmlJSGrammar::T_RPAREN) { + --parenthesesCount; + if (parenthesesCount == 0) + parenthesesState = BalancedParentheses; + } else if (token == QmlJSGrammar::T_LPAREN) { + ++parenthesesCount; + } + break; + case BalancedParentheses: + parenthesesState = IgnoreParentheses; + break; + } + + switch (state) { + case Eof: + return 0; + case Other: + if (token == QmlJSGrammar::T_RBRACE || token == QmlJSGrammar::T_SEMICOLON) + delimited = true; + return token; + case Identifier: + if ((token = findReservedWord(buffer16, pos16)) < 0) { + /* TODO: close leak on parse error. same holds true for String */ + if (driver) + qsyylval.ustr = driver->intern(buffer16, pos16); + else + qsyylval.ustr = 0; + return QmlJSGrammar::T_IDENTIFIER; + } + if (token == QmlJSGrammar::T_CONTINUE || token == QmlJSGrammar::T_BREAK + || token == QmlJSGrammar::T_RETURN || token == QmlJSGrammar::T_THROW) { + restrKeyword = true; + } else if (token == QmlJSGrammar::T_IF || token == QmlJSGrammar::T_FOR + || token == QmlJSGrammar::T_WHILE || token == QmlJSGrammar::T_WITH) { + parenthesesState = CountParentheses; + parenthesesCount = 0; + } else if (token == QmlJSGrammar::T_DO) { + parenthesesState = BalancedParentheses; + } + return token; + case String: + if (driver) + qsyylval.ustr = driver->intern(buffer16, pos16); + else + qsyylval.ustr = 0; + return multiLineString?QmlJSGrammar::T_MULTILINE_STRING_LITERAL:QmlJSGrammar::T_STRING_LITERAL; + case Number: + qsyylval.dval = dval; + return QmlJSGrammar::T_NUMERIC_LITERAL; + case Bad: + return -1; + default: + Q_ASSERT(!"unhandled numeration value in switch"); + return -1; + } +} + +bool Lexer::isWhiteSpace() const +{ + return (current == ' ' || current == '\t' || + current == 0x0b || current == 0x0c); +} + +bool Lexer::isLineTerminator() const +{ + return (current == '\n' || current == '\r'); +} + +bool Lexer::isIdentLetter(ushort c) +{ + /* TODO: allow other legitimate unicode chars */ + return ((c >= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z') + || c == '$' + || c == '_'); +} + +bool Lexer::isDecimalDigit(ushort c) +{ + return (c >= '0' && c <= '9'); +} + +bool Lexer::isHexDigit(ushort c) const +{ + return ((c >= '0' && c <= '9') + || (c >= 'a' && c <= 'f') + || (c >= 'A' && c <= 'F')); +} + +bool Lexer::isOctalDigit(ushort c) const +{ + return (c >= '0' && c <= '7'); +} + +int Lexer::matchPunctuator(ushort c1, ushort c2, + ushort c3, ushort c4) +{ + if (c1 == '>' && c2 == '>' && c3 == '>' && c4 == '=') { + shift(4); + return QmlJSGrammar::T_GT_GT_GT_EQ; + } else if (c1 == '=' && c2 == '=' && c3 == '=') { + shift(3); + return QmlJSGrammar::T_EQ_EQ_EQ; + } else if (c1 == '!' && c2 == '=' && c3 == '=') { + shift(3); + return QmlJSGrammar::T_NOT_EQ_EQ; + } else if (c1 == '>' && c2 == '>' && c3 == '>') { + shift(3); + return QmlJSGrammar::T_GT_GT_GT; + } else if (c1 == '<' && c2 == '<' && c3 == '=') { + shift(3); + return QmlJSGrammar::T_LT_LT_EQ; + } else if (c1 == '>' && c2 == '>' && c3 == '=') { + shift(3); + return QmlJSGrammar::T_GT_GT_EQ; + } else if (c1 == '<' && c2 == '=') { + shift(2); + return QmlJSGrammar::T_LE; + } else if (c1 == '>' && c2 == '=') { + shift(2); + return QmlJSGrammar::T_GE; + } else if (c1 == '!' && c2 == '=') { + shift(2); + return QmlJSGrammar::T_NOT_EQ; + } else if (c1 == '+' && c2 == '+') { + shift(2); + return QmlJSGrammar::T_PLUS_PLUS; + } else if (c1 == '-' && c2 == '-') { + shift(2); + return QmlJSGrammar::T_MINUS_MINUS; + } else if (c1 == '=' && c2 == '=') { + shift(2); + return QmlJSGrammar::T_EQ_EQ; + } else if (c1 == '+' && c2 == '=') { + shift(2); + return QmlJSGrammar::T_PLUS_EQ; + } else if (c1 == '-' && c2 == '=') { + shift(2); + return QmlJSGrammar::T_MINUS_EQ; + } else if (c1 == '*' && c2 == '=') { + shift(2); + return QmlJSGrammar::T_STAR_EQ; + } else if (c1 == '/' && c2 == '=') { + shift(2); + return QmlJSGrammar::T_DIVIDE_EQ; + } else if (c1 == '&' && c2 == '=') { + shift(2); + return QmlJSGrammar::T_AND_EQ; + } else if (c1 == '^' && c2 == '=') { + shift(2); + return QmlJSGrammar::T_XOR_EQ; + } else if (c1 == '%' && c2 == '=') { + shift(2); + return QmlJSGrammar::T_REMAINDER_EQ; + } else if (c1 == '|' && c2 == '=') { + shift(2); + return QmlJSGrammar::T_OR_EQ; + } else if (c1 == '<' && c2 == '<') { + shift(2); + return QmlJSGrammar::T_LT_LT; + } else if (c1 == '>' && c2 == '>') { + shift(2); + return QmlJSGrammar::T_GT_GT; + } else if (c1 == '&' && c2 == '&') { + shift(2); + return QmlJSGrammar::T_AND_AND; + } else if (c1 == '|' && c2 == '|') { + shift(2); + return QmlJSGrammar::T_OR_OR; + } + + switch(c1) { + case '=': shift(1); return QmlJSGrammar::T_EQ; + case '>': shift(1); return QmlJSGrammar::T_GT; + case '<': shift(1); return QmlJSGrammar::T_LT; + case ',': shift(1); return QmlJSGrammar::T_COMMA; + case '!': shift(1); return QmlJSGrammar::T_NOT; + case '~': shift(1); return QmlJSGrammar::T_TILDE; + case '?': shift(1); return QmlJSGrammar::T_QUESTION; + case ':': shift(1); return QmlJSGrammar::T_COLON; + case '.': shift(1); return QmlJSGrammar::T_DOT; + case '+': shift(1); return QmlJSGrammar::T_PLUS; + case '-': shift(1); return QmlJSGrammar::T_MINUS; + case '*': shift(1); return QmlJSGrammar::T_STAR; + case '/': shift(1); return QmlJSGrammar::T_DIVIDE_; + case '&': shift(1); return QmlJSGrammar::T_AND; + case '|': shift(1); return QmlJSGrammar::T_OR; + case '^': shift(1); return QmlJSGrammar::T_XOR; + case '%': shift(1); return QmlJSGrammar::T_REMAINDER; + case '(': shift(1); return QmlJSGrammar::T_LPAREN; + case ')': shift(1); return QmlJSGrammar::T_RPAREN; + case '{': shift(1); return QmlJSGrammar::T_LBRACE; + case '}': shift(1); return QmlJSGrammar::T_RBRACE; + case '[': shift(1); return QmlJSGrammar::T_LBRACKET; + case ']': shift(1); return QmlJSGrammar::T_RBRACKET; + case ';': shift(1); return QmlJSGrammar::T_SEMICOLON; + + default: return -1; + } +} + +ushort Lexer::singleEscape(ushort c) const +{ + switch(c) { + case 'b': + return 0x08; + case 't': + return 0x09; + case 'n': + return 0x0A; + case 'v': + return 0x0B; + case 'f': + return 0x0C; + case 'r': + return 0x0D; + case '"': + return 0x22; + case '\'': + return 0x27; + case '\\': + return 0x5C; + default: + return c; + } +} + +ushort Lexer::convertOctal(ushort c1, ushort c2, + ushort c3) const +{ + return ((c1 - '0') * 64 + (c2 - '0') * 8 + c3 - '0'); +} + +unsigned char Lexer::convertHex(ushort c) +{ + if (c >= '0' && c <= '9') + return (c - '0'); + else if (c >= 'a' && c <= 'f') + return (c - 'a' + 10); + else + return (c - 'A' + 10); +} + +unsigned char Lexer::convertHex(ushort c1, ushort c2) +{ + return ((convertHex(c1) << 4) + convertHex(c2)); +} + +QChar Lexer::convertUnicode(ushort c1, ushort c2, + ushort c3, ushort c4) +{ + return QChar((convertHex(c3) << 4) + convertHex(c4), + (convertHex(c1) << 4) + convertHex(c2)); +} + +void Lexer::record8(ushort c) +{ + Q_ASSERT(c <= 0xff); + + // enlarge buffer if full + if (pos8 >= size8 - 1) { + char *tmp = new char[2 * size8]; + memcpy(tmp, buffer8, size8 * sizeof(char)); + delete [] buffer8; + buffer8 = tmp; + size8 *= 2; + } + + buffer8[pos8++] = (char) c; +} + +void Lexer::record16(QChar c) +{ + // enlarge buffer if full + if (pos16 >= size16 - 1) { + QChar *tmp = new QChar[2 * size16]; + memcpy(tmp, buffer16, size16 * sizeof(QChar)); + delete [] buffer16; + buffer16 = tmp; + size16 *= 2; + } + + buffer16[pos16++] = c; +} + +void Lexer::recordStartPos() +{ + startpos = pos; + startlineno = yylineno; + startcolumn = yycolumn; +} + +bool Lexer::scanRegExp(RegExpBodyPrefix prefix) +{ + pos16 = 0; + bool lastWasEscape = false; + + if (prefix == EqualPrefix) + record16(QLatin1Char('=')); + + while (1) { + if (isLineTerminator() || current == 0) { + errmsg = QLatin1String("Unterminated regular expression literal"); + return false; + } + else if (current != '/' || lastWasEscape == true) + { + record16(current); + lastWasEscape = !lastWasEscape && (current == '\\'); + } + else { + if (driver) + pattern = driver->intern(buffer16, pos16); + else + pattern = 0; + pos16 = 0; + shift(1); + break; + } + shift(1); + } + + flags = 0; + while (isIdentLetter(current)) { + int flag = Ecma::RegExp::flagFromChar(current); + if (flag == 0) { + errmsg = QString::fromLatin1("Invalid regular expression flag '%0'") + .arg(QChar(current)); + return false; + } + flags |= flag; + record16(current); + shift(1); + } + + return true; +} + +void Lexer::syncProhibitAutomaticSemicolon() +{ + if (parenthesesState == BalancedParentheses) { + // we have seen something like "if (foo)", which means we should + // never insert an automatic semicolon at this point, since it would + // then be expanded into an empty statement (ECMA-262 7.9.1) + prohibitAutomaticSemicolon = true; + parenthesesState = IgnoreParentheses; + } else { + prohibitAutomaticSemicolon = false; + } +} + +QT_QML_END_NAMESPACE + + diff --git a/src/plugins/qmldesigner/core/filemanager/parser/qmljslexer_p.h b/src/plugins/qmldesigner/core/filemanager/parser/qmljslexer_p.h new file mode 100644 index 00000000000..1be466c3e62 --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/parser/qmljslexer_p.h @@ -0,0 +1,237 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef QMLJSLEXER_P_H +#define QMLJSLEXER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/QString> + +#include "qmljsglobal_p.h" + +QT_QML_BEGIN_NAMESPACE + +namespace QmlJS { + +class Engine; +class NameId; + +class Lexer +{ +public: + Lexer(Engine *eng, bool tokenizeComments = false); + ~Lexer(); + + void setCode(const QString &c, int lineno); + int lex(); + + int currentLineNo() const { return yylineno; } + int currentColumnNo() const { return yycolumn; } + + int tokenOffset() const { return startpos; } + int tokenLength() const { return pos - startpos; } + + int startLineNo() const { return startlineno; } + int startColumnNo() const { return startcolumn; } + + int endLineNo() const { return currentLineNo(); } + int endColumnNo() const + { int col = currentColumnNo(); return (col > 0) ? col - 1 : col; } + + bool prevTerminator() const { return terminator; } + + enum State { Start, + Identifier, + InIdentifier, + InSingleLineComment, + InMultiLineComment, + InNum, + InNum0, + InHex, + InOctal, + InDecimal, + InExponentIndicator, + InExponent, + Hex, + Octal, + Number, + String, + Eof, + InString, + InEscapeSequence, + InHexEscape, + InUnicodeEscape, + Other, + Bad }; + + enum Error { + NoError, + IllegalCharacter, + UnclosedStringLiteral, + IllegalEscapeSequence, + IllegalUnicodeEscapeSequence, + UnclosedComment, + IllegalExponentIndicator, + IllegalIdentifier + }; + + enum ParenthesesState { + IgnoreParentheses, + CountParentheses, + BalancedParentheses + }; + + enum RegExpBodyPrefix { + NoPrefix, + EqualPrefix + }; + + bool scanRegExp(RegExpBodyPrefix prefix = NoPrefix); + + NameId *pattern; + int flags; + + State lexerState() const + { return state; } + + QString errorMessage() const + { return errmsg; } + void setErrorMessage(const QString &err) + { errmsg = err; } + void setErrorMessage(const char *err) + { setErrorMessage(QString::fromLatin1(err)); } + + Error error() const + { return err; } + void clearError() + { err = NoError; } + +private: + Engine *driver; + int yylineno; + bool done; + char *buffer8; + QChar *buffer16; + uint size8, size16; + uint pos8, pos16; + bool terminator; + bool restrKeyword; + // encountered delimiter like "'" and "}" on last run + bool delimited; + int stackToken; + + State state; + void setDone(State s); + uint pos; + void shift(uint p); + int lookupKeyword(const char *); + + bool isWhiteSpace() const; + bool isLineTerminator() const; + bool isHexDigit(ushort c) const; + bool isOctalDigit(ushort c) const; + + int matchPunctuator(ushort c1, ushort c2, + ushort c3, ushort c4); + ushort singleEscape(ushort c) const; + ushort convertOctal(ushort c1, ushort c2, + ushort c3) const; +public: + static unsigned char convertHex(ushort c1); + static unsigned char convertHex(ushort c1, ushort c2); + static QChar convertUnicode(ushort c1, ushort c2, + ushort c3, ushort c4); + static bool isIdentLetter(ushort c); + static bool isDecimalDigit(ushort c); + + inline int ival() const { return qsyylval.ival; } + inline double dval() const { return qsyylval.dval; } + inline NameId *ustr() const { return qsyylval.ustr; } + + const QChar *characterBuffer() const { return buffer16; } + int characterCount() const { return pos16; } + +private: + void record8(ushort c); + void record16(QChar c); + void recordStartPos(); + + int findReservedWord(const QChar *buffer, int size) const; + + void syncProhibitAutomaticSemicolon(); + + const QChar *code; + uint length; + int yycolumn; + int startpos; + int startlineno; + int startcolumn; + int bol; // begin of line + + union { + int ival; + double dval; + NameId *ustr; + } qsyylval; + + // current and following unicode characters + ushort current, next1, next2, next3; + + struct keyword { + const char *name; + int token; + }; + + QString errmsg; + Error err; + + bool wantRx; + bool check_reserved; + + ParenthesesState parenthesesState; + int parenthesesCount; + bool prohibitAutomaticSemicolon; + bool tokenizeComments; +}; + +} // namespace QmlJS + +QT_QML_END_NAMESPACE + +#endif diff --git a/src/plugins/qmldesigner/core/filemanager/parser/qmljsmemorypool_p.h b/src/plugins/qmldesigner/core/filemanager/parser/qmljsmemorypool_p.h new file mode 100644 index 00000000000..4b79f174fb0 --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/parser/qmljsmemorypool_p.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef QMLJSMEMORYPOOL_P_H +#define QMLJSMEMORYPOOL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qglobal.h> +#include <QtCore/qshareddata.h> +#include <string.h> + +#include "qmljsglobal_p.h" + +QT_QML_BEGIN_NAMESPACE + +namespace QmlJS { + +class MemoryPool : public QSharedData +{ +public: + enum { maxBlockCount = -1 }; + enum { defaultBlockSize = 1 << 12 }; + + MemoryPool() { + m_blockIndex = maxBlockCount; + m_currentIndex = 0; + m_storage = 0; + m_currentBlock = 0; + m_currentBlockSize = 0; + } + + virtual ~MemoryPool() { + for (int index = 0; index < m_blockIndex + 1; ++index) + qFree(m_storage[index]); + + qFree(m_storage); + } + + char *allocate(int bytes) { + bytes += (8 - bytes) & 7; // ensure multiple of 8 bytes (maintain alignment) + if (m_currentBlock == 0 || m_currentBlockSize < m_currentIndex + bytes) { + ++m_blockIndex; + m_currentBlockSize = defaultBlockSize << m_blockIndex; + + m_storage = reinterpret_cast<char**>(qRealloc(m_storage, sizeof(char*) * (1 + m_blockIndex))); + m_currentBlock = m_storage[m_blockIndex] = reinterpret_cast<char*>(qMalloc(m_currentBlockSize)); + ::memset(m_currentBlock, 0, m_currentBlockSize); + + m_currentIndex = (8 - quintptr(m_currentBlock)) & 7; // ensure first chunk is 64-bit aligned + Q_ASSERT(m_currentIndex + bytes <= m_currentBlockSize); + } + + char *p = reinterpret_cast<char *> + (m_currentBlock + m_currentIndex); + + m_currentIndex += bytes; + + return p; + } + + int bytesAllocated() const { + int bytes = 0; + for (int index = 0; index < m_blockIndex; ++index) + bytes += (defaultBlockSize << index); + bytes += m_currentIndex; + return bytes; + } + +private: + int m_blockIndex; + int m_currentIndex; + char *m_currentBlock; + int m_currentBlockSize; + char **m_storage; + +private: + Q_DISABLE_COPY(MemoryPool) +}; + +} // namespace QmlJS + +QT_QML_END_NAMESPACE + +#endif diff --git a/src/plugins/qmldesigner/core/filemanager/parser/qmljsnodepool_p.h b/src/plugins/qmldesigner/core/filemanager/parser/qmljsnodepool_p.h new file mode 100644 index 00000000000..ba0c3678efd --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/parser/qmljsnodepool_p.h @@ -0,0 +1,127 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef QMLJSNODEPOOL_P_H +#define QMLJSNODEPOOL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/QHash> +#include <QtCore/QString> + +#include "qmljsglobal_p.h" +#include "qmljsmemorypool_p.h" + +QT_QML_BEGIN_NAMESPACE + +namespace QmlJS { + +namespace AST { +class Node; +} // namespace AST + +class Code; +class CompilationUnit; +class Engine; + +template <typename NodeType> +inline NodeType *makeAstNode(MemoryPool *storage) +{ + NodeType *node = new (storage->allocate(sizeof(NodeType))) NodeType(); + return node; +} + +template <typename NodeType, typename Arg1> +inline NodeType *makeAstNode(MemoryPool *storage, Arg1 arg1) +{ + NodeType *node = new (storage->allocate(sizeof(NodeType))) NodeType(arg1); + return node; +} + +template <typename NodeType, typename Arg1, typename Arg2> +inline NodeType *makeAstNode(MemoryPool *storage, Arg1 arg1, Arg2 arg2) +{ + NodeType *node = new (storage->allocate(sizeof(NodeType))) NodeType(arg1, arg2); + return node; +} + +template <typename NodeType, typename Arg1, typename Arg2, typename Arg3> +inline NodeType *makeAstNode(MemoryPool *storage, Arg1 arg1, Arg2 arg2, Arg3 arg3) +{ + NodeType *node = new (storage->allocate(sizeof(NodeType))) NodeType(arg1, arg2, arg3); + return node; +} + +template <typename NodeType, typename Arg1, typename Arg2, typename Arg3, typename Arg4> +inline NodeType *makeAstNode(MemoryPool *storage, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) +{ + NodeType *node = new (storage->allocate(sizeof(NodeType))) NodeType(arg1, arg2, arg3, arg4); + return node; +} + +class NodePool : public MemoryPool +{ +public: + NodePool(const QString &fileName, Engine *engine); + virtual ~NodePool(); + + Code *createCompiledCode(AST::Node *node, CompilationUnit &compilation); + + inline QString fileName() const { return m_fileName; } + inline Engine *engine() const { return m_engine; } +#ifndef J_SCRIPT_NO_EVENT_NOTIFY + inline qint64 id() const { return m_id; } +#endif + +private: + QHash<AST::Node*, Code*> m_codeCache; + QString m_fileName; + Engine *m_engine; +#ifndef J_SCRIPT_NO_EVENT_NOTIFY + qint64 m_id; +#endif + +private: + Q_DISABLE_COPY(NodePool) +}; + +} // namespace QmlJS + +QT_QML_END_NAMESPACE + +#endif diff --git a/src/plugins/qmldesigner/core/filemanager/parser/qmljsparser.cpp b/src/plugins/qmldesigner/core/filemanager/parser/qmljsparser.cpp new file mode 100644 index 00000000000..f5fab00f4cc --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/parser/qmljsparser.cpp @@ -0,0 +1,1812 @@ +// This file was generated by qlalr - DO NOT EDIT! + +/**************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include <QtCore/QtDebug> + +#include <string.h> + +#include "qmljsengine_p.h" +#include "qmljslexer_p.h" +#include "qmljsast_p.h" +#include "qmljsnodepool_p.h" + + + +#include "qmljsparser_p.h" +#include <QVarLengthArray> + +// +// This file is automatically generated from qmljs.g. +// Changes will be lost. +// + +using namespace QmlJS; + +QT_QML_BEGIN_NAMESPACE + +void Parser::reallocateStack() +{ + if (! stack_size) + stack_size = 128; + else + stack_size <<= 1; + + sym_stack = reinterpret_cast<Value*> (qRealloc(sym_stack, stack_size * sizeof(Value))); + state_stack = reinterpret_cast<int*> (qRealloc(state_stack, stack_size * sizeof(int))); + location_stack = reinterpret_cast<AST::SourceLocation*> (qRealloc(location_stack, stack_size * sizeof(AST::SourceLocation))); +} + +inline static bool automatic(Engine *driver, int token) +{ + return token == QmlJSGrammar::T_RBRACE + || token == 0 + || driver->lexer()->prevTerminator(); +} + + +Parser::Parser(Engine *engine): + driver(engine), + tos(0), + stack_size(0), + sym_stack(0), + state_stack(0), + location_stack(0), + first_token(0), + last_token(0) +{ +} + +Parser::~Parser() +{ + if (stack_size) { + qFree(sym_stack); + qFree(state_stack); + qFree(location_stack); + } +} + +static inline AST::SourceLocation location(Lexer *lexer) +{ + AST::SourceLocation loc; + loc.offset = lexer->tokenOffset(); + loc.length = lexer->tokenLength(); + loc.startLine = lexer->startLineNo(); + loc.startColumn = lexer->startColumnNo(); + return loc; +} + +AST::UiQualifiedId *Parser::reparseAsQualifiedId(AST::ExpressionNode *expr) +{ + QVarLengthArray<NameId *, 4> nameIds; + QVarLengthArray<AST::SourceLocation, 4> locations; + + AST::ExpressionNode *it = expr; + while (AST::FieldMemberExpression *m = AST::cast<AST::FieldMemberExpression *>(it)) { + nameIds.append(m->name); + locations.append(m->identifierToken); + it = m->base; + } + + if (AST::IdentifierExpression *idExpr = AST::cast<AST::IdentifierExpression *>(it)) { + AST::UiQualifiedId *q = makeAstNode<AST::UiQualifiedId>(driver->nodePool(), idExpr->name); + q->identifierToken = idExpr->identifierToken; + + AST::UiQualifiedId *currentId = q; + for (int i = nameIds.size() - 1; i != -1; --i) { + currentId = makeAstNode<AST::UiQualifiedId>(driver->nodePool(), currentId, nameIds[i]); + currentId->identifierToken = locations[i]; + } + + return currentId->finish(); + } + + return 0; +} + +bool Parser::parse(int startToken) +{ + Lexer *lexer = driver->lexer(); + bool hadErrors = false; + int yytoken = -1; + int action = 0; + + token_buffer[0].token = startToken; + first_token = &token_buffer[0]; + last_token = &token_buffer[1]; + + tos = -1; + program = 0; + + do { + if (++tos == stack_size) + reallocateStack(); + + state_stack[tos] = action; + + _Lcheck_token: + if (yytoken == -1 && -TERMINAL_COUNT != action_index[action]) { + yyprevlloc = yylloc; + + if (first_token == last_token) { + yytoken = lexer->lex(); + yylval = lexer->dval(); + yylloc = location(lexer); + } else { + yytoken = first_token->token; + yylval = first_token->dval; + yylloc = first_token->loc; + ++first_token; + } + } + + action = t_action(action, yytoken); + if (action > 0) { + if (action != ACCEPT_STATE) { + yytoken = -1; + sym(1).dval = yylval; + loc(1) = yylloc; + } else { + --tos; + return ! hadErrors; + } + } else if (action < 0) { + const int r = -action - 1; + tos -= rhs[r]; + + switch (r) { + +case 0: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; + +case 1: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; + +case 2: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; + +case 3: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; + +case 4: { + sym(1).UiProgram = makeAstNode<AST::UiProgram> (driver->nodePool(), sym(1).UiImportList, + sym(2).UiObjectMemberList->finish()); +} break; + +case 6: { + sym(1).Node = sym(1).UiImportList->finish(); +} break; + +case 7: { + sym(1).Node = makeAstNode<AST::UiImportList> (driver->nodePool(), sym(1).UiImport); +} break; + +case 8: { + sym(1).Node = makeAstNode<AST::UiImportList> (driver->nodePool(), + sym(1).UiImportList, sym(2).UiImport); +} break; + +case 11: { + sym(1).UiImport->semicolonToken = loc(2); +} break; + +case 13: { + sym(1).UiImport->versionToken = loc(2); + sym(1).UiImport->semicolonToken = loc(3); +} break; + +case 15: { + sym(1).UiImport->versionToken = loc(2); + sym(1).UiImport->asToken = loc(3); + sym(1).UiImport->importIdToken = loc(4); + sym(1).UiImport->importId = sym(4).sval; + sym(1).UiImport->semicolonToken = loc(5); +} break; + +case 17: { + sym(1).UiImport->asToken = loc(2); + sym(1).UiImport->importIdToken = loc(3); + sym(1).UiImport->importId = sym(3).sval; + sym(1).UiImport->semicolonToken = loc(4); +} break; + +case 18: { + AST::UiImport *node = 0; + + if (AST::StringLiteral *importIdLiteral = AST::cast<AST::StringLiteral *>(sym(2).Expression)) { + node = makeAstNode<AST::UiImport>(driver->nodePool(), importIdLiteral->value); + node->fileNameToken = loc(2); + } else if (AST::UiQualifiedId *qualifiedId = reparseAsQualifiedId(sym(2).Expression)) { + QString text; + for (AST::UiQualifiedId *q = qualifiedId; q; q = q->next) { + text += q->name->asString(); + if (q->next) text += QLatin1String("."); + } + node = makeAstNode<AST::UiImport>(driver->nodePool(), qualifiedId); + node->fileNameToken = loc(2); + } + + sym(1).Node = node; + + if (! node) { + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1), + QLatin1String("Expected a qualified name id or a string literal"))); + + return false; // ### remove me + } +} break; + +case 19: { + sym(1).Node = 0; +} break; + +case 20: { + sym(1).Node = makeAstNode<AST::UiObjectMemberList> (driver->nodePool(), sym(1).UiObjectMember); +} break; + +case 21: { + sym(1).Node = makeAstNode<AST::UiObjectMemberList> (driver->nodePool(), sym(1).UiObjectMember); +} break; + +case 22: { + AST::UiObjectMemberList *node = makeAstNode<AST:: UiObjectMemberList> (driver->nodePool(), + sym(1).UiObjectMemberList, sym(2).UiObjectMember); + sym(1).Node = node; +} break; + +case 23: { + sym(1).Node = makeAstNode<AST::UiArrayMemberList> (driver->nodePool(), sym(1).UiObjectMember); +} break; + +case 24: { + AST::UiArrayMemberList *node = makeAstNode<AST::UiArrayMemberList> (driver->nodePool(), + sym(1).UiArrayMemberList, sym(3).UiObjectMember); + node->commaToken = loc(2); + sym(1).Node = node; +} break; + +case 25: { + AST::UiObjectInitializer *node = makeAstNode<AST::UiObjectInitializer> (driver->nodePool(), (AST::UiObjectMemberList*)0); + node->lbraceToken = loc(1); + node->rbraceToken = loc(2); + sym(1).Node = node; +} break; + +case 26: { + AST::UiObjectInitializer *node = makeAstNode<AST::UiObjectInitializer> (driver->nodePool(), sym(2).UiObjectMemberList->finish()); + node->lbraceToken = loc(1); + node->rbraceToken = loc(3); + sym(1).Node = node; +} break; + +case 27: { + AST::UiObjectDefinition *node = makeAstNode<AST::UiObjectDefinition> (driver->nodePool(), sym(1).UiQualifiedId, + sym(2).UiObjectInitializer); + sym(1).Node = node; +} break; + +case 29: { + AST::UiArrayBinding *node = makeAstNode<AST::UiArrayBinding> (driver->nodePool(), + sym(1).UiQualifiedId, sym(4).UiArrayMemberList->finish()); + node->colonToken = loc(2); + node->lbracketToken = loc(3); + node->rbracketToken = loc(5); + sym(1).Node = node; +} break; + +case 30: { + AST::UiObjectBinding *node = makeAstNode<AST::UiObjectBinding> (driver->nodePool(), + sym(1).UiQualifiedId, sym(3).UiQualifiedId, sym(4).UiObjectInitializer); + node->colonToken = loc(2); + sym(1).Node = node; +} break; +case 31:case 32:case 33:case 34: +{ + AST::UiScriptBinding *node = makeAstNode<AST::UiScriptBinding> (driver->nodePool(), + sym(1).UiQualifiedId, sym(3).Statement); + node->colonToken = loc(2); + sym(1).Node = node; +} break; + +case 35: + +case 36: { + sym(1).sval = driver->intern(lexer->characterBuffer(), lexer->characterCount()); + break; +} + +case 38: { + sym(1).Node = 0; +} break; + +case 39: { + sym(1).Node = sym(1).UiParameterList->finish (); +} break; + +case 40: { + AST::UiParameterList *node = makeAstNode<AST::UiParameterList> (driver->nodePool(), sym(1).sval, sym(2).sval); + node->identifierToken = loc(2); + sym(1).Node = node; +} break; + +case 41: { + AST::UiParameterList *node = makeAstNode<AST::UiParameterList> (driver->nodePool(), sym(1).UiParameterList, sym(3).sval, sym(4).sval); + node->commaToken = loc(2); + node->identifierToken = loc(4); + sym(1).Node = node; +} break; + +case 43: { + AST::UiPublicMember *node = makeAstNode<AST::UiPublicMember> (driver->nodePool(), (NameId *)0, sym(2).sval); + node->type = AST::UiPublicMember::Signal; + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(2); + node->parameters = sym(4).UiParameterList; + node->semicolonToken = loc(6); + sym(1).Node = node; +} break; + +case 45: { + AST::UiPublicMember *node = makeAstNode<AST::UiPublicMember> (driver->nodePool(), (NameId *)0, sym(2).sval); + node->type = AST::UiPublicMember::Signal; + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(2); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; + +case 47: { + AST::UiPublicMember *node = makeAstNode<AST::UiPublicMember> (driver->nodePool(), sym(4).sval, sym(6).sval); + node->typeModifier = sym(2).sval; + node->propertyToken = loc(1); + node->typeModifierToken = loc(2); + node->typeToken = loc(4); + node->identifierToken = loc(6); + node->semicolonToken = loc(7); + sym(1).Node = node; +} break; + +case 49: { + AST::UiPublicMember *node = makeAstNode<AST::UiPublicMember> (driver->nodePool(), sym(2).sval, sym(3).sval); + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(3); + node->semicolonToken = loc(4); + sym(1).Node = node; +} break; + +case 51: { + AST::UiPublicMember *node = makeAstNode<AST::UiPublicMember> (driver->nodePool(), sym(3).sval, sym(4).sval); + node->isDefaultMember = true; + node->defaultToken = loc(1); + node->propertyToken = loc(2); + node->typeToken = loc(3); + node->identifierToken = loc(4); + node->semicolonToken = loc(5); + sym(1).Node = node; +} break; + +case 53: { + AST::UiPublicMember *node = makeAstNode<AST::UiPublicMember> (driver->nodePool(), sym(2).sval, sym(3).sval, + sym(5).Expression); + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(3); + node->colonToken = loc(4); + node->semicolonToken = loc(6); + sym(1).Node = node; +} break; + +case 55: { + AST::UiPublicMember *node = makeAstNode<AST::UiPublicMember> (driver->nodePool(), sym(3).sval, sym(4).sval, + sym(6).Expression); + node->isReadonlyMember = true; + node->readonlyToken = loc(1); + node->propertyToken = loc(2); + node->typeToken = loc(3); + node->identifierToken = loc(4); + node->colonToken = loc(5); + node->semicolonToken = loc(7); + sym(1).Node = node; +} break; + +case 57: { + AST::UiPublicMember *node = makeAstNode<AST::UiPublicMember> (driver->nodePool(), sym(3).sval, sym(4).sval, + sym(6).Expression); + node->isDefaultMember = true; + node->defaultToken = loc(1); + node->propertyToken = loc(2); + node->typeToken = loc(3); + node->identifierToken = loc(4); + node->colonToken = loc(5); + node->semicolonToken = loc(7); + sym(1).Node = node; +} break; + +case 58: { + sym(1).Node = makeAstNode<AST::UiSourceElement>(driver->nodePool(), sym(1).Node); +} break; + +case 59: { + sym(1).Node = makeAstNode<AST::UiSourceElement>(driver->nodePool(), sym(1).Node); +} break; + +case 61: { + QString s = QLatin1String(QmlJSGrammar::spell[T_PROPERTY]); + sym(1).sval = driver->intern(s.constData(), s.length()); + break; +} + +case 62: { + QString s = QLatin1String(QmlJSGrammar::spell[T_SIGNAL]); + sym(1).sval = driver->intern(s.constData(), s.length()); + break; +} + +case 63: { + QString s = QLatin1String(QmlJSGrammar::spell[T_READONLY]); + sym(1).sval = driver->intern(s.constData(), s.length()); + break; +} + +case 64: { + AST::ThisExpression *node = makeAstNode<AST::ThisExpression> (driver->nodePool()); + node->thisToken = loc(1); + sym(1).Node = node; +} break; + +case 65: { + AST::IdentifierExpression *node = makeAstNode<AST::IdentifierExpression> (driver->nodePool(), sym(1).sval); + node->identifierToken = loc(1); + sym(1).Node = node; +} break; + +case 66: { + AST::NullExpression *node = makeAstNode<AST::NullExpression> (driver->nodePool()); + node->nullToken = loc(1); + sym(1).Node = node; +} break; + +case 67: { + AST::TrueLiteral *node = makeAstNode<AST::TrueLiteral> (driver->nodePool()); + node->trueToken = loc(1); + sym(1).Node = node; +} break; + +case 68: { + AST::FalseLiteral *node = makeAstNode<AST::FalseLiteral> (driver->nodePool()); + node->falseToken = loc(1); + sym(1).Node = node; +} break; + +case 69: { + AST::NumericLiteral *node = makeAstNode<AST::NumericLiteral> (driver->nodePool(), sym(1).dval); + node->literalToken = loc(1); + sym(1).Node = node; +} break; +case 70: +case 71: { + AST::StringLiteral *node = makeAstNode<AST::StringLiteral> (driver->nodePool(), sym(1).sval); + node->literalToken = loc(1); + sym(1).Node = node; +} break; + +case 72: { + bool rx = lexer->scanRegExp(Lexer::NoPrefix); + if (!rx) { + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage())); + return false; // ### remove me + } + AST::RegExpLiteral *node = makeAstNode<AST::RegExpLiteral> (driver->nodePool(), lexer->pattern, lexer->flags); + node->literalToken = loc(1); + sym(1).Node = node; +} break; + +case 73: { + bool rx = lexer->scanRegExp(Lexer::EqualPrefix); + if (!rx) { + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage())); + return false; + } + AST::RegExpLiteral *node = makeAstNode<AST::RegExpLiteral> (driver->nodePool(), lexer->pattern, lexer->flags); + node->literalToken = loc(1); + sym(1).Node = node; +} break; + +case 74: { + AST::ArrayLiteral *node = makeAstNode<AST::ArrayLiteral> (driver->nodePool(), (AST::Elision *) 0); + node->lbracketToken = loc(1); + node->rbracketToken = loc(2); + sym(1).Node = node; +} break; + +case 75: { + AST::ArrayLiteral *node = makeAstNode<AST::ArrayLiteral> (driver->nodePool(), sym(2).Elision->finish()); + node->lbracketToken = loc(1); + node->rbracketToken = loc(3); + sym(1).Node = node; +} break; + +case 76: { + AST::ArrayLiteral *node = makeAstNode<AST::ArrayLiteral> (driver->nodePool(), sym(2).ElementList->finish ()); + node->lbracketToken = loc(1); + node->rbracketToken = loc(3); + sym(1).Node = node; +} break; + +case 77: { + AST::ArrayLiteral *node = makeAstNode<AST::ArrayLiteral> (driver->nodePool(), sym(2).ElementList->finish (), + (AST::Elision *) 0); + node->lbracketToken = loc(1); + node->commaToken = loc(3); + node->rbracketToken = loc(4); + sym(1).Node = node; +} break; + +case 78: { + AST::ArrayLiteral *node = makeAstNode<AST::ArrayLiteral> (driver->nodePool(), sym(2).ElementList->finish (), + sym(4).Elision->finish()); + node->lbracketToken = loc(1); + node->commaToken = loc(3); + node->rbracketToken = loc(5); + sym(1).Node = node; +} break; + +case 79: { + AST::ObjectLiteral *node = 0; + if (sym(2).Node) + node = makeAstNode<AST::ObjectLiteral> (driver->nodePool(), + sym(2).PropertyNameAndValueList->finish ()); + else + node = makeAstNode<AST::ObjectLiteral> (driver->nodePool()); + node->lbraceToken = loc(1); + node->lbraceToken = loc(3); + sym(1).Node = node; +} break; + +case 80: { + AST::ObjectLiteral *node = makeAstNode<AST::ObjectLiteral> (driver->nodePool(), + sym(2).PropertyNameAndValueList->finish ()); + node->lbraceToken = loc(1); + node->lbraceToken = loc(4); + sym(1).Node = node; +} break; + +case 81: { + AST::NestedExpression *node = makeAstNode<AST::NestedExpression>(driver->nodePool(), sym(2).Expression); + node->lparenToken = loc(1); + node->rparenToken = loc(3); + sym(1).Node = node; +} break; + +case 82: { + if (AST::ArrayMemberExpression *mem = AST::cast<AST::ArrayMemberExpression *>(sym(1).Expression)) { + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Warning, mem->lbracketToken, + QLatin1String("Ignored annotation"))); + + sym(1).Expression = mem->base; + } + + if (AST::UiQualifiedId *qualifiedId = reparseAsQualifiedId(sym(1).Expression)) { + sym(1).UiQualifiedId = qualifiedId; + } else { + sym(1).UiQualifiedId = 0; + + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1), + QLatin1String("Expected a qualified name id"))); + + return false; // ### recover + } +} break; + +case 83: { + sym(1).Node = makeAstNode<AST::ElementList> (driver->nodePool(), (AST::Elision *) 0, sym(1).Expression); +} break; + +case 84: { + sym(1).Node = makeAstNode<AST::ElementList> (driver->nodePool(), sym(1).Elision->finish(), sym(2).Expression); +} break; + +case 85: { + AST::ElementList *node = makeAstNode<AST::ElementList> (driver->nodePool(), sym(1).ElementList, + (AST::Elision *) 0, sym(3).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; + +case 86: { + AST::ElementList *node = makeAstNode<AST::ElementList> (driver->nodePool(), sym(1).ElementList, sym(3).Elision->finish(), + sym(4).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; + +case 87: { + AST::Elision *node = makeAstNode<AST::Elision> (driver->nodePool()); + node->commaToken = loc(1); + sym(1).Node = node; +} break; + +case 88: { + AST::Elision *node = makeAstNode<AST::Elision> (driver->nodePool(), sym(1).Elision); + node->commaToken = loc(2); + sym(1).Node = node; +} break; + +case 89: { + AST::PropertyNameAndValueList *node = makeAstNode<AST::PropertyNameAndValueList> (driver->nodePool(), + sym(1).PropertyName, sym(3).Expression); + node->colonToken = loc(2); + sym(1).Node = node; +} break; + +case 90: { + AST::PropertyNameAndValueList *node = makeAstNode<AST::PropertyNameAndValueList> (driver->nodePool(), + sym(1).PropertyNameAndValueList, sym(3).PropertyName, sym(5).Expression); + node->commaToken = loc(2); + node->colonToken = loc(4); + sym(1).Node = node; +} break; + +case 91: { + AST::IdentifierPropertyName *node = makeAstNode<AST::IdentifierPropertyName> (driver->nodePool(), sym(1).sval); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; +case 92: +case 93: { + AST::IdentifierPropertyName *node = makeAstNode<AST::IdentifierPropertyName> (driver->nodePool(), driver->intern(lexer->characterBuffer(), lexer->characterCount())); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; + +case 94: { + AST::StringLiteralPropertyName *node = makeAstNode<AST::StringLiteralPropertyName> (driver->nodePool(), sym(1).sval); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; + +case 95: { + AST::NumericLiteralPropertyName *node = makeAstNode<AST::NumericLiteralPropertyName> (driver->nodePool(), sym(1).dval); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; + +case 96: { + AST::IdentifierPropertyName *node = makeAstNode<AST::IdentifierPropertyName> (driver->nodePool(), sym(1).sval); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; + +case 97: + +case 98: + +case 99: + +case 100: + +case 101: + +case 102: + +case 103: + +case 104: + +case 105: + +case 106: + +case 107: + +case 108: + +case 109: + +case 110: + +case 111: + +case 112: + +case 113: + +case 114: + +case 115: + +case 116: + +case 117: + +case 118: + +case 119: + +case 120: + +case 121: + +case 122: + +case 123: + +case 124: + +case 125: + +case 126: + +case 127: +{ + sym(1).sval = driver->intern(lexer->characterBuffer(), lexer->characterCount()); +} break; + +case 132: { + AST::ArrayMemberExpression *node = makeAstNode<AST::ArrayMemberExpression> (driver->nodePool(), sym(1).Expression, sym(3).Expression); + node->lbracketToken = loc(2); + node->rbracketToken = loc(4); + sym(1).Node = node; +} break; + +case 133: { + AST::FieldMemberExpression *node = makeAstNode<AST::FieldMemberExpression> (driver->nodePool(), sym(1).Expression, sym(3).sval); + node->dotToken = loc(2); + node->identifierToken = loc(3); + sym(1).Node = node; +} break; + +case 134: { + AST::NewMemberExpression *node = makeAstNode<AST::NewMemberExpression> (driver->nodePool(), sym(2).Expression, sym(4).ArgumentList); + node->newToken = loc(1); + node->lparenToken = loc(3); + node->rparenToken = loc(5); + sym(1).Node = node; +} break; + +case 136: { + AST::NewExpression *node = makeAstNode<AST::NewExpression> (driver->nodePool(), sym(2).Expression); + node->newToken = loc(1); + sym(1).Node = node; +} break; + +case 137: { + AST::CallExpression *node = makeAstNode<AST::CallExpression> (driver->nodePool(), sym(1).Expression, sym(3).ArgumentList); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; + +case 138: { + AST::CallExpression *node = makeAstNode<AST::CallExpression> (driver->nodePool(), sym(1).Expression, sym(3).ArgumentList); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; + +case 139: { + AST::ArrayMemberExpression *node = makeAstNode<AST::ArrayMemberExpression> (driver->nodePool(), sym(1).Expression, sym(3).Expression); + node->lbracketToken = loc(2); + node->rbracketToken = loc(4); + sym(1).Node = node; +} break; + +case 140: { + AST::FieldMemberExpression *node = makeAstNode<AST::FieldMemberExpression> (driver->nodePool(), sym(1).Expression, sym(3).sval); + node->dotToken = loc(2); + node->identifierToken = loc(3); + sym(1).Node = node; +} break; + +case 141: { + sym(1).Node = 0; +} break; + +case 142: { + sym(1).Node = sym(1).ArgumentList->finish(); +} break; + +case 143: { + sym(1).Node = makeAstNode<AST::ArgumentList> (driver->nodePool(), sym(1).Expression); +} break; + +case 144: { + AST::ArgumentList *node = makeAstNode<AST::ArgumentList> (driver->nodePool(), sym(1).ArgumentList, sym(3).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; + +case 148: { + AST::PostIncrementExpression *node = makeAstNode<AST::PostIncrementExpression> (driver->nodePool(), sym(1).Expression); + node->incrementToken = loc(2); + sym(1).Node = node; +} break; + +case 149: { + AST::PostDecrementExpression *node = makeAstNode<AST::PostDecrementExpression> (driver->nodePool(), sym(1).Expression); + node->decrementToken = loc(2); + sym(1).Node = node; +} break; + +case 151: { + AST::DeleteExpression *node = makeAstNode<AST::DeleteExpression> (driver->nodePool(), sym(2).Expression); + node->deleteToken = loc(1); + sym(1).Node = node; +} break; + +case 152: { + AST::VoidExpression *node = makeAstNode<AST::VoidExpression> (driver->nodePool(), sym(2).Expression); + node->voidToken = loc(1); + sym(1).Node = node; +} break; + +case 153: { + AST::TypeOfExpression *node = makeAstNode<AST::TypeOfExpression> (driver->nodePool(), sym(2).Expression); + node->typeofToken = loc(1); + sym(1).Node = node; +} break; + +case 154: { + AST::PreIncrementExpression *node = makeAstNode<AST::PreIncrementExpression> (driver->nodePool(), sym(2).Expression); + node->incrementToken = loc(1); + sym(1).Node = node; +} break; + +case 155: { + AST::PreDecrementExpression *node = makeAstNode<AST::PreDecrementExpression> (driver->nodePool(), sym(2).Expression); + node->decrementToken = loc(1); + sym(1).Node = node; +} break; + +case 156: { + AST::UnaryPlusExpression *node = makeAstNode<AST::UnaryPlusExpression> (driver->nodePool(), sym(2).Expression); + node->plusToken = loc(1); + sym(1).Node = node; +} break; + +case 157: { + AST::UnaryMinusExpression *node = makeAstNode<AST::UnaryMinusExpression> (driver->nodePool(), sym(2).Expression); + node->minusToken = loc(1); + sym(1).Node = node; +} break; + +case 158: { + AST::TildeExpression *node = makeAstNode<AST::TildeExpression> (driver->nodePool(), sym(2).Expression); + node->tildeToken = loc(1); + sym(1).Node = node; +} break; + +case 159: { + AST::NotExpression *node = makeAstNode<AST::NotExpression> (driver->nodePool(), sym(2).Expression); + node->notToken = loc(1); + sym(1).Node = node; +} break; + +case 161: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Mul, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 162: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Div, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 163: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Mod, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 165: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Add, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 166: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Sub, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 168: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::LShift, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 169: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::RShift, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 170: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::URShift, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 172: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Lt, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 173: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Gt, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 174: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Le, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 175: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Ge, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 176: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::InstanceOf, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 177: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::In, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 179: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Lt, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 180: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Gt, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 181: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Le, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 182: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Ge, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 183: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::InstanceOf, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 185: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Equal, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 186: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::NotEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 187: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::StrictEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 188: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::StrictNotEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 190: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Equal, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 191: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::NotEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 192: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::StrictEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 193: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::StrictNotEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 195: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::BitAnd, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 197: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::BitAnd, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 199: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::BitXor, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 201: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::BitXor, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 203: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::BitOr, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 205: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::BitOr, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 207: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::And, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 209: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::And, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 211: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Or, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 213: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + QSOperator::Or, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 215: { + AST::ConditionalExpression *node = makeAstNode<AST::ConditionalExpression> (driver->nodePool(), sym(1).Expression, + sym(3).Expression, sym(5).Expression); + node->questionToken = loc(2); + node->colonToken = loc(4); + sym(1).Node = node; +} break; + +case 217: { + AST::ConditionalExpression *node = makeAstNode<AST::ConditionalExpression> (driver->nodePool(), sym(1).Expression, + sym(3).Expression, sym(5).Expression); + node->questionToken = loc(2); + node->colonToken = loc(4); + sym(1).Node = node; +} break; + +case 219: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + sym(2).ival, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 221: { + AST::BinaryExpression *node = makeAstNode<AST::BinaryExpression> (driver->nodePool(), sym(1).Expression, + sym(2).ival, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 222: { + sym(1).ival = QSOperator::Assign; +} break; + +case 223: { + sym(1).ival = QSOperator::InplaceMul; +} break; + +case 224: { + sym(1).ival = QSOperator::InplaceDiv; +} break; + +case 225: { + sym(1).ival = QSOperator::InplaceMod; +} break; + +case 226: { + sym(1).ival = QSOperator::InplaceAdd; +} break; + +case 227: { + sym(1).ival = QSOperator::InplaceSub; +} break; + +case 228: { + sym(1).ival = QSOperator::InplaceLeftShift; +} break; + +case 229: { + sym(1).ival = QSOperator::InplaceRightShift; +} break; + +case 230: { + sym(1).ival = QSOperator::InplaceURightShift; +} break; + +case 231: { + sym(1).ival = QSOperator::InplaceAnd; +} break; + +case 232: { + sym(1).ival = QSOperator::InplaceXor; +} break; + +case 233: { + sym(1).ival = QSOperator::InplaceOr; +} break; + +case 235: { + AST::Expression *node = makeAstNode<AST::Expression> (driver->nodePool(), sym(1).Expression, sym(3).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; + +case 236: { + sym(1).Node = 0; +} break; + +case 239: { + AST::Expression *node = makeAstNode<AST::Expression> (driver->nodePool(), sym(1).Expression, sym(3).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; + +case 240: { + sym(1).Node = 0; +} break; + +case 257: { + AST::Block *node = makeAstNode<AST::Block> (driver->nodePool(), sym(2).StatementList); + node->lbraceToken = loc(1); + node->rbraceToken = loc(3); + sym(1).Node = node; +} break; + +case 258: { + sym(1).Node = makeAstNode<AST::StatementList> (driver->nodePool(), sym(1).Statement); +} break; + +case 259: { + sym(1).Node = makeAstNode<AST::StatementList> (driver->nodePool(), sym(1).StatementList, sym(2).Statement); +} break; + +case 260: { + sym(1).Node = 0; +} break; + +case 261: { + sym(1).Node = sym(1).StatementList->finish (); +} break; + +case 263: { + AST::VariableStatement *node = makeAstNode<AST::VariableStatement> (driver->nodePool(), + sym(2).VariableDeclarationList->finish (/*readOnly=*/sym(1).ival == T_CONST)); + node->declarationKindToken = loc(1); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; + +case 264: { + sym(1).ival = T_CONST; +} break; + +case 265: { + sym(1).ival = T_VAR; +} break; + +case 266: { + sym(1).Node = makeAstNode<AST::VariableDeclarationList> (driver->nodePool(), sym(1).VariableDeclaration); +} break; + +case 267: { + AST::VariableDeclarationList *node = makeAstNode<AST::VariableDeclarationList> (driver->nodePool(), + sym(1).VariableDeclarationList, sym(3).VariableDeclaration); + node->commaToken = loc(2); + sym(1).Node = node; +} break; + +case 268: { + sym(1).Node = makeAstNode<AST::VariableDeclarationList> (driver->nodePool(), sym(1).VariableDeclaration); +} break; + +case 269: { + sym(1).Node = makeAstNode<AST::VariableDeclarationList> (driver->nodePool(), sym(1).VariableDeclarationList, sym(3).VariableDeclaration); +} break; + +case 270: { + AST::VariableDeclaration *node = makeAstNode<AST::VariableDeclaration> (driver->nodePool(), sym(1).sval, sym(2).Expression); + node->identifierToken = loc(1); + sym(1).Node = node; +} break; + +case 271: { + AST::VariableDeclaration *node = makeAstNode<AST::VariableDeclaration> (driver->nodePool(), sym(1).sval, sym(2).Expression); + node->identifierToken = loc(1); + sym(1).Node = node; +} break; + +case 272: { + // ### TODO: AST for initializer + sym(1) = sym(2); +} break; + +case 273: { + sym(1).Node = 0; +} break; + +case 275: { + // ### TODO: AST for initializer + sym(1) = sym(2); +} break; + +case 276: { + sym(1).Node = 0; +} break; + +case 278: { + AST::EmptyStatement *node = makeAstNode<AST::EmptyStatement> (driver->nodePool()); + node->semicolonToken = loc(1); + sym(1).Node = node; +} break; + +case 280: { + AST::ExpressionStatement *node = makeAstNode<AST::ExpressionStatement> (driver->nodePool(), sym(1).Expression); + node->semicolonToken = loc(2); + sym(1).Node = node; +} break; + +case 281: { + AST::IfStatement *node = makeAstNode<AST::IfStatement> (driver->nodePool(), sym(3).Expression, sym(5).Statement, sym(7).Statement); + node->ifToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + node->elseToken = loc(5); + sym(1).Node = node; +} break; + +case 282: { + AST::IfStatement *node = makeAstNode<AST::IfStatement> (driver->nodePool(), sym(3).Expression, sym(5).Statement); + node->ifToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; + +case 284: { + AST::DoWhileStatement *node = makeAstNode<AST::DoWhileStatement> (driver->nodePool(), sym(2).Statement, sym(5).Expression); + node->doToken = loc(1); + node->whileToken = loc(3); + node->lparenToken = loc(4); + node->rparenToken = loc(6); + node->semicolonToken = loc(7); + sym(1).Node = node; +} break; + +case 285: { + AST::WhileStatement *node = makeAstNode<AST::WhileStatement> (driver->nodePool(), sym(3).Expression, sym(5).Statement); + node->whileToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; + +case 286: { + AST::ForStatement *node = makeAstNode<AST::ForStatement> (driver->nodePool(), sym(3).Expression, + sym(5).Expression, sym(7).Expression, sym(9).Statement); + node->forToken = loc(1); + node->lparenToken = loc(2); + node->firstSemicolonToken = loc(4); + node->secondSemicolonToken = loc(6); + node->rparenToken = loc(8); + sym(1).Node = node; +} break; + +case 287: { + AST::LocalForStatement *node = makeAstNode<AST::LocalForStatement> (driver->nodePool(), + sym(4).VariableDeclarationList->finish (/*readOnly=*/false), sym(6).Expression, + sym(8).Expression, sym(10).Statement); + node->forToken = loc(1); + node->lparenToken = loc(2); + node->varToken = loc(3); + node->firstSemicolonToken = loc(5); + node->secondSemicolonToken = loc(7); + node->rparenToken = loc(9); + sym(1).Node = node; +} break; + +case 288: { + AST:: ForEachStatement *node = makeAstNode<AST::ForEachStatement> (driver->nodePool(), sym(3).Expression, + sym(5).Expression, sym(7).Statement); + node->forToken = loc(1); + node->lparenToken = loc(2); + node->inToken = loc(4); + node->rparenToken = loc(6); + sym(1).Node = node; +} break; + +case 289: { + AST::LocalForEachStatement *node = makeAstNode<AST::LocalForEachStatement> (driver->nodePool(), + sym(4).VariableDeclaration, sym(6).Expression, sym(8).Statement); + node->forToken = loc(1); + node->lparenToken = loc(2); + node->varToken = loc(3); + node->inToken = loc(5); + node->rparenToken = loc(7); + sym(1).Node = node; +} break; + +case 291: { + AST::ContinueStatement *node = makeAstNode<AST::ContinueStatement> (driver->nodePool()); + node->continueToken = loc(1); + node->semicolonToken = loc(2); + sym(1).Node = node; +} break; + +case 293: { + AST::ContinueStatement *node = makeAstNode<AST::ContinueStatement> (driver->nodePool(), sym(2).sval); + node->continueToken = loc(1); + node->identifierToken = loc(2); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; + +case 295: { + AST::BreakStatement *node = makeAstNode<AST::BreakStatement> (driver->nodePool()); + node->breakToken = loc(1); + node->semicolonToken = loc(2); + sym(1).Node = node; +} break; + +case 297: { + AST::BreakStatement *node = makeAstNode<AST::BreakStatement> (driver->nodePool(), sym(2).sval); + node->breakToken = loc(1); + node->identifierToken = loc(2); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; + +case 299: { + AST::ReturnStatement *node = makeAstNode<AST::ReturnStatement> (driver->nodePool(), sym(2).Expression); + node->returnToken = loc(1); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; + +case 300: { + AST::WithStatement *node = makeAstNode<AST::WithStatement> (driver->nodePool(), sym(3).Expression, sym(5).Statement); + node->withToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; + +case 301: { + AST::SwitchStatement *node = makeAstNode<AST::SwitchStatement> (driver->nodePool(), sym(3).Expression, sym(5).CaseBlock); + node->switchToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; + +case 302: { + AST::CaseBlock *node = makeAstNode<AST::CaseBlock> (driver->nodePool(), sym(2).CaseClauses); + node->lbraceToken = loc(1); + node->rbraceToken = loc(3); + sym(1).Node = node; +} break; + +case 303: { + AST::CaseBlock *node = makeAstNode<AST::CaseBlock> (driver->nodePool(), sym(2).CaseClauses, sym(3).DefaultClause, sym(4).CaseClauses); + node->lbraceToken = loc(1); + node->rbraceToken = loc(5); + sym(1).Node = node; +} break; + +case 304: { + sym(1).Node = makeAstNode<AST::CaseClauses> (driver->nodePool(), sym(1).CaseClause); +} break; + +case 305: { + sym(1).Node = makeAstNode<AST::CaseClauses> (driver->nodePool(), sym(1).CaseClauses, sym(2).CaseClause); +} break; + +case 306: { + sym(1).Node = 0; +} break; + +case 307: { + sym(1).Node = sym(1).CaseClauses->finish (); +} break; + +case 308: { + AST::CaseClause *node = makeAstNode<AST::CaseClause> (driver->nodePool(), sym(2).Expression, sym(4).StatementList); + node->caseToken = loc(1); + node->colonToken = loc(3); + sym(1).Node = node; +} break; + +case 309: { + AST::DefaultClause *node = makeAstNode<AST::DefaultClause> (driver->nodePool(), sym(3).StatementList); + node->defaultToken = loc(1); + node->colonToken = loc(2); + sym(1).Node = node; +} break; +case 310: +case 311: { + AST::LabelledStatement *node = makeAstNode<AST::LabelledStatement> (driver->nodePool(), driver->intern(lexer->characterBuffer(), lexer->characterCount()), sym(3).Statement); + node->identifierToken = loc(1); + node->colonToken = loc(2); + sym(1).Node = node; +} break; + +case 312: { + AST::LabelledStatement *node = makeAstNode<AST::LabelledStatement> (driver->nodePool(), sym(1).sval, sym(3).Statement); + node->identifierToken = loc(1); + node->colonToken = loc(2); + sym(1).Node = node; +} break; + +case 314: { + AST::ThrowStatement *node = makeAstNode<AST::ThrowStatement> (driver->nodePool(), sym(2).Expression); + node->throwToken = loc(1); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; + +case 315: { + AST::TryStatement *node = makeAstNode<AST::TryStatement> (driver->nodePool(), sym(2).Statement, sym(3).Catch); + node->tryToken = loc(1); + sym(1).Node = node; +} break; + +case 316: { + AST::TryStatement *node = makeAstNode<AST::TryStatement> (driver->nodePool(), sym(2).Statement, sym(3).Finally); + node->tryToken = loc(1); + sym(1).Node = node; +} break; + +case 317: { + AST::TryStatement *node = makeAstNode<AST::TryStatement> (driver->nodePool(), sym(2).Statement, sym(3).Catch, sym(4).Finally); + node->tryToken = loc(1); + sym(1).Node = node; +} break; + +case 318: { + AST::Catch *node = makeAstNode<AST::Catch> (driver->nodePool(), sym(3).sval, sym(5).Block); + node->catchToken = loc(1); + node->lparenToken = loc(2); + node->identifierToken = loc(3); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; + +case 319: { + AST::Finally *node = makeAstNode<AST::Finally> (driver->nodePool(), sym(2).Block); + node->finallyToken = loc(1); + sym(1).Node = node; +} break; + +case 321: { + AST::DebuggerStatement *node = makeAstNode<AST::DebuggerStatement> (driver->nodePool()); + node->debuggerToken = loc(1); + node->semicolonToken = loc(2); + sym(1).Node = node; +} break; + +case 322: { + AST::FunctionDeclaration *node = makeAstNode<AST::FunctionDeclaration> (driver->nodePool(), sym(2).sval, sym(4).FormalParameterList, sym(7).FunctionBody); + node->functionToken = loc(1); + node->identifierToken = loc(2); + node->lparenToken = loc(3); + node->rparenToken = loc(5); + node->lbraceToken = loc(6); + node->rbraceToken = loc(8); + sym(1).Node = node; +} break; + +case 323: { + AST::FunctionExpression *node = makeAstNode<AST::FunctionExpression> (driver->nodePool(), sym(2).sval, sym(4).FormalParameterList, sym(7).FunctionBody); + node->functionToken = loc(1); + if (sym(2).sval) + node->identifierToken = loc(2); + node->lparenToken = loc(3); + node->rparenToken = loc(5); + node->lbraceToken = loc(6); + node->rbraceToken = loc(8); + sym(1).Node = node; +} break; + +case 324: { + AST::FormalParameterList *node = makeAstNode<AST::FormalParameterList> (driver->nodePool(), sym(1).sval); + node->identifierToken = loc(1); + sym(1).Node = node; +} break; + +case 325: { + AST::FormalParameterList *node = makeAstNode<AST::FormalParameterList> (driver->nodePool(), sym(1).FormalParameterList, sym(3).sval); + node->commaToken = loc(2); + node->identifierToken = loc(3); + sym(1).Node = node; +} break; + +case 326: { + sym(1).Node = 0; +} break; + +case 327: { + sym(1).Node = sym(1).FormalParameterList->finish (); +} break; + +case 328: { + sym(1).Node = 0; +} break; + +case 330: { + sym(1).Node = makeAstNode<AST::FunctionBody> (driver->nodePool(), sym(1).SourceElements->finish ()); +} break; + +case 331: { + sym(1).Node = makeAstNode<AST::SourceElements> (driver->nodePool(), sym(1).SourceElement); +} break; + +case 332: { + sym(1).Node = makeAstNode<AST::SourceElements> (driver->nodePool(), sym(1).SourceElements, sym(2).SourceElement); +} break; + +case 333: { + sym(1).Node = makeAstNode<AST::StatementSourceElement> (driver->nodePool(), sym(1).Statement); +} break; + +case 334: { + sym(1).Node = makeAstNode<AST::FunctionSourceElement> (driver->nodePool(), sym(1).FunctionDeclaration); +} break; + +case 335: { + sym(1).sval = 0; +} break; + +case 337: { + sym(1).Node = 0; +} break; + + } // switch + action = nt_action(state_stack[tos], lhs[r] - TERMINAL_COUNT); + } // if + } while (action != 0); + + if (first_token == last_token) { + const int errorState = state_stack[tos]; + + // automatic insertion of `;' + if (t_action(errorState, T_AUTOMATIC_SEMICOLON) && automatic(driver, yytoken)) { + SavedToken &tk = token_buffer[0]; + tk.token = yytoken; + tk.dval = yylval; + tk.loc = yylloc; + + yylloc = yyprevlloc; + yylloc.offset += yylloc.length; + yylloc.startColumn += yylloc.length; + yylloc.length = 0; + + //const QString msg = QString::fromUtf8("Missing `;'"); + //diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Warning, yylloc, msg)); + + first_token = &token_buffer[0]; + last_token = &token_buffer[1]; + + yytoken = T_SEMICOLON; + yylval = 0; + + action = errorState; + + goto _Lcheck_token; + } + + hadErrors = true; + + token_buffer[0].token = yytoken; + token_buffer[0].dval = yylval; + token_buffer[0].loc = yylloc; + + token_buffer[1].token = yytoken = lexer->lex(); + token_buffer[1].dval = yylval = lexer->dval(); + token_buffer[1].loc = yylloc = location(lexer); + + if (t_action(errorState, yytoken)) { + const QString msg = QString::fromUtf8("Unexpected token `%1'").arg(QLatin1String(spell[token_buffer[0].token])); + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); + + action = errorState; + goto _Lcheck_token; + } + + static int tokens[] = { + T_PLUS, + T_EQ, + + T_COMMA, + T_COLON, + T_SEMICOLON, + + T_RPAREN, T_RBRACKET, T_RBRACE, + + T_NUMERIC_LITERAL, + T_IDENTIFIER, + + T_LPAREN, T_LBRACKET, T_LBRACE, + + EOF_SYMBOL + }; + + for (int *tk = tokens; *tk != EOF_SYMBOL; ++tk) { + int a = t_action(errorState, *tk); + if (a > 0 && t_action(a, yytoken)) { + const QString msg = QString::fromUtf8("Expected token `%1'").arg(QLatin1String(spell[*tk])); + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); + + yytoken = *tk; + yylval = 0; + yylloc = token_buffer[0].loc; + yylloc.length = 0; + + first_token = &token_buffer[0]; + last_token = &token_buffer[2]; + + action = errorState; + goto _Lcheck_token; + } + } + + for (int tk = 1; tk < TERMINAL_COUNT; ++tk) { + if (tk == T_AUTOMATIC_SEMICOLON || tk == T_FEED_UI_PROGRAM || + tk == T_FEED_JS_STATEMENT || tk == T_FEED_JS_EXPRESSION) + continue; + + int a = t_action(errorState, tk); + if (a > 0 && t_action(a, yytoken)) { + const QString msg = QString::fromUtf8("Expected token `%1'").arg(QLatin1String(spell[tk])); + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); + + yytoken = tk; + yylval = 0; + yylloc = token_buffer[0].loc; + yylloc.length = 0; + + action = errorState; + goto _Lcheck_token; + } + } + + const QString msg = QString::fromUtf8("Syntax error"); + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); + } + + return false; +} + +QT_QML_END_NAMESPACE + + diff --git a/src/plugins/qmldesigner/core/filemanager/parser/qmljsparser_p.h b/src/plugins/qmldesigner/core/filemanager/parser/qmljsparser_p.h new file mode 100644 index 00000000000..7ee69a57d56 --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/parser/qmljsparser_p.h @@ -0,0 +1,230 @@ +// This file was generated by qlalr - DO NOT EDIT! + +/**************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +// +// This file is automatically generated from qmljs.g. +// Changes will be lost. +// + +#ifndef QMLJSPARSER_P_H +#define QMLJSPARSER_P_H + +#include "qmljsglobal_p.h" +#include "qmljsgrammar_p.h" +#include "qmljsast_p.h" +#include "qmljsengine_p.h" + +#include <QtCore/QList> +#include <QtCore/QString> + +QT_QML_BEGIN_NAMESPACE + +namespace QmlJS { + +class Engine; +class NameId; + +class Parser: protected QmlJSGrammar +{ +public: + union Value { + int ival; + double dval; + NameId *sval; + AST::ArgumentList *ArgumentList; + AST::CaseBlock *CaseBlock; + AST::CaseClause *CaseClause; + AST::CaseClauses *CaseClauses; + AST::Catch *Catch; + AST::DefaultClause *DefaultClause; + AST::ElementList *ElementList; + AST::Elision *Elision; + AST::ExpressionNode *Expression; + AST::Finally *Finally; + AST::FormalParameterList *FormalParameterList; + AST::FunctionBody *FunctionBody; + AST::FunctionDeclaration *FunctionDeclaration; + AST::Node *Node; + AST::PropertyName *PropertyName; + AST::PropertyNameAndValueList *PropertyNameAndValueList; + AST::SourceElement *SourceElement; + AST::SourceElements *SourceElements; + AST::Statement *Statement; + AST::StatementList *StatementList; + AST::Block *Block; + AST::VariableDeclaration *VariableDeclaration; + AST::VariableDeclarationList *VariableDeclarationList; + + AST::UiProgram *UiProgram; + AST::UiImportList *UiImportList; + AST::UiImport *UiImport; + AST::UiParameterList *UiParameterList; + AST::UiPublicMember *UiPublicMember; + AST::UiObjectDefinition *UiObjectDefinition; + AST::UiObjectInitializer *UiObjectInitializer; + AST::UiObjectBinding *UiObjectBinding; + AST::UiScriptBinding *UiScriptBinding; + AST::UiArrayBinding *UiArrayBinding; + AST::UiObjectMember *UiObjectMember; + AST::UiObjectMemberList *UiObjectMemberList; + AST::UiArrayMemberList *UiArrayMemberList; + AST::UiQualifiedId *UiQualifiedId; + AST::UiSignature *UiSignature; + AST::UiFormalList *UiFormalList; + AST::UiFormal *UiFormal; + }; + +public: + Parser(Engine *engine); + ~Parser(); + + // parse a UI program + bool parse() { return parse(T_FEED_UI_PROGRAM); } + bool parseStatement() { return parse(T_FEED_JS_STATEMENT); } + bool parseExpression() { return parse(T_FEED_JS_EXPRESSION); } + bool parseUiObjectMember() { return parse(T_FEED_UI_OBJECT_MEMBER); } + + AST::UiProgram *ast() const + { return AST::cast<AST::UiProgram *>(program); } + + AST::Statement *statement() const + { + if (! program) + return 0; + + return program->statementCast(); + } + + AST::ExpressionNode *expression() const + { + if (! program) + return 0; + + return program->expressionCast(); + } + + AST::UiObjectMember *uiObjectMember() const + { + if (! program) + return 0; + + return program->uiObjectMemberCast(); + } + + QList<DiagnosticMessage> diagnosticMessages() const + { return diagnostic_messages; } + + inline DiagnosticMessage diagnosticMessage() const + { + foreach (const DiagnosticMessage &d, diagnostic_messages) { + if (! d.kind == DiagnosticMessage::Warning) + return d; + } + + return DiagnosticMessage(); + } + + inline QString errorMessage() const + { return diagnosticMessage().message; } + + inline int errorLineNumber() const + { return diagnosticMessage().loc.startLine; } + + inline int errorColumnNumber() const + { return diagnosticMessage().loc.startColumn; } + +protected: + bool parse(int startToken); + + void reallocateStack(); + + inline Value &sym(int index) + { return sym_stack [tos + index - 1]; } + + inline AST::SourceLocation &loc(int index) + { return location_stack [tos + index - 1]; } + + AST::UiQualifiedId *reparseAsQualifiedId(AST::ExpressionNode *expr); + +protected: + Engine *driver; + int tos; + int stack_size; + Value *sym_stack; + int *state_stack; + AST::SourceLocation *location_stack; + + AST::Node *program; + + // error recovery + enum { TOKEN_BUFFER_SIZE = 3 }; + + struct SavedToken { + int token; + double dval; + AST::SourceLocation loc; + }; + + double yylval; + AST::SourceLocation yylloc; + AST::SourceLocation yyprevlloc; + + SavedToken token_buffer[TOKEN_BUFFER_SIZE]; + SavedToken *first_token; + SavedToken *last_token; + + QList<DiagnosticMessage> diagnostic_messages; +}; + +} // end of namespace QmlJS + + + +#define J_SCRIPT_REGEXPLITERAL_RULE1 72 + +#define J_SCRIPT_REGEXPLITERAL_RULE2 73 + +QT_QML_END_NAMESPACE + + + +#endif // QMLJSPARSER_P_H diff --git a/src/plugins/qmldesigner/core/filemanager/qmldocument.cpp b/src/plugins/qmldesigner/core/filemanager/qmldocument.cpp new file mode 100644 index 00000000000..d89e69609f5 --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/qmldocument.cpp @@ -0,0 +1,165 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "idcollector.h" +#include "qmldocument.h" +#include "qmljsast_p.h" +#include "qmljslexer_p.h" +#include "qmljsparser_p.h" +#include "qmljsengine_p.h" +#include "qmljsnodepool_p.h" + +using namespace QmlEditor; +using namespace QmlJS; + +QmlDocument::QmlDocument(const QString &fileName) + : _engine(0) + , _pool(0) + , _program(0) + , _fileName(fileName) + , _parsedCorrectly(false) +{ + const int slashIdx = fileName.lastIndexOf('/'); + if (slashIdx != -1) + _path = fileName.left(slashIdx); + + if (fileName.toLower().endsWith(".qml")) + _componentName = fileName.mid(slashIdx + 1, fileName.size() - (slashIdx + 1) - 4); +} + +QmlDocument::~QmlDocument() +{ + if (_engine) + delete _engine; + + if (_pool) + delete _pool; + + qDeleteAll(_ids.values()); +} + +QmlDocument::Ptr QmlDocument::create(const QString &fileName) +{ + QmlDocument::Ptr doc(new QmlDocument(fileName)); + return doc; +} + +AST::UiProgram *QmlDocument::program() const +{ + return _program; +} + +QList<DiagnosticMessage> QmlDocument::diagnosticMessages() const +{ + return _diagnosticMessages; +} + +QString QmlDocument::source() const +{ + return _source; +} + +void QmlDocument::setSource(const QString &source) +{ + _source = source; +} + +bool QmlDocument::parse() +{ + Q_ASSERT(! _engine); + Q_ASSERT(! _pool); + Q_ASSERT(! _program); + + _engine = new Engine(); + _pool = new NodePool(_fileName, _engine); + _ids.clear(); + + Lexer lexer(_engine); + Parser parser(_engine); + + lexer.setCode(_source, /*line = */ 1); + + _parsedCorrectly = parser.parse(); + _program = parser.ast(); + _diagnosticMessages = parser.diagnosticMessages(); + + if (_parsedCorrectly && _program) { + Internal::IdCollector collect; + _ids = collect(_fileName, _program); + } + + return _parsedCorrectly; +} + +Snapshot::Snapshot() +{ +} + +Snapshot::~Snapshot() +{ +} + +void Snapshot::insert(const QmlDocument::Ptr &document) +{ + QMap<QString, QmlDocument::Ptr>::insert(document->fileName(), document); +} + +QmlDocument::PtrList Snapshot::importedDocuments(const QmlDocument::Ptr &doc, const QString &importPath) const +{ + QmlDocument::PtrList result; + + const QString docPath = doc->path() + '/' + importPath; + + foreach (QmlDocument::Ptr candidate, *this) { + if (candidate == doc) + continue; + + if (candidate->path() == doc->path() || candidate->path() == docPath) + result.append(candidate); + } + + return result; +} + +QMap<QString, QmlDocument::Ptr> Snapshot::componentsDefinedByImportedDocuments(const QmlDocument::Ptr &doc, const QString &importPath) const +{ + QMap<QString, QmlDocument::Ptr> result; + + const QString docPath = doc->path() + '/' + importPath; + + foreach (QmlDocument::Ptr candidate, *this) { + if (candidate == doc) + continue; + + if (candidate->path() == doc->path() || candidate->path() == docPath) + result.insert(candidate->componentName(), candidate); + } + + return result; +} diff --git a/src/plugins/qmldesigner/core/filemanager/qmldocument.h b/src/plugins/qmldesigner/core/filemanager/qmldocument.h new file mode 100644 index 00000000000..4e712d8327a --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/qmldocument.h @@ -0,0 +1,109 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ +#ifndef QMLDOCUMENT_H +#define QMLDOCUMENT_H + +#include <corelib_global.h> + +#include <QtCore/QList> +#include <QtCore/QMap> +#include <QtCore/QPair> +#include <QtCore/QSharedPointer> +#include <QtCore/QString> + +#include "qmleditor_global.h" +#include "qmljsengine_p.h" +#include "qmljsastfwd_p.h" +#include "qmlsymbol.h" + +namespace QmlEditor { + +class CORESHARED_EXPORT QmlDocument +{ +public: + typedef QSharedPointer<QmlDocument> Ptr; + typedef QList<QmlDocument::Ptr> PtrList; + typedef QMap<QString, QmlIdSymbol*> IdTable; + +protected: + QmlDocument(const QString &fileName); + +public: + ~QmlDocument(); + + static QmlDocument::Ptr create(const QString &fileName); + + QmlJS::AST::UiProgram *program() const; + QList<QmlJS::DiagnosticMessage> diagnosticMessages() const; + + QString source() const; + void setSource(const QString &source); + + bool parse(); + + bool isParsedCorrectly() const + { return _parsedCorrectly; } + + IdTable ids() const { return _ids; } + + QString fileName() const { return _fileName; } + QString path() const { return _path; } + QString componentName() const { return _componentName; } + +private: + QmlJS::Engine *_engine; + QmlJS::NodePool *_pool; + QmlJS::AST::UiProgram *_program; + QList<QmlJS::DiagnosticMessage> _diagnosticMessages; + QString _fileName; + QString _path; + QString _componentName; + QString _source; + bool _parsedCorrectly; + IdTable _ids; +}; + +class CORESHARED_EXPORT Snapshot: public QMap<QString, QmlDocument::Ptr> +{ +public: + Snapshot(); + ~Snapshot(); + + void insert(const QmlDocument::Ptr &document); + + QmlDocument::Ptr document(const QString &fileName) const + { return value(fileName); } + + QmlDocument::PtrList importedDocuments(const QmlDocument::Ptr &doc, const QString &importPath) const; + QMap<QString, QmlDocument::Ptr> componentsDefinedByImportedDocuments(const QmlDocument::Ptr &doc, const QString &importPath) const; +}; + +} // emd of namespace QmlEditor + +#endif // QMLDOCUMENT_H diff --git a/src/plugins/qmldesigner/core/filemanager/qmleditor_global.h b/src/plugins/qmldesigner/core/filemanager/qmleditor_global.h new file mode 100644 index 00000000000..81eacd75b40 --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/qmleditor_global.h @@ -0,0 +1,41 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef QMLEDITOR_GLOBAL_H +#define QMLEDITOR_GLOBAL_H + +#include <QtGlobal> + +#if defined(QMLEDITOR_LIBRARY) +# define QMLEDITOR_EXPORT Q_DECL_EXPORT +#else +# define QMLEDITOR_EXPORT Q_DECL_IMPORT +#endif + +#endif // QMLEDITOR_GLOBAL_H diff --git a/src/plugins/qmldesigner/core/filemanager/qmlrefactoring.cpp b/src/plugins/qmldesigner/core/filemanager/qmlrefactoring.cpp new file mode 100644 index 00000000000..530c1a9ebcc --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/qmlrefactoring.cpp @@ -0,0 +1,160 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include <QDebug> + +#include "addarraymembervisitor.h" +#include "addobjectvisitor.h" +#include "addpropertyvisitor.h" +#include "changeobjecttypevisitor.h" +#include "changepropertyvisitor.h" +#include "moveobjectvisitor.h" +#include "moveobjectbeforeobjectvisitor.h" +#include "qmlrefactoring.h" +#include "removepropertyvisitor.h" +#include "removeuiobjectmembervisitor.h" + +using namespace QmlEditor; +using namespace QmlEditor::Internal; + +QmlRefactoring::QmlRefactoring(const QmlDocument::Ptr &doc, QmlDesigner::TextModifier &modifier, const QStringList &propertyOrder): + qmlDocument(doc), + textModifier(&modifier), + m_propertyOrder(propertyOrder) +{ +} + +bool QmlRefactoring::reparseDocument() +{ + const QString newSource = textModifier->text(); + +// qDebug() << "QmlRefactoring::reparseDocument() new QML source:" << newSource; + + QmlEditor::QmlDocument::Ptr tmpDocument(QmlEditor::QmlDocument::create("<ModelToTextMerger>")); + tmpDocument->setSource(newSource); + + if (tmpDocument->parse()) { + qmlDocument = tmpDocument; + return true; + } else { + qWarning() << "*** Possible problem: QML file wasn't parsed correctly."; + qDebug() << "*** QML text:" << textModifier->text(); + return false; + } +} + +bool QmlRefactoring::changeImports(const QSet<QmlDesigner::Import> &/*addedImports*/, const QSet<QmlDesigner::Import> &/*removedImports*/) +{ +// ChangeImportsVisitor visit(*textModifier, addedImports, removedImports, qmlDocument->source()); +// visit(qmlDocument->program()); + return false; +} + +bool QmlRefactoring::addToArrayMemberList(int parentLocation, const QString &propertyName, const QString &content) +{ + Q_ASSERT(parentLocation >= 0); + + AddArrayMemberVisitor visit(*textModifier, (quint32) parentLocation, propertyName, content); + visit.setConvertObjectBindingIntoArrayBinding(true); + return visit(qmlDocument->program()); +} + +bool QmlRefactoring::addToObjectMemberList(int parentLocation, const QString &content) +{ + Q_ASSERT(parentLocation >= 0); + + AddObjectVisitor visit(*textModifier, (quint32) parentLocation, content, m_propertyOrder); + return visit(qmlDocument->program()); +} + +bool QmlRefactoring::addProperty(int parentLocation, const QString &name, const QString &value, PropertyType propertyType) +{ + Q_ASSERT(parentLocation >= 0); + + AddPropertyVisitor visit(*textModifier, (quint32) parentLocation, name, value, propertyType, m_propertyOrder); + return visit(qmlDocument->program()); +} + +bool QmlRefactoring::changeProperty(int parentLocation, const QString &name, const QString &value, PropertyType propertyType) +{ + Q_ASSERT(parentLocation >= 0); + + ChangePropertyVisitor visit(*textModifier, (quint32) parentLocation, name, value, propertyType); + return visit(qmlDocument->program()); +} + +bool QmlRefactoring::changeObjectType(int nodeLocation, const QString &newType) +{ + Q_ASSERT(nodeLocation >= 0); + Q_ASSERT(!newType.isEmpty()); + + ChangeObjectTypeVisitor visit(*textModifier, (quint32) nodeLocation, newType); + return visit(qmlDocument->program()); +} + +bool QmlRefactoring::moveObject(int objectLocation, const QString &targetPropertyName, bool targetIsArrayBinding, int targetParentObjectLocation) +{ + Q_ASSERT(objectLocation >= 0); + Q_ASSERT(targetParentObjectLocation >= 0); + + MoveObjectVisitor visit(*textModifier, (quint32) objectLocation, targetPropertyName, targetIsArrayBinding, (quint32) targetParentObjectLocation, m_propertyOrder); + return visit(qmlDocument->program()); +} + +bool QmlRefactoring::moveObjectBeforeObject(int movingObjectLocation, int beforeObjectLocation) +{ + Q_ASSERT(movingObjectLocation >= 0); + Q_ASSERT(beforeObjectLocation >= -1); + + if (beforeObjectLocation == -1) { + MoveObjectBeforeObjectVisitor visit(*textModifier, movingObjectLocation); + return visit(qmlDocument->program()); + } else { + MoveObjectBeforeObjectVisitor visit(*textModifier, movingObjectLocation, beforeObjectLocation); + return visit(qmlDocument->program()); + } + return false; +} + +bool QmlRefactoring::removeObject(int nodeLocation) +{ + Q_ASSERT(nodeLocation >= 0); + + RemoveUIObjectMemberVisitor visit(*textModifier, (quint32) nodeLocation); + return visit(qmlDocument->program()); +} + +bool QmlRefactoring::removeProperty(int parentLocation, const QString &name) +{ + Q_ASSERT(parentLocation >= 0); + Q_ASSERT(!name.isEmpty()); + + RemovePropertyVisitor visit(*textModifier, (quint32) parentLocation, name); + return visit(qmlDocument->program()); +} diff --git a/src/plugins/qmldesigner/core/filemanager/qmlrefactoring.h b/src/plugins/qmldesigner/core/filemanager/qmlrefactoring.h new file mode 100644 index 00000000000..9739230aa31 --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/qmlrefactoring.h @@ -0,0 +1,80 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef QMLREFACTORING_H +#define QMLREFACTORING_H + +#include <import.h> +#include <textmodifier.h> +#include <filemanager/qmldocument.h> +#include <QSet> +#include <QString> + +namespace QmlEditor { + +class QmlRefactoring +{ + QmlRefactoring(const QmlRefactoring &); + QmlRefactoring &operator=(const QmlRefactoring &); + +public: + enum PropertyType { + ArrayBinding = 1, + ObjectBinding = 2, + ScriptBinding = 3 + }; + +public: + QmlRefactoring(const QmlDocument::Ptr &doc, QmlDesigner::TextModifier &modifier, const QStringList &propertyOrder); + + bool reparseDocument(); + + bool changeImports(const QSet<QmlDesigner::Import> &addedImports, const QSet<QmlDesigner::Import> &removedImports); + + bool addToArrayMemberList(int parentLocation, const QString &propertyName, const QString &content); + bool addToObjectMemberList(int parentLocation, const QString &content); + bool addProperty(int parentLocation, const QString &name, const QString &value, PropertyType propertyType); + bool changeProperty(int parentLocation, const QString &name, const QString &value, PropertyType propertyType); + bool changeObjectType(int nodeLocation, const QString &newType); + + bool moveObject(int objectLocation, const QString &targetPropertyName, bool targetIsArray, int targetParentObjectLocation); + bool moveObjectBeforeObject(int movingObjectLocation, int beforeObjectLocation); + + bool removeObject(int nodeLocation); + bool removeProperty(int parentLocation, const QString &name); + +private: + QmlDocument::Ptr qmlDocument; + QmlDesigner::TextModifier *textModifier; + QStringList m_propertyOrder; +}; + +} // namespace QmlEditor + +#endif // QMLREFACTORING_H diff --git a/src/plugins/qmldesigner/core/filemanager/qmlrewriter.cpp b/src/plugins/qmldesigner/core/filemanager/qmlrewriter.cpp new file mode 100644 index 00000000000..3d10930685d --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/qmlrewriter.cpp @@ -0,0 +1,317 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include <QDebug> +#include <QTextBlock> + +#include <qmljsast_p.h> +#include <qmljsengine_p.h> + +#include "qmlrewriter.h" + +#include <typeinfo> + +using namespace QmlJS; +using namespace QmlJS::AST; +using namespace QmlEditor::Internal; + +QMLRewriter::QMLRewriter(QmlDesigner::TextModifier &textModifier): + m_textModifier(&textModifier), + m_didRewriting(false) +{ +} + +bool QMLRewriter::operator()(QmlJS::AST::UiProgram *ast) +{ + setDidRewriting(false); + + Node::accept(ast, this); + + return didRewriting(); +} + +void QMLRewriter::replace(int offset, int length, const QString &text) +{ + m_textModifier->replace(offset, length, text); +} + +void QMLRewriter::move(const QmlDesigner::TextModifier::MoveInfo &moveInfo) +{ + m_textModifier->move(moveInfo); +} + +QString QMLRewriter::textBetween(int startPosition, int endPosition) const +{ + return m_textModifier->text().mid(startPosition, endPosition - startPosition); +} + +QString QMLRewriter::textAt(const QmlJS::AST::SourceLocation &location) const +{ + return m_textModifier->text().mid(location.offset, location.length); +} + +unsigned QMLRewriter::calculateIndentDepth(const SourceLocation &position) const +{ + QTextDocument *doc = m_textModifier->textDocument(); + QTextCursor tc(doc); + tc.setPosition(position.offset); + const int lineOffset = tc.block().position(); + unsigned indentDepth = 0; + + forever { + const QChar ch = doc->characterAt(lineOffset + indentDepth); + + if (ch.isNull() || !ch.isSpace()) + break; + else + ++indentDepth; + } + + return indentDepth; +} + +QString QMLRewriter::addIndentation(const QString &text, unsigned depth) +{ + const QString indentation(depth, QLatin1Char(' ')); + + if (text.isEmpty()) + return indentation; + + const QLatin1Char lineSep('\n'); + const QStringList lines = text.split(lineSep); + QString result; + + for (int i = 0; i < lines.size(); ++i) { + if (i > 0) + result += lineSep; + const QString &line = lines.at(i); + if (!line.isEmpty()) { + result += indentation; + result += line; + } + } + + return result; +} + +QString QMLRewriter::removeIndentationFromLine(const QString &text, int depth) +{ + int charsToRemove = 0; + for (int i = 0; i < depth && i < text.length(); ++i) { + if (text.at(i).isSpace()) + charsToRemove++; + else + break; + } + + if (charsToRemove == 0) + return text; + else + return text.mid(charsToRemove); +} + +QString QMLRewriter::removeIndentation(const QString &text, unsigned depth) +{ + const QLatin1Char lineSep('\n'); + const QStringList lines = text.split(lineSep); + QString result; + + for (int i = 0; i < lines.size(); ++i) { + if (i > 0) + result += lineSep; + result += removeIndentationFromLine(lines.at(i), depth); + } + + return result; +} + +QmlJS::AST::SourceLocation QMLRewriter::calculateLocation(QmlJS::AST::UiQualifiedId *id) +{ + Q_ASSERT(id != 0); + + const SourceLocation startLocation = id->identifierToken; + + UiQualifiedId *nextId = id; + while (nextId->next) { + nextId = nextId->next; + } + + const SourceLocation endLocation = nextId->identifierToken; + + return SourceLocation(startLocation.offset, endLocation.end() - startLocation.offset); +} + +bool QMLRewriter::isMissingSemicolon(QmlJS::AST::UiObjectMember *member) +{ + UiScriptBinding *binding = AST::cast<UiScriptBinding *>(member); + if (binding) + return isMissingSemicolon(binding->statement); + else + return false; +} + +bool QMLRewriter::isMissingSemicolon(QmlJS::AST::Statement *stmt) +{ + if (ExpressionStatement *eStmt = AST::cast<ExpressionStatement *>(stmt)) { + return !eStmt->semicolonToken.isValid(); + } else if (IfStatement *iStmt = AST::cast<IfStatement *>(stmt)) { + if (iStmt->elseToken.isValid()) + return isMissingSemicolon(iStmt->ko); + else + return isMissingSemicolon(iStmt->ok); + } else if (DebuggerStatement *dStmt = AST::cast<DebuggerStatement *>(stmt)) { + return !dStmt->semicolonToken.isValid(); + } else { + return false; + } +} + +QString QMLRewriter::flatten(UiQualifiedId *first) +{ + QString flatId; + + for (UiQualifiedId* current = first; current; current = current->next) { + if (current != first) + flatId += '.'; + + flatId += current->name->asString(); + } + + return flatId; +} + +bool QMLRewriter::includeSurroundingWhitespace(int &start, int &end) const +{ + QTextDocument *doc = m_textModifier->textDocument(); + bool includeStartingWhitespace = true; + bool paragraphFound = false; + + if (end >= 0) { + QChar c = doc->characterAt(end); + while (c.isSpace()) { + ++end; + + if (c == QChar::ParagraphSeparator) { + paragraphFound = true; + break; + } else if (end == doc->characterCount()) { + break; + } + + c = doc->characterAt(end); + } + + includeStartingWhitespace = paragraphFound; + } + + if (includeStartingWhitespace) { + while (start > 0) { + const QChar c = doc->characterAt(start - 1); + + if (!c.isSpace()) + break; + else if (c == QChar::ParagraphSeparator) + break; + + --start; + } + } + + return paragraphFound; +} + +void QMLRewriter::includeLeadingEmptyLine(int &start) const +{ + QTextDocument *doc = textModifier()->textDocument(); + + if (start == 0) + return; + + if (doc->characterAt(start - 1) != QChar::ParagraphSeparator) + return; + + QTextCursor tc(doc); + tc.setPosition(start); + const int blockNr = tc.blockNumber(); + if (blockNr == 0) + return; + + const QTextBlock prevBlock = tc.block().previous(); + const QString trimmedPrevBlockText = prevBlock.text().trimmed(); + if (!trimmedPrevBlockText.isEmpty()) + return; + + start = prevBlock.position(); +} + +UiObjectMemberList *QMLRewriter::searchMemberToInsertAfter(UiObjectMemberList *members, const QString &propertyName, const QStringList &propertyOrder) +{ + if (!members) + return 0; // empty members + + QHash<QString, UiObjectMemberList *> orderedMembers; + + for (UiObjectMemberList *iter = members; iter; iter = iter->next) { + UiObjectMember *member = iter->member; + + if (UiArrayBinding *arrayBinding = cast<UiArrayBinding*>(member)) + orderedMembers[flatten(arrayBinding->qualifiedId)] = iter; + else if (UiObjectBinding *objectBinding = cast<UiObjectBinding*>(member)) + orderedMembers[flatten(objectBinding->qualifiedId)] = iter; + else if (cast<UiObjectDefinition*>(member)) + orderedMembers[QString::null] = iter; + else if (UiScriptBinding *scriptBinding = cast<UiScriptBinding*>(member)) + orderedMembers[flatten(scriptBinding->qualifiedId)] = iter; + else if (cast<UiPublicMember*>(member)) + orderedMembers[QLatin1String("property")] = iter; + } + + int idx = propertyOrder.indexOf(propertyName); + if (idx == -1) + idx = propertyOrder.indexOf(QString::null); + if (idx == -1) + idx = propertyOrder.size() - 1; + + for (; idx >= 0; --idx) { + const QString prop = propertyOrder.at(idx); + UiObjectMemberList *candidate = orderedMembers[prop]; + if (candidate != 0) + return candidate; + } + + return 0; +} + +void QMLRewriter::dump(const ASTPath &path) +{ + qDebug() << "AST path with" << path.size() << "node(s):"; + for (int i = 0; i < path.size(); ++i) { + qDebug() << qPrintable(QString(i + 1, QLatin1Char('-'))) << typeid(*path.at(i)).name(); + } +} diff --git a/src/plugins/qmldesigner/core/filemanager/qmlrewriter.h b/src/plugins/qmldesigner/core/filemanager/qmlrewriter.h new file mode 100644 index 00000000000..ab58a07770e --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/qmlrewriter.h @@ -0,0 +1,97 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef QMLREWRITER_H +#define QMLREWRITER_H + +#include <QtCore/QStack> +#include <QtCore/QString> + +#include <qmljsastvisitor_p.h> + +#include "textmodifier.h" + +namespace QmlEditor { +namespace Internal { + +class QMLRewriter: protected QmlJS::AST::Visitor +{ +public: + typedef QStack<QmlJS::AST::Node *> ASTPath; + +public: + QMLRewriter(QmlDesigner::TextModifier &textModifier); + + virtual bool operator()(QmlJS::AST::UiProgram *ast); + + static void dump(const ASTPath &path); + +protected: + using QmlJS::AST::Visitor::visit; + + virtual void replace(int offset, int length, const QString &text); + virtual void move(const QmlDesigner::TextModifier::MoveInfo &moveInfo); + + QString textBetween(int startPosition, int endPosition) const; + QString textAt(const QmlJS::AST::SourceLocation &location) const; + + unsigned calculateIndentDepth(const QmlJS::AST::SourceLocation &position) const; + static QString addIndentation(const QString &text, unsigned depth); + static QString removeIndentation(const QString &text, unsigned depth); + static QString removeIndentationFromLine(const QString &text, int depth); + + static QmlJS::AST::SourceLocation calculateLocation(QmlJS::AST::UiQualifiedId *id); + static bool isMissingSemicolon(QmlJS::AST::UiObjectMember *member); + static bool isMissingSemicolon(QmlJS::AST::Statement *stmt); + static QString flatten(QmlJS::AST::UiQualifiedId *first); + + QmlDesigner::TextModifier *textModifier() const + { return m_textModifier; } + + bool includeSurroundingWhitespace(int &start, int &end) const; + void includeLeadingEmptyLine(int &start) const; + + static QmlJS::AST::UiObjectMemberList *searchMemberToInsertAfter(QmlJS::AST::UiObjectMemberList *members, const QString &propertyName, const QStringList &propertyOrder); + +protected: + bool didRewriting() const + { return m_didRewriting; } + + void setDidRewriting(bool didRewriting) + { m_didRewriting = didRewriting; } + +private: + QmlDesigner::TextModifier *m_textModifier; + bool m_didRewriting; +}; + +} // namespace Internal +} // namespace QmlEditor + +#endif // QMLREWRITER_H diff --git a/src/plugins/qmldesigner/core/filemanager/qmlsymbol.cpp b/src/plugins/qmldesigner/core/filemanager/qmlsymbol.cpp new file mode 100644 index 00000000000..baa1c1195a6 --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/qmlsymbol.cpp @@ -0,0 +1,116 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "qmljsast_p.h" +#include "qmlsymbol.h" + +using namespace QmlEditor; +using namespace QmlJS; +using namespace QmlJS::AST; + +QmlSymbol::~QmlSymbol() +{ +} + +bool QmlSymbol::isBuildInSymbol() const +{ return asBuildInSymbol() != 0; } + +bool QmlSymbol::isSymbolFromFile() const +{ return asSymbolFromFile() != 0; } + +bool QmlSymbol::isIdSymbol() const +{ return asIdSymbol() != 0; } + +QmlBuildInSymbol const *QmlSymbol::asBuildInSymbol() const +{ return 0; } + +QmlSymbolFromFile const *QmlSymbol::asSymbolFromFile() const +{ return 0; } + +QmlIdSymbol const *QmlSymbol::asIdSymbol() const +{ return 0; } + +QmlBuildInSymbol::~QmlBuildInSymbol() +{} + +QmlBuildInSymbol const* QmlBuildInSymbol::asBuildInSymbol() const +{ return this; } + +QmlSymbolFromFile::QmlSymbolFromFile(const QString &fileName, QmlJS::AST::UiObjectMember *node): + _fileName(fileName), + _node(node) +{} + +QmlSymbolFromFile::~QmlSymbolFromFile() +{} + +const QmlSymbolFromFile *QmlSymbolFromFile::asSymbolFromFile() const +{ return this; } + +int QmlSymbolFromFile::line() const +{ return _node->firstSourceLocation().startLine; } + +int QmlSymbolFromFile::column() const +{ return _node->firstSourceLocation().startColumn; } + +QmlIdSymbol::QmlIdSymbol(const QString &fileName, QmlJS::AST::UiScriptBinding *idNode, const QmlSymbolFromFile &parentNode): + QmlSymbolFromFile(fileName, idNode), + _parentNode(parentNode) +{} + +QmlIdSymbol::~QmlIdSymbol() +{} + +QmlIdSymbol const *QmlIdSymbol::asIdSymbol() const +{ return this; } + +int QmlIdSymbol::line() const +{ return idNode()->statement->firstSourceLocation().startLine; } + +int QmlIdSymbol::column() const +{ return idNode()->statement->firstSourceLocation().startColumn; } + +QmlJS::AST::UiScriptBinding *QmlIdSymbol::idNode() const +{ return cast<UiScriptBinding*>(node()); } + +QmlPropertyDefinitionSymbol::QmlPropertyDefinitionSymbol(const QString &fileName, QmlJS::AST::UiPublicMember *propertyNode): + QmlSymbolFromFile(fileName, propertyNode) +{} + +QmlPropertyDefinitionSymbol::~QmlPropertyDefinitionSymbol() +{} + +int QmlPropertyDefinitionSymbol::line() const +{ return propertyNode()->identifierToken.startLine; } + +int QmlPropertyDefinitionSymbol::column() const +{ return propertyNode()->identifierToken.startColumn; } + +QmlJS::AST::UiPublicMember *QmlPropertyDefinitionSymbol::propertyNode() const +{ return cast<UiPublicMember*>(node()); } diff --git a/src/plugins/qmldesigner/core/filemanager/qmlsymbol.h b/src/plugins/qmldesigner/core/filemanager/qmlsymbol.h new file mode 100644 index 00000000000..1c1aa211923 --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/qmlsymbol.h @@ -0,0 +1,121 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef QMLSYMBOL_H +#define QMLSYMBOL_H + +#include <QString> + +#include "qmljsastfwd_p.h" + +namespace QmlEditor { + +class QmlSymbol +{ +public: + virtual ~QmlSymbol() = 0; + + bool isBuildInSymbol() const; + bool isSymbolFromFile() const; + bool isIdSymbol() const; + + virtual class QmlBuildInSymbol const *asBuildInSymbol() const; + virtual class QmlSymbolFromFile const *asSymbolFromFile() const; + virtual class QmlIdSymbol const *asIdSymbol() const; +}; + +class QmlBuildInSymbol: public QmlSymbol +{ +public: + virtual ~QmlBuildInSymbol(); + + virtual QmlBuildInSymbol const *asBuildInSymbol() const; + +private: +}; + +class QmlSymbolFromFile: public QmlSymbol +{ +public: + QmlSymbolFromFile(const QString &fileName, QmlJS::AST::UiObjectMember *node); + virtual ~QmlSymbolFromFile(); + + virtual QmlSymbolFromFile const *asSymbolFromFile() const; + + QString fileName() const + { return _fileName; } + + virtual int line() const; + virtual int column() const; + + QmlJS::AST::UiObjectMember *node() const + { return _node; } + +private: + QString _fileName; + QmlJS::AST::UiObjectMember *_node; +}; + +class QmlIdSymbol: public QmlSymbolFromFile +{ +public: + QmlIdSymbol(const QString &fileName, QmlJS::AST::UiScriptBinding *idNode, const QmlSymbolFromFile &parentNode); + virtual ~QmlIdSymbol(); + + QmlIdSymbol const *asIdSymbol() const; + + virtual int line() const; + virtual int column() const; + + QmlSymbolFromFile const *parentNode() const + { return &_parentNode; } + +private: + QmlJS::AST::UiScriptBinding *idNode() const; + +private: + QmlSymbolFromFile _parentNode; +}; + +class QmlPropertyDefinitionSymbol: public QmlSymbolFromFile +{ +public: + QmlPropertyDefinitionSymbol(const QString &fileName, QmlJS::AST::UiPublicMember *propertyNode); + virtual ~QmlPropertyDefinitionSymbol(); + + virtual int line() const; + virtual int column() const; + +private: + QmlJS::AST::UiPublicMember *propertyNode() const; +}; + +} // namespace QmlEditor + +#endif // QMLSYMBOL_H diff --git a/src/plugins/qmldesigner/core/filemanager/removepropertyvisitor.cpp b/src/plugins/qmldesigner/core/filemanager/removepropertyvisitor.cpp new file mode 100644 index 00000000000..5c38e7f4cde --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/removepropertyvisitor.cpp @@ -0,0 +1,108 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include <qmljsast_p.h> +#include <qmljsengine_p.h> + +#include "removepropertyvisitor.h" + +using namespace QmlEditor::Internal; +using namespace QmlJS; +using namespace QmlJS::AST; + +RemovePropertyVisitor::RemovePropertyVisitor(QmlDesigner::TextModifier &modifier, + quint32 parentLocation, + const QString &propertyName): + QMLRewriter(modifier), + parentLocation(parentLocation), + propertyName(propertyName) +{ +} + +bool RemovePropertyVisitor::visit(QmlJS::AST::UiObjectBinding *ast) +{ + if (ast->firstSourceLocation().offset == parentLocation) { + removeFrom(ast->initializer); + } + + return !didRewriting(); +} + +bool RemovePropertyVisitor::visit(QmlJS::AST::UiObjectDefinition *ast) +{ + if (ast->firstSourceLocation().offset == parentLocation) { + removeFrom(ast->initializer); + } + + return !didRewriting(); +} + +void RemovePropertyVisitor::removeFrom(QmlJS::AST::UiObjectInitializer *ast) +{ + UiObjectMember *previousMember = 0, *wantedMember = 0, *nextMember = 0; + + for (UiObjectMemberList *it = ast->members; it; it = it->next) { + if (memberNameMatchesPropertyName(it->member)) { + wantedMember = it->member; + + if (it->next) + nextMember = it->next->member; + + break; + } + + previousMember = it->member; + } + + if (!wantedMember) + return; + + int start = wantedMember->firstSourceLocation().offset; + int end = wantedMember->lastSourceLocation().end(); + + includeSurroundingWhitespace(start, end); + + replace(start, end - start, QLatin1String("")); + + setDidRewriting(true); +} + +bool RemovePropertyVisitor::memberNameMatchesPropertyName(QmlJS::AST::UiObjectMember *ast) const +{ + if (UiPublicMember *publicMember = cast<UiPublicMember*>(ast)) + return publicMember->name->asString() == propertyName; + else if (UiObjectBinding *objectBinding = cast<UiObjectBinding*>(ast)) + return flatten(objectBinding->qualifiedId) == propertyName; + else if (UiScriptBinding *scriptBinding = cast<UiScriptBinding*>(ast)) + return flatten(scriptBinding->qualifiedId) == propertyName; + else if (UiArrayBinding *arrayBinding = cast<UiArrayBinding*>(ast)) + return flatten(arrayBinding->qualifiedId) == propertyName; + else + return false; +} diff --git a/src/plugins/qmldesigner/core/filemanager/removepropertyvisitor.h b/src/plugins/qmldesigner/core/filemanager/removepropertyvisitor.h new file mode 100644 index 00000000000..a396b8bac02 --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/removepropertyvisitor.h @@ -0,0 +1,63 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef REMOVEPROPERTYVISITOR_H +#define REMOVEPROPERTYVISITOR_H + +#include <QString> + +#include "qmlrewriter.h" + +namespace QmlEditor { +namespace Internal { + +class RemovePropertyVisitor: public QMLRewriter +{ +public: + RemovePropertyVisitor(QmlDesigner::TextModifier &modifier, + quint32 parentLocation, + const QString &name); + +protected: + virtual bool visit(QmlJS::AST::UiObjectBinding *ast); + virtual bool visit(QmlJS::AST::UiObjectDefinition *ast); + +private: + void removeFrom(QmlJS::AST::UiObjectInitializer *ast); + bool memberNameMatchesPropertyName(QmlJS::AST::UiObjectMember *ast) const; + +private: + quint32 parentLocation; + QString propertyName; +}; + +} // namespace Internal +} // namespace QmlEditor + +#endif // REMOVEPROPERTYVISITOR_H diff --git a/src/plugins/qmldesigner/core/filemanager/removeuiobjectmembervisitor.cpp b/src/plugins/qmldesigner/core/filemanager/removeuiobjectmembervisitor.cpp new file mode 100644 index 00000000000..bf0bcfb5ef5 --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/removeuiobjectmembervisitor.cpp @@ -0,0 +1,139 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include <QDebug> +#include <qmljsast_p.h> +#include <qmljsengine_p.h> + +#include "removeuiobjectmembervisitor.h" + +using namespace QmlEditor; +using namespace QmlEditor::Internal; +using namespace QmlJS; +using namespace QmlJS::AST; + +RemoveUIObjectMemberVisitor::RemoveUIObjectMemberVisitor(QmlDesigner::TextModifier &modifier, + quint32 objectLocation): + QMLRewriter(modifier), + objectLocation(objectLocation) +{ +} + +bool RemoveUIObjectMemberVisitor::preVisit(Node *ast) +{ + parents.push(ast); + + return true; +} + +void RemoveUIObjectMemberVisitor::postVisit(Node *) +{ + parents.pop(); +} + +bool RemoveUIObjectMemberVisitor::visit(QmlJS::AST::UiPublicMember *ast) { return visitObjectMember(ast); } +bool RemoveUIObjectMemberVisitor::visit(QmlJS::AST::UiObjectDefinition *ast) { return visitObjectMember(ast); } +bool RemoveUIObjectMemberVisitor::visit(QmlJS::AST::UiSourceElement *ast) { return visitObjectMember(ast); } +bool RemoveUIObjectMemberVisitor::visit(QmlJS::AST::UiObjectBinding *ast) { return visitObjectMember(ast); } +bool RemoveUIObjectMemberVisitor::visit(QmlJS::AST::UiScriptBinding *ast) { return visitObjectMember(ast); } +bool RemoveUIObjectMemberVisitor::visit(QmlJS::AST::UiArrayBinding *ast) { return visitObjectMember(ast); } + +bool RemoveUIObjectMemberVisitor::visitObjectMember(QmlJS::AST::UiObjectMember *ast) +{ + const quint32 memberStart = ast->firstSourceLocation().offset; + + if (memberStart == objectLocation) { + // found it + int start = objectLocation; + int end = ast->lastSourceLocation().end(); + + if (UiArrayBinding *parentArray = containingArray()) { + extendToLeadingOrTrailingComma(parentArray, ast, start, end); + } else { + includeSurroundingWhitespace(start, end); + } + + includeLeadingEmptyLine(start); + replace(start, end - start, QLatin1String("")); + + setDidRewriting(true); + + return false; + } else if (ast->lastSourceLocation().end() <= objectLocation) { + // optimization: if the location of the object-to-be-removed is not inside the current member, skip any children + return false; + } else { + // only visit children if the rewriting isn't done yet. + return !didRewriting(); + } +} + +UiArrayBinding *RemoveUIObjectMemberVisitor::containingArray() const +{ + if (parents.size() > 2) { + if (cast<UiArrayMemberList*>(parents[parents.size() - 2])) { + return cast<UiArrayBinding*>(parents[parents.size() - 3]); + } + } + + return 0; +} + +void RemoveUIObjectMemberVisitor::extendToLeadingOrTrailingComma(QmlJS::AST::UiArrayBinding *parentArray, + QmlJS::AST::UiObjectMember *ast, + int &start, + int &end) const +{ + UiArrayMemberList *currentMember = 0; + for (UiArrayMemberList *it = parentArray->members; it; it = it->next) { + if (it->member == ast) { + currentMember = it; + break; + } + } + + if (!currentMember) + return; + + if (currentMember->commaToken.isValid()) { + // leading comma + start = currentMember->commaToken.offset; + if (includeSurroundingWhitespace(start, end)) + --end; + } else if (currentMember->next && currentMember->next->commaToken.isValid()) { + // trailing comma + end = currentMember->next->commaToken.end(); + includeSurroundingWhitespace(start, end); + } else { + // array with 1 element, so remove the complete binding + start = parentArray->firstSourceLocation().offset; + end = parentArray->lastSourceLocation().end(); + includeSurroundingWhitespace(start, end); + } +} diff --git a/src/plugins/qmldesigner/core/filemanager/removeuiobjectmembervisitor.h b/src/plugins/qmldesigner/core/filemanager/removeuiobjectmembervisitor.h new file mode 100644 index 00000000000..85816f197ad --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/removeuiobjectmembervisitor.h @@ -0,0 +1,75 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef REMOVEUIOBJECTMEMBERVISITOR_H +#define REMOVEUIOBJECTMEMBERVISITOR_H + +#include <QStack> + +#include "qmlrewriter.h" + +namespace QmlEditor { +namespace Internal { + +class RemoveUIObjectMemberVisitor: public QMLRewriter +{ +public: + RemoveUIObjectMemberVisitor(QmlDesigner::TextModifier &modifier, + quint32 objectLocation); + +protected: + virtual bool preVisit(QmlJS::AST::Node *ast); + virtual void postVisit(QmlJS::AST::Node *); + + virtual bool visit(QmlJS::AST::UiPublicMember *ast); + virtual bool visit(QmlJS::AST::UiObjectDefinition *ast); + virtual bool visit(QmlJS::AST::UiSourceElement *ast); + virtual bool visit(QmlJS::AST::UiObjectBinding *ast); + virtual bool visit(QmlJS::AST::UiScriptBinding *ast); + virtual bool visit(QmlJS::AST::UiArrayBinding *ast); + +private: + bool visitObjectMember(QmlJS::AST::UiObjectMember *ast); + + QmlJS::AST::UiArrayBinding *containingArray() const; + + void extendToLeadingOrTrailingComma(QmlJS::AST::UiArrayBinding *parentArray, + QmlJS::AST::UiObjectMember *ast, + int &start, + int &end) const; + +private: + quint32 objectLocation; + QStack<QmlJS::AST::Node*> parents; +}; + +} // namespace Internal +} // namespace QmlEditor + +#endif // REMOVEUIOBJECTMEMBERVISITOR_H diff --git a/src/plugins/qmldesigner/core/filemanager/utils_global.h b/src/plugins/qmldesigner/core/filemanager/utils_global.h new file mode 100644 index 00000000000..a97322e5c9e --- /dev/null +++ b/src/plugins/qmldesigner/core/filemanager/utils_global.h @@ -0,0 +1,37 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef UTILS_GLOBAL_H +#define UTILS_GLOBAL_H + +#include <QtCore/qglobal.h> + +#define QTCREATOR_UTILS_EXPORT + +#endif // UTILS_GLOBAL_H diff --git a/src/plugins/qmldesigner/core/include/abstractproperty.h b/src/plugins/qmldesigner/core/include/abstractproperty.h new file mode 100644 index 00000000000..3cd457bd683 --- /dev/null +++ b/src/plugins/qmldesigner/core/include/abstractproperty.h @@ -0,0 +1,127 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef ABSTRACTROPERTY_H +#define ABSTRACTROPERTY_H + +#include <QtCore/QVariant> +#include <QWeakPointer> +#include <QSharedPointer> +#include "corelib_global.h" + +class QTextStream; + +namespace QmlDesigner { + namespace Internal { + class InternalNode; + class InternalProperty; + + typedef QSharedPointer<InternalNode> InternalNodePointer; + typedef QSharedPointer<InternalProperty> InternalPropertyPointer; + typedef QWeakPointer<InternalNode> InternalNodeWeakPointer; + } + +class Model; +class ModelNode; +class AbstractView; +class PropertyMetaInfo; +class CORESHARED_EXPORT VariantProperty; +class CORESHARED_EXPORT NodeListProperty; +class CORESHARED_EXPORT NodeAbstractProperty; +class CORESHARED_EXPORT BindingProperty; +class CORESHARED_EXPORT NodeProperty; +class QmlObjectNode; + + +namespace Internal { + class InternalNode; + class ModelPrivate; +} + +class CORESHARED_EXPORT AbstractProperty +{ + friend class QmlDesigner::ModelNode; + friend class QmlDesigner::Internal::ModelPrivate; + + friend CORESHARED_EXPORT bool operator ==(const AbstractProperty &property1, const AbstractProperty &property2); + friend CORESHARED_EXPORT bool operator !=(const AbstractProperty &property1, const AbstractProperty &property2); + friend CORESHARED_EXPORT uint qHash(const AbstractProperty& property); + +public: + AbstractProperty(); + ~AbstractProperty(); + AbstractProperty(const AbstractProperty &other); + AbstractProperty& operator=(const AbstractProperty &other); + AbstractProperty(const AbstractProperty &property, AbstractView *view); + + QString name() const; + + bool isValid() const; + ModelNode parentModelNode() const; + QmlObjectNode parentQmlObjectNode() const; + + PropertyMetaInfo metaInfo() const; + bool isDefaultProperty() const; + VariantProperty toVariantProperty() const; + NodeListProperty toNodeListProperty() const; + NodeAbstractProperty toNodeAbstractProperty() const; + BindingProperty toBindingProperty() const; + NodeProperty toNodeProperty() const; + + bool isVariantProperty() const; + bool isNodeListProperty() const; + bool isNodeAbstractProperty() const; + bool isBindingProperty() const; + bool isNodeProperty() const; + + bool isDynamic() const; + QString dynamicTypeName() const; + +protected: + AbstractProperty(const QString &propertyName, const Internal::InternalNodePointer &internalNode, Model* model, AbstractView *view); + AbstractProperty(const Internal::InternalPropertyPointer &property, Model* model, AbstractView *view); + Internal::InternalNodePointer internalNode() const; + Model *model() const; + AbstractView *view() const; + +private: + QString m_propertyName; + Internal::InternalNodePointer m_internalNode; + QWeakPointer<Model> m_model; + QWeakPointer<AbstractView> m_view; +}; + +CORESHARED_EXPORT bool operator ==(const AbstractProperty &property1, const AbstractProperty &property2); +CORESHARED_EXPORT bool operator !=(const AbstractProperty &property1, const AbstractProperty &property2); +CORESHARED_EXPORT uint qHash(const AbstractProperty& property); +CORESHARED_EXPORT QTextStream& operator<<(QTextStream &stream, const AbstractProperty &property); +CORESHARED_EXPORT QDebug operator<<(QDebug debug, const AbstractProperty &AbstractProperty); +} + +#endif //ABSTRACTPROPERTY_H diff --git a/src/plugins/qmldesigner/core/include/abstractview.h b/src/plugins/qmldesigner/core/include/abstractview.h new file mode 100644 index 00000000000..9f585226574 --- /dev/null +++ b/src/plugins/qmldesigner/core/include/abstractview.h @@ -0,0 +1,148 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef ABSTRACTVIEW_H +#define ABSTRACTVIEW_H + +#include <corelib_global.h> + +#include <model.h> +#include <modelnode.h> +#include <abstractproperty.h> +#include <rewritertransaction.h> + +#include <QObject> + +class QStyle; + +namespace QmlDesigner { + namespace Internal { + class InternalNode; + typedef QSharedPointer<InternalNode> InternalNodePointer; + typedef QWeakPointer<InternalNode> InternalNodeWeakPointer; + } +} + +namespace QmlDesigner { + +class QmlModelView; + +class CORESHARED_EXPORT AbstractView : public QObject +{ + Q_OBJECT +public: + Q_FLAGS(PropertyChangeFlag PropertyChangeFlags); + typedef QWeakPointer<AbstractView> Pointer; + + enum PropertyChangeFlag { + NoAdditionalChanges = 0x0, + PropertiesAdded = 0x1, + EmptyPropertiesRemoved = 0x2, + }; + Q_DECLARE_FLAGS(PropertyChangeFlags, PropertyChangeFlag); + AbstractView(QObject *parent = 0) + : QObject(parent) {} + + virtual ~AbstractView(); + + Model* model() const; + + RewriterTransaction beginRewriterTransaction(); + + ModelNode createModelNode(const QString &typeString, + int majorVersion, + int minorVersion, + const PropertyListType &propertyList = PropertyListType()); + + const ModelNode rootModelNode() const; + ModelNode rootModelNode(); + + void setSelectedModelNodes(const QList<ModelNode> &selectedNodeList); + void selectModelNode(const ModelNode &node); + void deselectModelNode(const ModelNode &node); + void clearSelectedModelNodes(); + + QList<ModelNode> selectedModelNodes() const; + + ModelNode modelNodeForId(const QString &id); + bool hasId(const QString &id) const; + + QList<ModelNode> allModelNodes(); + + void emitCustomNotification(const QString &identifier); + void emitCustomNotification(const QString &identifier, const QList<ModelNode> &nodeList); + void emitCustomNotification(const QString &identifier, const QList<ModelNode> &nodeList, const QList<QVariant> &data); + + virtual void modelAttached(Model *model); + virtual void modelAboutToBeDetached(Model *model); + + virtual void nodeCreated(const ModelNode &createdNode) = 0; + virtual void nodeAboutToBeRemoved(const ModelNode &removedNode) = 0; + virtual void nodeRemoved(const ModelNode &removedNode, const NodeAbstractProperty &parentProperty, PropertyChangeFlags propertyChange) = 0; + virtual void nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent, const NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags propertyChange) = 0; + virtual void nodeIdChanged(const ModelNode& node, const QString& newId, const QString& oldId) = 0; + virtual void propertiesAboutToBeRemoved(const QList<AbstractProperty>& propertyList) = 0; + virtual void propertiesRemoved(const QList<AbstractProperty>& propertyList) = 0; + virtual void variantPropertiesChanged(const QList<VariantProperty>& propertyList, PropertyChangeFlags propertyChange) = 0; + virtual void bindingPropertiesChanged(const QList<BindingProperty>& propertyList, PropertyChangeFlags propertyChange) = 0; + virtual void nodeTypeChanged(const ModelNode &node,const QString &type, int majorVersion, int minorVersion) = 0; + + virtual void selectedNodesChanged(const QList<ModelNode> &selectedNodeList, + const QList<ModelNode> &lastSelectedNodeList) = 0; + + virtual void fileUrlChanged(const QUrl &oldUrl, const QUrl &newUrl); + + virtual void nodeSlidedToIndex(const NodeListProperty &listProperty, int newIndex, int oldIndex) = 0; + + virtual void importsChanged(); + + virtual void auxiliaryDataChanged(const ModelNode &node, const QString &name, const QVariant &data); + + virtual void customNotification(const AbstractView *view, const QString &identifier, const QList<ModelNode> &nodeList, const QList<QVariant> &data); + + QmlModelView *toQmlModelView(); + +protected: + void setModel(Model * model); + void removeModel(); + +private: //functions + QList<ModelNode> toModelNodeList(const QList<Internal::InternalNodePointer> &nodeList) const; + + +private: + QWeakPointer<Model> m_model; +}; + +CORESHARED_EXPORT QList<Internal::InternalNodePointer> toInternalNodeList(const QList<ModelNode> &nodeList); +CORESHARED_EXPORT QList<ModelNode> toModelNodeList(const QList<Internal::InternalNodePointer> &nodeList, AbstractView *view); + +} + +#endif // ABSTRACTVIEW_H diff --git a/src/plugins/qmldesigner/core/include/anchorline.h b/src/plugins/qmldesigner/core/include/anchorline.h new file mode 100644 index 00000000000..5cc2793e78f --- /dev/null +++ b/src/plugins/qmldesigner/core/include/anchorline.h @@ -0,0 +1,118 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef ANCHORLINE_H +#define ANCHORLINE_H + +#include <QMetaType> +#include <QSharedPointer> +#include <QWeakPointer> +#include <QVariant> + +#include "corelib_global.h" + +namespace QmlDesigner { + +namespace Internal { + class ModelPrivate; + class InternalNode; + typedef QSharedPointer<InternalNode> InternalNodePointer; + typedef QWeakPointer<InternalNode> InternalNodeWeakPointer; + + class InternalNodeAnchors; + + class InternalNodeState; + typedef QSharedPointer<InternalNodeState> InternalNodeStatePointer; + typedef QWeakPointer<InternalNodeState> InternalNodeStateWeakPointer; + + class TextToModelMerger; +} + +class NodeAnchors; +class ModelNode; +class NodeState; +class Model; + +class CORESHARED_EXPORT AnchorLine +{ +public: + friend class NodeAnchors; + friend class Internal::InternalNodeAnchors; + friend class Internal::ModelPrivate; + friend class Internal::TextToModelMerger; + + enum Type { + Invalid = 0x0, + NoAnchor = Invalid, + Left = 0x01, + Right = 0x02, + Top = 0x04, + Bottom = 0x08, + HorizontalCenter = 0x10, + VerticalCenter = 0x20, + Baseline = 0x40, + + HorizontalMask = Left | Right | HorizontalCenter, + VerticalMask = Top | Bottom | VerticalCenter | Baseline, + AllMask = VerticalMask | HorizontalMask + }; + + AnchorLine(); + AnchorLine(const NodeState &nodeState, Type type); + ~AnchorLine(); + AnchorLine(const AnchorLine &other); + AnchorLine &operator =(const AnchorLine &AnchorLine); + + ModelNode modelNode() const; + Type type() const; + bool isValid() const; + + QVariant toVariant() const; + +protected: + Internal::InternalNodeStatePointer internalNodeState() const; + Internal::InternalNodePointer internalNode() const; + +private: + AnchorLine(const Internal::InternalNodeStatePointer &internalNodeState, + const Internal::InternalNodePointer &internalNode, + Model* model, + Type type); + + Internal::InternalNodeStateWeakPointer m_internalNodeState; + Internal::InternalNodeWeakPointer m_internalNode; + QWeakPointer<Model> m_model; + Type m_anchorType; +}; + +} // namespace QmlDesigner + +Q_DECLARE_METATYPE(QmlDesigner::AnchorLine); + +#endif // ANCHORLINE_H diff --git a/src/plugins/qmldesigner/core/include/bindingproperty.h b/src/plugins/qmldesigner/core/include/bindingproperty.h new file mode 100644 index 00000000000..9b38930b258 --- /dev/null +++ b/src/plugins/qmldesigner/core/include/bindingproperty.h @@ -0,0 +1,64 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef BINDINGPROPERTY_H +#define BINDINGPROPERTY_H + +#include "corelib_global.h" +#include "abstractproperty.h" + +namespace QmlDesigner { + +class CORESHARED_EXPORT BindingProperty : public QmlDesigner::AbstractProperty +{ + friend class QmlDesigner::ModelNode; + friend class QmlDesigner::Internal::ModelPrivate; + friend class QmlDesigner::AbstractProperty; + +public: + void setExpression(const QString &expression); + QString expression() const; + BindingProperty& operator= (const QString &expression); + + BindingProperty(); + BindingProperty(const BindingProperty &property, AbstractView *view); + + void setDynamicTypeNameAndExpression(const QString &type, const QString &expression); + BindingProperty& operator= (const QPair<QString, QString> &typeExpressionPair); + + ModelNode resolveToModelNode() const; + AbstractProperty resolveToProperty() const; + +protected: + BindingProperty(const QString &propertyName, const Internal::InternalNodePointer &internalNode, Model* model, AbstractView *view); +}; + +} // namespace QmlDesigner + +#endif // BINDINGPROPERTY_H diff --git a/src/plugins/qmldesigner/core/include/bytearraymodifier.h b/src/plugins/qmldesigner/core/include/bytearraymodifier.h new file mode 100644 index 00000000000..31d76132897 --- /dev/null +++ b/src/plugins/qmldesigner/core/include/bytearraymodifier.h @@ -0,0 +1,58 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef BYTEARRAYMODIFIER_H +#define BYTEARRAYMODIFIER_H + +#include <QPlainTextEdit> + +#include "plaintexteditmodifier.h" + +namespace QmlDesigner { + +class CORESHARED_EXPORT ByteArrayModifier: public PlainTextEditModifier +{ +public: + static ByteArrayModifier* create(const QString& data); + + ByteArrayModifier(QPlainTextEdit* textEdit); + ~ByteArrayModifier(); + + void setText(const QString& text); + + void undo(); + void redo(); + +private: + QPlainTextEdit* m_textEdit; +}; + +} + +#endif // BYTEARRAYMODIFIER_H diff --git a/src/plugins/qmldesigner/core/include/componenttextmodifier.h b/src/plugins/qmldesigner/core/include/componenttextmodifier.h new file mode 100644 index 00000000000..723425531e6 --- /dev/null +++ b/src/plugins/qmldesigner/core/include/componenttextmodifier.h @@ -0,0 +1,69 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef COMPONENTTEXTMODIFIER_H +#define COMPONENTTEXTMODIFIER_H + +#include "textmodifier.h" + +namespace QmlDesigner { + +class ComponentTextModifier: public TextModifier +{ + Q_OBJECT +public: + ComponentTextModifier(TextModifier *originalModifier, int componentStartOffset, int componentEndOffset, int rootStartOffset); + ~ComponentTextModifier(); + + virtual void replace(int offset, int length, const QString& replacement); + virtual void move(const MoveInfo &moveInfo); + virtual void startGroup(); + virtual void flushGroup(); + virtual void commitGroup(); + + virtual QTextDocument *textDocument() const; + virtual QString text() const; + virtual QTextCursor textCursor() const; + + virtual void deactivateChangeSignals(); + virtual void reactivateChangeSignals(); + +public slots: + void contentsChange(int position, int charsRemoved, int charsAdded); + +private: + TextModifier *m_originalModifier; + int m_componentStartOffset; + int m_componentEndOffset; + int m_rootStartOffset; +}; + +} // namespace QmlDesigner + +#endif // COMPONENTTEXTMODIFIER_H diff --git a/src/plugins/qmldesigner/core/include/corelib_global.h b/src/plugins/qmldesigner/core/include/corelib_global.h new file mode 100644 index 00000000000..8e2ae37e9f6 --- /dev/null +++ b/src/plugins/qmldesigner/core/include/corelib_global.h @@ -0,0 +1,56 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef CORELIB_GLOBAL_H +#define CORELIB_GLOBAL_H + +// Unnecessary since core isn't a dll any more. + +#define CORESHARED_EXPORT +#define TEST_CORESHARED_EXPORT + +//#if defined(CORE_LIBRARY) +//# define CORESHARED_EXPORT Q_DECL_EXPORT +//#else +//# define CORESHARED_EXPORT Q_DECL_IMPORT +//#endif +// +//#if defined(TEST_EXPORTS) +//#if defined(CORE_LIBRARY) +//# define TEST_CORESHARED_EXPORT Q_DECL_EXPORT +//#else +//# define TEST_CORESHARED_EXPORT Q_DECL_IMPORT +//#endif +//#else +//# define TEST_CORESHARED_EXPORT +//#endif + +#include <QtCore/qglobal.h> + +#endif // CORELIB_GLOBAL_H diff --git a/src/plugins/qmldesigner/core/include/customnotifications.h b/src/plugins/qmldesigner/core/include/customnotifications.h new file mode 100644 index 00000000000..7fcaf1e8f45 --- /dev/null +++ b/src/plugins/qmldesigner/core/include/customnotifications.h @@ -0,0 +1,42 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef CUSTOMNOTIFICATIONS_H +#define CUSTOMNOTIFICATIONS_H + +#include <QLatin1String> + +namespace QmlDesigner { + +const QLatin1String StartRewriterAmend("__start rewriter amend__"); +const QLatin1String EndRewriterAmend("__end rewriter amend__"); + +} + +#endif //CUSTOMNOTIFICATIONS_H diff --git a/src/plugins/qmldesigner/core/include/enumeratormetainfo.h b/src/plugins/qmldesigner/core/include/enumeratormetainfo.h new file mode 100644 index 00000000000..b7ddf090ff3 --- /dev/null +++ b/src/plugins/qmldesigner/core/include/enumeratormetainfo.h @@ -0,0 +1,85 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef ENUMERATORMETAINFO_H +#define ENUMERATORMETAINFO_H + +#include "corelib_global.h" + +#include <QExplicitlySharedDataPointer> +#include <QList> +#include <QString> + + +namespace QmlDesigner { + +class MetaInfo; + +namespace Internal { + +class MetaInfoParser; +class MetaInfoPrivate; +class EnumeratorMetaInfoData; + +} + +class CORESHARED_EXPORT EnumeratorMetaInfo +{ + friend class QmlDesigner::MetaInfo; + friend class QmlDesigner::Internal::MetaInfoPrivate; + friend class QmlDesigner::Internal::MetaInfoParser; +public: + EnumeratorMetaInfo(); + ~EnumeratorMetaInfo(); + + EnumeratorMetaInfo(const EnumeratorMetaInfo &other); + EnumeratorMetaInfo& operator=(const EnumeratorMetaInfo &other); + + QString name() const; + QString scope() const; + bool isFlagType() const; + bool isValid() const; + QString scopeAndName(const QString &combiner = QString("::")) const; + QList<QString> elementNames() const; + int elementValue(const QString &enumeratorName) const; + +private: + void setScope(const QString &scope); + void setName(const QString &name); + void setIsFlagType(bool isFlagType); + void setValid(bool valid); + void addElement(const QString &enumeratorName, int enumeratorValue); + +private: + QExplicitlySharedDataPointer<Internal::EnumeratorMetaInfoData> m_data; +}; + +} + +#endif // ENUMERATORMETAINFO_H diff --git a/src/plugins/qmldesigner/core/include/exception.h b/src/plugins/qmldesigner/core/include/exception.h new file mode 100644 index 00000000000..0c003242c2e --- /dev/null +++ b/src/plugins/qmldesigner/core/include/exception.h @@ -0,0 +1,72 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef EXCEPTION_H +#define EXCEPTION_H + +#include <corelib_global.h> + +#include <QString> +#include <QtDebug> + +namespace QmlDesigner { + +class CORESHARED_EXPORT Exception +{ +public: + Exception(int line, + const QString &function, + const QString &file); + virtual ~Exception(); + + virtual QString type() const=0; + virtual QString description() const; + + int line() const; + QString function() const; + QString file() const; + QString backTrace() const; + + static void setShouldAssert(bool assert); + static bool shouldAssert(); + +private: + int m_line; + QString m_function; + QString m_file; + QString m_backTrace; + static bool s_shouldAssert; +}; + +CORESHARED_EXPORT QDebug operator<<(QDebug debug, const Exception &exception); + +} + + +#endif // EXCEPTION_H diff --git a/src/plugins/qmldesigner/core/include/forwardview.h b/src/plugins/qmldesigner/core/include/forwardview.h new file mode 100644 index 00000000000..a69dd6ea8c7 --- /dev/null +++ b/src/plugins/qmldesigner/core/include/forwardview.h @@ -0,0 +1,272 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef FORWARDVIEW_H +#define FORWARDVIEW_H + +#include <abstractview.h> + +#include <nodeabstractproperty.h> +#include <nodelistproperty.h> +#include <variantproperty.h> +#include <bindingproperty.h> +#include <QtDebug> + +namespace QmlDesigner { +class NodeInstanceView; + +template <class ViewType> +class ForwardView : public AbstractView +{ +public: + typedef QWeakPointer<ForwardView> Pointer; + typedef typename ViewType::Pointer ViewTypePointer; + + ForwardView(QObject *parent); + + void modelAttached(Model *model); + void modelAboutToBeDetached(Model *model); + + void nodeCreated(const ModelNode &createdNode); + void nodeAboutToBeRemoved(const ModelNode &removedNode); + void nodeRemoved(const ModelNode &removedNode, const NodeAbstractProperty &parentProperty, PropertyChangeFlags propertyChange); + void nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent, const NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags propertyChange); + void nodeIdChanged(const ModelNode& node, const QString& newId, const QString& oldId); + void propertiesAboutToBeRemoved(const QList<AbstractProperty>& propertyList); + void propertiesRemoved(const QList<AbstractProperty>& propertyList); + void variantPropertiesChanged(const QList<VariantProperty>& propertyList, PropertyChangeFlags propertyChange); + void bindingPropertiesChanged(const QList<BindingProperty>& propertyList, PropertyChangeFlags propertyChange); + void nodeTypeChanged(const ModelNode &node,const QString &type, int majorVersion, int minorVersion); + + void selectedNodesChanged(const QList<ModelNode> &selectedNodeList, + const QList<ModelNode> &lastSelectedNodeList); + + void fileUrlChanged(const QUrl &oldUrl, const QUrl &newUrl); + + void nodeSlidedToIndex(const NodeListProperty &listProperty, int newIndex, int oldIndex); + + void importsChanged(); + + void auxiliaryDataChanged(const ModelNode &node, const QString &name, const QVariant &data); + + +protected: + void appendView(ViewType *view); + void removeView(ViewType *view); + QList<ViewType*> viewList() const; + ViewType *firstView() const; + +private: + QList<ViewTypePointer> m_targetViewList; +}; + +template <class ViewType> +ForwardView<ViewType>::ForwardView(QObject *parent) + : AbstractView(parent) +{ +} + +template <class ViewType> +void ForwardView<ViewType>::modelAttached(Model *model) +{ + AbstractView::modelAttached(model); + foreach (const ViewTypePointer &view, m_targetViewList) + view->modelAttached(model); +} + +template <class ViewType> +void ForwardView<ViewType>::modelAboutToBeDetached(Model *model) +{ + foreach (const ViewTypePointer &view, m_targetViewList) + view->modelAboutToBeDetached(model); + + AbstractView::modelAboutToBeDetached(model); +} + +template <class ViewType> +void ForwardView<ViewType>::nodeCreated(const ModelNode &createdNode) +{ + foreach (const ViewTypePointer &view, m_targetViewList) + view->nodeCreated(ModelNode(createdNode, view.data())); +} + +template <class ViewType> +void ForwardView<ViewType>::nodeAboutToBeRemoved(const ModelNode &removedNode) +{ + foreach (const ViewTypePointer &view, m_targetViewList) + view->nodeAboutToBeRemoved(ModelNode(removedNode, view.data())); +} + +template <class ViewType> +void ForwardView<ViewType>::nodeRemoved(const ModelNode &removedNode, const NodeAbstractProperty &parentProperty, PropertyChangeFlags propertyChange) +{ + foreach (const ViewTypePointer &view, m_targetViewList) + view->nodeRemoved(ModelNode(removedNode, view.data()), NodeAbstractProperty(parentProperty, view.data()), propertyChange); +} + +template <class ViewType> +void ForwardView<ViewType>::nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent, const NodeAbstractProperty &oldPropertyParent, PropertyChangeFlags propertyChange) +{ + foreach (const ViewTypePointer &view, m_targetViewList) + view->nodeReparented(ModelNode(node, view.data()), NodeAbstractProperty(newPropertyParent, view.data()), NodeAbstractProperty(oldPropertyParent, view.data()), propertyChange); +} + +template <class ViewType> +void ForwardView<ViewType>::nodeIdChanged(const ModelNode& node, const QString& newId, const QString& oldId) +{ + foreach (const ViewTypePointer &view, m_targetViewList) + view->nodeIdChanged(ModelNode(node, view.data()), newId, oldId); +} + +template <class T> +static QList<T> adjustedList(const QList<T>& oldList, AbstractView *view) +{ + QList<T> newList; + + foreach (const T &item, oldList) + { + newList.append(T(item, view)); + } + + return newList; +} + +template <class ViewType> +void ForwardView<ViewType>::propertiesAboutToBeRemoved(const QList<AbstractProperty>& propertyList) +{ + foreach (const ViewTypePointer &view, m_targetViewList) + view->propertiesAboutToBeRemoved(adjustedList(propertyList, view.data())); +} + +template <class ViewType> +void ForwardView<ViewType>::propertiesRemoved(const QList<AbstractProperty>& propertyList) +{ + foreach (const ViewTypePointer &view, m_targetViewList) + view->propertiesRemoved(adjustedList(propertyList, view.data())); +} + +template <class ViewType> +void ForwardView<ViewType>::variantPropertiesChanged(const QList<VariantProperty>& propertyList, PropertyChangeFlags propertyChange) +{ + foreach (const ViewTypePointer &view, m_targetViewList) + view->variantPropertiesChanged(adjustedList(propertyList, view.data()), propertyChange); +} + +template <class ViewType> +void ForwardView<ViewType>::bindingPropertiesChanged(const QList<BindingProperty>& propertyList, PropertyChangeFlags propertyChange) +{ + foreach (const ViewTypePointer &view, m_targetViewList) + view->bindingPropertiesChanged(adjustedList(propertyList, view.data()), propertyChange); +} + +template <class ViewType> +void ForwardView<ViewType>::nodeTypeChanged(const ModelNode &node,const QString &type, int majorVersion, int minorVersion) +{ + foreach (const ViewTypePointer &view, m_targetViewList) + view->nodeTypeChanged(ModelNode(node, view.data()), type, majorVersion, minorVersion); +} + +template <class ViewType> +void ForwardView<ViewType>::selectedNodesChanged(const QList<ModelNode> &selectedNodeList, + const QList<ModelNode> &lastSelectedNodeList) +{ + foreach (const ViewTypePointer &view, m_targetViewList) + view->selectedNodesChanged(adjustedList(selectedNodeList, view.data()), adjustedList(lastSelectedNodeList, view.data())); +} + +template <class ViewType> +void ForwardView<ViewType>::fileUrlChanged(const QUrl &oldUrl, const QUrl &newUrl) +{ + AbstractView::fileUrlChanged(oldUrl, newUrl); + + foreach (const ViewTypePointer &view, m_targetViewList) + view->fileUrlChanged(oldUrl, newUrl); +} + +template <class ViewType> +void ForwardView<ViewType>::nodeSlidedToIndex(const NodeListProperty &listProperty, int newIndex, int oldIndex) +{ + foreach (const ViewTypePointer &view, m_targetViewList) + view->nodeSlidedToIndex(NodeListProperty(listProperty, view.data()), newIndex, oldIndex); +} + +template <class ViewType> +void ForwardView<ViewType>::importsChanged() +{ + AbstractView::importsChanged(); + + foreach (const ViewTypePointer &view, m_targetViewList) + view->importsChanged(); +} + +template <class ViewType> +void ForwardView<ViewType>::auxiliaryDataChanged(const ModelNode &node, const QString &name, const QVariant &data) +{ + AbstractView::auxiliaryDataChanged(node, name, data); + + foreach (const ViewTypePointer &view, m_targetViewList) + view->auxiliaryDataChanged(ModelNode(node, view.data()), name, data); +} + +template <class ViewType> +void ForwardView<ViewType>::appendView(ViewType *view) +{ + m_targetViewList.append(view); +} + +template <class ViewType> +void ForwardView<ViewType>::removeView(ViewType *view) +{ + m_targetViewList.append(view); +} + +template <class ViewType> +QList<ViewType*> ForwardView<ViewType>::viewList() const +{ + QList<ViewType*> newList; + + foreach (const ViewTypePointer &view, m_targetViewList) + newList.append(view.data()); + + return newList; +} + +template <class ViewType> +ViewType *ForwardView<ViewType>::firstView() const +{ + if (m_targetViewList.isEmpty()) + return 0; + + return m_targetViewList.first().data(); +} + + +} // namespace QmlDesigner + +#endif // FORWARDVIEW_H diff --git a/src/plugins/qmldesigner/core/include/import.h b/src/plugins/qmldesigner/core/include/import.h new file mode 100644 index 00000000000..f10f89b5f4c --- /dev/null +++ b/src/plugins/qmldesigner/core/include/import.h @@ -0,0 +1,76 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef IMPORT_H +#define IMPORT_H + +#include <QtCore/QString> +#include <QtCore/QUrl> + +#include "corelib_global.h" + +namespace QmlDesigner { + +class CORESHARED_EXPORT Import +{ +public: + static Import createLibraryImport(const QUrl &url, const QString &version = QString(), const QString &alias = QString()); + static Import createFileImport(const QString &file, const QString &version = QString(), const QString &alias = QString()); + static Import empty(); + + bool isEmpty() const { return m_url.isEmpty() && m_file.isEmpty(); } + bool isFileImport() const { return m_url.isEmpty() && !m_file.isEmpty(); } + bool isLibraryImport() const { return !m_url.isEmpty() && m_file.isEmpty(); } + bool hasVersion() const { return !m_version.isEmpty(); } + bool hasAlias() const { return !m_alias.isEmpty(); } + + QUrl url() const { return m_url; } + QString file() const { return m_file; } + QString version() const { return m_version; } + QString alias() const { return m_alias; } + + QString toString(bool addSemicolon = false) const; + + bool operator==(const Import &other) const; + +private: + Import(const QUrl &url, const QString &file, const QString &version, const QString &alias); + +private: + QUrl m_url; + QString m_file; + QString m_version; + QString m_alias; +}; + +CORESHARED_EXPORT uint qHash(const Import &import); + +} // namespace QmlDesigner + +#endif // IMPORT_H diff --git a/src/plugins/qmldesigner/core/include/invalidargumentexception.h b/src/plugins/qmldesigner/core/include/invalidargumentexception.h new file mode 100644 index 00000000000..f18480cb807 --- /dev/null +++ b/src/plugins/qmldesigner/core/include/invalidargumentexception.h @@ -0,0 +1,55 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef INVALIDARGUMENTEXCEPTION_H +#define INVALIDARGUMENTEXCEPTION_H + +#include "exception.h" + + +namespace QmlDesigner { + +class CORESHARED_EXPORT InvalidArgumentException : public Exception +{ +public: + InvalidArgumentException(int line, + const QString &function, + const QString &file, + const QString &argument); + + QString type() const; + QString argument() const; + +private: + const QString m_argument; +}; + +} + +#endif // INVALIDARGUMENTEXCEPTION_H diff --git a/src/plugins/qmldesigner/core/include/invalididexception.h b/src/plugins/qmldesigner/core/include/invalididexception.h new file mode 100644 index 00000000000..fd045bc32af --- /dev/null +++ b/src/plugins/qmldesigner/core/include/invalididexception.h @@ -0,0 +1,50 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef INVALIDIDEXCEPTION_H +#define INVALIDIDEXCEPTION_H + +#include "invalidargumentexception.h" + +namespace QmlDesigner { + +class CORESHARED_EXPORT InvalidIdException : public InvalidArgumentException +{ +public: + InvalidIdException(int line, + const QString &function, + const QString &file); + + QString type() const; + +}; + +} + +#endif // INVALIDIDEXCEPTION_H diff --git a/src/plugins/qmldesigner/core/include/invalidmetainfoexception.h b/src/plugins/qmldesigner/core/include/invalidmetainfoexception.h new file mode 100644 index 00000000000..619b3bc101a --- /dev/null +++ b/src/plugins/qmldesigner/core/include/invalidmetainfoexception.h @@ -0,0 +1,50 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef INVALIDMETAINFOEXCEPTION_H +#define INVALIDMETAINFOEXCEPTION_H + +#include "exception.h" + +namespace QmlDesigner { + +class CORESHARED_EXPORT InvalidMetaInfoException : public Exception +{ +public: + InvalidMetaInfoException(int line, + const QString &function, + const QString &file); + + QString type() const; + +}; + +} + +#endif // INVALIDMETAINFOEXCEPTION_H diff --git a/src/plugins/qmldesigner/core/include/invalidmodelnodeexception.h b/src/plugins/qmldesigner/core/include/invalidmodelnodeexception.h new file mode 100644 index 00000000000..b3e73cae220 --- /dev/null +++ b/src/plugins/qmldesigner/core/include/invalidmodelnodeexception.h @@ -0,0 +1,50 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef INVALIDMODELNODEEXCEPTION_H +#define INVALIDMODELNODEEXCEPTION_H + +#include "exception.h" + +namespace QmlDesigner { + +class CORESHARED_EXPORT InvalidModelNodeException : public Exception +{ +public: + InvalidModelNodeException(int line, + const QString &function, + const QString &file); + + QString type() const; + +}; + +} + +#endif // INVALIDMODELNODEEXCEPTION_H diff --git a/src/plugins/qmldesigner/core/include/invalidmodelstateexception.h b/src/plugins/qmldesigner/core/include/invalidmodelstateexception.h new file mode 100644 index 00000000000..bafc3aa7405 --- /dev/null +++ b/src/plugins/qmldesigner/core/include/invalidmodelstateexception.h @@ -0,0 +1,50 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef INVALIDMODELSTATEEXCEPTION_H +#define INVALIDMODELSTATEEXCEPTION_H + +#include "exception.h" + +namespace QmlDesigner { + +class CORESHARED_EXPORT InvalidModelStateException : public Exception +{ +public: + InvalidModelStateException(int line, + const QString &function, + const QString &file); + + + QString type() const; +}; + +} // namespace QmlDesigner + +#endif // INVALIDMODELSTATEEXCEPTION_H diff --git a/src/plugins/qmldesigner/core/include/invalidnodeinstanceexception.h b/src/plugins/qmldesigner/core/include/invalidnodeinstanceexception.h new file mode 100644 index 00000000000..f6cc1ba7330 --- /dev/null +++ b/src/plugins/qmldesigner/core/include/invalidnodeinstanceexception.h @@ -0,0 +1,48 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef INVALIDNODEINSTANCEEXCEPTION_H +#define INVALIDNODEINSTANCEEXCEPTION_H + +#include "exception.h" + +namespace QmlDesigner { + +class CORESHARED_EXPORT InvalidNodeInstanceException : public Exception +{ +public: + InvalidNodeInstanceException(int line, + const QString &function, + const QString &file); + QString type() const; +}; + +} + +#endif // INVALIDNODEINSTANCEEXCEPTION_H diff --git a/src/plugins/qmldesigner/core/include/invalidnodestateexception.h b/src/plugins/qmldesigner/core/include/invalidnodestateexception.h new file mode 100644 index 00000000000..826f6644b72 --- /dev/null +++ b/src/plugins/qmldesigner/core/include/invalidnodestateexception.h @@ -0,0 +1,50 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef INVALIDNODESTATEEXCEPTION_H +#define INVALIDNODESTATEEXCEPTION_H + +#include <exception.h> + +namespace QmlDesigner { + +class CORESHARED_EXPORT InvalidNodeStateException : public Exception +{ +public: + InvalidNodeStateException(int line, + const QString &function, + const QString &file); + + QString type() const; + +}; + +} // namespace QmlDesigner + +#endif // INVALIDNODESTATEEXCEPTION_H diff --git a/src/plugins/qmldesigner/core/include/invalidpropertyexception.h b/src/plugins/qmldesigner/core/include/invalidpropertyexception.h new file mode 100644 index 00000000000..db5dc01621e --- /dev/null +++ b/src/plugins/qmldesigner/core/include/invalidpropertyexception.h @@ -0,0 +1,54 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef INVALIDPROPERTYEXCEPTION_H +#define INVALIDPROPERTYEXCEPTION_H + +#include "exception.h" + +namespace QmlDesigner { + +class CORESHARED_EXPORT InvalidPropertyException : public Exception +{ +public: + InvalidPropertyException(int line, + const QString &function, + const QString &file, + const QString &argument); + + QString type() const; + QString argument() const; + +private: + const QString m_argument; +}; + +} // namespace QmlDesigner + +#endif // INVALIDPROPERTYEXCEPTION_H diff --git a/src/plugins/qmldesigner/core/include/invalidqmlsourceexception.h b/src/plugins/qmldesigner/core/include/invalidqmlsourceexception.h new file mode 100644 index 00000000000..63918dda7e5 --- /dev/null +++ b/src/plugins/qmldesigner/core/include/invalidqmlsourceexception.h @@ -0,0 +1,54 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef INVALIDQMLSOURCEEXCEPTION_H +#define INVALIDQMLSOURCEEXCEPTION_H + +#include <exception.h> + +namespace QmlDesigner { + +class CORESHARED_EXPORT InvalidQmlSourceException : public QmlDesigner::Exception +{ +public: + InvalidQmlSourceException(int line, + const QString &function, + const QString &file, + const QString &qmlSource = QString()); + + QString type() const; + QString description() const; + +private: + QString m_qmlSource; +}; + +} // namespace QmlDesigner + +#endif // INVALIDQMLSOURCEEXCEPTION_H diff --git a/src/plugins/qmldesigner/core/include/invalidreparentingexception.h b/src/plugins/qmldesigner/core/include/invalidreparentingexception.h new file mode 100644 index 00000000000..e3f4237c2b2 --- /dev/null +++ b/src/plugins/qmldesigner/core/include/invalidreparentingexception.h @@ -0,0 +1,49 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef INVALIDREPARENTINGEXCEPTION_H +#define INVALIDREPARENTINGEXCEPTION_H + +#include "exception.h" + +namespace QmlDesigner { + +class CORESHARED_EXPORT InvalidReparentingException : public Exception +{ +public: + InvalidReparentingException(int line, + const QString &function, + const QString &file); + + QString type() const; +}; + +} // namespace QmlDesigner + +#endif // INVALIDREPARENTINGEXCEPTION_H diff --git a/src/plugins/qmldesigner/core/include/invalidslideindexexception.h b/src/plugins/qmldesigner/core/include/invalidslideindexexception.h new file mode 100644 index 00000000000..dd41c4a2843 --- /dev/null +++ b/src/plugins/qmldesigner/core/include/invalidslideindexexception.h @@ -0,0 +1,48 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef INVALIDSLIDEINDEXEXCEPTION_H +#define INVALIDSLIDEINDEXEXCEPTION_H + +#include "exception.h" + +namespace QmlDesigner { + +class CORESHARED_EXPORT InvalidSlideIndexException : public Exception +{ +public: + InvalidSlideIndexException(int line, + const QString &function, + const QString &file); + QString type() const; +}; + +} // namespace QmlDesigner + +#endif // INVALIDSLIDEINDEXEXCEPTION_H diff --git a/src/plugins/qmldesigner/core/include/itemlibraryinfo.h b/src/plugins/qmldesigner/core/include/itemlibraryinfo.h new file mode 100644 index 00000000000..05ef252ec21 --- /dev/null +++ b/src/plugins/qmldesigner/core/include/itemlibraryinfo.h @@ -0,0 +1,100 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef ItemLibraryINFO_H +#define ItemLibraryINFO_H + +#include "corelib_global.h" + +#include <QExplicitlySharedDataPointer> +#include <QList> +#include <QString> +#include <QIcon> +#include "propertycontainer.h" +#include <qml.h> + +namespace QmlDesigner { + +class NodeMetaInfo; + +namespace Internal { + +class ItemLibraryInfoData; +} + +class ItemLibraryInfo; + +CORESHARED_EXPORT QDataStream& operator<<(QDataStream& stream, const ItemLibraryInfo& ItemLibraryInfo); +CORESHARED_EXPORT QDataStream& operator>>(QDataStream& stream, ItemLibraryInfo& ItemLibraryInfo); + +class CORESHARED_EXPORT ItemLibraryInfo +{ + friend class QmlDesigner::MetaInfo; + friend class QmlDesigner::Internal::MetaInfoParser; + friend CORESHARED_EXPORT QDataStream& QmlDesigner::operator<<(QDataStream& stream, const ItemLibraryInfo& image); + friend CORESHARED_EXPORT QDataStream& QmlDesigner::operator>>(QDataStream& stream, ItemLibraryInfo& image); +public: + ItemLibraryInfo(); + ~ItemLibraryInfo(); + + QString name() const; + QString typeName() const; + QIcon icon() const; + int majorVersion() const; + int minorVersion() const; + QString category() const; + QIcon dragIcon() const; + QString qml() const; + + ItemLibraryInfo(const ItemLibraryInfo &other); + ItemLibraryInfo& operator=(const ItemLibraryInfo &other); + + typedef QmlDesigner::PropertyContainer Property; + + QList<Property> properties() const; + + void setTypeName(const QString &typeName); + void setMajorVersion(int majorNumber); + void setMinorVersion(int minorNumber); + void setName(const QString &name); + void setIcon(const QIcon &icon); + void addProperty(const Property &p); + void addProperty(QString &name, QString &type, QString &value); + void setDragIcon(const QIcon &icon); + void setCategory(const QString &category); + void setQml(const QString &qml); +private: + QExplicitlySharedDataPointer<Internal::ItemLibraryInfoData> m_data; +}; + +} + +Q_DECLARE_METATYPE(QmlDesigner::ItemLibraryInfo) + +#endif // ItemLibraryINFO_H diff --git a/src/plugins/qmldesigner/core/include/iwidgetplugin.h b/src/plugins/qmldesigner/core/include/iwidgetplugin.h new file mode 100644 index 00000000000..c151aaa948c --- /dev/null +++ b/src/plugins/qmldesigner/core/include/iwidgetplugin.h @@ -0,0 +1,52 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef IWIDGETPLUGIN_H +#define IWIDGETPLUGIN_H + +#include <QtCore/QObject> + +#define QMLDESIGNER_WIDGETPLUGIN_INTERFACE "com.Nokia.QmlDesigner.IWidgetPlugin.v10" + +namespace QmlDesigner { + +class IWidgetPlugin +{ +public: + virtual ~IWidgetPlugin() {} + + virtual QString metaInfo() = 0; + virtual QString pluginName() = 0; +}; + +} // namespace QmlDesigner + +Q_DECLARE_INTERFACE(QmlDesigner::IWidgetPlugin, QMLDESIGNER_WIDGETPLUGIN_INTERFACE) + +#endif // IWIDGETPLUGIN_H diff --git a/src/plugins/qmldesigner/core/include/mathutils.h b/src/plugins/qmldesigner/core/include/mathutils.h new file mode 100644 index 00000000000..62ece009fac --- /dev/null +++ b/src/plugins/qmldesigner/core/include/mathutils.h @@ -0,0 +1,46 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef MATHUTILS_H +#define MATHUTILS_H + +#include <QtGlobal> + +namespace QmlDesigner { + +inline double round(double value, int digits) +{ + double factor = digits * 10.; + return double(qRound64(value * factor)) / factor; +} + + +} + +#endif // MATHUTILS_H diff --git a/src/plugins/qmldesigner/core/include/metainfo.h b/src/plugins/qmldesigner/core/include/metainfo.h new file mode 100644 index 00000000000..329dc1c0f3e --- /dev/null +++ b/src/plugins/qmldesigner/core/include/metainfo.h @@ -0,0 +1,123 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef METAINFO_H +#define METAINFO_H + +#include "corelib_global.h" + +#include <QMultiHash> +#include <QString> +#include <QStringList> +#include <QtCore/QSharedPointer> + +#include <nodemetainfo.h> +#include "propertymetainfo.h" +#include "itemlibraryinfo.h" +#include "enumeratormetainfo.h" + + +namespace QmlDesigner { + +class ModelNode; +class AbstractProperty; + +namespace Internal { + class MetaInfoPrivate; + class ModelPrivate; + class SubComponentManagerPrivate; + typedef QSharedPointer<MetaInfoPrivate> MetaInfoPrivatePointer; +} + +CORESHARED_EXPORT bool operator==(const MetaInfo &first, const MetaInfo &second); +CORESHARED_EXPORT bool operator!=(const MetaInfo &first, const MetaInfo &second); + +class CORESHARED_EXPORT MetaInfo +{ + friend class QmlDesigner::Internal::MetaInfoPrivate; + friend class QmlDesigner::Internal::ModelPrivate; + friend class QmlDesigner::Internal::MetaInfoParser; + friend class QmlDesigner::Internal::SubComponentManagerPrivate; + friend class QmlDesigner::NodeMetaInfo; + friend bool QmlDesigner::operator==(const MetaInfo &, const MetaInfo &); + +public: + MetaInfo(const MetaInfo &metaInfo); + ~MetaInfo(); + MetaInfo& operator=(const MetaInfo &other); + + bool hasNodeMetaInfo(const QString &typeName, int majorVersion = 4, int minorVersion = 6) const; + NodeMetaInfo nodeMetaInfo(const QString &typeName, int majorVersion = 4, int minorVersion = 6) const; + + // TODO: Move these to private + bool isSubclassOf(const QString &className, const QString &superClassName) const; + bool isSubclassOf(const ModelNode &modelNode, const QString &superClassName) const; + + bool hasEnumerator(const QString &enumeratorName) const; + EnumeratorMetaInfo enumerator(const QString &enumeratorName) const; + + QStringList itemLibraryItems() const; + QList<ItemLibraryInfo> itemLibraryRepresentations(const NodeMetaInfo &nodeMetaInfo) const; + ItemLibraryInfo itemLibraryRepresentation(const QString &name) const; + +public: + static MetaInfo global(); + static void clearGlobal(); + + static void setPluginPaths(const QStringList &paths); + +private: + QStringList superClasses(const QString &className) const; + QStringList directSuperClasses(const QString &className) const; + QList<NodeMetaInfo> superClasses(const NodeMetaInfo &nodeMetaInfo) const; + QList<NodeMetaInfo> directSuperClasses(const NodeMetaInfo &nodeMetaInfo) const; + + void addSuperClassRelationship(const QString &superClassName, const QString &className); + + void addNodeInfo(NodeMetaInfo &info, const QString &baseType); + void removeNodeInfo(NodeMetaInfo &info); + void replaceNodeInfo(NodeMetaInfo &oldInfo, NodeMetaInfo &newInfo, const QString &baseType); + + EnumeratorMetaInfo addEnumerator(const QString &enumeratorScope, const QString &enumeratorName); + EnumeratorMetaInfo addFlag(const QString &enumeratorScope, const QString &enumeratorName); + ItemLibraryInfo addItemLibraryInfo(const NodeMetaInfo &nodeMetaInfo, const QString &itemLibraryRepresentationName); + + bool isGlobal() const; + +private: + MetaInfo(); + + Internal::MetaInfoPrivatePointer m_p; + static MetaInfo s_global; + static QStringList s_pluginDirs; +}; + +} //namespace QmlDesigner + +#endif // METAINFO_H diff --git a/src/plugins/qmldesigner/core/include/metainfoparser.h b/src/plugins/qmldesigner/core/include/metainfoparser.h new file mode 100644 index 00000000000..757806a9392 --- /dev/null +++ b/src/plugins/qmldesigner/core/include/metainfoparser.h @@ -0,0 +1,77 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef METAINFOPARSER_H +#define METAINFOPARSER_H + +#include "corelib_global.h" +#include <QtCore/QXmlStreamReader> +#include <QtCore/QString> +#include <QtCore/QFile> +#include <metainfo.h> + +namespace QmlDesigner { + +class NodeMetaInfo; +class EnumeratorMetaInfo; +class ItemLibraryInfo; + +namespace Internal { + + +class TEST_CORESHARED_EXPORT MetaInfoParser +{ +public: + MetaInfoParser(const MetaInfo &metaInfo); + + void parseFile(const QString &path); + +protected: + void errorHandling(QXmlStreamReader &reader, QFile &file); + void tokenHandler(QXmlStreamReader &reader); + void metaInfoHandler(QXmlStreamReader &reader); + void handleMetaInfoElement(QXmlStreamReader &reader); + void handleEnumElement(QXmlStreamReader &reader); + void handleEnumElementElement(QXmlStreamReader &reader, EnumeratorMetaInfo &enumeratorMetaInfo); + void handleFlagElement(QXmlStreamReader &reader); + void handleFlagElementElement(QXmlStreamReader &reader, EnumeratorMetaInfo &enumeratorMetaInfo); + void handleNodeElement(QXmlStreamReader &reader); + void handleNodeInheritElement(QXmlStreamReader &reader, const QString &className); + void handleNodeItemLibraryRepresentationElement(QXmlStreamReader &reader, const QString &className); + void handleAbstractPropertyElement(QXmlStreamReader &reader, NodeMetaInfo &nodeMetaInfo); + void handleAbstractPropertyDefaultValueElement(QXmlStreamReader &reader, NodeMetaInfo &nodeMetaInfo); + void handleItemLibraryInfoPropertyElement(QXmlStreamReader &reader, ItemLibraryInfo &ItemLibraryInfo); + +private: + MetaInfo m_metaInfo; +}; + +} +} +#endif // METAINFOPARSER_H diff --git a/src/plugins/qmldesigner/core/include/model.h b/src/plugins/qmldesigner/core/include/model.h new file mode 100644 index 00000000000..ce6715433d8 --- /dev/null +++ b/src/plugins/qmldesigner/core/include/model.h @@ -0,0 +1,109 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef DESIGNERMODEL_H +#define DESIGNERMODEL_H + +#include <corelib_global.h> +#include <QtCore/QObject> +#include <QtCore/QMimeData> +#include <QtCore/QPair> +#include <QtDeclarative/QmlError> + +#include <import.h> + +class QUrl; + +namespace QmlDesigner { + +namespace Internal { + class ModelPrivate; +} + +class AnchorLine; +class ModelNode; +class NodeState; +class AbstractView; +class WidgetQueryView; +class NodeStateChangeSet; +class MetaInfo; +class ModelState; +class NodeAnchors; +class AbstractProperty; + +typedef QList<QPair<QString, QVariant> > PropertyListType; + +class CORESHARED_EXPORT Model : public QObject +{ + friend class QmlDesigner::ModelNode; + friend class QmlDesigner::NodeState; + friend class QmlDesigner::ModelState; + friend class QmlDesigner::NodeAnchors; + friend class QmlDesigner::AbstractProperty; + friend class QmlDesigner::AbstractView; + friend class Internal::ModelPrivate; + + Q_DISABLE_COPY(Model) + Q_OBJECT +public: + enum ViewNotification { NotifyView, DoNotNotifyView }; + + virtual ~Model(); + + static Model *create(QString type, int major = 4, int minor = 6); + + Model *masterModel() const; + void setMasterModel(Model *model); + + QUrl fileUrl() const; + void setFileUrl(const QUrl &url); + + const MetaInfo metaInfo() const; + MetaInfo metaInfo(); + + void attachView(AbstractView *view); + void detachView(AbstractView *view, ViewNotification emitDetachNotify = NotifyView); + + // Editing sub-components: + + // Imports: + QSet<Import> imports() const; + void addImport(const Import &import); + void removeImport(const Import &import); + +protected: + Model(); + +public: + Internal::ModelPrivate *m_d; +}; + +} + +#endif // DESIGNERMODEL_H diff --git a/src/plugins/qmldesigner/core/include/modelmerger.h b/src/plugins/qmldesigner/core/include/modelmerger.h new file mode 100644 index 00000000000..39c96f799e7 --- /dev/null +++ b/src/plugins/qmldesigner/core/include/modelmerger.h @@ -0,0 +1,59 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef MODELMERGER_H +#define MODELMERGER_H + + +#include <QWeakPointer> + +namespace QmlDesigner { + +class AbstractView; +class ModelNode; + +class ModelMerger +{ +public: + ModelMerger(AbstractView *view) : m_view(view) {} + + ModelNode insertModel(const ModelNode &modelNode); + void replaceModel(const ModelNode &modelNode); + +protected: + AbstractView *view() const + { return m_view.data(); } + +private: + QWeakPointer<AbstractView> m_view; +}; + +} //namespace QmlDesigner + +#endif // MODELMERGER_H diff --git a/src/plugins/qmldesigner/core/include/modelnode.h b/src/plugins/qmldesigner/core/include/modelnode.h new file mode 100644 index 00000000000..843af5f8592 --- /dev/null +++ b/src/plugins/qmldesigner/core/include/modelnode.h @@ -0,0 +1,186 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef DESIGNERNODE_H +#define DESIGNERNODE_H + +#include "corelib_global.h" +#include <QWeakPointer> +#include <QtCore/QList> +#include <QtCore/QMetaType> +#include <QVariant> + +class QTextStream; + +namespace QmlDesigner { + +namespace Internal { + class InternalNode; + class ModelPrivate; + class InternalNode; + class InternalProperty; + + typedef QSharedPointer<InternalNode> InternalNodePointer; + typedef QSharedPointer<InternalProperty> InternalPropertyPointer; + typedef QWeakPointer<InternalNode> InternalNodeWeakPointer; +} +class NodeMetaInfo; +class AbstractProperty; +class BindingProperty; +class VariantProperty; +class Model; +class AbstractView; +class NodeListProperty; +class NodeProperty; +class NodeAbstractProperty; +class NodeInstance; +class ModelNode; + +CORESHARED_EXPORT QList<Internal::InternalNodePointer> toInternalNodeList(const QList<ModelNode> &nodeList); + +typedef QList<QPair<QString, QVariant> > PropertyListType; + +class CORESHARED_EXPORT ModelNode +{ + friend CORESHARED_EXPORT bool operator ==(const ModelNode &firstNode, const ModelNode &secondNode); + friend CORESHARED_EXPORT bool operator !=(const ModelNode &firstNode, const ModelNode &secondNode); + friend CORESHARED_EXPORT uint qHash(const ModelNode & node); + friend CORESHARED_EXPORT QDebug operator<<(QDebug debug, const ModelNode &modelNode); + friend CORESHARED_EXPORT bool operator <(const ModelNode &firstNode, const ModelNode &secondNode); + friend CORESHARED_EXPORT QList<Internal::InternalNodePointer> QmlDesigner::toInternalNodeList(const QList<ModelNode> &nodeList); + friend class QmlDesigner::Model; + friend class QmlDesigner::AbstractView; + friend class QmlDesigner::NodeListProperty; + friend class QmlDesigner::Internal::ModelPrivate; + friend class QmlDesigner::NodeAbstractProperty; + friend class QmlDesigner::NodeProperty; + +public: + ModelNode(); + ModelNode(const Internal::InternalNodePointer &internalNode, Model *model, AbstractView *view); + ModelNode(const ModelNode modelNode, AbstractView *view); + ModelNode(const ModelNode &other); + ~ModelNode(); + + ModelNode& operator=(const ModelNode &other); + QString type() const; + void changeType(const QString &type, int majorVersion, int minorVersion); + QString simplifiedTypeName() const; + int minorVersion() const; + int majorVersion() const; + + bool isValid() const; + bool isInHierarchy() const; + + + NodeAbstractProperty parentProperty() const; + void setParentProperty(NodeAbstractProperty parent); + void setParentProperty(const ModelNode &newParentNode, const QString &propertyName); + bool hasParentProperty() const; + + ModelNode addChildNode(const QString &nodeTypeString, int majorVersion, int minorVersion, const QString &propertyName, const PropertyListType &propertyList = PropertyListType()); + const QList<ModelNode> allDirectSubModelNodes() const; + const QList<ModelNode> allSubModelNodes() const; + bool hasAnySubModelNodes() const; + + //### + + AbstractProperty property(const QString &name) const; + VariantProperty variantProperty(const QString &name) const; + BindingProperty bindingProperty(const QString &name) const; + NodeListProperty nodeListProperty(const QString &name) const; + NodeProperty nodeProperty(const QString &name) const; + NodeAbstractProperty nodeAbstractProperty(const QString &name) const; + + void removeProperty(const QString &name); //### also implement in AbstractProperty + QList<AbstractProperty> properties() const; + QList<VariantProperty> variantProperties() const; + QList<NodeAbstractProperty> nodeAbstractProperties() const; + QList<NodeProperty> nodeProperties() const; + QList<NodeListProperty> nodeListProperties() const; + QList<BindingProperty> bindingProperties() const; + QStringList propertyNames() const; + + bool hasProperties() const; + bool hasProperty(const QString &name) const; + bool hasVariantProperty(const QString &name) const; + bool hasBindingProperty(const QString &name) const; + bool hasNodeAbstracProperty(const QString &name) const; + bool hasNodeProperty(const QString &name) const; + bool hasNodeListProperty(const QString &name) const; + + //### + void destroy(); + + QString id() const; + QString validId(); + void setId(const QString &id); + static bool isValidId(const QString &id); + + Model *model() const; + AbstractView *view() const; + + const NodeMetaInfo metaInfo() const; + + bool isSelected() const; + bool isRootNode() const; + + bool isAncestorOf(const ModelNode &node) const; + void selectNode(); + void deselectNode(); + + static int variantUserType(); + QVariant toVariant() const; + + QVariant auxiliaryData(const QString &name) const; + void setAuxiliaryData(const QString &name, const QVariant &data); + bool hasAuxiliaryData(const QString &name) const; + +private: // functions + Internal::InternalNodePointer internalNode() const; + QString generateNewId() const; + +private: // variables + Internal::InternalNodePointer m_internalNode; + QWeakPointer<Model> m_model; + QWeakPointer<AbstractView> m_view; +}; + +CORESHARED_EXPORT bool operator ==(const ModelNode &firstNode, const ModelNode &secondNode); +CORESHARED_EXPORT bool operator !=(const ModelNode &firstNode, const ModelNode &secondNode); +CORESHARED_EXPORT uint qHash(const ModelNode & node); +CORESHARED_EXPORT bool operator <(const ModelNode &firstNode, const ModelNode &secondNode); +CORESHARED_EXPORT QDebug operator<<(QDebug debug, const ModelNode &modelNode); +CORESHARED_EXPORT QTextStream& operator<<(QTextStream &stream, const ModelNode &modelNode); +} + +Q_DECLARE_METATYPE(QmlDesigner::ModelNode) +Q_DECLARE_METATYPE(QList<QmlDesigner::ModelNode>) + +#endif // DESIGNERNODE_H diff --git a/src/plugins/qmldesigner/core/include/modelnodepositionstorage.h b/src/plugins/qmldesigner/core/include/modelnodepositionstorage.h new file mode 100644 index 00000000000..c9b93805d11 --- /dev/null +++ b/src/plugins/qmldesigner/core/include/modelnodepositionstorage.h @@ -0,0 +1,82 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef MODELNODEPOSITIONSTORAGE_H +#define MODELNODEPOSITIONSTORAGE_H + +#include "modelnode.h" + +namespace QmlDesigner { +namespace Internal { + +class ModelNodePositionStorage +{ +public: + void clear() + { m_rewriterData.clear(); } + + int nodeOffset(const ModelNode &modelNode); + void setNodeOffset(const ModelNode &modelNode, int fileOffset); + void cleanupInvalidOffsets(); + void removeNodeOffset(const ModelNode &node) + { m_rewriterData.remove(node); } + + QList<ModelNode> modelNodes() const; + +public: + static const int INVALID_LOCATION = -1; + +private: + class RewriterData { //custom rewriter data for each node can be kept here + public: + RewriterData(int offset = INVALID_LOCATION) + : _offset(offset) + {} + + bool isValid() const + { return _offset != INVALID_LOCATION; } + + int offset() const + { return _offset; } + + void setOffset(int offset) + { _offset = offset; } + + private: + int _offset; + }; + +private: + QHash<ModelNode, RewriterData> m_rewriterData; +}; + +} // namespace Internal +} // namespace QmlDesigner + +#endif // MODELNODEPOSITIONSTORAGE_H diff --git a/src/plugins/qmldesigner/core/include/modelutilities.h b/src/plugins/qmldesigner/core/include/modelutilities.h new file mode 100644 index 00000000000..2dd369c4f3f --- /dev/null +++ b/src/plugins/qmldesigner/core/include/modelutilities.h @@ -0,0 +1,51 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef MODELUTILITIES_H +#define MODELUTILITIES_H + +#include "corelib_global.h" +#include <modelnode.h> +#include <nodeinstance.h> + +namespace QmlDesigner { +namespace ModelUtilities { + +CORESHARED_EXPORT bool canReparent(const ModelNode &child, const ModelNode &parent); +CORESHARED_EXPORT bool isGraphicsItem(const ModelNode &node); +CORESHARED_EXPORT bool isQWidget(const ModelNode &node); +CORESHARED_EXPORT QVariant parseProperty(const QString &className, const QString &propertyName, const QString &value); +CORESHARED_EXPORT void setAbsolutePosition(ModelNode node, QPointF position); +CORESHARED_EXPORT QList<ModelNode> descendantNodes(const ModelNode &parent); + +CORESHARED_EXPORT NodeInstance instanceForNode(const ModelNode &node); +} //namespace ModelUtilities +} //namespace QmlDesigner + +#endif //MODELUTILITIES_H diff --git a/src/plugins/qmldesigner/core/include/modificationgroupexception.h b/src/plugins/qmldesigner/core/include/modificationgroupexception.h new file mode 100644 index 00000000000..e9709eed019 --- /dev/null +++ b/src/plugins/qmldesigner/core/include/modificationgroupexception.h @@ -0,0 +1,47 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef MODIFICATIONGROUPEXCEPTION_H +#define MODIFICATIONGROUPEXCEPTION_H + +#include "exception.h" + +namespace QmlDesigner { + +class CORESHARED_EXPORT ModificationGroupException: public Exception +{ +public: + ModificationGroupException(int line, const QString &function, const QString &file); + + QString type() const; +}; + +} + +#endif // MODIFICATIONGROUPEXCEPTION_H diff --git a/src/plugins/qmldesigner/core/include/modificationgrouptoken.h b/src/plugins/qmldesigner/core/include/modificationgrouptoken.h new file mode 100644 index 00000000000..9110e1ab519 --- /dev/null +++ b/src/plugins/qmldesigner/core/include/modificationgrouptoken.h @@ -0,0 +1,75 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef MODIFICATIONGROUPTOKEN_H +#define MODIFICATIONGROUPTOKEN_H + +#include <corelib_global.h> + +namespace QmlDesigner { + +namespace Internal { + class ModelRewriter; + class ModelPrivate; +} + +/*! +\class QmlDesigner::ModificationGroupToken +\ingroup CoreModel +\brief TBD +*/ + +class CORESHARED_EXPORT ModificationGroupToken +{ + friend class Internal::ModelRewriter; + friend class Internal::ModelPrivate; + +public: + ModificationGroupToken(): m_depth(0), m_uniqueNumber(-1) {} + + unsigned depth() const { return m_depth; } + bool isValid() const { return m_uniqueNumber != -1; } + + bool operator==(const ModificationGroupToken& token) const { return m_uniqueNumber == token.m_uniqueNumber; } + bool operator!=(const ModificationGroupToken& token) const { return m_uniqueNumber != token.m_uniqueNumber; } + +private: + void makeInvalid() { m_uniqueNumber = -1; } + ModificationGroupToken(unsigned depth); + +private: + static long uniqueNumberCounter; + + unsigned m_depth; + long m_uniqueNumber; +}; + +} + +#endif // MODIFICATIONGROUPTOKEN_H diff --git a/src/plugins/qmldesigner/core/include/noanchoringpossibleexception.h b/src/plugins/qmldesigner/core/include/noanchoringpossibleexception.h new file mode 100644 index 00000000000..3ece337bbce --- /dev/null +++ b/src/plugins/qmldesigner/core/include/noanchoringpossibleexception.h @@ -0,0 +1,50 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef NOANCHORINGPOSSIBLEEXCEPTION_H +#define NOANCHORINGPOSSIBLEEXCEPTION_H + +#include "exception.h" + +namespace QmlDesigner { + +class CORESHARED_EXPORT NoAnchoringPossibleException : public Exception +{ +public: + NoAnchoringPossibleException(int line, + const QString &function, + const QString &file); + + QString type() const; + +}; + +} // namespace QKinecticDesigner + +#endif // NOANCHORINGPOSSIBLEEXCEPTION_H diff --git a/src/plugins/qmldesigner/core/include/nodeabstractproperty.h b/src/plugins/qmldesigner/core/include/nodeabstractproperty.h new file mode 100644 index 00000000000..33fec654c16 --- /dev/null +++ b/src/plugins/qmldesigner/core/include/nodeabstractproperty.h @@ -0,0 +1,63 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef NODEABSTRACTPROPERTY_H +#define NODEABSTRACTPROPERTY_H + +#include "abstractproperty.h" + +namespace QmlDesigner { + +namespace Internal { + class InternalNodeAbstractProperty; + typedef QSharedPointer<InternalNodeAbstractProperty> InternalNodeAbstractPropertyPointer; +} + +class NodeAbstractProperty : public AbstractProperty +{ + friend class QmlDesigner::ModelNode; + friend class QmlDesigner::Internal::ModelPrivate; + friend class QmlDesigner::AbstractProperty; +public: + NodeAbstractProperty(); + NodeAbstractProperty(const NodeAbstractProperty &property, AbstractView *view); + void reparentHere(const ModelNode &modelNode); + bool isEmpty() const; + + QList<ModelNode> allSubNodes(); + +protected: + NodeAbstractProperty(const QString &propertyName, const Internal::InternalNodePointer &internalNode, Model *model, AbstractView *view); + NodeAbstractProperty(const Internal::InternalNodeAbstractPropertyPointer &property, Model *model, AbstractView *view); + void reparentHere(const ModelNode &modelNode, bool isNodeList); +}; + +} // namespace QmlDesigner + +#endif // NODEABSTRACTPROPERTY_H diff --git a/src/plugins/qmldesigner/core/include/nodeanchors.h b/src/plugins/qmldesigner/core/include/nodeanchors.h new file mode 100644 index 00000000000..763d2044a76 --- /dev/null +++ b/src/plugins/qmldesigner/core/include/nodeanchors.h @@ -0,0 +1,114 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef NODEANCHORS_H +#define NODEANCHORS_H + + +#include <QWeakPointer> +#include <QSharedPointer> +#include "corelib_global.h" +#include <anchorline.h> +#include <nodestate.h> + +namespace QmlDesigner { + class Model; + class NodeState; + + namespace Internal { + class InternalNode; + typedef QSharedPointer<InternalNode> InternalNodePointer; + typedef QWeakPointer<InternalNode> InternalNodeWeakPointer; + + class InternalNodeState; + typedef QSharedPointer<InternalNodeState> InternalNodeStatePointer; + + class TextToModelMerger; + } +} + +namespace QmlDesigner { + +class CORESHARED_EXPORT NodeAnchors +{ + friend class NodeState; + friend class ModelNode; + friend class Internal::TextToModelMerger; + +public: + explicit NodeAnchors(const NodeState &nodeState); + + NodeAnchors(const NodeAnchors &other); + NodeAnchors& operator=(const NodeAnchors &other); + ~NodeAnchors(); + + ModelNode modelNode() const; + NodeState nodeState() const; + + bool isValid() const; + + void setAnchor(AnchorLine::Type sourceAnchorLineType, + const ModelNode &targetModelNode, + AnchorLine::Type targetAnchorLineType); + bool canAnchor(AnchorLine::Type sourceAnchorLineType, + const ModelNode &targetModelNode, + AnchorLine::Type targetAnchorLineType) const; + bool canAnchor(const ModelNode &targetModelNode) const; + AnchorLine::Type possibleAnchorLines(AnchorLine::Type sourceAnchorLineType, + const ModelNode &targetModelNode) const; + AnchorLine localAnchor(AnchorLine::Type anchorLineType) const; + AnchorLine anchor(AnchorLine::Type anchorLineType) const; + void removeAnchor(AnchorLine::Type sourceAnchorLineType); + void removeAnchors(); + bool hasLocalAnchor(AnchorLine::Type sourceAnchorLineType) const; + bool hasAnchor(AnchorLine::Type sourceAnchorLineType) const; + bool hasLocalAnchors() const; + bool hasAnchors() const; + void setMargin(AnchorLine::Type sourceAnchorLineType, double margin) const; + bool hasMargin(AnchorLine::Type sourceAnchorLineType) const; + double localMargin(AnchorLine::Type sourceAnchorLineType) const; + double margin(AnchorLine::Type sourceAnchorLineType) const; + void removeMargin(AnchorLine::Type sourceAnchorLineType); + void removeMargins(); + +private: // functions + NodeAnchors(const Internal::InternalNodeStatePointer &internalNodeState, Model *model); + +private: //variables + Internal::InternalNodePointer m_internalNode; + Internal::InternalNodeStatePointer m_internalNodeState; + QWeakPointer<Model> m_model; +}; + +CORESHARED_EXPORT QDebug operator<<(QDebug debug, const NodeAnchors &anchors); +CORESHARED_EXPORT QTextStream& operator<<(QTextStream &stream, const NodeAnchors &anchors); + +} // namespace QmlDesigner + +#endif // NODEANCHORS_H diff --git a/src/plugins/qmldesigner/core/include/nodeinstance.h b/src/plugins/qmldesigner/core/include/nodeinstance.h new file mode 100644 index 00000000000..d417b0eef9a --- /dev/null +++ b/src/plugins/qmldesigner/core/include/nodeinstance.h @@ -0,0 +1,166 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef NODEINSTANCE_H +#define NODEINSTANCE_H + +#include "corelib_global.h" +#include <QSharedPointer> +#include <QHash> +#include <QRectF> +#include <propertymetainfo.h> +#include <qmlanchors.h> + +class QPainter; +class QStyleOptionGraphicsItem; +class QmlContext; +class QGraphicsItem; +class QGraphicsTransform; + +namespace QmlDesigner { + +class ModelNode; +class NodeInstanceView; +class Preview; +class NodeMetaInfo; +class NodeState; +class WidgetQueryView; + + +namespace Internal { + class ObjectNodeInstance; + class QmlGraphicsItemNodeInstance; +} + +class CORESHARED_EXPORT NodeInstance +{ + friend class CORESHARED_EXPORT QmlDesigner::WidgetQueryView; + friend CORESHARED_EXPORT class Preview; + friend CORESHARED_EXPORT class NodeInstanceView; + friend class QHash<ModelNode, NodeInstance>; + friend CORESHARED_EXPORT uint qHash(const NodeInstance &instance); + friend CORESHARED_EXPORT bool operator==(const NodeInstance &first, const NodeInstance &second); + friend CORESHARED_EXPORT class NodeMetaInfo; + friend class QmlDesigner::Internal::QmlGraphicsItemNodeInstance; + friend class QmlDesigner::Internal::ObjectNodeInstance; +public: + NodeInstance(); + ~NodeInstance(); + NodeInstance(const NodeInstance &other); + NodeInstance& operator=(const NodeInstance &other); + + void paint(QPainter *painter) const; + + NodeInstance parent() const; + bool hasParent() const; + ModelNode modelNode() const; + void setModelNode(const ModelNode &node); + + bool isTopLevel() const; + + bool isQmlGraphicsItem() const; + bool isGraphicsScene() const; + bool isGraphicsView() const; + bool isGraphicsWidget() const; + bool isProxyWidget() const; + bool isWidget() const; + bool isQmlView() const; + bool isGraphicsObject() const; + bool isTransition() const; + + bool equalGraphicsItem(QGraphicsItem *item) const; + + QRectF boundingRect() const; + QPointF position() const; + QSizeF size() const; + QTransform transform() const; + QTransform customTransform() const; + QTransform sceneTransform() const; + double rotation() const; + double scale() const; + QList<QGraphicsTransform *> transformations() const; + QPointF transformOriginPoint() const; + double zValue() const; + + double opacity() const; + QVariant property(const QString &name) const; + QVariant defaultValue(const QString &name) const; + + bool isVisible() const; + bool isValid() const; + void makeInvalid(); + bool hasContent() const; + + const QObject *testHandle() const; + + void setPropertyVariant(const QString &name, const QVariant &value); + void setPropertyDynamicVariant(const QString &name, const QString &typeName, const QVariant &value); + + void setPropertyBinding(const QString &name, const QString &expression); + void setPropertyDynamicBinding(const QString &name, const QString &typeName, const QString &expression); + + bool hasAnchor(const QString &name) const; + bool isAnchoredBy() const; + QPair<QString, NodeInstance> anchor(const QString &name) const; + +private: // functions + NodeInstance(const QSharedPointer<Internal::ObjectNodeInstance> &abstractInstance); + + static NodeInstance create(NodeInstanceView *nodeInstanceView, const ModelNode &node, QObject *objectToBeWrapped); + static NodeInstance create(NodeInstanceView *nodeInstanceView, const NodeMetaInfo &metaInfo, QmlContext *context); + + void setDeleteHeldInstance(bool deleteInstance); + void reparent(const NodeInstance &oldParentInstance, const QString &oldParentProperty, const NodeInstance &newParentInstance, const QString &newParentProperty); + + void resetProperty(const QString &name); + + void setId(const QString &id); + + static QSharedPointer<Internal::ObjectNodeInstance> createInstance(const NodeMetaInfo &metaInfo, QmlContext *context, QObject *objectToBeWrapped); + QSharedPointer<Internal::QmlGraphicsItemNodeInstance> qmlGraphicsItemNodeInstance() const; + + void paintUpdate(); + + void show(); + void hide(); + + + QObject *internalObject() const; // should be not used outside of the nodeinstances!!!! + +private: // variables + QSharedPointer<Internal::ObjectNodeInstance> m_nodeInstance; +}; + +CORESHARED_EXPORT uint qHash(const NodeInstance &instance); +CORESHARED_EXPORT bool operator==(const NodeInstance &first, const NodeInstance &second); +} + +Q_DECLARE_METATYPE(QmlDesigner::NodeInstance); + +#endif // NODEINSTANCE_H diff --git a/src/plugins/qmldesigner/core/include/nodeinstanceview.h b/src/plugins/qmldesigner/core/include/nodeinstanceview.h new file mode 100644 index 00000000000..17cec2c1be3 --- /dev/null +++ b/src/plugins/qmldesigner/core/include/nodeinstanceview.h @@ -0,0 +1,153 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef ABSTRACTNODEINSTANCEVIEW_H +#define ABSTRACTNODEINSTANCEVIEW_H + +#include "corelib_global.h" +#include "abstractview.h" +#include <QtGui/QWidget> +#include <QtCore/QHash> +#include <QtScript/QScriptEngine> +#include <QWeakPointer> +#include <QtCore/QHash> + +#include <modelnode.h> +#include <nodeinstance.h> + +class QmlEngine; +class QGraphicsScene; +class QGraphicsView; + +namespace QmlDesigner { + +namespace Internal { + class ChildrenChangeEventFilter; +} + +class CORESHARED_EXPORT NodeInstanceView : public AbstractView +{ + Q_OBJECT + + friend class NodeInstance; + friend class Internal::ObjectNodeInstance; +public: + typedef QWeakPointer<NodeInstanceView> Pointer; + + NodeInstanceView(QObject *parent = 0); + ~NodeInstanceView(); + + void modelAttached(Model *model); + void modelAboutToBeDetached(Model *model); + void nodeCreated(const ModelNode &createdNode); + void nodeAboutToBeRemoved(const ModelNode &removedNode); + void nodeRemoved(const ModelNode &removedNode, const NodeAbstractProperty &parentProperty, PropertyChangeFlags propertyChange); + void propertiesAdded(const ModelNode &node, const QList<AbstractProperty>& propertyList); + void propertiesAboutToBeRemoved(const QList<AbstractProperty>& propertyList); + void propertiesRemoved(const QList<AbstractProperty>& propertyList); + void variantPropertiesChanged(const QList<VariantProperty>& propertyList, PropertyChangeFlags propertyChange); + void bindingPropertiesChanged(const QList<BindingProperty>& propertyList, PropertyChangeFlags propertyChange); + void nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent, const NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags propertyChange); + void nodeTypeChanged(const ModelNode &node,const QString &type, int majorVersion, int minorVersion); + + void fileUrlChanged(const QUrl &oldUrl, const QUrl &newUrl); + void nodeIdChanged(const ModelNode& node, const QString& newId, const QString& oldId); + + void modelStateAboutToBeRemoved(const ModelState &modelState); + void modelStateAdded(const ModelState &modelState); + + void nodeStatesAboutToBeRemoved(const QList<ModelNode> &nodeStateList); + void nodeStatesAdded(const QList<ModelNode> &nodeStateList); + + void nodeSlidedToIndex(const NodeListProperty &listProperty, int newIndex, int oldIndex); + + NodeInstance rootNodeInstance() const; + NodeInstance viewNodeInstance() const; + + void selectedNodesChanged(const QList<ModelNode> &selectedNodeList, const QList<ModelNode> &lastSelectedNodeList); + + QList<NodeInstance> instances() const; + NodeInstance instanceForNode(const ModelNode &node); + bool hasInstanceForNode(const ModelNode &node); + + NodeInstance instanceForObject(QObject *object); + bool hasInstanceForObject(QObject *object); + + void anchorsChanged(const ModelNode &nodeState); + + void render(QPainter *painter, const QRectF &target=QRectF(), const QRectF &source=QRect(), Qt::AspectRatioMode aspectRatioMode=Qt::KeepAspectRatio); + + QRectF boundingRect() const; + QRectF sceneRect() const; + void setBlockChangeSignal(bool block); + +signals: + void instanceRemoved(const NodeInstance &nodeInstance); + void transformPropertyChanged(const NodeInstance &nodeInstance); + void parentPropertyChanged(const NodeInstance &nodeInstance); + void otherPropertyChanged(const NodeInstance &nodeInstance); + void updateItem(const NodeInstance &nodeInstance); + +private slots: + void emitOtherPropertyChanged(); + void emitParentPropertyChanged(); + void emitTransformPropertyChanged(); + void emitUpdateItem(QObject *object); + +private: // functions + NodeInstance loadNode(const ModelNode &rootNode, QObject *objectToBeWrapped = 0); + void loadModel(Model *model); + void loadNodes(const QList<ModelNode> &nodeList); + + void removeAllInstanceNodeRelationships(); + + void removeRecursiveChildRelationship(const ModelNode &removedNode); + + void insertInstanceNodeRelationship(const ModelNode &node, const NodeInstance &instance); + void removeInstanceNodeRelationship(const ModelNode &node); + + QmlEngine *engine() const; + Internal::ChildrenChangeEventFilter *childrenChangeEventFilter(); + void removeInstanceAndSubInstances(const ModelNode &node); + +private: //variables + NodeInstance m_rootNodeInstance; + QScopedPointer<QGraphicsView> m_graphicsView; + + QHash<ModelNode, NodeInstance> m_nodeInstanceHash; + QHash<QObject*, NodeInstance> m_objectInstanceHash; // This is purely internal. Might contain dangling pointers! + QWeakPointer<QmlEngine> m_engine; + QWeakPointer<Internal::ChildrenChangeEventFilter> m_childrenChangeEventFilter; + + bool m_blockChangeSignal; +}; + +} + +#endif // ABSTRACTNODEINSTANCEVIEW_H diff --git a/src/plugins/qmldesigner/core/include/nodelistproperty.h b/src/plugins/qmldesigner/core/include/nodelistproperty.h new file mode 100644 index 00000000000..061c911f60e --- /dev/null +++ b/src/plugins/qmldesigner/core/include/nodelistproperty.h @@ -0,0 +1,71 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef NODELISTPROPERTY_H +#define NODELISTPROPERTY_H + + +#include "corelib_global.h" +#include "nodeabstractproperty.h" +#include <QList> + + +namespace QmlDesigner { + +namespace Internal { + class ModelPrivate; + class InternalNodeListProperty; + typedef QSharedPointer<InternalNodeListProperty> InternalNodeListPropertyPointer; +} + + +class CORESHARED_EXPORT NodeListProperty : public NodeAbstractProperty +{ + friend class QmlDesigner::ModelNode; + friend class QmlDesigner::AbstractProperty; + friend class QmlDesigner::Internal::ModelPrivate; +public: + NodeListProperty& operator= (const QVariant &value); + + NodeListProperty(); + NodeListProperty(const NodeListProperty &nodeListProperty, AbstractView *view); + const QList<ModelNode> toModelNodeList() const; + const QList<QmlObjectNode> toQmlObjectNodeList() const; + void slide(int, int) const; + void reparentHere(const ModelNode &modelNode); + + bool isEmpty() const; + +protected: + NodeListProperty(const QString &propertyName, const Internal::InternalNodePointer &internalNode, Model* model, AbstractView *view); + NodeListProperty(const Internal::InternalNodeListPropertyPointer &internalNodeListProperty, Model* model, AbstractView *view); +}; +} + +#endif //NODELISTROPERTY_H diff --git a/src/plugins/qmldesigner/core/include/nodemetainfo.h b/src/plugins/qmldesigner/core/include/nodemetainfo.h new file mode 100644 index 00000000000..7b80ef07072 --- /dev/null +++ b/src/plugins/qmldesigner/core/include/nodemetainfo.h @@ -0,0 +1,139 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef NODEMETAINFO_H +#define NODEMETAINFO_H + +#include <QList> +#include <QString> +#include <QExplicitlySharedDataPointer> +#include <QIcon> + +#include "corelib_global.h" +#include "invalidmetainfoexception.h" + +class QmlContext; + +namespace QmlDesigner { + +class MetaInfo; +class AbstractProperty; + +namespace Internal { + class MetaInfoPrivate; + class MetaInfoParser; + class NodeMetaInfoData; + class SubComponentManagerPrivate; + class ItemLibraryInfoData; +} + +class PropertyMetaInfo; + +class CORESHARED_EXPORT NodeMetaInfo +{ + friend class QmlDesigner::MetaInfo; + friend class QmlDesigner::Internal::ItemLibraryInfoData; + friend class QmlDesigner::Internal::MetaInfoPrivate; + friend class QmlDesigner::Internal::MetaInfoParser; + friend class QmlDesigner::Internal::SubComponentManagerPrivate; + friend class QmlDesigner::AbstractProperty; + friend CORESHARED_EXPORT uint qHash(const NodeMetaInfo &nodeMetaInfo); + friend CORESHARED_EXPORT bool operator ==(const NodeMetaInfo &firstNodeInfo, const NodeMetaInfo &secondNodeInfo); + +public: + ~NodeMetaInfo(); + + NodeMetaInfo(const NodeMetaInfo &other); + NodeMetaInfo &operator=(const NodeMetaInfo &other); + + bool isValid() const; + MetaInfo metaInfo() const; + + QObject *createInstance(QmlContext *parentContext) const; + + PropertyMetaInfo property(const QString &propertyName, bool resolveDotSyntax = false) const; + + QList<NodeMetaInfo> superClasses() const; + QList<NodeMetaInfo> directSuperClasses() const; + QHash<QString,PropertyMetaInfo> properties(bool resolveDotSyntax = false) const; + + + QString typeName() const; + int majorVersion() const; + int minorVersion() const; + + bool hasDefaultProperty() const; + QString defaultProperty() const; + + bool hasProperty(const QString &propertyName, bool resolveDotSyntax = false) const; + bool isContainer() const; + bool isVisibleToItemLibrary() const; + + bool isWidget() const; + bool isGraphicsWidget() const; + bool isGraphicsObject() const; + bool isQmlGraphicsItem() const; + bool isComponent() const; + bool isSubclassOf(const QString& type, int majorVersion = 4, int minorVersion = 6) const; + + QIcon icon() const; + QString category() const; + +private: + NodeMetaInfo(); + NodeMetaInfo(const MetaInfo &metaInfo); + + void setInvalid(); + void setTypeName(const QString &typeName); + void addProperty(const PropertyMetaInfo &property); + void setIsContainer(bool isContainer); + void setIsVisibleToItemLibrary(bool isVisibleToItemLibrary); + void setIcon(const QIcon &icon); + void setCategory(const QString &category); + void setQmlFile(const QString &filePath); + void setDefaultProperty(const QString &defaultProperty); + void setMajorVersion(int version); + void setMinorVersion(int version); + + bool hasLocalProperty(const QString &propertyName, bool resolveDotSyntax = false) const; + QHash<QString,PropertyMetaInfo> dotProperties() const; + +private: + QExplicitlySharedDataPointer<Internal::NodeMetaInfoData> m_data; +}; + +CORESHARED_EXPORT uint qHash(const NodeMetaInfo &nodeMetaInfo); +CORESHARED_EXPORT bool operator ==(const NodeMetaInfo &firstNodeInfo, + const NodeMetaInfo &secondNodeInfo); + +//QDataStream& operator<<(QDataStream& stream, const NodeMetaInfo& nodeMetaInfo); +//QDataStream& operator>>(QDataStream& stream, NodeMetaInfo& nodeMetaInfo); +} + +#endif // NODEMETAINFO_H diff --git a/src/plugins/qmldesigner/core/include/nodeproperty.h b/src/plugins/qmldesigner/core/include/nodeproperty.h new file mode 100644 index 00000000000..61ac97143b1 --- /dev/null +++ b/src/plugins/qmldesigner/core/include/nodeproperty.h @@ -0,0 +1,62 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef NODEPROPERTY_H +#define NODEPROPERTY_H + +#include "corelib_global.h" +#include "nodeabstractproperty.h" + +namespace QmlDesigner { + +namespace Internal { + class ModelPrivate; +} + +class CORESHARED_EXPORT NodeProperty : public NodeAbstractProperty +{ + friend class QmlDesigner::ModelNode; + friend class QmlDesigner::Internal::ModelPrivate; + friend class QmlDesigner::AbstractProperty; + +public: + void setModelNode(const ModelNode &modelNode); + ModelNode modelNode() const; + QmlObjectNode fxObjectNode() const; + + void reparentHere(const ModelNode &modelNode); + + NodeProperty(); +protected: + NodeProperty(const QString &propertyName, const Internal::InternalNodePointer &internalNode, Model* model, AbstractView *view); +}; + +} // namespace QmlDesigner + +#endif // NODEPROPERTY_H diff --git a/src/plugins/qmldesigner/core/include/notimplementedexception.h b/src/plugins/qmldesigner/core/include/notimplementedexception.h new file mode 100644 index 00000000000..1711c2864d0 --- /dev/null +++ b/src/plugins/qmldesigner/core/include/notimplementedexception.h @@ -0,0 +1,50 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef NOTIMPLEMENTEDEXCEPTION_H +#define NOTIMPLEMENTEDEXCEPTION_H + +#include "exception.h" + +namespace QmlDesigner { + +class CORESHARED_EXPORT NotImplementedException : public Exception +{ +public: + NotImplementedException(int line, + const QString &function, + const QString &file); + + QString type() const; + +}; + +} + +#endif // NOTIMPLEMENTEDEXCEPTION_H diff --git a/src/plugins/qmldesigner/core/include/objectpropertybinding.h b/src/plugins/qmldesigner/core/include/objectpropertybinding.h new file mode 100644 index 00000000000..cfb613a4d21 --- /dev/null +++ b/src/plugins/qmldesigner/core/include/objectpropertybinding.h @@ -0,0 +1,55 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef OBJECTPROPERTYBINDING_H +#define OBJECTPROPERTYBINDING_H + +#include <modelnode.h> + +namespace QmlDesigner { + +class CORESHARED_EXPORT ObjectPropertyBinding +{ +public: + ObjectPropertyBinding(); + ObjectPropertyBinding(const ModelNode &node); + + + ModelNode modelNode() const; + bool isValid() const; + +private: + ModelNode m_node; +}; + +} // namespace QmlDesigner + +Q_DECLARE_METATYPE(QmlDesigner::ObjectPropertyBinding); + +#endif // OBJECTPROPERTYBINDING_H diff --git a/src/plugins/qmldesigner/core/include/plaintexteditmodifier.h b/src/plugins/qmldesigner/core/include/plaintexteditmodifier.h new file mode 100644 index 00000000000..29a33b57250 --- /dev/null +++ b/src/plugins/qmldesigner/core/include/plaintexteditmodifier.h @@ -0,0 +1,90 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef PLAINTEXTEDITMODIFIER_H +#define PLAINTEXTEDITMODIFIER_H + +#include "corelib_global.h" +#include "textmodifier.h" + +QT_BEGIN_NAMESPACE +class QIODevice; +class QPlainTextEdit; +QT_END_NAMESPACE + +namespace Utils { + class ChangeSet; +} + +namespace QmlDesigner { + +class CORESHARED_EXPORT PlainTextEditModifier: public TextModifier +{ + Q_OBJECT + +private: + PlainTextEditModifier(const PlainTextEditModifier &); + PlainTextEditModifier &operator=(const PlainTextEditModifier &); + +public: + PlainTextEditModifier(QPlainTextEdit *textEdit); + ~PlainTextEditModifier(); + + virtual void save(QIODevice *device); + + virtual QTextDocument *textDocument() const; + virtual QString text() const; + virtual QTextCursor textCursor() const; + + virtual void replace(int offset, int length, const QString &replacement); + virtual void move(const MoveInfo &moveInfo); + virtual void startGroup(); + virtual void flushGroup(); + virtual void commitGroup(); + + virtual void deactivateChangeSignals(); + virtual void reactivateChangeSignals(); + +private slots: + void textEditChanged(); + +private: + void runRewriting(Utils::ChangeSet *writer); + +private: + Utils::ChangeSet *m_changeSet; + QPlainTextEdit *m_textEdit; + bool m_changeSignalsEnabled; + bool m_pendingChangeSignal; + bool m_ongoingTextChange; +}; + +} + +#endif // PLAINTEXTEDITMODIFIER_H diff --git a/src/plugins/qmldesigner/core/include/propertybinding.h b/src/plugins/qmldesigner/core/include/propertybinding.h new file mode 100644 index 00000000000..8b826db6e37 --- /dev/null +++ b/src/plugins/qmldesigner/core/include/propertybinding.h @@ -0,0 +1,62 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef PROPERTYBINDING_H +#define PROPERTYBINDING_H + +#include <QtCore/QMetaType> +#include <QtCore/QSharedPointer> +#include <QtCore/QString> + +#include "corelib_global.h" +#include "modelnode.h" + +namespace QmlDesigner { + +class CORESHARED_EXPORT PropertyBinding +{ +public: + PropertyBinding(); + PropertyBinding(const QString &value); + PropertyBinding(const PropertyBinding &other); + + PropertyBinding &operator=(const PropertyBinding &other); + + bool isValid() const; + QString value() const; + +private: + QString m_value; +}; + +} // namespace QmlDesigner + +Q_DECLARE_METATYPE(QmlDesigner::PropertyBinding); + +#endif // PROPERTYBINDING_H diff --git a/src/plugins/qmldesigner/core/include/propertycontainer.h b/src/plugins/qmldesigner/core/include/propertycontainer.h new file mode 100644 index 00000000000..9fee3a5f0dc --- /dev/null +++ b/src/plugins/qmldesigner/core/include/propertycontainer.h @@ -0,0 +1,82 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef PROPERTYCONTAINER_H +#define PROPERTYCONTAINER_H + +#include "corelib_global.h" + +#include <QString> +#include <QExplicitlySharedDataPointer> +#include <QDataStream> +#include <QVariant> + +#include <enumeratormetainfo.h> + +namespace QmlDesigner { + +class PropertyContainer; + +CORESHARED_EXPORT QDataStream &operator<<(QDataStream &stream, const PropertyContainer &propertyContainer); +CORESHARED_EXPORT QDataStream &operator>>(QDataStream &stream, PropertyContainer &propertyContainer); + +class CORESHARED_EXPORT PropertyContainer +{ + friend CORESHARED_EXPORT QDataStream &QmlDesigner::operator<<(QDataStream &stream, const PropertyContainer &propertyContainer); + friend CORESHARED_EXPORT QDataStream &QmlDesigner::operator>>(QDataStream &stream, PropertyContainer &propertyContainer); + +public: + PropertyContainer(); + PropertyContainer(const QString &name, const QString &type, const QVariant &value); + + bool isValid() const; + + QString name() const; + QVariant value() const; + QString type() const; + + + void setValue(const QVariant &value); + void set(const QString &name, const QString &type, const QVariant &value); + + +private: + QString m_name; + QString m_type; + mutable QVariant m_value; +}; + +CORESHARED_EXPORT QDataStream &operator<<(QDataStream &stream, const QList<PropertyContainer> &propertyContainerList); +CORESHARED_EXPORT QDataStream &operator>>(QDataStream &stream, QList<PropertyContainer> &propertyContainerList); + +} //namespace QmlDesigner + + + +#endif //PROPERTYCONTAINER_H diff --git a/src/plugins/qmldesigner/core/include/propertymetainfo.h b/src/plugins/qmldesigner/core/include/propertymetainfo.h new file mode 100644 index 00000000000..7157975ed68 --- /dev/null +++ b/src/plugins/qmldesigner/core/include/propertymetainfo.h @@ -0,0 +1,123 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef PROPERTYMETAINFO_H +#define PROPERTYMETAINFO_H + +#include "corelib_global.h" + +#include <QString> +#include <QExplicitlySharedDataPointer> +#include <QVariant> + +#include <enumeratormetainfo.h> + +namespace QmlDesigner { + +class AbstractProperty; +class MetaInfo; +class NodeMetaInfo; + +namespace Internal { + +class MetaInfoPrivate; +class MetaInfoParser; +class PropertyMetaInfoData; +class ObjectNodeInstance; +class SubComponentManagerPrivate; + +} + +class CORESHARED_EXPORT PropertyMetaInfo +{ + friend class QmlDesigner::Internal::MetaInfoPrivate; + friend class QmlDesigner::Internal::MetaInfoParser; + friend class QmlDesigner::Internal::ObjectNodeInstance; + friend class QmlDesigner::Internal::SubComponentManagerPrivate; + friend class QmlDesigner::MetaInfo; + friend class QmlDesigner::AbstractProperty; + friend class QmlDesigner::NodeMetaInfo; +public: + PropertyMetaInfo(); + ~PropertyMetaInfo(); + + PropertyMetaInfo(const PropertyMetaInfo &other); + PropertyMetaInfo& operator=(const PropertyMetaInfo &other); + + bool isValid() const; + + QString name() const; + QString type() const; + + QVariant::Type variantTypeId() const; + + bool isReadable() const; + bool isWriteable() const; + bool isResettable() const; + bool isValueType() const; + bool isListProperty() const; + + bool isEnumType() const; + bool isFlagType() const; + + QVariant defaultValue(const NodeMetaInfo &nodeMetaInfo) const; + bool isVisibleToPropertyEditor() const; + + const EnumeratorMetaInfo enumerator() const; + + QVariant castedValue(const QVariant &variant) const; + +private: + void setName(const QString &name); + void setType(const QString &type); + void setValid(bool isValid); + + void setReadable(bool isReadable); + void setWritable(bool isWritable); + void setResettable(bool isRessetable); + + void setEnumType(bool isEnumType); + void setFlagType(bool isFlagType); + + void setDefaultValue(const NodeMetaInfo &nodeMetaInfo, const QVariant &value); + void setIsVisibleToPropertyEditor(bool isVisible); + + void setEnumerator(const EnumeratorMetaInfo &info); + + bool hasDotSubProperties() const; + + +private: + QExplicitlySharedDataPointer<Internal::PropertyMetaInfoData> m_data; +}; + +} + + +#endif // PROPERTYMETAINFO_H diff --git a/src/plugins/qmldesigner/core/include/propertynode.h b/src/plugins/qmldesigner/core/include/propertynode.h new file mode 100644 index 00000000000..419b97cfa08 --- /dev/null +++ b/src/plugins/qmldesigner/core/include/propertynode.h @@ -0,0 +1,48 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef PROPERTYNODE_H +#define PROPERTYNODE_H + +#include "corelib_global.h" + +namespace QmlDesigner { + +class CORESHARED_EXPORT PropertyNode // : public BaseModelNode +{ +public: + PropertyNode(); + +// static int variantUserType() { return qMetaTypeId<InternalNode::Pointer>(); } +// static QVariant toVariant(const InternalNode::Pointer &internalNodePointer) { return QVariant::fromValue(internalNodePointer); } +}; + +} // namespace QmlDesigner + +#endif // PROPERTYNODE_H diff --git a/src/plugins/qmldesigner/core/include/qmlanchors.h b/src/plugins/qmldesigner/core/include/qmlanchors.h new file mode 100644 index 00000000000..5bbb759bd8c --- /dev/null +++ b/src/plugins/qmldesigner/core/include/qmlanchors.h @@ -0,0 +1,113 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef QmlAnchors_H +#define QmlAnchors_H + +#include <corelib_global.h> +#include <qmlitemnode.h> + + +namespace QmlDesigner { + +class CORESHARED_EXPORT AnchorLine +{ +public: + enum Type { + Invalid = 0x0, + NoAnchor = Invalid, + Left = 0x01, + Right = 0x02, + Top = 0x04, + Bottom = 0x08, + HorizontalCenter = 0x10, + VerticalCenter = 0x20, + Baseline = 0x40, + + Fill = Left | Right | Top | Bottom, + Center = VerticalCenter | HorizontalCenter, + HorizontalMask = Left | Right | HorizontalCenter, + VerticalMask = Top | Bottom | VerticalCenter | Baseline, + AllMask = VerticalMask | HorizontalMask + }; + + AnchorLine() : m_qmlItemNode(QmlItemNode()), m_type(Invalid) {} + AnchorLine(const QmlItemNode &fxItemNode, Type type) : m_qmlItemNode(fxItemNode), m_type(type) {} + Type type() const { return m_type; } + bool isValid() const { return m_type != Invalid && m_qmlItemNode.isValid(); } + + static bool isHorizontalAnchorLine(Type anchorline); + static bool isVerticalAnchorLine(Type anchorline); + + QmlItemNode qmlItemNode() const; + +private: + QmlItemNode m_qmlItemNode; + Type m_type; +}; + + +class CORESHARED_EXPORT QmlAnchors +{ +public: + QmlAnchors(const QmlItemNode &fxItemNode); + + bool isValid() const; + + void setAnchor(AnchorLine::Type sourceAnchorLineType, + const QmlItemNode &targetModelNode, + AnchorLine::Type targetAnchorLineType); + bool canAnchor(const QmlItemNode &targetModelNode) const; + AnchorLine::Type possibleAnchorLines(AnchorLine::Type sourceAnchorLineType, + const QmlItemNode &targetModelNode) const; + AnchorLine instanceAnchor(AnchorLine::Type sourceAnchorLineType) const; + + void removeAnchor(AnchorLine::Type sourceAnchorLineType); + void removeAnchors(); + bool instanceHasAnchor(AnchorLine::Type sourceAnchorLineType) const; + bool instanceHasAnchors() const; + void setMargin(AnchorLine::Type sourceAnchorLineType, double margin) const; + bool instanceHasMargin(AnchorLine::Type sourceAnchorLineType) const; + double instanceMargin(AnchorLine::Type sourceAnchorLineType) const; + void removeMargin(AnchorLine::Type sourceAnchorLineType); + void removeMargins(); + + void fill(); + void centerIn(); + +protected: + QmlItemNode qmlItemNode() const; + +private: + QmlItemNode m_qmlItemNode; +}; + +} //QmlDesigner + +#endif // QmlAnchors_H diff --git a/src/plugins/qmldesigner/core/include/qmlchangeset.h b/src/plugins/qmldesigner/core/include/qmlchangeset.h new file mode 100644 index 00000000000..643cf57ecc6 --- /dev/null +++ b/src/plugins/qmldesigner/core/include/qmlchangeset.h @@ -0,0 +1,61 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef QMLCHANGESET_H +#define QMLCHANGESET_H + +#include <corelib_global.h> +#include <modelnode.h> +#include "qmlmodelnodefacade.h" + +namespace QmlDesigner { + +class CORESHARED_EXPORT QmlModelStateOperation : public QmlModelNodeFacade +{ +public: + QmlModelStateOperation() : QmlModelNodeFacade() {} + QmlModelStateOperation(const ModelNode &modelNode) : QmlModelNodeFacade(modelNode) {} + ModelNode target() const; + void setTarget(const ModelNode &target); + bool isValid() const; +}; + + +class CORESHARED_EXPORT QmlPropertyChanges : public QmlModelStateOperation +{ +public: + QmlPropertyChanges() : QmlModelStateOperation() {} + QmlPropertyChanges(const ModelNode &modelNode) : QmlModelStateOperation(modelNode) {} + bool isValid() const; + void removeProperty(const QString &name); +}; + +} //QmlDesigner + +#endif // QMLCHANGESET_H diff --git a/src/plugins/qmldesigner/core/include/qmlitemnode.h b/src/plugins/qmldesigner/core/include/qmlitemnode.h new file mode 100644 index 00000000000..643dea042e2 --- /dev/null +++ b/src/plugins/qmldesigner/core/include/qmlitemnode.h @@ -0,0 +1,120 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef QmlItemNode_H +#define QmlItemNode_H + +#include <corelib_global.h> +#include <modelnode.h> +#include "qmlobjectnode.h" +#include "qmlstate.h" + +#include <QStringList> +#include <QRectF> +#include <QTransform> + +namespace QmlDesigner { + +class QmlModelStateGroup; +class QmlAnchors; + +class CORESHARED_EXPORT QmlItemNode : public QmlObjectNode +{ + friend class CORESHARED_EXPORT QmlAnchors; +public: + QmlItemNode() : QmlObjectNode() {} + QmlItemNode(const ModelNode &modelNode) : QmlObjectNode(modelNode) {} + bool isValid() const; + bool isRootNode() const; + + QmlModelStateGroup states() const; + QList<QmlItemNode> children() const; + QList<QmlObjectNode> resources() const; + QmlAnchors anchors() const; + + bool hasChildren() const; + bool hasResources() const; + bool instanceHasAnchors() const; + bool hasShowContent() const; + + bool canReparent() const; + bool instanceIsAnchoredBy() const; + + QRectF instanceBoundingRect() const; + QTransform instanceTransform() const; + QTransform instanceSceneTransform() const; + QPointF instancePosition() const; + QSizeF instanceSize() const; + + void paintInstance(QPainter *painter) const; + + void setSize(const QSizeF &size); + void setPosition(const QPointF &position); + + void selectNode(); + void deselectNode(); + bool isSelected() const; + + QString simplfiedTypeName() const; + + const QList<QmlItemNode> allDirectSubModelNodes() const; + const QList<QmlItemNode> allSubModelNodes() const; + bool hasAnySubModelNodes() const; +}; + +CORESHARED_EXPORT uint qHash(const QmlItemNode &node); + +class CORESHARED_EXPORT QmlModelStateGroup +{ + friend class QmlItemNode; +public: + + QmlModelStateGroup() : m_modelNode(ModelNode()) {} + + ModelNode modelNode() const { return m_modelNode; } + QStringList names() const; + QList<QmlModelState> allStates() const; + QmlModelState state(const QString &name) const; + QmlModelState addState(const QString &name); + void removeState(const QString &name); + +protected: + QmlModelStateGroup(const ModelNode &modelNode) : m_modelNode(modelNode) {} + +private: + ModelNode m_modelNode; +}; + +CORESHARED_EXPORT QList<ModelNode> toModelNodeList(const QList<QmlItemNode> &fxItemNodeList); +CORESHARED_EXPORT QList<QmlItemNode> toQmlItemNodeList(const QList<ModelNode> &modelNodeList); + +} //QmlDesigner + + +#endif // QmlItemNode_H diff --git a/src/plugins/qmldesigner/core/include/qmlmodelnodefacade.h b/src/plugins/qmldesigner/core/include/qmlmodelnodefacade.h new file mode 100644 index 00000000000..0f5393bd307 --- /dev/null +++ b/src/plugins/qmldesigner/core/include/qmlmodelnodefacade.h @@ -0,0 +1,61 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef FXMODELNODEFACADE_H +#define FXMODELNODEFACADE_H + +#include <corelib_global.h> +#include <modelnode.h> + +namespace QmlDesigner { + +class QmlModelView; + +class CORESHARED_EXPORT QmlModelNodeFacade +{ +public: + operator ModelNode() const { return m_modelNode; } + ModelNode modelNode() const { return m_modelNode; } + virtual bool isValid() const; + virtual ~QmlModelNodeFacade(); + QmlModelNodeFacade(); + + bool isRootNode() const; + +protected: + QmlModelNodeFacade(const ModelNode &modelNode); + QmlModelView* qmlModelView() const; + +private: + ModelNode m_modelNode; +}; + +} //QmlDesigner + +#endif // FXMODELNODEFACADE_H diff --git a/src/plugins/qmldesigner/core/include/qmlmodelview.h b/src/plugins/qmldesigner/core/include/qmlmodelview.h new file mode 100644 index 00000000000..4aacd47eefa --- /dev/null +++ b/src/plugins/qmldesigner/core/include/qmlmodelview.h @@ -0,0 +1,114 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef QMLMODELVIEW_H +#define QMLMODELVIEW_H + +#include <corelib_global.h> +#include <forwardview.h> +#include <abstractview.h> +#include "qmlitemnode.h" +#include "qmlstate.h" +#include "nodeinstanceview.h" + + +namespace QmlDesigner { + +class ItemLibraryInfo; + +class CORESHARED_EXPORT QmlModelView : public ForwardView<NodeInstanceView> +{ + Q_OBJECT + friend CORESHARED_EXPORT class QmlObjectNode; + friend CORESHARED_EXPORT class QmlModelNodeFacade; + +public: + QmlModelView(QObject *parent) ; + + void setCurrentState(const QmlModelState &state); + QmlModelState currentState() const; + + QmlModelState baseState() const; + + QmlObjectNode createQmlObjectNode(const QString &typeString, + int majorVersion, + int minorVersion, + const PropertyListType &propertyList = PropertyListType()); + + QmlItemNode createQmlItemNode(const QString &typeString, + int majorVersion, + int minorVersion, + const PropertyListType &propertyList = PropertyListType()); + + QmlItemNode createQmlItemNode(const ItemLibraryInfo &ItemLibraryRepresentation, const QPointF &position, QmlItemNode parentNode); + QmlItemNode createQmlItemNodeFromImage(const QString &imageName, const QPointF &position, QmlItemNode parentNode); + + QmlObjectNode rootQmlObjectNode() const; + QmlItemNode rootQmlItemNode() const; + + + void setSelectedQmlObjectNodes(const QList<QmlObjectNode> &selectedNodeList); + void setSelectedQmlItemNodes(const QList<QmlItemNode> &selectedNodeList); + void selectQmlObjectNode(const QmlObjectNode &node); + void deselectQmlObjectNode(const QmlObjectNode &node); + + QList<QmlObjectNode> selectedQmlObjectNodes() const; + QList<QmlItemNode> selectedQmlItemNodes() const; + + QmlObjectNode fxObjectNodeForId(const QString &id); + + void customNotification(const AbstractView *view, const QString &identifier, const QList<ModelNode> &nodeList, const QList<QVariant> &data); + + void modelAttached(Model *model); + void modelAboutToBeDetached(Model *model); + +protected: + NodeInstance instanceForModelNode(const ModelNode &modelNode); + bool hasInstanceForModelNode(const ModelNode &modelNode); + NodeInstanceView *nodeInstanceView() const; + virtual void transformChanged(const QmlObjectNode &qmlObjectNode) ; + virtual void parentChanged(const QmlObjectNode &qmlObjectNode); + virtual void otherPropertyChanged(const QmlObjectNode &qmlObjectNode); + virtual void stateChanged(const QmlModelState &newQmlModelState, const QmlModelState &oldQmlModelState); + virtual void updateItem(const QmlObjectNode &qmlObjectNode); + + +private slots: + void notifyTransformChanged(const NodeInstance &nodeInstance); + void notifyParentChanged(const NodeInstance &nodeInstance); + void notifyOtherPropertyChanged(const NodeInstance &nodeInstance); + void notifyUpdateItem(const NodeInstance &nodeInstance); + +private: + QmlModelState m_state; +}; + +} //QmlDesigner + +#endif // QMLMODELVIEW_H diff --git a/src/plugins/qmldesigner/core/include/qmlobjectnode.h b/src/plugins/qmldesigner/core/include/qmlobjectnode.h new file mode 100644 index 00000000000..7dc37f322af --- /dev/null +++ b/src/plugins/qmldesigner/core/include/qmlobjectnode.h @@ -0,0 +1,103 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef FXOBJECTNODE_H +#define FXOBJECTNODE_H + +#include <corelib_global.h> +#include "qmlmodelnodefacade.h" +#include "qmlstate.h" +#include "qmlchangeset.h" + +namespace QmlDesigner { + +class QmlItemNode; +class QmlPropertyChanges; + +class CORESHARED_EXPORT QmlObjectNode : public QmlModelNodeFacade +{ +public: + QmlObjectNode() : QmlModelNodeFacade() {} + QmlObjectNode(const ModelNode &modelNode) : QmlModelNodeFacade(modelNode) {} + + bool hasNodeParent() const; + bool hasInstanceParent() const; + void setParentProperty(const NodeAbstractProperty &parentProeprty); + QmlObjectNode instanceParent() const; + + void setId(const QString &id); + QString id() const; + QString validId(); + + QmlModelState currentState() const; + void setVariantProperty(const QString &name, const QVariant &value); + void setBindingProperty(const QString &name, const QString &expression); + NodeAbstractProperty nodeAbstractProperty(const QString &name) const; + NodeProperty nodeProperty(const QString &name) const; + NodeListProperty nodeListProperty(const QString &name) const; + + QVariant instanceValue(const QString &name) const; + + bool hasProperty(const QString &name) const; + bool propertyAffectedByCurrentState(const QString &name) const; + QVariant modelValue(const QString &name) const; + bool isInBaseState() const; + QmlPropertyChanges propertyChangeForCurrentState() const; + + virtual bool canReparent() const; + + bool isRootModelNode() const; + + void destroy(); + + QList<QmlModelState> allAffectingStates() const; + QList<QmlModelStateOperation> allAffectingStatesOperations() const; + + void removeVariantProperty(const QString &name); + + void setParent(QmlObjectNode newParent); + + QmlItemNode toQmlItemNode() const; + + bool isAncestorOf(const QmlObjectNode &objectNode) const; + +protected: + NodeInstance nodeInstance() const; + QmlObjectNode nodeForInstance(const NodeInstance &instance) const; + +protected: + QList<QmlModelState> allDefinedStates() const; +}; + +CORESHARED_EXPORT uint qHash(const QmlObjectNode &node); +CORESHARED_EXPORT QList<ModelNode> toModelNodeList(const QList<QmlObjectNode> &fxObjectNodeList); +CORESHARED_EXPORT QList<QmlObjectNode> toQmlObjectNodeList(const QList<ModelNode> &modelNodeList); +}// QmlDesigner + +#endif // FXOBJECTNODE_H diff --git a/src/plugins/qmldesigner/core/include/qmlstate.h b/src/plugins/qmldesigner/core/include/qmlstate.h new file mode 100644 index 00000000000..80e5bb58a7d --- /dev/null +++ b/src/plugins/qmldesigner/core/include/qmlstate.h @@ -0,0 +1,82 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef FXSTATE_H +#define FXSTATE_H + +#include <corelib_global.h> +#include "qmlmodelnodefacade.h" +#include "qmlchangeset.h" + +namespace QmlDesigner { + +class QmlModelView; +class QmlModelStateGroup; + +class CORESHARED_EXPORT QmlModelState : public QmlModelNodeFacade +{ + friend class CORESHARED_EXPORT QmlModelView; + +public: + QmlModelState(); + QmlModelState(const ModelNode &modelNode); + + QmlPropertyChanges propertyChanges(const ModelNode &node); + QList<QmlModelStateOperation> stateOperations(const ModelNode &node) const; + QList<QmlPropertyChanges> propertyChanges() const; + QList<QmlModelStateOperation> stateOperations() const; + + bool hasPropertyChanges(const ModelNode &node) const; + + bool hasStateOperation(const ModelNode &node) const; + + void removePropertyChanges(const ModelNode &node); + + bool affectsModelNode(const ModelNode &node) const; + QString name() const; + void setName(const QString &name); + bool isValid() const; + void destroy(); + + bool isBaseState() const; + QmlModelState duplicate(const QString &name) const; + QmlModelStateGroup stateGroup() const; + +protected: + void addChangeSetIfNotExists(const ModelNode &node); + static QmlModelState createBaseState(const QmlModelView *view); + +private: + bool m_isBaseState; +}; + +} //QmlDesigner + + +#endif // FXSTATE_H diff --git a/src/plugins/qmldesigner/core/include/removebasestateexception.h b/src/plugins/qmldesigner/core/include/removebasestateexception.h new file mode 100644 index 00000000000..38446aa88d6 --- /dev/null +++ b/src/plugins/qmldesigner/core/include/removebasestateexception.h @@ -0,0 +1,49 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef REMOVEBASESTATEEXCEPTION_H +#define REMOVEBASESTATEEXCEPTION_H + +#include <exception.h> + +namespace QmlDesigner { + +class CORESHARED_EXPORT RemoveBaseStateException : public Exception +{ +public: + RemoveBaseStateException(int line, + const QString &function, + const QString &file); + + QString type() const; + +}; +} // namespace QmlDesigner + +#endif // REMOVEBASESTATEEXCEPTION_H diff --git a/src/plugins/qmldesigner/core/include/replaceallobjectdefinitionsvisitor.h b/src/plugins/qmldesigner/core/include/replaceallobjectdefinitionsvisitor.h new file mode 100644 index 00000000000..2da2bb73ada --- /dev/null +++ b/src/plugins/qmldesigner/core/include/replaceallobjectdefinitionsvisitor.h @@ -0,0 +1,59 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef REPLACEALLOBJECTDEFINITIONSVISITOR_H +#define REPLACEALLOBJECTDEFINITIONSVISITOR_H + +#include "qmlrewriter.h" + +namespace QmlDesigner { +namespace Internal { + +class ReplaceAllObjectDefinitionsVisitor: public QMLRewriter +{ +public: + ReplaceAllObjectDefinitionsVisitor(TextModifier &textModifier, + const TextLocation &objectLocation, + const QString &newContent); + +protected: + virtual bool visit(QmlJS::AST::UiObjectDefinition *ast); + virtual bool visit(QmlJS::AST::UiObjectBinding *ast); + +private: + void replaceMembers(QmlJS::AST::UiObjectInitializer *initializer); + +private: + QString m_newContent; +}; + +} // namespace Internal +} // namespace QmlDesigner + +#endif // REPLACEALLOBJECTDEFINITIONSVISITOR_H diff --git a/src/plugins/qmldesigner/core/include/rewriterview.h b/src/plugins/qmldesigner/core/include/rewriterview.h new file mode 100644 index 00000000000..4806bf41b97 --- /dev/null +++ b/src/plugins/qmldesigner/core/include/rewriterview.h @@ -0,0 +1,177 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef REWRITERVIEW_H +#define REWRITERVIEW_H + +#include "corelib_global.h" +#include "abstractview.h" +#include "exception.h" +#include <modelnodepositionstorage.h> +#include <QtCore/QHash> +#include <QtCore/QMap> +#include <QPointer> +#include <QWeakPointer> +#include <QtCore/QHash> + +#include <modelnode.h> +#include <QScopedPointer> + + +namespace QmlDesigner { + +class CORESHARED_EXPORT TextModifier; + +namespace Internal { + +class TextToModelMerger; +class ModelToTextMerger; +class ModelNodePositionStorage; + +} //Internal + + +class CORESHARED_EXPORT RewriterView : public AbstractView +{ + Q_OBJECT + +public: + enum DifferenceHandling { + Validate, + Amend + }; + + class Error { + public: + enum Type { + NoError = 0, + InternalError = 1, + ParseError = 2 + }; + + public: + Error(); + Error(const QmlError &qmlError); + Error(const Exception &exception); + + Type type() const + { return m_type; } + + int line() const + { return m_line; } + + int column() const + { return m_column; } + + QString description() const + { return m_description; } + + QUrl url() const + { return m_url; } + + QString toString() const; + + private: + Type m_type; + int m_line; + int m_column; + QString m_description; + QUrl m_url; + }; + +public: + RewriterView(DifferenceHandling differenceHandling, QObject *parent); + ~RewriterView(); + + void modelAttached(Model *model); + void modelAboutToBeDetached(Model *model); + void nodeCreated(const ModelNode &createdNode); + void nodeAboutToBeRemoved(const ModelNode &removedNode); + void nodeRemoved(const ModelNode &removedNode, const NodeAbstractProperty &parentProperty, PropertyChangeFlags propertyChange); + void propertiesAdded(const ModelNode &node, const QList<AbstractProperty>& propertyList); + void propertiesAboutToBeRemoved(const QList<AbstractProperty>& propertyList); + void propertiesRemoved(const QList<AbstractProperty>& propertyList); + void variantPropertiesChanged(const QList<VariantProperty>& propertyList, PropertyChangeFlags propertyChange); + void bindingPropertiesChanged(const QList<BindingProperty>& propertyList, PropertyChangeFlags propertyChange); + void nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent, const NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags propertyChange); + void nodeIdChanged(const ModelNode& node, const QString& newId, const QString& oldId); + void nodeSlidedToIndex(const NodeListProperty &listProperty, int newIndex, int /*oldIndex*/); + void nodeTypeChanged(const ModelNode &node,const QString &type, int majorVersion, int minorVersion); + void customNotification(const AbstractView *view, const QString &identifier, const QList<ModelNode> &nodeList, const QList<QVariant> &data); + + void fileUrlChanged(const QUrl &oldUrl, const QUrl &newUrl); + + void selectedNodesChanged(const QList<ModelNode> &selectedNodeList, const QList<ModelNode> &lastSelectedNodeList); + + TextModifier *textModifier() const; + void setTextModifier(TextModifier *textModifier); + + Internal::ModelNodePositionStorage *positionStorage() const + { return m_positionStorage; } + + QList<Error> errors() const; + void clearErrors(); + void addErrors(const QList<Error> &errors); + void addError(const Error &error); + + QMap<ModelNode, QString> extractText(const QList<ModelNode> &nodes) const; + int nodeOffset(const ModelNode &node) const; + int nodeLength(const ModelNode &node) const; + int firstDefinitionInsideOffset(const ModelNode &node) const; + int firstDefinitionInsideLength(const ModelNode &node) const; + +signals: + void errorsChanged(const QList<RewriterView::Error> &errors); + +public slots: + void qmlTextChanged(); + +protected: // functions + Internal::ModelToTextMerger *modelToTextMerger() const; + Internal::TextToModelMerger *textToModelMerger() const; + bool isModificationGroupActive() const; + void setModificationGroupActive(bool active); + void applyModificationGroupChanges(); + void setupComponent(const ModelNode &node); + +private: //variables + DifferenceHandling m_differenceHandling; + bool m_modificationGroupActive; + Internal::ModelNodePositionStorage *m_positionStorage; + QScopedPointer<Internal::ModelToTextMerger> m_modelToTextMerger; + QScopedPointer<Internal::TextToModelMerger> m_textToModelMerger; + TextModifier *m_textModifier; + QList<Error> m_errors; + int transactionLevel; + RewriterTransaction m_removeDefaultPropertyTransaction; +}; + +} //QmlDesigner + +#endif // REWRITERVIEW_H diff --git a/src/plugins/qmldesigner/core/include/stylemanager.h b/src/plugins/qmldesigner/core/include/stylemanager.h new file mode 100644 index 00000000000..a68c8d4bd27 --- /dev/null +++ b/src/plugins/qmldesigner/core/include/stylemanager.h @@ -0,0 +1,68 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef STYLEMANAGER_H +#define STYLEMANAGER_H + +#include "corelib_global.h" +#include <nodeinstanceview.h> +#include <QList> +#include <QStringList> + +namespace QmlDesigner { + +namespace Internal { +class StyleManagerGuard; +} + +class CORESHARED_EXPORT StyleManager { +public: + static QStringList styles(); + static void setStyle(const QString &styleName); + static QString applicationStyle(); + +private: + StyleManager() {} + static StyleManager* instance(); + static StyleManager* m_instance; + QList<NodeInstanceView*> m_views; + static void addView(NodeInstanceView* view); + static void removeView(NodeInstanceView* view); + + friend class Internal::StyleManagerGuard; + friend class NodeInstanceView; +}; + + +} //namespace QmlDesigner + +#endif //STYLEMANAGER_H + + + diff --git a/src/plugins/qmldesigner/core/include/subcomponentmanager.h b/src/plugins/qmldesigner/core/include/subcomponentmanager.h new file mode 100644 index 00000000000..517e79072cd --- /dev/null +++ b/src/plugins/qmldesigner/core/include/subcomponentmanager.h @@ -0,0 +1,68 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef SUBCOMPONENTMANAGER_H +#define SUBCOMPONENTMANAGER_H + +#include "corelib_global.h" + +#include <QObject> +#include <QString> +#include <QUrl> +#include <QmlDomImport> + +#include <metainfo.h> + +namespace QmlDesigner { + +class CORESHARED_EXPORT SubComponentManager : public QObject +{ + Q_OBJECT +public: + SubComponentManager(MetaInfo metaInfo, QObject *parent = 0); + ~SubComponentManager(); + + void update(const QUrl &fileUrl, const QByteArray &data); + void update(const QUrl &fileUrl, const QList<QmlDomImport> &imports); + + QStringList qmlFiles() const; + QStringList directories() const; + +signals: + void qmlFilesChanged(const QStringList &oldPathList, const QStringList &newPathList); + +private: + friend class Internal::SubComponentManagerPrivate; + class Internal::SubComponentManagerPrivate *m_d; +}; + +} // namespace QmlDesigner + + +#endif // SUBCOMPONENTMANAGER_H diff --git a/src/plugins/qmldesigner/core/include/textmodifier.h b/src/plugins/qmldesigner/core/include/textmodifier.h new file mode 100644 index 00000000000..79092e38368 --- /dev/null +++ b/src/plugins/qmldesigner/core/include/textmodifier.h @@ -0,0 +1,91 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef TEXTMODIFIER_H +#define TEXTMODIFIER_H + +#include <QtCore/QByteArray> +#include <QtCore/QObject> + +#include <QTextCursor> +#include <QTextDocument> + +#include "corelib_global.h" + +namespace QmlDesigner { + +class CORESHARED_EXPORT TextModifier: public QObject +{ + Q_OBJECT + +private: + TextModifier(const TextModifier &); + TextModifier &operator=(const TextModifier &); + +public: + struct MoveInfo { + int objectStart; + int objectEnd; + int leadingCharsToRemove; + int trailingCharsToRemove; + + int destination; + QString prefixToInsert; + QString suffixToInsert; + + MoveInfo(): objectStart(-1), objectEnd(-1), leadingCharsToRemove(0), trailingCharsToRemove(0), destination(-1) {} + }; + +public: + TextModifier() {} + virtual ~TextModifier() = 0; + + virtual void replace(int offset, int length, const QString& replacement) = 0; + virtual void move(const MoveInfo &moveInfo) = 0; + virtual void startGroup() = 0; + virtual void flushGroup() = 0; + virtual void commitGroup() = 0; + + virtual QTextDocument *textDocument() const = 0; + virtual QString text() const = 0; + virtual QTextCursor textCursor() const = 0; + + virtual void deactivateChangeSignals() = 0; + virtual void reactivateChangeSignals() = 0; + +signals: + void textChanged(); + + void replaced(int offset, int oldLength, int newLength); + void moved(const TextModifier::MoveInfo &moveInfo); +}; + +} + +#endif // TEXTMODIFIER_H diff --git a/src/plugins/qmldesigner/core/include/variantproperty.h b/src/plugins/qmldesigner/core/include/variantproperty.h new file mode 100644 index 00000000000..76a93c5662c --- /dev/null +++ b/src/plugins/qmldesigner/core/include/variantproperty.h @@ -0,0 +1,70 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef VARIANTROPERTY_H +#define VARIANTROPERTY_H + +#include "corelib_global.h" +#include "abstractproperty.h" + + +namespace QmlDesigner { + +class AbstractView; + +namespace Internal { + class ModelPrivate; +} + +class CORESHARED_EXPORT VariantProperty : public AbstractProperty +{ + friend class QmlDesigner::ModelNode; + friend class QmlDesigner::Internal::ModelPrivate; + friend class QmlDesigner::AbstractProperty; + +public: + void setValue(const QVariant &value); + QVariant value() const; + VariantProperty& operator= (const QVariant &value); + + void setDynamicTypeNameAndValue(const QString &type, const QVariant &value); + VariantProperty& operator= (const QPair<QString, QVariant> &typeValuePair); + + VariantProperty(); + VariantProperty(const VariantProperty &property, AbstractView *view); +protected: + VariantProperty(const QString &propertyName, const Internal::InternalNodePointer &internalNode, Model* model, AbstractView *view); +}; + +CORESHARED_EXPORT QTextStream& operator<<(QTextStream &stream, const VariantProperty &property); +CORESHARED_EXPORT QDebug operator<<(QDebug debug, const VariantProperty &VariantProperty); + +} + +#endif //VARIANTPROPERTY_H diff --git a/src/plugins/qmldesigner/core/include/widgetplugin_helper.h b/src/plugins/qmldesigner/core/include/widgetplugin_helper.h new file mode 100644 index 00000000000..1d305b0adab --- /dev/null +++ b/src/plugins/qmldesigner/core/include/widgetplugin_helper.h @@ -0,0 +1,37 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef WIDGETPLUGIN_HELPER_H +#define WIDGETPLUGIN_HELPER_H + +#define QMLDESIGNER_REGISTER_WIDGET(TYPE) uiEngine->addObjectCreator(new QUiWidgetCreator<TYPE>(QLatin1String(#TYPE))); +#define QMLDESIGNER_REGISTER_GRAPHICSWIDGET(TYPE) uiEngine->addObjectCreator(new QUiGraphicsWidgetCreator<TYPE>(QLatin1String(#TYPE))); + +#endif //WIDGETPLUGIN_HELPER + diff --git a/src/plugins/qmldesigner/core/include/widgetqueryview.h b/src/plugins/qmldesigner/core/include/widgetqueryview.h new file mode 100644 index 00000000000..bba8d8cc27b --- /dev/null +++ b/src/plugins/qmldesigner/core/include/widgetqueryview.h @@ -0,0 +1,56 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef WIDGETQUERYVIEW_H +#define WIDGETQUERYVIEW_H + +#include "corelib_global.h" +#include "nodeinstanceview.h" +#include "propertycontainer.h" +#include <QtCore/QHash> +#include <QtCore/QList> +#include <QtCore/QString> +#include <QtCore/QVariant> +#include <QtGui/QImage> + +namespace QmlDesigner { + +class CORESHARED_EXPORT WidgetQueryView : public NodeInstanceView +{ + Q_OBJECT +public: + WidgetQueryView(QObject *parent); + + QImage paintObject(const NodeMetaInfo &metaInfo); + QImage paintObject(const NodeMetaInfo &metaInfo, const QList<PropertyContainer> &properties); +}; + +} //namespace QmlDesigner + +#endif // WIDGETQUERYVIEW_H diff --git a/src/plugins/qmldesigner/core/instances/componentnodeinstance.cpp b/src/plugins/qmldesigner/core/instances/componentnodeinstance.cpp new file mode 100644 index 00000000000..83301f5f4d7 --- /dev/null +++ b/src/plugins/qmldesigner/core/instances/componentnodeinstance.cpp @@ -0,0 +1,100 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "componentnodeinstance.h" + +#include <invalidnodeinstanceexception.h> +#include <QmlComponent> +#include <QmlContext> + +namespace QmlDesigner { +namespace Internal { + + +ComponentNodeInstance::ComponentNodeInstance(QmlComponent *component) + : ObjectNodeInstance(component) +{ +} + +QmlComponent *ComponentNodeInstance::component() const +{ + Q_ASSERT(qobject_cast<QmlComponent*>(object())); + return static_cast<QmlComponent*>(object()); +} + +ComponentNodeInstance::Pointer ComponentNodeInstance::create(const NodeMetaInfo &/*metaInfo*/, QmlContext *context, QObject *objectToBeWrapped) +{ + QmlComponent *component = 0; + if (objectToBeWrapped) + component = qobject_cast<QmlComponent *>(objectToBeWrapped); + else + component = new QmlComponent(context->engine()); + + if (component == 0) + throw InvalidNodeInstanceException(__LINE__, __FUNCTION__, __FILE__); + + + Pointer instance(new ComponentNodeInstance(component)); + + if (objectToBeWrapped) + instance->setDeleteHeldInstance(false); // the object isn't owned + + instance->populateResetValueHash(); + + return instance; +} + +bool ComponentNodeInstance::hasContent() const +{ + return true; +} + +void ComponentNodeInstance::setPropertyVariant(const QString &name, const QVariant &value) +{ + if (name == "__component_data") { + QByteArray data(value.toByteArray()); + QByteArray imports; + foreach(const Import &import, modelNode().model()->imports()) { + imports.append(import.toString(true).toLatin1()); + } + + data.prepend(imports); + + component()->setData(data, nodeInstanceView()->model()->fileUrl()); + + } + if (component()->isError()) { + qDebug() << value; + foreach(const QmlError &error, component()->errors()) + qDebug() << error; + } + +} +} // Internal +} // QmlDesigner diff --git a/src/plugins/qmldesigner/core/instances/componentnodeinstance.h b/src/plugins/qmldesigner/core/instances/componentnodeinstance.h new file mode 100644 index 00000000000..4554e57ad51 --- /dev/null +++ b/src/plugins/qmldesigner/core/instances/componentnodeinstance.h @@ -0,0 +1,60 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef COMPONENTNODEINSTANCE_H +#define COMPONENTNODEINSTANCE_H + +#include "objectnodeinstance.h" + +class QmlComponent; + +namespace QmlDesigner { +namespace Internal { + +class ComponentNodeInstance : public ObjectNodeInstance +{ +public: + typedef QSharedPointer<ComponentNodeInstance> Pointer; + typedef QWeakPointer<ComponentNodeInstance> WeakPointer; + ComponentNodeInstance(QmlComponent *component); + static Pointer create(const NodeMetaInfo &metaInfo, QmlContext *context, QObject *objectToBeWrapped); + + void setPropertyVariant(const QString &name, const QVariant &value); + + bool hasContent() const; + +private: //function + QmlComponent *component() const; + +}; + +} // Internal +} // QmlDesigner + +#endif // COMPONENTNODEINSTANCE_H diff --git a/src/plugins/qmldesigner/core/instances/dummynodeinstance.cpp b/src/plugins/qmldesigner/core/instances/dummynodeinstance.cpp new file mode 100644 index 00000000000..5c1ba815f0e --- /dev/null +++ b/src/plugins/qmldesigner/core/instances/dummynodeinstance.cpp @@ -0,0 +1,121 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "dummynodeinstance.h" + +#include <QmlMetaType> +#include <nodemetainfo.h> + +#include <invalidnodeinstanceexception.h> +#include <propertymetainfo.h> + +namespace QmlDesigner { +namespace Internal { + +DummyNodeInstance::DummyNodeInstance() + : ObjectNodeInstance(0) +{ +} + +DummyNodeInstance::Pointer DummyNodeInstance::create(const NodeMetaInfo & metaInfo, QmlContext *context) +{ + Q_UNUSED(context); + qWarning() << "DummyNodeInstance created" << metaInfo.typeName(); + foreach(const NodeMetaInfo &metaInfo, metaInfo.superClasses()) + qWarning() << "\t" << metaInfo.typeName(); + return Pointer(new DummyNodeInstance); +} + +void DummyNodeInstance::paint(QPainter * /*painter*/) const +{ +} + +bool DummyNodeInstance::isTopLevel() const +{ + return false; +} + +QRectF DummyNodeInstance::boundingRect() const +{ + return QRectF(); +} + +QPointF DummyNodeInstance::position() const +{ + return QPointF(); +} + +QSizeF DummyNodeInstance::size() const +{ + return QSizeF(); +} + +QTransform DummyNodeInstance::transform() const +{ + return QTransform(); +} + +double DummyNodeInstance::opacity() const +{ + return 0.0; +} + +void DummyNodeInstance::setPropertyVariant(const QString &/*name*/, const QVariant &/*value*/) +{ +} + +QVariant DummyNodeInstance::property(const QString &/*name*/) const +{ + return QVariant(); +} + +QStringList DummyNodeInstance::properties() +{ + return QStringList(); +} + +QStringList DummyNodeInstance::localProperties() +{ + return QStringList(); +} + +bool DummyNodeInstance::isVisible() const +{ + return false; +} + +void DummyNodeInstance::setVisible(bool /*isVisible*/) +{ + Q_ASSERT_X(0, Q_FUNC_INFO, "Cannot set a dummy node instance to visible/non-visible"); + throw InvalidNodeInstanceException(__LINE__, __FUNCTION__, __FILE__); +} + + +} // namespace Internal +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/core/instances/dummynodeinstance.h b/src/plugins/qmldesigner/core/instances/dummynodeinstance.h new file mode 100644 index 00000000000..e1b9c7917dc --- /dev/null +++ b/src/plugins/qmldesigner/core/instances/dummynodeinstance.h @@ -0,0 +1,73 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef DUMMYNODEINSTANCE_H +#define DUMMYNODEINSTANCE_H + +#include <QWeakPointer> + +#include "objectnodeinstance.h" + +namespace QmlDesigner { +namespace Internal { + +class DummyNodeInstance : public ObjectNodeInstance +{ +public: + typedef QSharedPointer<DummyNodeInstance> Pointer; + typedef QWeakPointer<DummyNodeInstance> WeakPointer; + + static Pointer create(const NodeMetaInfo &metaInfo, QmlContext *context); + + void paint(QPainter *painter) const; + + bool isTopLevel() const; + + QRectF boundingRect() const; + QPointF position() const; + QSizeF size() const; + QTransform transform() const; + double opacity() const; + + void setPropertyVariant(const QString &name, const QVariant &value); + QVariant property(const QString &name) const; + QStringList properties(); + QStringList localProperties(); + + bool isVisible() const; + void setVisible(bool isVisible); + +protected: + DummyNodeInstance(); + +}; + +} +} +#endif // DUMMYNODEINSTANCE_H diff --git a/src/plugins/qmldesigner/core/instances/graphicsobjectnodeinstance.cpp b/src/plugins/qmldesigner/core/instances/graphicsobjectnodeinstance.cpp new file mode 100644 index 00000000000..973ec1029e6 --- /dev/null +++ b/src/plugins/qmldesigner/core/instances/graphicsobjectnodeinstance.cpp @@ -0,0 +1,226 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "graphicsobjectnodeinstance.h" + +#include "invalidnodeinstanceexception.h" +#include <QGraphicsObject> +#include "nodemetainfo.h" + +namespace QmlDesigner { +namespace Internal { + +GraphicsObjectNodeInstance::GraphicsObjectNodeInstance(QGraphicsObject *graphicsObject, bool hasContent) + : ObjectNodeInstance(graphicsObject), + m_isVisible(true), + m_hasContent(hasContent) +{ +} + +QGraphicsObject *GraphicsObjectNodeInstance::graphicsObject() const +{ + Q_ASSERT(qobject_cast<QGraphicsObject*>(object())); + return static_cast<QGraphicsObject*>(object()); +} + +bool GraphicsObjectNodeInstance::hasContent() const +{ + return m_hasContent; +} + +bool GraphicsObjectNodeInstance::isVisible() const +{ + return m_isVisible; +} + +void GraphicsObjectNodeInstance::setVisible(bool isVisible) +{ + m_isVisible = isVisible; +} + +QPointF GraphicsObjectNodeInstance::position() const +{ + return graphicsObject()->pos(); +} + +QSizeF GraphicsObjectNodeInstance::size() const +{ + return graphicsObject()->boundingRect().size(); +} + +QTransform GraphicsObjectNodeInstance::transform() const +{ + if (graphicsObject()->parentItem()) + return graphicsObject()->itemTransform(graphicsObject()->parentItem()); + else + return sceneTransform(); +} + +QTransform GraphicsObjectNodeInstance::customTransform() const +{ + return graphicsObject()->transform(); +} + +QTransform GraphicsObjectNodeInstance::sceneTransform() const +{ + return graphicsObject()->sceneTransform(); +} + +double GraphicsObjectNodeInstance::rotation() const +{ + return graphicsObject()->rotation(); +} + +double GraphicsObjectNodeInstance::scale() const +{ + return graphicsObject()->scale(); +} + +QList<QGraphicsTransform *> GraphicsObjectNodeInstance::transformations() const +{ + return graphicsObject()->transformations(); +} + +QPointF GraphicsObjectNodeInstance::transformOriginPoint() const +{ + return graphicsObject()->transformOriginPoint(); +} + +double GraphicsObjectNodeInstance::zValue() const +{ + return graphicsObject()->zValue(); +} + +double GraphicsObjectNodeInstance::opacity() const +{ + return graphicsObject()->opacity(); +} + +QObject *GraphicsObjectNodeInstance::parent() const +{ + if (!graphicsObject() || !graphicsObject()->parentItem()) + return 0; + + return graphicsObject()->parentItem()->toGraphicsObject(); +} + +QRectF GraphicsObjectNodeInstance::boundingRect() const +{ + return graphicsObject()->boundingRect(); +} + +bool GraphicsObjectNodeInstance::isTopLevel() const +{ + Q_ASSERT(graphicsObject()); + return !graphicsObject()->parentItem(); +} + +bool GraphicsObjectNodeInstance::isGraphicsObject() const +{ + return true; +} + +void GraphicsObjectNodeInstance::setPropertyVariant(const QString &name, const QVariant &value) +{ + if (name == "visible") { + setVisible(value.toBool()); + return; + } + + ObjectNodeInstance::setPropertyVariant(name, value); +} + +QVariant GraphicsObjectNodeInstance::property(const QString &name) const +{ + if (name == "visible") + return isVisible(); + + return ObjectNodeInstance::property(name); +} + +bool GraphicsObjectNodeInstance::equalGraphicsItem(QGraphicsItem *item) const +{ + return item == graphicsObject(); +} + +void GraphicsObjectNodeInstance::paintRecursively(QGraphicsItem *graphicsItem, QPainter *painter) const +{ + if (graphicsItem->isVisible()) { + painter->save(); + painter->setTransform(graphicsItem->itemTransform(graphicsItem->parentItem()), true); + painter->setOpacity(graphicsItem->opacity() * painter->opacity()); + graphicsItem->paint(painter, 0); + foreach(QGraphicsItem *childItem, graphicsItem->childItems()) { + paintRecursively(childItem, painter); + } + painter->restore(); + } +} + +void GraphicsObjectNodeInstance::paint(QPainter *painter) const +{ + painter->save(); + Q_ASSERT(graphicsObject()); + if (isVisible()) { + if (hasContent()) + graphicsObject()->paint(painter, 0); + + foreach(QGraphicsItem *graphicsItem, graphicsObject()->childItems()) { + QGraphicsObject *graphicsObject = qgraphicsitem_cast<QGraphicsObject*>(graphicsItem); + if (graphicsObject && !graphicsObject->property("HasFormEditorItem").toBool()) + paintRecursively(graphicsItem, painter); + } + } + + painter->restore(); +} + +QPair<QGraphicsObject*, bool> GraphicsObjectNodeInstance::createGraphicsObject(const NodeMetaInfo &metaInfo, QmlContext *context) +{ + QObject *object = ObjectNodeInstance::createObject(metaInfo, context); + QGraphicsObject *graphicsObject = qobject_cast<QGraphicsObject*>(object); + + if (graphicsObject == 0) + throw InvalidNodeInstanceException(__LINE__, __FUNCTION__, __FILE__); + +// graphicsObject->setCacheMode(QGraphicsItem::ItemCoordinateCache); + bool hasContent = !graphicsObject->flags().testFlag(QGraphicsItem::ItemHasNoContents) || metaInfo.isComponent(); + graphicsObject->setFlag(QGraphicsItem::ItemHasNoContents, false); + + graphicsObject->setProperty("HasFormEditorItem", true); + + return qMakePair(graphicsObject, hasContent); +} + +void GraphicsObjectNodeInstance::paintUpdate() +{ + graphicsObject()->update(); +} +} // namespace Internal +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/core/instances/graphicsobjectnodeinstance.h b/src/plugins/qmldesigner/core/instances/graphicsobjectnodeinstance.h new file mode 100644 index 00000000000..797b064f674 --- /dev/null +++ b/src/plugins/qmldesigner/core/instances/graphicsobjectnodeinstance.h @@ -0,0 +1,90 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef GRAPHICSOBJECTNODEINSTANCE_H +#define GRAPHICSOBJECTNODEINSTANCE_H + +#include "objectnodeinstance.h" + +class QGraphicsObject; + +namespace QmlDesigner { +namespace Internal { + +class GraphicsObjectNodeInstance : public ObjectNodeInstance +{ +public: + GraphicsObjectNodeInstance(QGraphicsObject *graphicsObject, bool hasContent); + + void paint(QPainter *painter) const; + + bool isTopLevel() const; + bool isGraphicsObject() const; + + QRectF boundingRect() const; + QPointF position() const; + QSizeF size() const; + QTransform transform() const; + QTransform customTransform() const; + QTransform sceneTransform() const; + double opacity() const; + + QObject *parent() const; + + double rotation() const; + double scale() const; + QList<QGraphicsTransform *> transformations() const; + QPointF transformOriginPoint() const; + double zValue() const; + + bool equalGraphicsItem(QGraphicsItem *item) const; + + bool isVisible() const; + void setVisible(bool isVisible); + + void setPropertyVariant(const QString &name, const QVariant &value); + QVariant property(const QString &name) const; + + bool hasContent() const; + + void paintUpdate(); + +protected: + QGraphicsObject *graphicsObject() const; + void paintRecursively(QGraphicsItem *graphicsItem, QPainter *painter) const; + static QPair<QGraphicsObject*, bool> createGraphicsObject(const NodeMetaInfo &metaInfo, QmlContext *context); +private: // variables + bool m_isVisible; + bool m_hasContent; +}; + +} // namespace Internal +} // namespace QmlDesigner + +#endif // GRAPHICSOBJECTNODEINSTANCE_H diff --git a/src/plugins/qmldesigner/core/instances/graphicsscenenodeinstance.cpp b/src/plugins/qmldesigner/core/instances/graphicsscenenodeinstance.cpp new file mode 100644 index 00000000000..41e919b947e --- /dev/null +++ b/src/plugins/qmldesigner/core/instances/graphicsscenenodeinstance.cpp @@ -0,0 +1,158 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "graphicsscenenodeinstance.h" + +#include <QmlMetaType> + +#include "graphicsviewnodeinstance.h" + +#include <invalidnodeinstanceexception.h> +#include <propertymetainfo.h> + +namespace QmlDesigner { +namespace Internal { + +GraphicsSceneNodeInstance::GraphicsSceneNodeInstance(QGraphicsScene *scene) + :ObjectNodeInstance(scene) +{ +} + +GraphicsSceneNodeInstance::~GraphicsSceneNodeInstance() +{ +} + +GraphicsSceneNodeInstance::Pointer GraphicsSceneNodeInstance::create(const NodeMetaInfo &nodeMetaInfo, QmlContext *context, QObject *objectToBeWrapped) +{ + QObject *object = 0; + if (objectToBeWrapped) + object = objectToBeWrapped; + else + object = createObject(nodeMetaInfo, context); + + QGraphicsScene* scene = qobject_cast<QGraphicsScene*>(object); + if (scene == 0) + throw InvalidNodeInstanceException(__LINE__, __FUNCTION__, __FILE__); + + Pointer instance(new GraphicsSceneNodeInstance(scene)); + + if (objectToBeWrapped) + instance->setDeleteHeldInstance(false); // the object isn't owned + + instance->populateResetValueHash(); + + return instance; +} + +void GraphicsSceneNodeInstance::paint(QPainter *) const +{ + Q_ASSERT(graphicsScene()); +} + +bool GraphicsSceneNodeInstance::isTopLevel() const +{ + Q_ASSERT(graphicsScene()); + return graphicsScene()->views().isEmpty(); +} + + +void GraphicsSceneNodeInstance::addItem(QGraphicsItem *item) +{ + graphicsScene()->addItem(item); +} + +bool GraphicsSceneNodeInstance::isGraphicsScene() const +{ + return true; +} + +QRectF GraphicsSceneNodeInstance::boundingRect() const +{ + return graphicsScene()->sceneRect(); +} + +QPointF GraphicsSceneNodeInstance::position() const +{ + return graphicsScene()->sceneRect().topLeft(); +} + +QSizeF GraphicsSceneNodeInstance::size() const +{ + return graphicsScene()->sceneRect().size(); +} + +QGraphicsScene *GraphicsSceneNodeInstance::graphicsScene() const +{ + Q_ASSERT(qobject_cast<QGraphicsScene*>(object())); + return static_cast<QGraphicsScene*>(object()); +} + +bool GraphicsSceneNodeInstance::isVisible() const +{ + return false; +} + +void GraphicsSceneNodeInstance::setVisible(bool /*isVisible*/) +{ + +} + + +QList<NodeInstance> GraphicsSceneNodeInstance::instancesInRegions(const QList<QRectF> ®ions) +{ + QRectF combinedRect; + foreach (const QRectF &rect, regions) { + combinedRect = combinedRect.united(rect); + } + + QList<QGraphicsItem*> itemList; + // collect list of possibly changed items + // (actually QGraphicsScene could export this) + + foreach (QGraphicsItem *item, graphicsScene()->items(combinedRect.adjusted(0, 0, 1, 1), Qt::ContainsItemBoundingRect)) { + if (!itemList.contains(item)) + itemList.append(item); + } + + QList<NodeInstance> instances; + if (!itemList.isEmpty()) { + foreach (const NodeInstance &nodeInstance, nodeInstanceView()->instances()) { + foreach (QGraphicsItem *gvItem, itemList) { + if (nodeInstance.equalGraphicsItem(gvItem)) { + instances += nodeInstance; + } + } + } + } + + return instances; +} + +} +} diff --git a/src/plugins/qmldesigner/core/instances/graphicsscenenodeinstance.h b/src/plugins/qmldesigner/core/instances/graphicsscenenodeinstance.h new file mode 100644 index 00000000000..0c92e731a2a --- /dev/null +++ b/src/plugins/qmldesigner/core/instances/graphicsscenenodeinstance.h @@ -0,0 +1,75 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef GRAPHICSSCENENODEINSTANCE_H +#define GRAPHICSSCENENODEINSTANCE_H + +#include "objectnodeinstance.h" +#include <QGraphicsScene> +#include <QWeakPointer> + +namespace QmlDesigner { +namespace Internal { + +class GraphicsSceneNodeInstance : public ObjectNodeInstance +{ +public: + typedef QSharedPointer<GraphicsSceneNodeInstance> Pointer; + typedef QWeakPointer<GraphicsSceneNodeInstance> WeakPointer; + + ~GraphicsSceneNodeInstance(); + + static Pointer create(const NodeMetaInfo &metaInfo, QmlContext *context, QObject *objectToBeWrapped); + + void paint(QPainter *painter) const; + + bool isTopLevel() const; + + void addItem(QGraphicsItem *item); + + bool isGraphicsScene() const; + + QRectF boundingRect() const; + QPointF position() const; + QSizeF size() const; + + + bool isVisible() const; + void setVisible(bool isVisible); + + QList<NodeInstance> instancesInRegions(const QList<QRectF> ®ion); + +protected: + GraphicsSceneNodeInstance(QGraphicsScene *scene); + QGraphicsScene *graphicsScene() const; +}; + +} +} +#endif // GRAPHICSSCENENODEINSTANCE_H diff --git a/src/plugins/qmldesigner/core/instances/graphicsviewnodeinstance.cpp b/src/plugins/qmldesigner/core/instances/graphicsviewnodeinstance.cpp new file mode 100644 index 00000000000..be32b0b813e --- /dev/null +++ b/src/plugins/qmldesigner/core/instances/graphicsviewnodeinstance.cpp @@ -0,0 +1,111 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "graphicsviewnodeinstance.h" + +#include <QmlMetaType> +#include <QmlEngine> +#include <invalidnodeinstanceexception.h> + +#include <QtOpenGL/QGLWidget> + +namespace QmlDesigner { +namespace Internal { + +GraphicsViewNodeInstance::GraphicsViewNodeInstance(QGraphicsView *view) + : WidgetNodeInstance(view) +{ +} + + +GraphicsViewNodeInstance::Pointer GraphicsViewNodeInstance::create(const NodeMetaInfo &nodeMetaInfo, + QmlContext *context, + QObject *objectToBeWrapped) +{ + QObject *object = 0; + if (objectToBeWrapped) + object = objectToBeWrapped; + else + object = createObject(nodeMetaInfo, context); + + QGraphicsView* view = qobject_cast<QGraphicsView*>(object); + if (view == 0) + throw InvalidNodeInstanceException(__LINE__, __FUNCTION__, __FILE__); + + Pointer instance(new GraphicsViewNodeInstance(view)); + + if (objectToBeWrapped) + instance->setDeleteHeldInstance(false); // the object isn't owned + + instance->populateResetValueHash(); + + return instance; +} + +QGraphicsView* GraphicsViewNodeInstance::graphicsView() const +{ + QGraphicsView* view = qobject_cast<QGraphicsView*>(widget()); + Q_ASSERT(view); + return view; +} + +void GraphicsViewNodeInstance::setScene(QGraphicsScene *scene) +{ + graphicsView()->setScene(scene); +} + +bool GraphicsViewNodeInstance::isGraphicsView() const +{ + return true; +} + +QSizeF GraphicsViewNodeInstance::size() const +{ + return graphicsView()->scene()->itemsBoundingRect().size(); +} + +QTransform GraphicsViewNodeInstance::transform() const +{ + return graphicsView()->transform(); +} + +void GraphicsViewNodeInstance::paint(QPainter *painter) const +{ + painter->save(); + painter->setRenderHint(QPainter::Antialiasing, true); + painter->setRenderHint(QPainter::TextAntialiasing, true); + painter->setRenderHint(QPainter::SmoothPixmapTransform, true); + painter->setRenderHint(QPainter::HighQualityAntialiasing, true); + painter->setRenderHint(QPainter::NonCosmeticDefaultPen, true); + graphicsView()->render(painter, QRectF(), graphicsView()->scene()->itemsBoundingRect().toRect()); + painter->restore(); +} + +} +} diff --git a/src/plugins/qmldesigner/core/instances/graphicsviewnodeinstance.h b/src/plugins/qmldesigner/core/instances/graphicsviewnodeinstance.h new file mode 100644 index 00000000000..3a55007beba --- /dev/null +++ b/src/plugins/qmldesigner/core/instances/graphicsviewnodeinstance.h @@ -0,0 +1,67 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef GRAPHICSVIEWNODEINSTANCE_H +#define GRAPHICSVIEWNODEINSTANCE_H + +#include <QWeakPointer> +#include <QGraphicsView> + +#include "widgetnodeinstance.h" + +namespace QmlDesigner { +namespace Internal { + +class GraphicsViewNodeInstance : public WidgetNodeInstance +{ +public: + typedef QSharedPointer<GraphicsViewNodeInstance> Pointer; + typedef QWeakPointer<GraphicsViewNodeInstance> WeakPointer; + + static Pointer create(const NodeMetaInfo &metaInfo, QmlContext *context, QObject *objectToBeWrapped); + + void setScene(QGraphicsScene *scene); + + bool isGraphicsView() const; + + QTransform transform() const; + QSizeF size() const; + + + void paint(QPainter *painter) const; + +protected: + GraphicsViewNodeInstance(QGraphicsView *view); + + QGraphicsView* graphicsView() const; +}; + +} +} +#endif // GRAPHICSVIEWNODEINSTANCE_H diff --git a/src/plugins/qmldesigner/core/instances/graphicswidgetnodeinstance.cpp b/src/plugins/qmldesigner/core/instances/graphicswidgetnodeinstance.cpp new file mode 100644 index 00000000000..9cd44efdf8a --- /dev/null +++ b/src/plugins/qmldesigner/core/instances/graphicswidgetnodeinstance.cpp @@ -0,0 +1,185 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "graphicswidgetnodeinstance.h" +#include "graphicsscenenodeinstance.h" + +#include "objectnodeinstance.h" + +#include <QmlMetaType> + +#include <invalidnodeinstanceexception.h> +#include <propertymetainfo.h> + +namespace QmlDesigner { +namespace Internal { + +GraphicsWidgetNodeInstance::GraphicsWidgetNodeInstance(QGraphicsWidget *widget) + : ObjectNodeInstance(widget) +{ +} + +GraphicsWidgetNodeInstance::~GraphicsWidgetNodeInstance() +{ +} + +GraphicsWidgetNodeInstance::Pointer GraphicsWidgetNodeInstance::create(const NodeMetaInfo &nodeMetaInfo, QmlContext *context, QObject *objectToBeWrapped) +{ + QObject *object = 0; + if (objectToBeWrapped) + object = objectToBeWrapped; + else + object = createObject(nodeMetaInfo, context); + + QGraphicsWidget* graphicsWidget = qobject_cast<QGraphicsWidget*>(object); + if (graphicsWidget == 0) + throw InvalidNodeInstanceException(__LINE__, __FUNCTION__, __FILE__); + + Pointer instance(new GraphicsWidgetNodeInstance(graphicsWidget)); + + if (objectToBeWrapped) + instance->setDeleteHeldInstance(false); // the object isn't owned + + instance->populateResetValueHash(); + + return instance; +} + +void GraphicsWidgetNodeInstance::paint(QPainter *painter) const +{ + graphicsWidget()->show(); + graphicsWidget()->paint(painter, 0); + + paintChildren(painter, graphicsWidget()); +} + +bool GraphicsWidgetNodeInstance::isTopLevel() const +{ + return !graphicsWidget()->parentItem(); +} + + +bool isChildNode(QGraphicsItem *item) +{ + // there should be a better implementation + if (qgraphicsitem_cast<QGraphicsWidget*>(item)) + return false; + else + return true; +} + +void GraphicsWidgetNodeInstance::paintChildren(QPainter *painter, QGraphicsItem *item) const +{ + foreach (QGraphicsItem *childItem, item->childItems()) { + if (!isChildNode(childItem)) { + painter->save(); + painter->setTransform(item->transform(), true); + item->paint(painter, 0); + paintChildren(painter, childItem); + painter->restore(); + } + } +} + +void GraphicsWidgetNodeInstance::setParentItem(QGraphicsItem *item) +{ + graphicsWidget()->setParentItem(item); +} + +QGraphicsWidget* GraphicsWidgetNodeInstance::graphicsWidget() const +{ + return static_cast<QGraphicsWidget*>(object()); +} + +bool GraphicsWidgetNodeInstance::isGraphicsWidget() const +{ + return true; +} + +bool GraphicsWidgetNodeInstance::isGraphicsItem(QGraphicsItem *item) const +{ + return graphicsWidget() == item; +} + +QRectF GraphicsWidgetNodeInstance::boundingRect() const +{ + return graphicsWidget()->boundingRect(); +} + +void GraphicsWidgetNodeInstance::setPropertyVariant(const QString &name, const QVariant &value) +{ + if (name == "x") + graphicsWidget()->setPos(value.toDouble(), graphicsWidget()->y()); + else if (name == "y") + graphicsWidget()->setPos(graphicsWidget()->x(), value.toDouble()); + else if (name == "width") + graphicsWidget()->resize(value.toDouble(), graphicsWidget()->size().height()); + else if (name == "height") + graphicsWidget()->resize(graphicsWidget()->size().width(), value.toDouble()); + else + graphicsWidget()->setProperty(name.toLatin1(), value); +} + +QVariant GraphicsWidgetNodeInstance::property(const QString &name) const +{ + return graphicsWidget()->property(name.toLatin1()); +} + +bool GraphicsWidgetNodeInstance::isVisible() const +{ + return graphicsWidget()->isVisible(); +} + +void GraphicsWidgetNodeInstance::setVisible(bool isVisible) +{ + graphicsWidget()->setVisible(isVisible); +} + +QPointF GraphicsWidgetNodeInstance::position() const +{ + return graphicsWidget()->pos(); +} + +QSizeF GraphicsWidgetNodeInstance::size() const +{ + return graphicsWidget()->size(); +} + +QTransform GraphicsWidgetNodeInstance::transform() const +{ + return graphicsWidget()->transform(); +} + +double GraphicsWidgetNodeInstance::opacity() const +{ + return graphicsWidget()->opacity(); +} + +} +} diff --git a/src/plugins/qmldesigner/core/instances/graphicswidgetnodeinstance.h b/src/plugins/qmldesigner/core/instances/graphicswidgetnodeinstance.h new file mode 100644 index 00000000000..7bbcf96deff --- /dev/null +++ b/src/plugins/qmldesigner/core/instances/graphicswidgetnodeinstance.h @@ -0,0 +1,80 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef GRAPHICSWIDGETNODEINSTANCE_H +#define GRAPHICSWIDGETNODEINSTANCE_H + +#include <QWeakPointer> +#include <QGraphicsWidget> + +#include "objectnodeinstance.h" + +namespace QmlDesigner { +namespace Internal { + +class GraphicsWidgetNodeInstance : public ObjectNodeInstance +{ +public: + typedef QSharedPointer<GraphicsWidgetNodeInstance> Pointer; + typedef QWeakPointer<GraphicsWidgetNodeInstance> WeakPointer; + + ~GraphicsWidgetNodeInstance(); + + static Pointer create(const NodeMetaInfo &metaInfo, QmlContext *context, QObject *objectToBeWrapped); + + void paint(QPainter *painter) const; + + bool isTopLevel() const; + + bool isGraphicsWidget() const; + bool isGraphicsItem(QGraphicsItem *item) const; + + QRectF boundingRect() const; + QPointF position() const; + QSizeF size() const; + QTransform transform() const; + double opacity() const; + + void setPropertyVariant(const QString &name, const QVariant &value); + QVariant property(const QString &name) const; + + bool isVisible() const; + void setVisible(bool isVisible); + +protected: + GraphicsWidgetNodeInstance(QGraphicsWidget *widget); + void paintChildren(QPainter *painter, QGraphicsItem *item) const; + void setParentItem(QGraphicsItem *item); + QGraphicsWidget* graphicsWidget() const; + +}; + +} +} +#endif // GRAPHICSWIDGETNODEINSTANCE_H diff --git a/src/plugins/qmldesigner/core/instances/nodeinstance.cpp b/src/plugins/qmldesigner/core/instances/nodeinstance.cpp new file mode 100644 index 00000000000..00c4d556e48 --- /dev/null +++ b/src/plugins/qmldesigner/core/instances/nodeinstance.cpp @@ -0,0 +1,589 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "nodeinstance.h" + +#include "objectnodeinstance.h" +#include "metainfo.h" +#include "graphicswidgetnodeinstance.h" +#include "widgetnodeinstance.h" +#include "qmlgraphicsitemnodeinstance.h" +#include "graphicsscenenodeinstance.h" +#include "graphicsviewnodeinstance.h" +#include "nodeinstanceview.h" +#include "qmlviewnodeinstance.h" +#include "dummynodeinstance.h" +#include "componentnodeinstance.h" +#include "qmltransitionnodeinstance.h" +#include "qmlpropertychangesnodeinstance.h" +#include "qmlstatenodeinstance.h" +#include "nodeabstractproperty.h" +#include "variantproperty.h" + +#include <invalidnodeinstanceexception.h> + +#include <QHash> +#include <QSet> + +#include <QtDeclarative/QmlEngine> + +/*! + \class QmlDesigner::NodeInstance + \ingroup CoreInstance + \brief NodeInstance is a common handle for the actual object representation of a ModelNode. + + NodeInstance abstracts away the differences e.g. in terms of position and size + for QWidget, QGraphicsView, QLayout etc objects. Multiple NodeInstance objects can share + the pointer to the same instance object. The actual instance will be deleted when + the last NodeInstance object referencing to it is deleted. This can be disabled by + setDeleteHeldInstance(). + + \see QmlDesigner::NodeInstanceView +*/ + +namespace QmlDesigner { + +/*! +\brief Constructor - creates a invalid NodeInstance + + +\see NodeInstanceView +*/ +NodeInstance::NodeInstance() +{ +} + +/*! +\brief Destructor + +*/ +NodeInstance::~NodeInstance() +{ +} + +/*! +\brief Constructor - creates a valid NodeInstance + +*/ +NodeInstance::NodeInstance(const Internal::ObjectNodeInstance::Pointer &abstractInstance) + : m_nodeInstance(abstractInstance) +{ + +} + + +NodeInstance::NodeInstance(const NodeInstance &other) + : m_nodeInstance(other.m_nodeInstance) +{ +} + +NodeInstance &NodeInstance::operator=(const NodeInstance &other) +{ + m_nodeInstance = other.m_nodeInstance; + return *this; +} + +/*! +\brief Paints the NodeInstance with this painter. +\param painter used QPainter +*/ +void NodeInstance::paint(QPainter *painter) const +{ + m_nodeInstance->paint(painter); +} + +/*! +\brief Creates a new NodeInstace for this NodeMetaInfo + +\param metaInfo MetaInfo for which a Instance should be created +\param context QmlContext which should be used +\returns Internal Pointer of a NodeInstance +\see NodeMetaInfo +*/ +Internal::ObjectNodeInstance::Pointer NodeInstance::createInstance(const NodeMetaInfo &metaInfo, + QmlContext *context, QObject *objectToBeWrapped) +{ + Internal::ObjectNodeInstance::Pointer instance; + + if (metaInfo.isSubclassOf("Qt/QGraphicsView", 4, 6)) + instance = Internal::GraphicsViewNodeInstance::create(metaInfo, context, objectToBeWrapped); + else if (metaInfo.isSubclassOf("Qt/QmlView", 4, 6)) + instance = Internal::QmlViewNodeInstance::create(metaInfo, context, objectToBeWrapped); + else if (metaInfo.isSubclassOf("Qt/QGraphicsWidget", 4, 6)) + instance = Internal::GraphicsWidgetNodeInstance::create(metaInfo, context, objectToBeWrapped); + else if (metaInfo.isSubclassOf("Qt/Item", 4, 6)) + instance = Internal::QmlGraphicsItemNodeInstance::create(metaInfo, context, objectToBeWrapped); + else if (metaInfo.isSubclassOf("Qt/QWidget", 4, 6)) + instance = Internal::WidgetNodeInstance::create(metaInfo, context, objectToBeWrapped); + else if (metaInfo.isSubclassOf("Qt/QGraphicsScene", 4, 6)) + instance = Internal::GraphicsSceneNodeInstance::create(metaInfo, context, objectToBeWrapped); + else if (metaInfo.isSubclassOf("Qt/Component", 4, 6)) + instance = Internal::ComponentNodeInstance::create(metaInfo, context, objectToBeWrapped); + else if (metaInfo.isSubclassOf("Qt/PropertyChanges", 4, 6)) + instance = Internal::QmlPropertyChangesNodeInstance::create(metaInfo, context, objectToBeWrapped); + else if (metaInfo.isSubclassOf("Qt/State", 4, 6)) + instance = Internal::QmlStateNodeInstance::create(metaInfo, context, objectToBeWrapped); + else if (metaInfo.isSubclassOf("Qt/Transition", 4, 6)) + instance = Internal::QmlTransitionNodeInstance::create(metaInfo, context, objectToBeWrapped); + else if (metaInfo.isSubclassOf("Qt/QtObject", 4, 6)) + instance = Internal::ObjectNodeInstance::create(metaInfo, context, objectToBeWrapped); + + if (instance.isNull()) { + instance = Internal::DummyNodeInstance::create(metaInfo, context); + } + + return instance; +} + + + +NodeInstance NodeInstance::create(NodeInstanceView *nodeInstanceView, const ModelNode &node, QObject *objectToBeWrapped) +{ + Q_ASSERT(node.isValid()); + Q_ASSERT(nodeInstanceView); + + // For the moment just use the root context of the engine + // for all items. However, this is a hack ... ideally we should + // rebuild the same context hierarchy as the qml compiler does + + QmlContext *context = nodeInstanceView->engine()->rootContext(); + + NodeInstance instance(createInstance(node.metaInfo(), context, objectToBeWrapped)); + + instance.m_nodeInstance->setModelNode(node); + + instance.m_nodeInstance->setNodeInstance(nodeInstanceView); + + instance.setId(node.id()); + + foreach (const VariantProperty &property, node.variantProperties()) { + if (property.isDynamic()) + instance.setPropertyDynamicVariant(property.name(), property.dynamicTypeName(), property.value()); + else + instance.setPropertyVariant(property.name(), property.value()); + } + + return instance; +} + +NodeInstance NodeInstance::create(NodeInstanceView *nodeInstanceView, const NodeMetaInfo &metaInfo, QmlContext *context) +{ + NodeInstance instance(createInstance(metaInfo, context, 0)); + instance.m_nodeInstance->setNodeInstance(nodeInstanceView); + + return instance; +} + +/*! +\brief Returns the ModelNode of this NodeInstance. +\returns ModelNode of this NodeState +*/ +ModelNode NodeInstance::modelNode() const +{ + if (m_nodeInstance.isNull()) + return ModelNode(); + + return m_nodeInstance->modelNode(); +} + + +/*! +\brief Changes the NodeState of the ModelNode of this NodeInstance. + All properties are updated. +\param state NodeState of this NodeInstance +*/ +void NodeInstance::setModelNode(const ModelNode &node) +{ + Q_ASSERT(node.isValid()); + if (m_nodeInstance->modelNode() == node) + return; + + m_nodeInstance->setModelNode(node); +} + +/*! +\brief Returns if the NodeInstance is a top level item. +\returns true if this NodeInstance is a top level item +*/ +bool NodeInstance::isTopLevel() const +{ + return m_nodeInstance->isTopLevel(); +} + +void NodeInstance::reparent(const NodeInstance &oldParentInstance, const QString &oldParentProperty, const NodeInstance &newParentInstance, const QString &newParentProperty) +{ + m_nodeInstance->reparent(oldParentInstance, oldParentProperty, newParentInstance, newParentProperty); +} + + +/*! +\brief Returns the parent NodeInstance of this NodeInstance. + + If there is not parent than the parent is invalid. + +\returns Parent NodeInstance. +*/ +NodeInstance NodeInstance::parent() const +{ + return m_nodeInstance->nodeInstanceView()->instanceForObject(m_nodeInstance->parent()); +} + +bool NodeInstance::hasParent() const +{ + return m_nodeInstance->object()->parent(); +} + +/*! +\brief Returns if the NodeInstance is a QmlGraphicsItem. +\returns true if this NodeInstance is a QmlGraphicsItem +*/ +bool NodeInstance::isQmlGraphicsItem() const +{ + return m_nodeInstance->isQmlGraphicsItem(); +} + +/*! +\brief Returns if the NodeInstance is a QGraphicsScene. +\returns true if this NodeInstance is a QGraphicsScene +*/ +bool NodeInstance::isGraphicsScene() const +{ + return m_nodeInstance->isGraphicsScene(); +} + +/*! +\brief Returns if the NodeInstance is a QGraphicsView. +\returns true if this NodeInstance is a QGraphicsView +*/ +bool NodeInstance::isGraphicsView() const +{ + return m_nodeInstance->isGraphicsView(); +} + +/*! +\brief Returns if the NodeInstance is a QGraphicsWidget. +\returns true if this NodeInstance is a QGraphicsWidget +*/ +bool NodeInstance::isGraphicsWidget() const +{ + return m_nodeInstance->isGraphicsWidget(); +} + +/*! +\brief Returns if the NodeInstance is a QGraphicsProxyWidget. +\returns true if this NodeInstance is a QGraphicsProxyWidget +*/ +bool NodeInstance::isProxyWidget() const +{ + return m_nodeInstance->isProxyWidget(); +} + +/*! +\brief Returns if the NodeInstance is a QWidget. +\returns true if this NodeInstance is a QWidget +*/ +bool NodeInstance::isWidget() const +{ + return m_nodeInstance->isWidget(); +} + +/*! +\brief Returns if the NodeInstance is a QmlView. +\returns true if this NodeInstance is a QmlView +*/ +bool NodeInstance::isQmlView() const +{ + return m_nodeInstance->isQmlView(); +} + +bool NodeInstance::isGraphicsObject() const +{ + return m_nodeInstance->isGraphicsObject(); +} + +bool NodeInstance::isTransition() const +{ + return m_nodeInstance->isTransition(); +} + +/*! +\brief Returns if the NodeInstance is a QGraphicsItem. +\returns true if this NodeInstance is a QGraphicsItem +*/ +bool NodeInstance::equalGraphicsItem(QGraphicsItem *item) const +{ + return m_nodeInstance->equalGraphicsItem(item); +} + +/*! +\brief Returns the bounding rect of the NodeInstance. +\returns QRectF of the NodeInstance +*/ +QRectF NodeInstance::boundingRect() const +{ + QRectF boundingRect(m_nodeInstance->boundingRect()); + +// +// if (modelNode().isValid()) { // TODO implement recursiv stuff +// if (qFuzzyIsNull(boundingRect.width())) +// boundingRect.setWidth(nodeState().property("width").value().toDouble()); +// +// if (qFuzzyIsNull(boundingRect.height())) +// boundingRect.setHeight(nodeState().property("height").value().toDouble()); +// } + + return boundingRect; +} + +void NodeInstance::setPropertyVariant(const QString &name, const QVariant &value) +{ + m_nodeInstance->setPropertyVariant(name, value); +} + +void NodeInstance::setPropertyDynamicVariant(const QString &/*name*/, const QString &/*typeName*/, const QVariant &/*value*/) +{ +// m_nodeInstance->createDynamicProperty(name, typeName); +// m_nodeInstance->setPropertyVariant(name, value); +} + +void NodeInstance::setPropertyBinding(const QString &name, const QString &expression) +{ + m_nodeInstance->setPropertyBinding(name, expression); +} + +void NodeInstance::setPropertyDynamicBinding(const QString &/*name*/, const QString &/*typeName*/, const QString &/*expression*/) +{ +// m_nodeInstance->createDynamicProperty(name, typeName); +// m_nodeInstance->setPropertyBinding(name, value); +} + +void NodeInstance::resetProperty(const QString &name) +{ + m_nodeInstance->resetProperty(name); +} + +void NodeInstance::setId(const QString &id) +{ + m_nodeInstance->setId(id); +} + +/*! +\brief Returns the property value of the property of this NodeInstance. +\returns QVariant value +*/ +QVariant NodeInstance::property(const QString &name) const +{ + return m_nodeInstance->property(name); +} + +/*! +\brief Returns the property default value of the property of this NodeInstance. +\returns QVariant default value which is the reset value to +*/ +QVariant NodeInstance::defaultValue(const QString &name) const +{ + return m_nodeInstance->resetValue(name); +} + +/*! +\brief Returns if the NodeInstance is visible. +\returns true if the NodeInstance is visible +*/ +bool NodeInstance::isVisible() const +{ + return m_nodeInstance->isVisible(); +} + +void NodeInstance::show() +{ + m_nodeInstance->setVisible(true); +} +void NodeInstance::hide() +{ + m_nodeInstance->setVisible(false); +} + +/*! +\brief Returns if the NodeInstance is valid. +\returns true if the NodeInstance is valid +*/ +bool NodeInstance::isValid() const +{ + return m_nodeInstance && internalObject(); +} + +void NodeInstance::makeInvalid() +{ + if (m_nodeInstance) + m_nodeInstance->destroy(); + m_nodeInstance.clear(); +} + +bool NodeInstance::hasContent() const +{ + return m_nodeInstance->hasContent(); +} + +bool NodeInstance::hasAnchor(const QString &name) const +{ + return m_nodeInstance->hasAnchor(name); +} + +bool NodeInstance::isAnchoredBy() const +{ + return m_nodeInstance->isAnchoredBy(); +} + +QPair<QString, NodeInstance> NodeInstance::anchor(const QString &name) const +{ + return m_nodeInstance->anchor(name); +} + +uint qHash(const NodeInstance &instance) +{ + return ::qHash(instance.m_nodeInstance.data()); +} + +bool operator==(const NodeInstance &first, const NodeInstance &second) +{ + return first.m_nodeInstance.data() == second.m_nodeInstance.data(); +} + +const QObject *NodeInstance::testHandle() const +{ + return internalObject(); +} + +/*! +\brief Returns the position in parent coordiantes. +\returns QPointF of the position of the instance. +*/ +QPointF NodeInstance::position() const +{ + return m_nodeInstance->position(); +} + +/*! +\brief Returns the size in local coordiantes. +\returns QSizeF of the size of the instance. +*/ +QSizeF NodeInstance::size() const +{ + QSizeF instanceSize = m_nodeInstance->size(); + +// if (nodeState().isValid()) { +// if (qFuzzyIsNull(instanceSize.width())) +// instanceSize.setWidth(nodeState().property("width").value().toDouble()); +// +// if (qFuzzyIsNull(instanceSize.height())) +// instanceSize.setHeight(nodeState().property("height").value().toDouble()); +// } + return instanceSize; +} + +QTransform NodeInstance::transform() const +{ + return m_nodeInstance->transform(); +} + +/*! +\brief Returns the transform matrix of the instance. +\returns QTransform of the instance. +*/ +QTransform NodeInstance::customTransform() const +{ + return m_nodeInstance->customTransform(); +} + +QTransform NodeInstance::sceneTransform() const +{ + return m_nodeInstance->sceneTransform(); +} + +double NodeInstance::rotation() const +{ + return m_nodeInstance->rotation(); +} + +double NodeInstance::scale() const +{ + return m_nodeInstance->scale(); +} + +QList<QGraphicsTransform *> NodeInstance::transformations() const +{ + return m_nodeInstance->transformations(); +} + +QPointF NodeInstance::transformOriginPoint() const +{ + return m_nodeInstance->transformOriginPoint(); +} + +double NodeInstance::zValue() const +{ + return m_nodeInstance->zValue(); +} + +/*! +\brief Returns the opacity of the instance. +\returns 0.0 mean transparent and 1.0 opaque. +*/ +double NodeInstance::opacity() const +{ + return m_nodeInstance->opacity(); +} + + +void NodeInstance::setDeleteHeldInstance(bool deleteInstance) +{ + m_nodeInstance->setDeleteHeldInstance(deleteInstance); +} + + +void NodeInstance::paintUpdate() +{ + m_nodeInstance->paintUpdate(); +} + + +Internal::QmlGraphicsItemNodeInstance::Pointer NodeInstance::qmlGraphicsItemNodeInstance() const +{ + return m_nodeInstance.dynamicCast<Internal::QmlGraphicsItemNodeInstance>(); +} + +QObject *NodeInstance::internalObject() const +{ + if (m_nodeInstance.isNull()) + return 0; + + return m_nodeInstance->object(); +} + +} diff --git a/src/plugins/qmldesigner/core/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/core/instances/nodeinstanceview.cpp new file mode 100644 index 00000000000..8e75d82160d --- /dev/null +++ b/src/plugins/qmldesigner/core/instances/nodeinstanceview.cpp @@ -0,0 +1,610 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "nodeinstanceview.h" + +#include <QtDeclarative/QmlEngine> +#include <QtDeclarative/QmlContext> + +#include <QtDebug> +#include <QUrl> +#include <QGraphicsView> +#include <QGraphicsScene> +#include <QGraphicsObject> + +#include <model.h> +#include <modelnode.h> +#include <propertymetainfo.h> +#include <metainfo.h> +#include <nodeinstance.h> + +#include <typeinfo> +#include <iwidgetplugin.h> + +#include "abstractproperty.h" +#include "variantproperty.h" +#include "bindingproperty.h" +#include "nodeabstractproperty.h" +#include "nodelistproperty.h" + +#include "objectnodeinstance.h" + +enum { + debug = false +}; + +/*! +\defgroup CoreInstance +*/ +/*! +\class QmlDesigner::NodeInstanceView +\ingroup CoreInstance +\brief Central class to create and manage instances of a ModelNode. + +This view is used to instance the ModelNodes. Many AbstractViews hold a +NodeInstanceView to get values from tghe NodeInstances back. +For this purpose this view can be rendered offscreen. + +\see NodeInstance ModelNode +*/ + +namespace QmlDesigner { + +/*! \brief Constructor + + The class will be rendered offscreen if not setted otherwise. + +\param Parent of this object. If this parent is deleted this instance is +deleted too. + +\see ~NodeInstanceView setRenderOffScreen +*/ +NodeInstanceView::NodeInstanceView(QObject *parent) + : AbstractView(parent), + m_graphicsView(new QGraphicsView), + m_engine(new QmlEngine(this)), + m_blockChangeSignal(false) +{ + m_graphicsView->setAttribute(Qt::WA_DontShowOnScreen, true); + m_graphicsView->setViewportUpdateMode(QGraphicsView::NoViewportUpdate); + m_graphicsView->setScene(new QGraphicsScene(m_graphicsView.data())); +} + + +/*! \brief Destructor + +*/ +NodeInstanceView::~NodeInstanceView() +{ + removeAllInstanceNodeRelationships(); +} + +/*! \name Overloaded Notifiers + * This methodes notify the view that something has happen in the model + */ +//\{ +/*! \brief Notifing the view that it was attached to a model. + + For every ModelNode in the model a NodeInstance will be created. +\param model Model to which the view is attached +*/ +void NodeInstanceView::modelAttached(Model *model) +{ + AbstractView::modelAttached(model); + engine()->setBaseUrl(model->fileUrl()); + loadModel(model); +} + +void NodeInstanceView::modelAboutToBeDetached(Model * model) +{ + removeAllInstanceNodeRelationships(); + AbstractView::modelAboutToBeDetached(model); +} + + +/*! \brief Notifing the view that a node was created. + A NodeInstance will be created for the new created ModelNode. +\param createdNode New created ModelNode. +*/ +void NodeInstanceView::nodeCreated(const ModelNode &createdNode) +{ + NodeInstance instance(loadNode(createdNode)); + instance.show(); +} + +/*! \brief Notifing the view that a node was created. +\param removedNode +*/ +void NodeInstanceView::nodeAboutToBeRemoved(const ModelNode &removedNode) +{ + removeInstanceAndSubInstances(removedNode); +} + +void NodeInstanceView::nodeRemoved(const ModelNode &/*removedNode*/, const NodeAbstractProperty &/*parentProperty*/, PropertyChangeFlags /*propertyChange*/) +{ + +} + +/*! \brief Notifing the view that a AbstractProperty was added to a ModelNode. + + The property will be setted for the NodeInstance. + +\param state ModelNode to wich the Property belongs +\param property AbstractProperty which was added +\see AbstractProperty NodeInstance ModelNode +*/ + +void NodeInstanceView::propertiesAboutToBeRemoved(const QList<AbstractProperty>& propertyList) +{ + foreach (const AbstractProperty &property, propertyList) { + if (hasInstanceForNode(property.parentModelNode())) { // TODO ugly workaround + NodeInstance instance = instanceForNode(property.parentModelNode()); + instance.resetProperty(property.name()); + } + + if (property.isNodeAbstractProperty()) { + foreach (const ModelNode &subNode, property.toNodeAbstractProperty().allSubNodes()) + removeInstanceNodeRelationship(subNode); + } + } +} + +void NodeInstanceView::propertiesRemoved(const QList<AbstractProperty>& /*propertyList*/) +{ +} + +void NodeInstanceView::removeInstanceAndSubInstances(const ModelNode &node) +{ + if (hasInstanceForNode(node)) + removeInstanceNodeRelationship(node); + + foreach(const ModelNode &subNode, node.allSubModelNodes()) { + if (hasInstanceForNode(subNode)) + removeInstanceNodeRelationship(subNode); + } +} + +void NodeInstanceView::nodeTypeChanged(const ModelNode &node, const QString &/*type*/, int /*majorVersion*/, int /*minorVersion*/) +{ + removeInstanceAndSubInstances(node); + + QList<ModelNode> nodeList; + + nodeList.append(node.allSubModelNodes()); + nodeList.append(node); + + loadNodes(nodeList); +} + +void NodeInstanceView::bindingPropertiesChanged(const QList<BindingProperty>& propertyList, PropertyChangeFlags /*propertyChange*/) +{ + foreach (const BindingProperty &property, propertyList) { + NodeInstance instance = instanceForNode(property.parentModelNode()); + + if (property.isDynamic()) + instance.setPropertyDynamicBinding(property.name(), property.dynamicTypeName(), property.expression()); + else + instance.setPropertyBinding(property.name(), property.expression()); + + if (property.parentModelNode().isRootNode() + && (property.name() == "width" || property.name() == "height")) { + QGraphicsObject *rootGraphicsObject = qobject_cast<QGraphicsObject*>(instance.internalObject()); + m_graphicsView->setSceneRect(rootGraphicsObject->boundingRect()); + } + + instance.paintUpdate(); + } +} + +/*! \brief Notifing the view that a AbstractProperty value was changed to a ModelNode. + + The property will be setted for the NodeInstance. + +\param state ModelNode to wich the Property belongs +\param property AbstractProperty which was changed +\param newValue New Value of the property +\param oldValue Old Value of the property +\see AbstractProperty NodeInstance ModelNode +*/ + +void NodeInstanceView::variantPropertiesChanged(const QList<VariantProperty>& propertyList, PropertyChangeFlags /*propertyChange*/) +{ + foreach (const VariantProperty &property, propertyList) { + NodeInstance instance = instanceForNode(property.parentModelNode()); + + if (property.isDynamic()) + instance.setPropertyDynamicVariant(property.name(), property.dynamicTypeName(), property.value()); + else + instance.setPropertyVariant(property.name(), property.value()); + + if (property.parentModelNode().isRootNode() + && (property.name() == "width" || property.name() == "height")) { + QGraphicsObject *rootGraphicsObject = qobject_cast<QGraphicsObject*>(instance.internalObject()); + if (rootGraphicsObject) { + m_graphicsView->setSceneRect(rootGraphicsObject->boundingRect()); + } + } + + instance.paintUpdate(); + } +} +/*! \brief Notifing the view that a ModelNode has a new Parent. + + Note that also the ModelNode::childNodes() list was changed. The + NodeInstance tree will be changed to reflect the ModelNode tree change. + +\param node ModelNode which parent was changed. +\param oldParent Old parent of the node. +\param newParent New parent of the node. + +\see NodeInstance ModelNode +*/ + +void NodeInstanceView::nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent, const NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags /*propertyChange*/) +{ + NodeInstance nodeInstance(instanceForNode(node)); + NodeInstance oldParentInstance; + if (hasInstanceForNode(oldPropertyParent.parentModelNode())) + oldParentInstance = instanceForNode(oldPropertyParent.parentModelNode()); + NodeInstance newParentInstance; + if (hasInstanceForNode(newPropertyParent.parentModelNode())) + newParentInstance = instanceForNode(newPropertyParent.parentModelNode()); + nodeInstance.reparent(oldParentInstance, oldPropertyParent.name(), newParentInstance, newPropertyParent.name()); +} + +void NodeInstanceView::fileUrlChanged(const QUrl &/*oldUrl*/, const QUrl &/*newUrl*/) +{ + // TODO: We have to probably reload everything, so that images etc are updated!!! + engine()->setBaseUrl(model()->fileUrl()); +} + +void NodeInstanceView::nodeIdChanged(const ModelNode& node, const QString& newId, const QString& /*oldId*/) +{ + if (hasInstanceForNode(node)) { + NodeInstance instance = instanceForNode(node); + + instance.setId(newId); + } +} + +void NodeInstanceView::nodeSlidedToIndex(const NodeListProperty &listProperty, int /*newIndex*/, int /*oldIndex*/) +{ + QList<ModelNode> newOrderModelNodeList = listProperty.toModelNodeList(); + foreach(const ModelNode &node, newOrderModelNodeList) { + NodeInstance instance = instanceForNode(node); + instance.reparent(instance.parent(), listProperty.name(), instance.parent(), listProperty.name()); + } +} + +/*! \brief Notifing the view that the selection has been changed. + + Do nothing. + +\param selectedNodeList List of ModelNode which has been selected +\param lastSelectedNodeList List of ModelNode which was selected + +\see ModelNode NodeInstance +*/ +void NodeInstanceView::selectedNodesChanged(const QList<ModelNode> &/*selectedNodeList*/, + const QList<ModelNode> &/*lastSelectedNodeList*/) +{ +} + + +//\} + +void NodeInstanceView::loadNodes(const QList<ModelNode> &nodeList) +{ + foreach (const ModelNode &node, nodeList) + loadNode(node); + + foreach (const ModelNode &node, nodeList) { + if (node.hasParentProperty()) + instanceForNode(node).reparent(NodeInstance(), QString(), instanceForNode(node.parentProperty().parentModelNode()), node.parentProperty().name()); + } + + foreach (const ModelNode &node, nodeList) { + foreach (const BindingProperty &property, node.bindingProperties()) + instanceForNode(node).setPropertyBinding(property.name(), property.expression()); + } +} + +// TODO: Set base state as current model state +void NodeInstanceView::loadModel(Model *model) +{ + Q_ASSERT(rootModelNode().isValid()); + + removeAllInstanceNodeRelationships(); + + engine()->rootContext()->setBaseUrl(model->fileUrl()); + + loadNodes(allModelNodes()); +} + +void NodeInstanceView::removeAllInstanceNodeRelationships() +{ + // prevent destroyed() signals calling back + + //first delete the root object + if (rootNodeInstance().internalObject()) + rootNodeInstance().internalObject()->disconnect(); + + rootNodeInstance().makeInvalid(); + + + foreach (NodeInstance instance, m_objectInstanceHash.values()) { + if (instance.internalObject()) + instance.internalObject()->disconnect(); + instance.makeInvalid(); + } + + m_nodeInstanceHash.clear(); + m_objectInstanceHash.clear(); +} + +/*! \brief Returns a List of all NodeInstances + +\see NodeInstance +*/ + +QList<NodeInstance> NodeInstanceView::instances() const +{ + return m_nodeInstanceHash.values(); +} + +/*! \brief Returns the NodeInstance for this ModelNode + + Returns a invalid NodeInstance if no NodeInstance for this ModelNode exists. + +\param node ModelNode must be valid. +\returns NodeStance for ModelNode. +\see NodeInstance +*/ +NodeInstance NodeInstanceView::instanceForNode(const ModelNode &node) +{ + Q_ASSERT(node.isValid()); + Q_ASSERT(m_nodeInstanceHash.contains(node)); + Q_ASSERT(m_nodeInstanceHash.value(node).modelNode() == node); + return m_nodeInstanceHash.value(node); +} + +bool NodeInstanceView::hasInstanceForNode(const ModelNode &node) +{ + return m_nodeInstanceHash.contains(node); +} + +NodeInstance NodeInstanceView::instanceForObject(QObject *object) +{ + if (object == 0) + return NodeInstance(); + + return m_objectInstanceHash.value(object); +} + +bool NodeInstanceView::hasInstanceForObject(QObject *object) +{ + if (object == 0) + return false; + + return m_objectInstanceHash.contains(object); +} + + +/*! \brief Returns the root NodeInstance of this view. + + +\returns Root NodeIntance for this view. +\see NodeInstance +*/ +NodeInstance NodeInstanceView::rootNodeInstance() const +{ + return m_rootNodeInstance; +} + +/*! \brief Returns the view NodeInstance of this view. + + This can be the root NodeInstance if it is specified in the qml file. +\code + QGraphicsView { + QGraphicsScene { + Item {} + } + } +\endcode + + If there is node view in the qml file: + \code + + Item {} + +\endcode + Than there will be a new NodeInstance for this QGraphicsView + generated which is not the root instance of this NodeInstanceView. + + This is the way to get this QGraphicsView NodeInstance. + +\returns Root NodeIntance for this view. +\see NodeInstance +*/ + +static bool isTransformProperty(const QString &name) +{ + static QStringList transformProperties(QStringList() << "xChanged()" << "yChanged()" << "zChanged()" << "rotationChanged()" << "scaleChanged()" << "widthChanged()" << "heightChanged()"); + + return transformProperties.contains(name); +} + +void NodeInstanceView::insertInstanceNodeRelationship(const ModelNode &node, const NodeInstance &instance) +{ + // connect with every signal of the object to get the formeditor updated + int otherPropertuChangedSlotIndex = staticMetaObject.indexOfSlot("emitOtherPropertyChanged()"); + int transformPropertuChangedSlotIndex = staticMetaObject.indexOfSlot("emitTransformPropertyChanged()"); + int parentPropertuChangedSlotIndex = staticMetaObject.indexOfSlot("emitParentPropertyChanged()"); + for (int index = QObject::staticMetaObject.methodCount(); + index < instance.internalObject()->metaObject()->methodCount(); + index++) { + QMetaMethod metaMethod = instance.internalObject()->metaObject()->method(index); + if (metaMethod.methodType() == QMetaMethod::Signal) { + if (isTransformProperty(metaMethod.signature())) { + staticMetaObject.connect(instance.internalObject(),index, this, transformPropertuChangedSlotIndex); + } else if (QByteArray(metaMethod.signature()) == QByteArray("parentChanged()")) { + staticMetaObject.connect(instance.internalObject(),index, this, parentPropertuChangedSlotIndex); + } else { + staticMetaObject.connect(instance.internalObject(),index, this, otherPropertuChangedSlotIndex); + } + } + } + + instance.internalObject()->installEventFilter(childrenChangeEventFilter()); + + Q_ASSERT(!m_nodeInstanceHash.contains(node)); + m_nodeInstanceHash.insert(node, instance); + m_objectInstanceHash.insert(instance.internalObject(), instance); +} + +QmlEngine *NodeInstanceView::engine() const +{ + return m_engine.data(); +} + +Internal::ChildrenChangeEventFilter *NodeInstanceView::childrenChangeEventFilter() +{ + if (m_childrenChangeEventFilter.isNull()) { + m_childrenChangeEventFilter = new Internal::ChildrenChangeEventFilter(this); + connect(m_childrenChangeEventFilter.data(), SIGNAL(childrenChanged(QObject*)), this, SLOT(emitUpdateItem(QObject*))); + } + + return m_childrenChangeEventFilter.data(); +} + +void NodeInstanceView::removeInstanceNodeRelationship(const ModelNode &node) +{ + Q_ASSERT(m_nodeInstanceHash.contains(node)); + NodeInstance instance = instanceForNode(node); + m_objectInstanceHash.remove(instanceForNode(node).internalObject()); + m_nodeInstanceHash.remove(node); + emit instanceRemoved(instance); + instance.makeInvalid(); +} + +void NodeInstanceView::emitOtherPropertyChanged() +{ + if (m_blockChangeSignal) + return; + if (hasInstanceForObject(sender())) + emit otherPropertyChanged(instanceForObject(sender())); +} + +void NodeInstanceView::emitParentPropertyChanged() +{ + if (m_blockChangeSignal) + return; + if (hasInstanceForObject(sender())) + emit parentPropertyChanged(instanceForObject(sender())); +} + +void NodeInstanceView::emitTransformPropertyChanged() +{ + if (m_blockChangeSignal) + return; + if (hasInstanceForObject(sender())) + emit transformPropertyChanged(instanceForObject(sender())); +} + +void NodeInstanceView::emitUpdateItem(QObject *object) +{ + if (m_blockChangeSignal) + return; + if (hasInstanceForObject(object)) + emit updateItem(instanceForObject(object)); +} + + +NodeInstance NodeInstanceView::loadNode(const ModelNode &node, QObject *objectToBeWrapped) +{ + Q_ASSERT(node.isValid()); + NodeInstance instance(NodeInstance::create(this, node, objectToBeWrapped)); + + insertInstanceNodeRelationship(node, instance); + + if (node.isRootNode()) { + m_rootNodeInstance = instance; + QGraphicsObject *rootGraphicsObject = qobject_cast<QGraphicsObject*>(instance.internalObject()); + if (rootGraphicsObject) { + m_graphicsView->scene()->addItem(rootGraphicsObject); + m_graphicsView->setSceneRect(rootGraphicsObject->boundingRect()); + } + } + + return instance; +} + +void NodeInstanceView::removeRecursiveChildRelationship(const ModelNode &removedNode) +{ + foreach (const ModelNode &childNode, removedNode.allDirectSubModelNodes()) + removeRecursiveChildRelationship(childNode); + + removeInstanceNodeRelationship(removedNode); +} + +void NodeInstanceView::render(QPainter * painter, const QRectF &target, const QRectF &source, Qt::AspectRatioMode aspectRatioMode) +{ + if (m_graphicsView) { + painter->save(); + painter->setRenderHint(QPainter::Antialiasing, true); + painter->setRenderHint(QPainter::TextAntialiasing, true); + painter->setRenderHint(QPainter::SmoothPixmapTransform, true); + painter->setRenderHint(QPainter::HighQualityAntialiasing, true); + painter->setRenderHint(QPainter::NonCosmeticDefaultPen, true); + m_graphicsView->scene()->render(painter, target, source, aspectRatioMode); + painter->restore(); + } +} + +QRectF NodeInstanceView::boundingRect() const +{ + if (m_graphicsView) + return m_graphicsView->scene()->itemsBoundingRect(); + + return QRectF(); +} +QRectF NodeInstanceView::sceneRect() const +{ + if (m_graphicsView) + return m_graphicsView->sceneRect(); + + return QRectF(); +} + +void NodeInstanceView::setBlockChangeSignal(bool block) +{ + m_blockChangeSignal = block; +} + +} diff --git a/src/plugins/qmldesigner/core/instances/objectnodeinstance.cpp b/src/plugins/qmldesigner/core/instances/objectnodeinstance.cpp new file mode 100644 index 00000000000..6ebcc111da4 --- /dev/null +++ b/src/plugins/qmldesigner/core/instances/objectnodeinstance.cpp @@ -0,0 +1,690 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "objectnodeinstance.h" + +#include <invalidreparentingexception.h> +#include <invalidnodeinstanceexception.h> +#include <notimplementedexception.h> +#include <noanchoringpossibleexception.h> + +#include <QmlContext> +#include <QtDeclarative/private/qmlcontext_p.h> +#include <QmlList> +#include <QmlBinding> +#include <private/qmllistaccessor_p.h> + +#include <metainfo.h> +#include <propertymetainfo.h> +#include <qmlmetaproperty.h> +#include <QmlMetaType> +#include <QmlEngine> +#include <private/qmlgraphicsanchors_p.h> +#include <private/qmlgraphicsrectangle_p.h> + +#include "qmlgraphicsitemnodeinstance.h" +#include "graphicsobjectnodeinstance.h" +#include "graphicsviewnodeinstance.h" +#include "graphicsscenenodeinstance.h" +#include "graphicswidgetnodeinstance.h" +#include "qmlviewnodeinstance.h" +#include "widgetnodeinstance.h" +#include "proxywidgetnodeinstance.h" +#include "variantproperty.h" +#include <nodelistproperty.h> + +namespace QmlDesigner { +namespace Internal { + +ChildrenChangeEventFilter::ChildrenChangeEventFilter(QObject *parent) + : QObject(parent) +{ +} + + +bool ChildrenChangeEventFilter::eventFilter(QObject *object, QEvent *event) +{ + switch (event->type()) { + case QEvent::ChildAdded: + case QEvent::ChildRemoved: emit childrenChanged(object); break; + default: break; + } + + return false; +} + +ObjectNodeInstance::ObjectNodeInstance(QObject *object) + : m_deleteHeldInstance(true), + m_object(object) +{ +} + +ObjectNodeInstance::~ObjectNodeInstance() +{ + destroy(); +} + +static bool isChildrenProperty(const QString &name) +{ + return name == "data" || name == "children" || name == "resources"; +} + +static void specialRemoveParentForQmlGraphicsItemChildren(QObject *object) +{ + QmlGraphicsItem *item = qobject_cast<QmlGraphicsItem*>(object); + if (item && item->scene()) + item->scene()->removeItem(item); + + object->setParent(0); +} + +void ObjectNodeInstance::destroy() +{ + // Remove from old property + if (object() && modelNode().isValid() && modelNode().parentProperty().isValid()) { + NodeAbstractProperty parentProperty = modelNode().parentProperty(); + ModelNode parentNode = parentProperty.parentModelNode(); + if (parentNode.isValid() && nodeInstanceView()->hasInstanceForNode(parentNode)) { + NodeInstance parentInstance = nodeInstanceView()->instanceForNode(parentNode); + if (parentInstance.isQmlGraphicsItem() && isChildrenProperty(parentProperty.name())) { + specialRemoveParentForQmlGraphicsItemChildren(object()); + } else { + removeFromOldProperty(object(), parentInstance.internalObject(), parentProperty.name()); + } + } + } + + if (deleteHeldInstance() && object()) { + if (!object()->objectName().isEmpty()) { + context()->engine()->rootContext()->setContextProperty(object()->objectName(), 0); + } + QObject *obj = object(); + m_object.clear(); + delete obj; + } +} + +ModelNode ObjectNodeInstance::modelNode() const +{ + return m_modelNode; +} + +void ObjectNodeInstance::setModelNode(const ModelNode &node) +{ + m_modelNode = node; +} + +NodeInstanceView *ObjectNodeInstance::nodeInstanceView() const +{ + return m_nodeInstanceView.data(); +} + +void ObjectNodeInstance::setNodeInstance(NodeInstanceView *view) +{ + m_nodeInstanceView = view; +} + +void ObjectNodeInstance::setId(const QString &id) +{ + object()->setObjectName(id); + + if (!id.isEmpty()) { + context()->engine()->rootContext()->setContextProperty(id, object()); // will also force refresh of all bindings + } else { + context()->engine()->rootContext()->setContextProperty(id, 0); + } +} + +bool ObjectNodeInstance::isQmlGraphicsItem() const +{ + return false; +} + +bool ObjectNodeInstance::isGraphicsScene() const +{ + return false; +} + +bool ObjectNodeInstance::isGraphicsView() const +{ + return false; +} + +bool ObjectNodeInstance::isGraphicsWidget() const +{ + return false; +} + +bool ObjectNodeInstance::isProxyWidget() const +{ + return false; +} + +bool ObjectNodeInstance::isWidget() const +{ + return false; +} + +bool ObjectNodeInstance::isQmlView() const +{ + return false; +} + +bool ObjectNodeInstance::isGraphicsObject() const +{ + return false; +} + +bool ObjectNodeInstance::isTransition() const +{ + return false; +} + +bool ObjectNodeInstance::equalGraphicsItem(QGraphicsItem * /*item*/) const +{ + return false; +} + +QTransform ObjectNodeInstance::transform() const +{ + return QTransform(); +} + +QTransform ObjectNodeInstance::customTransform() const +{ + return QTransform(); +} + +QTransform ObjectNodeInstance::sceneTransform() const +{ + return QTransform(); +} + +double ObjectNodeInstance::rotation() const +{ + return 0.0; +} + +double ObjectNodeInstance::scale() const +{ + return 1.0; +} + +QList<QGraphicsTransform *> ObjectNodeInstance::transformations() const +{ + QList<QGraphicsTransform *> transformationsList; + + return transformationsList; +} + +QPointF ObjectNodeInstance::transformOriginPoint() const +{ + return QPoint(); +} + +double ObjectNodeInstance::zValue() const +{ + return 0.0; +} + +double ObjectNodeInstance::opacity() const +{ + return 1.0; +} + +bool ObjectNodeInstance::hasAnchor(const QString &/*name*/) const +{ + return false; +} + +bool ObjectNodeInstance::isAnchoredBy() const +{ + return false; +} + +QPair<QString, NodeInstance> ObjectNodeInstance::anchor(const QString &/*name*/) const +{ + return qMakePair(QString(), NodeInstance()); +} + + +static bool isList(const QmlMetaProperty &metaProperty) +{ + return metaProperty.propertyCategory() == QmlMetaProperty::List || metaProperty.propertyCategory() == QmlMetaProperty::QmlList; +} + +static bool isObject(const QmlMetaProperty &metaProperty) +{ + return metaProperty.propertyCategory() == QmlMetaProperty::Object; +} + +static QVariant objectToVariant(QObject *object) +{ + const QMetaObject *metaObject = object->metaObject(); + QmlType *qmlType = QmlMetaType::qmlType(metaObject); + + return qmlType->fromObject(object); +} + +static void removeObjectFromList(const QmlMetaProperty &metaProperty, QObject *object, QmlEngine *engine) +{ + QmlListAccessor listAccessor; + listAccessor.setList(metaProperty.read(), engine); + + for (int i = 0; i < listAccessor.count(); ++i) { + if (QmlMetaType::toQObject(listAccessor.at(i)) == object) { + listAccessor.removeAt(i); + break; + } + } +} + +void ObjectNodeInstance::removeFromOldProperty(QObject *object, QObject *oldParent, const QString &oldParentProperty) +{ + QmlMetaProperty metaProperty = QmlMetaProperty::createProperty(oldParent, oldParentProperty, context()); + + if (isList(metaProperty)) { + removeObjectFromList(metaProperty, object, nodeInstanceView()->engine()); + } else if (isObject(metaProperty)) { + resetProperty(object, oldParentProperty); + } + + object->setParent(0); +} + +void ObjectNodeInstance::addToNewProperty(QObject *object, QObject *newParent, const QString &newParentProperty) +{ + QmlMetaProperty metaProperty = QmlMetaProperty::createProperty(newParent, newParentProperty, context()); + + if (isList(metaProperty)) { + QmlListAccessor listAccessor; + listAccessor.setList(metaProperty.read(), nodeInstanceView()->engine()); + listAccessor.append(objectToVariant(object)); + } else if (isObject(metaProperty)) { + metaProperty.write(objectToVariant(object)); + } + object->setParent(newParent); + + Q_ASSERT(objectToVariant(object).isValid()); +} + +static void specialSetParentForQmlGraphicsItemChildren(QObject *object, QmlGraphicsItem *parent) +{ + QmlGraphicsItem *item = qobject_cast<QmlGraphicsItem*>(object); + if (item) + item->setParentItem(parent); + else + object->setParent(parent); +} + +void ObjectNodeInstance::reparent(const NodeInstance &oldParentInstance, const QString &oldParentProperty, const NodeInstance &newParentInstance, const QString &newParentProperty) +{ + if (oldParentInstance.isValid()) { + if (oldParentInstance.isQmlGraphicsItem() && isChildrenProperty(oldParentProperty)) + specialRemoveParentForQmlGraphicsItemChildren(object()); + else + removeFromOldProperty(object(), oldParentInstance.internalObject(), oldParentProperty); + } + + if (newParentInstance.isValid()) { + if (newParentInstance.isQmlGraphicsItem() && isChildrenProperty(newParentProperty)) + specialSetParentForQmlGraphicsItemChildren(object(), qobject_cast<QmlGraphicsItem*>(newParentInstance.internalObject())); + else + addToNewProperty(object(), newParentInstance.internalObject(), newParentProperty); + } + + refreshBindings(context()->engine()->rootContext()); +} + +void ObjectNodeInstance::setPropertyVariant(const QString &name, const QVariant &valueArg) +{ + QVariant value = valueArg; + + QmlMetaProperty qmlMetaProperty = QmlMetaProperty::createProperty(object(), name, context()); + +// if (qmlMetaProperty.propertyType() == QVariant::Url) { +// QUrl url = value.toUrl(); +// if (url.isRelative()) { +// QUrl baseUrl(nodeInstanceView()->model()->fileUrl()); +// value.setValue(baseUrl.resolved(url)); +// } +// } + + qmlMetaProperty.write(value); +} + +void ObjectNodeInstance::setPropertyBinding(const QString &name, const QString &expression) +{ + QmlContext *qmlContext = QmlEngine::contextForObject(object()); + + QmlMetaProperty metaProperty = QmlMetaProperty::createProperty(object(), name, context()); + if (metaProperty.isValid() && metaProperty.isProperty()) { + QmlBinding *qmlBinding = new QmlBinding(expression, object(), qmlContext); + qmlBinding->setTarget(metaProperty); + qmlBinding->setTrackChange(true); + QmlAbstractBinding *oldBinding = metaProperty.setBinding(qmlBinding); + delete oldBinding; + qmlBinding->update(); + } else { + qWarning() << "Cannot set binding for property" << name << ": property is unknown for type" + << (modelNode().isValid() ? modelNode().type() : "unknown"); + } +} + +void ObjectNodeInstance::deleteObjectsInList(const QmlMetaProperty &metaProperty) +{ + QObjectList objectList; + QmlListAccessor listAccessor; + listAccessor.setList(metaProperty.read()); + + for(int i = 0; i < listAccessor.count(); i++) { + objectList += QmlMetaType::toQObject(listAccessor.at(i)); + } + + listAccessor.clear(); +} + +void ObjectNodeInstance::resetProperty(const QString &name) +{ + resetProperty(object(), name); + + if (name == "font.pixelSize") + resetProperty(object(), "font.pointSize"); + + if (name == "font.pointSize") + resetProperty(object(), "font.pixelSize"); +} + +NodeInstance ObjectNodeInstance::instanceForNode(const ModelNode &node, const QString &fullname) +{ + if (nodeInstanceView()->hasInstanceForNode(node)) { + return nodeInstanceView()->instanceForNode(node); + } else { + NodeInstance instance(nodeInstanceView()->loadNode(node)); + m_modelAbstractPropertyHash.insert(fullname, instance); + return instance; + } +} + +void ObjectNodeInstance::resetProperty(QObject *object, const QString &propertyName) +{ + m_modelAbstractPropertyHash.remove(propertyName); + + QmlMetaProperty qmlMetaProperty = QmlMetaProperty::createProperty(object, propertyName, context()); + QMetaProperty metaProperty = qmlMetaProperty.property(); + + QmlAbstractBinding *binding = qmlMetaProperty.binding(); + if (binding) { + binding->setEnabled(false, 0); + delete binding; + } + + if (metaProperty.isResettable()) { + metaProperty.reset(object); + } else if (qmlMetaProperty.isWritable()) { + if (qmlMetaProperty.read() == resetValue(propertyName)) + return; + qmlMetaProperty.write(resetValue(propertyName)); + } else if (QmlMetaType::isList(qmlMetaProperty.propertyType()) || + QmlMetaType::isQmlList(qmlMetaProperty.propertyType())) { + QmlMetaType::clear(object->property(propertyName.toLatin1())); + } +} + +QVariant ObjectNodeInstance::property(const QString &name) const +{ + if (m_modelAbstractPropertyHash.contains(name)) + return QVariant::fromValue(m_modelAbstractPropertyHash.value(name)); + + + // TODO: handle model nodes + + QmlMetaProperty metaProperty = QmlMetaProperty::createProperty(object(), name, context()); + if (metaProperty.property().isEnumType()) { + QVariant value = object()->property(name.toLatin1()); + return metaProperty.property().enumerator().valueToKey(value.toInt()); + } + + if (metaProperty.propertyType() == QVariant::Url) { + QUrl url = metaProperty.read().toUrl(); + if (url.isEmpty()) + return QVariant(); + + if (url.scheme() == "file") { + int basePathLength = nodeInstanceView()->model()->fileUrl().toLocalFile().lastIndexOf("/"); + return QUrl(url.toLocalFile().mid(basePathLength + 1)); + } + } + + return metaProperty.read(); +} + + +void ObjectNodeInstance::setDeleteHeldInstance(bool deleteInstance) +{ + m_deleteHeldInstance = deleteInstance; +} + +bool ObjectNodeInstance::deleteHeldInstance() const +{ + return m_deleteHeldInstance; +} + +ObjectNodeInstance::Pointer ObjectNodeInstance::create(const NodeMetaInfo &nodeMetaInfo, QmlContext *context, QObject *objectToBeWrapped) +{ + QObject *object = 0; + if (objectToBeWrapped) + object = objectToBeWrapped; + else + object = createObject(nodeMetaInfo, context); + + Pointer instance(new ObjectNodeInstance(object)); + + if (objectToBeWrapped) + instance->setDeleteHeldInstance(false); // the object isn't owned + + instance->populateResetValueHash(); + + return instance; +} + +void ObjectNodeInstance::updateObjectSignals(QObject *object) +{ + // iterates over all properties and find the QObjects. + // connect all signal of this property objects to the visibleChanged signal of this object + // to get updates of this item + int visibleSignalIndex = QGraphicsObject::staticMetaObject.indexOfSignal("visibleChanged()"); + int transformSignalIndex = QGraphicsObject::staticMetaObject.indexOfSignal("xChanged()"); + // iterate over all proeprties and find the object + for(int propertyIndex = QObject::staticMetaObject.propertyCount(); propertyIndex < object->metaObject()->propertyCount(); propertyIndex++) { + if (QmlMetaType::isObject(object->metaObject()->property(propertyIndex).userType())) { + QObject *propertyObject = QmlMetaType::toQObject(object->metaObject()->property(propertyIndex).read(object)); + if (propertyObject) { + int signalIndex = -1; + if (qobject_cast<QmlGraphicsAnchors*>(propertyObject) + || qobject_cast<QmlGraphicsPen*>(propertyObject)) // test if this is a anchor and use the tranform signal instead + signalIndex = transformSignalIndex; + else + signalIndex = visibleSignalIndex; + + // connect with every signal of the object to get the formeditor updated + + for (int methodIndex = QObject::staticMetaObject.methodCount(); + methodIndex < propertyObject->metaObject()->methodCount(); + methodIndex++) { + QMetaMethod metaMethod = propertyObject->metaObject()->method(methodIndex); + if (metaMethod.methodType() == QMetaMethod::Signal) { + QObject::staticMetaObject.connect(propertyObject, methodIndex, object, signalIndex); + } + } + + } + } + } +} + +QObject* ObjectNodeInstance::createObject(const NodeMetaInfo &metaInfo, QmlContext *context) +{ + QObject *object = metaInfo.createInstance(context); + + if (object == 0) + throw InvalidNodeInstanceException(__LINE__, __FUNCTION__, __FILE__); + + updateObjectSignals(object); + + return object; +} + +QObject *ObjectNodeInstance::object() const +{ + return m_object.data(); +} + +bool ObjectNodeInstance::hasContent() const +{ + return false; +} + +void ObjectNodeInstance::updateAnchors() +{ +} + +QmlContext *ObjectNodeInstance::context() const +{ + QmlContext *context = QmlEngine::contextForObject(object()); + if (context) + return context; + else if (nodeInstanceView()) + return nodeInstanceView()->engine()->rootContext(); + + return 0; +} + +void ObjectNodeInstance::paintUpdate() +{ +} + +QStringList propertyNameForWritableProperties(QObject *object, const QString &baseName = QString()) +{ + QStringList propertyNameList; + + const QMetaObject *metaObject = object->metaObject(); + for (int index = 0; index < metaObject->propertyCount(); ++index) { + QMetaProperty metaProperty = metaObject->property(index); + if (metaProperty.isReadable() && !metaProperty.isWritable()) { + QObject *childObject = QmlMetaType::toQObject(metaProperty.read(object)); + if (childObject) + propertyNameList.append(propertyNameForWritableProperties(childObject, baseName + QString::fromUtf8(metaProperty.name()) + ".")); + } else if (QmlValueTypeFactory::valueType(metaProperty.userType())) { + QmlValueType *valueType = QmlValueTypeFactory::valueType(metaProperty.userType()); + valueType->setValue(metaProperty.read(object)); + propertyNameList.append(propertyNameForWritableProperties(valueType, baseName + QString::fromUtf8(metaProperty.name()) + ".")); + } else if (metaProperty.isReadable() && metaProperty.isWritable()) { + propertyNameList.append(baseName + QString::fromUtf8(metaProperty.name())); + } + } + + return propertyNameList; +} + +void ObjectNodeInstance::populateResetValueHash() +{ + QStringList propertyNameList = propertyNameForWritableProperties(object()); + + foreach(const QString &propertyName, propertyNameList) { + QmlMetaProperty metaProperty = QmlMetaProperty::createProperty(object(), propertyName, context()); + if (metaProperty.isWritable()) + m_resetValueHash.insert(propertyName, metaProperty.read()); + } +} + +QVariant ObjectNodeInstance::resetValue(const QString &propertyName) const +{ + return m_resetValueHash.value(propertyName); +} + +void ObjectNodeInstance::paint(QPainter * /*painter*/) const +{ +} + +bool ObjectNodeInstance::isTopLevel() const +{ + return false; +} + +QObject *ObjectNodeInstance::parent() const +{ + if (!object()) + return 0; + + return object()->parent(); +} + +QRectF ObjectNodeInstance::boundingRect() const +{ + return QRect(); +} + +QPointF ObjectNodeInstance::position() const +{ + return QPointF(); +} + +QSizeF ObjectNodeInstance::size() const +{ + return QSizeF(); +} + +bool ObjectNodeInstance::isVisible() const +{ + return false; +} + +void ObjectNodeInstance::setVisible(bool /*isVisible*/) +{ +} + +void ObjectNodeInstance::createDynamicProperty(const QString &/*name*/, const QString &/*typeName*/) +{ + //todo not implemented at all + +} + +/** + Force all bindings in this or a sub context to be re-evaluated. + */ +void ObjectNodeInstance::refreshBindings(QmlContext *context) +{ + // TODO: Maybe do this via a timer to prevent update flooding + QmlContextPrivate::get(context)->refreshExpressions(); +} + +} +} + diff --git a/src/plugins/qmldesigner/core/instances/objectnodeinstance.h b/src/plugins/qmldesigner/core/instances/objectnodeinstance.h new file mode 100644 index 00000000000..16b6af3a42f --- /dev/null +++ b/src/plugins/qmldesigner/core/instances/objectnodeinstance.h @@ -0,0 +1,185 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef ABSTRACTNODEINSTANCE_H +#define ABSTRACTNODEINSTANCE_H + +#include <QPainter> +#include <QStyleOptionGraphicsItem> +#include "modelnode.h" +#include <QSharedPointer> +#include <QWeakPointer> +#include <propertymetainfo.h> +#include <nodeinstanceview.h> + +class QGraphicsItem; +class QmlContext; +class QmlMetaProperty; +class QmlContext; +class QmlBinding; + +namespace QmlDesigner { + + +namespace Internal { + +class QmlGraphicsItemNodeInstance; +class GraphicsWidgetNodeInstance; +class GraphicsViewNodeInstance; +class GraphicsSceneNodeInstance; +class ProxyWidgetNodeInstance; +class WidgetNodeInstance; +class QmlViewNodeInstance; + +class ChildrenChangeEventFilter : public QObject +{ + Q_OBJECT +public: + ChildrenChangeEventFilter(QObject *parent); + + +signals: + void childrenChanged(QObject *object); + +protected: + bool eventFilter(QObject *object, QEvent *event); + +}; + +class ObjectNodeInstance +{ +public: + typedef QSharedPointer<ObjectNodeInstance> Pointer; + typedef QWeakPointer<ObjectNodeInstance> WeakPointer; + ObjectNodeInstance(QObject *object); + + virtual ~ObjectNodeInstance(); + void destroy(); + //void setModelNode(const ModelNode &node); + + static Pointer create(const NodeMetaInfo &metaInfo, QmlContext *context, QObject *objectToBeWrapped); + + ModelNode modelNode() const; + void setModelNode(const ModelNode &node); + + NodeInstanceView *nodeInstanceView() const; + void setNodeInstance(NodeInstanceView *view); + + virtual void paint(QPainter *painter) const; + + virtual bool isTopLevel() const; + + virtual QObject *parent() const; + + void reparent(const NodeInstance &oldParentInstance, const QString &oldParentProperty, const NodeInstance &newParentInstance, const QString &newParentProperty); + + void setId(const QString &id); + + virtual bool isQmlGraphicsItem() const; + virtual bool isGraphicsScene() const; + virtual bool isGraphicsView() const; + virtual bool isGraphicsWidget() const; + virtual bool isProxyWidget() const; + virtual bool isWidget() const; + virtual bool isQmlView() const; + virtual bool isGraphicsObject() const; + virtual bool isTransition() const; + + virtual bool equalGraphicsItem(QGraphicsItem *item) const; + + virtual QRectF boundingRect() const; + + virtual QPointF position() const; + virtual QSizeF size() const; + virtual QTransform transform() const; + virtual QTransform customTransform() const; + virtual QTransform sceneTransform() const; + virtual double opacity() const; + + virtual bool hasAnchor(const QString &name) const; + virtual QPair<QString, NodeInstance> anchor(const QString &name) const; + virtual bool isAnchoredBy() const; + + virtual double rotation() const; + virtual double scale() const; + virtual QList<QGraphicsTransform *> transformations() const; + virtual QPointF transformOriginPoint() const; + virtual double zValue() const; + + virtual void setPropertyVariant(const QString &name, const QVariant &value); + virtual void setPropertyBinding(const QString &name, const QString &expression); + virtual QVariant property(const QString &name) const; + virtual void resetProperty(const QString &name); + virtual bool isVisible() const; + virtual void setVisible(bool isVisible); + + void createDynamicProperty(const QString &name, const QString &typeName); + void setDeleteHeldInstance(bool deleteInstance); + bool deleteHeldInstance() const; + + virtual void updateAnchors(); + virtual void paintUpdate(); + + void populateResetValueHash(); + QVariant resetValue(const QString &propertyName) const; + + QObject *object() const; + + virtual bool hasContent() const; + + QmlContext *context() const; + +protected: + static QObject* createObject(const NodeMetaInfo &metaInfo, QmlContext *context); + + void resetProperty(QObject *object, const QString &propertyName); + static void updateObjectSignals(QObject *object); + NodeInstance instanceForNode(const ModelNode &node, const QString &fullname); + + void removeFromOldProperty(QObject *object, QObject *oldParent, const QString &oldParentProperty); + void addToNewProperty(QObject *object, QObject *newParent, const QString &newParentProperty); + void deleteObjectsInList(const QmlMetaProperty &metaProperty); + +private: + static void refreshBindings(QmlContext *context); + + QHash<QString, QVariant> m_resetValueHash; + QHash<QString, NodeInstance> m_modelAbstractPropertyHash; + ModelNode m_modelNode; + + QWeakPointer<NodeInstanceView> m_nodeInstanceView; + bool m_deleteHeldInstance; + QWeakPointer<QObject> m_object; +}; + + +} // namespace Internal +} // namespace QmlDesigner + +#endif // ABSTRACTNODEINSTANCE_H diff --git a/src/plugins/qmldesigner/core/instances/proxywidgetnodeinstance.cpp b/src/plugins/qmldesigner/core/instances/proxywidgetnodeinstance.cpp new file mode 100644 index 00000000000..1749e7304b5 --- /dev/null +++ b/src/plugins/qmldesigner/core/instances/proxywidgetnodeinstance.cpp @@ -0,0 +1,82 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "proxywidgetnodeinstance.h" + +#include <QmlMetaType> + +#include <QGraphicsProxyWidget> + +namespace QmlDesigner { +namespace Internal { + + +ProxyWidgetNodeInstance::ProxyWidgetNodeInstance(QGraphicsProxyWidget *widget) + : GraphicsWidgetNodeInstance(widget) +{ +} + + +ProxyWidgetNodeInstance::Pointer ProxyWidgetNodeInstance::create(const QString &typeName) +{ + QObject *object = QmlMetaType::qmlType(typeName.toLatin1(), 4, 6)->create(); + Q_ASSERT(object); + if (object == 0) + return Pointer(); + + QGraphicsProxyWidget* widget = qobject_cast<QGraphicsProxyWidget*>(object); + Q_ASSERT(widget); + if (widget == 0) + return Pointer(); + + return Pointer(new ProxyWidgetNodeInstance(widget)); +} + +QGraphicsProxyWidget* ProxyWidgetNodeInstance::proxyWidget() const +{ + QGraphicsProxyWidget* proxyWidget = qobject_cast<QGraphicsProxyWidget*>(graphicsWidget()); + Q_ASSERT(proxyWidget); + + return proxyWidget; +} + + +void ProxyWidgetNodeInstance::setWidget(QWidget *widget) +{ + proxyWidget()->setWidget(widget); +} + +bool ProxyWidgetNodeInstance::isProxyWidget() const +{ + return true; +} + + +} +} diff --git a/src/plugins/qmldesigner/core/instances/proxywidgetnodeinstance.h b/src/plugins/qmldesigner/core/instances/proxywidgetnodeinstance.h new file mode 100644 index 00000000000..1c0ff8133d0 --- /dev/null +++ b/src/plugins/qmldesigner/core/instances/proxywidgetnodeinstance.h @@ -0,0 +1,59 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef PROXYWIDGETNODEINSTANCE_H +#define PROXYWIDGETNODEINSTANCE_H + + +#include "graphicswidgetnodeinstance.h" + +namespace QmlDesigner { +namespace Internal { + +class ProxyWidgetNodeInstance : public GraphicsWidgetNodeInstance +{ +public: + typedef QSharedPointer<ProxyWidgetNodeInstance> Pointer; + typedef QWeakPointer<ProxyWidgetNodeInstance> WeakPointer; + + static Pointer create(const QString &typeName); + + void setWidget(QWidget *widget); + + bool isProxyWidget() const; + +protected: + ProxyWidgetNodeInstance(QGraphicsProxyWidget *widget); + + QGraphicsProxyWidget* proxyWidget() const; +}; + +} +} +#endif // PROXYWIDGETNODEINSTANCE_H diff --git a/src/plugins/qmldesigner/core/instances/qmlgraphicsitemnodeinstance.cpp b/src/plugins/qmldesigner/core/instances/qmlgraphicsitemnodeinstance.cpp new file mode 100644 index 00000000000..d2abed4752e --- /dev/null +++ b/src/plugins/qmldesigner/core/instances/qmlgraphicsitemnodeinstance.cpp @@ -0,0 +1,562 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "qmlgraphicsitemnodeinstance.h" +#include "qmlviewnodeinstance.h" +#include "graphicsscenenodeinstance.h" + + +#include <invalidnodeinstanceexception.h> +#include <propertymetainfo.h> + +#include "bindingproperty.h" +#include "variantproperty.h" + +#include <private/qmlgraphicsanchors_p.h> +#include <private/qmlgraphicsanchors_p_p.h> + +#include <private/qmlexpression_p.h> + +#include <cmath> + +#include <QHash> + +namespace QmlDesigner { +namespace Internal { + +QmlGraphicsItemNodeInstance::QmlGraphicsItemNodeInstance(QmlGraphicsItem *item, bool hasContent) + : GraphicsObjectNodeInstance(item, hasContent), + m_hasHeight(false), + m_hasWidth(false) +{ +} + +QmlGraphicsItemNodeInstance::~QmlGraphicsItemNodeInstance() +{ +} + +QmlGraphicsItemNodeInstance::Pointer QmlGraphicsItemNodeInstance::create(const NodeMetaInfo &metaInfo, QmlContext *context, QObject *objectToBeWrapped) +{ + QPair<QGraphicsObject*, bool> objectPair; + + if (objectToBeWrapped) + objectPair = qMakePair(qobject_cast<QGraphicsObject*>(objectToBeWrapped), false); + else + objectPair = GraphicsObjectNodeInstance::createGraphicsObject(metaInfo, context); + + QmlGraphicsItem *qmlGraphicsItem = dynamic_cast<QmlGraphicsItem*>(objectPair.first); + + if (qmlGraphicsItem == 0) + throw InvalidNodeInstanceException(__LINE__, __FUNCTION__, __FILE__); + + Pointer instance(new QmlGraphicsItemNodeInstance(qmlGraphicsItem, objectPair.second)); + + if (objectToBeWrapped) + instance->setDeleteHeldInstance(false); // the object isn't owned + + instance->populateResetValueHash(); + + return instance; +} + +bool QmlGraphicsItemNodeInstance::isQmlGraphicsItem() const +{ + return true; +} + +QSizeF QmlGraphicsItemNodeInstance::size() const +{ + if (modelNode().isValid()) { + double implicitWidth = qmlGraphicsItem()->implicitWidth(); + if (!m_hasWidth + && implicitWidth // WORKAROUND + && implicitWidth != qmlGraphicsItem()->width()) + qmlGraphicsItem()->setWidth(implicitWidth); + + double implicitHeight = qmlGraphicsItem()->implicitHeight(); + if (!m_hasHeight + && implicitWidth // WORKAROUND + && implicitHeight != qmlGraphicsItem()->height()) + qmlGraphicsItem()->setHeight(implicitHeight); + + } + + return QSizeF(qmlGraphicsItem()->width(), qmlGraphicsItem()->height()); +} + +QRectF QmlGraphicsItemNodeInstance::boundingRect() const +{ + if (modelNode().isValid()) { + double implicitWidth = qmlGraphicsItem()->implicitWidth(); + if (!m_hasWidth + && implicitWidth // WORKAROUND + && implicitWidth != qmlGraphicsItem()->width()) + qmlGraphicsItem()->setWidth(implicitWidth); + + double implicitHeight = qmlGraphicsItem()->implicitHeight(); + if (!m_hasHeight + && implicitWidth // WORKAROUND + && implicitHeight != qmlGraphicsItem()->height()) + qmlGraphicsItem()->setHeight(implicitHeight); + + } + + return qmlGraphicsItem()->boundingRect(); +} + +//QVariant anchorLineFor(QmlGraphicsItem *item, const AnchorLine &anchorLine) +//{ +// switch(anchorLine.type()) { +// case AnchorLine::Top : return item->property("top"); +// case AnchorLine::Bottom : return item->property("bottom"); +// case AnchorLine::Left : return item->property("left"); +// case AnchorLine::Right : return item->property("right"); +// case AnchorLine::HorizontalCenter : return item->property("horizontalCenter"); +// case AnchorLine::VerticalCenter : return item->property("verticalCenter"); +// case AnchorLine::Baseline : return item->property("baseline"); +// default: QVariant(); +// } +// +// Q_ASSERT_X(false, Q_FUNC_INFO, QString::number(anchorLine.type()).toLatin1()); +// return QVariant(); +//} + +void QmlGraphicsItemNodeInstance::setPropertyVariant(const QString &name, const QVariant &value) +{ + if (name == "height") { + if (value.isValid()) + m_hasHeight = true; + else + m_hasHeight = false; + } + + if (name == "width") { + if (value.isValid()) + m_hasWidth = true; + else + m_hasWidth = false; + } + + GraphicsObjectNodeInstance::setPropertyVariant(name, value); +} + +void QmlGraphicsItemNodeInstance::setPropertyBinding(const QString &name, const QString &expression) +{ + GraphicsObjectNodeInstance::setPropertyBinding(name, expression); +} + +QVariant QmlGraphicsItemNodeInstance::property(const QString &name) const +{ + if (name == "width" && modelNode().isValid()) { + double implicitWidth = qmlGraphicsItem()->implicitWidth(); + if (!m_hasWidth + && implicitWidth // WORKAROUND + && implicitWidth != qmlGraphicsItem()->width()) + qmlGraphicsItem()->setWidth(implicitWidth); + } + + if (name == "height" && modelNode().isValid()) { + double implicitHeight = qmlGraphicsItem()->implicitHeight(); + if (!m_hasHeight + && implicitHeight // WORKAROUND + && implicitHeight != qmlGraphicsItem()->height()) + qmlGraphicsItem()->setHeight(implicitHeight); + } + + return GraphicsObjectNodeInstance::property(name); +} + +void QmlGraphicsItemNodeInstance::resetHorizontal() + { + if (modelNode().hasBindingProperty("x")) + setPropertyBinding("x", modelNode().bindingProperty("x").expression()); + else if (modelNode().hasVariantProperty("x")) + setPropertyVariant("x", modelNode().variantProperty("x").value()); + else + setPropertyVariant("x", 0.0); + + if (modelNode().hasBindingProperty("width")) + setPropertyBinding("width", modelNode().bindingProperty("width").expression()); + else if (modelNode().hasVariantProperty("width")) + setPropertyVariant("width", modelNode().variantProperty("width").value()); + else + setPropertyVariant("width", qmlGraphicsItem()->implicitWidth()); +} + +void QmlGraphicsItemNodeInstance::resetVertical() + { + if (modelNode().hasBindingProperty("y")) + setPropertyBinding("y", modelNode().bindingProperty("y").expression()); + else if (modelNode().hasVariantProperty("y")) + setPropertyVariant("y", modelNode().variantProperty("y").value()); + else + setPropertyVariant("y", 0.0); + + if (modelNode().hasBindingProperty("height")) + setPropertyBinding("height", modelNode().bindingProperty("height").expression()); + else if (modelNode().hasVariantProperty("height")) + setPropertyVariant("height", modelNode().variantProperty("height").value()); + else + setPropertyVariant("height", qmlGraphicsItem()->implicitHeight()); +} + + +void QmlGraphicsItemNodeInstance::resetProperty(const QString &name) +{ + GraphicsObjectNodeInstance::resetProperty(name); + if (name == "anchors.fill") { + qmlGraphicsItem()->anchors()->resetFill(); + resetHorizontal(); + resetVertical(); + } else if (name == "anchors.centerIn") { + qmlGraphicsItem()->anchors()->resetCenterIn(); + resetHorizontal(); + resetVertical(); + } else if (name == "anchors.top") { + qmlGraphicsItem()->anchors()->resetTop(); + resetVertical(); + } else if (name == "anchors.left") { + qmlGraphicsItem()->anchors()->resetLeft(); + resetHorizontal(); + } else if (name == "anchors.right") { + qmlGraphicsItem()->anchors()->resetRight(); + resetHorizontal(); + } else if (name == "anchors.bottom") { + qmlGraphicsItem()->anchors()->resetBottom(); + resetVertical(); + } else if (name == "anchors.horizontalCenter") { + qmlGraphicsItem()->anchors()->resetHorizontalCenter(); + resetHorizontal(); + } else if (name == "anchors.verticalCenter") { + qmlGraphicsItem()->anchors()->resetVerticalCenter(); + resetVertical(); + } else if (name == "anchors.baseline") { + qmlGraphicsItem()->anchors()->resetBaseline(); + resetVertical(); + } +} + +//void QmlGraphicsItemNodeInstance::updateAnchors() +//{ +// NodeAnchors anchors(modelNode()); +// +// if (anchors.hasAnchor(AnchorLine::Top)) { +// AnchorLine anchorLine(anchors.anchor(AnchorLine::Top)); +// NodeInstance instance(nodeInstanceView()->instanceForNode(anchorLine.modelNode())); +// +// if (instance.isQmlGraphicsItem()) { +// Pointer qmlGraphicsItemInstance(instance.QmlGraphicsItemNodeInstance()); +// qmlGraphicsItem()->anchors()->setProperty("top", anchorLineFor(qmlGraphicsItemInstance->qmlGraphicsItem(), anchorLine)); +// } +// } else { +// if (qmlGraphicsItem()->anchors()->usedAnchors().testFlag(QmlGraphicsAnchors::HasTopAnchor)) { +// qmlGraphicsItem()->anchors()->resetTop(); +// setPropertyValue("y", modelNode().property("y").value()); +// setPropertyValue("height", modelNode().property("height").value()); +// } +// } +// +// +// if (anchors.hasAnchor(AnchorLine::Left)) { +// AnchorLine anchorLine(anchors.anchor(AnchorLine::Left)); +// NodeInstance instance(nodeInstanceView()->instanceForNode(anchorLine.modelNode())); +// +// if (instance.isQmlGraphicsItem()) { +// Pointer qmlGraphicsItemInstance(instance.QmlGraphicsItemNodeInstance()); +// qmlGraphicsItem()->anchors()->setProperty("left", anchorLineFor(qmlGraphicsItemInstance->qmlGraphicsItem(), anchorLine)); +// } +// } else { +// if (qmlGraphicsItem()->anchors()->usedAnchors().testFlag(QmlGraphicsAnchors::HasLeftAnchor)) { +// qmlGraphicsItem()->anchors()->resetLeft(); +// setPropertyValue("x", modelNode().property("x").value()); +// setPropertyValue("width", modelNode().property("width").value()); +// } +// } +// +// +// if (anchors.hasAnchor(AnchorLine::Right)) { +// AnchorLine anchorLine(anchors.anchor(AnchorLine::Right)); +// NodeInstance instance(nodeInstanceView()->instanceForNode(anchorLine.modelNode())); +// +// if (instance.isQmlGraphicsItem()) { +// Pointer qmlGraphicsItemInstance(instance.QmlGraphicsItemNodeInstance()); +// qmlGraphicsItem()->anchors()->setProperty("right", anchorLineFor(qmlGraphicsItemInstance->qmlGraphicsItem(), anchorLine)); +// } +// } else { +// if (qmlGraphicsItem()->anchors()->usedAnchors().testFlag(QmlGraphicsAnchors::HasRightAnchor)) { +// qmlGraphicsItem()->anchors()->resetRight(); +// setPropertyValue("x", modelNode().property("x").value()); +// setPropertyValue("width", modelNode().property("width").value()); +// } +// } +// +// +// if (anchors.hasAnchor(AnchorLine::Bottom)) { +// AnchorLine anchorLine(anchors.anchor(AnchorLine::Bottom)); +// NodeInstance instance(nodeInstanceView()->instanceForNode(anchorLine.modelNode())); +// +// if (instance.isQmlGraphicsItem()) { +// Pointer qmlGraphicsItemInstance(instance.QmlGraphicsItemNodeInstance()); +// qmlGraphicsItem()->anchors()->setProperty("bottom", anchorLineFor(qmlGraphicsItemInstance->qmlGraphicsItem(), anchorLine)); +// } +// } else { +// if (qmlGraphicsItem()->anchors()->usedAnchors().testFlag(QmlGraphicsAnchors::HasBottomAnchor)) { +// qmlGraphicsItem()->anchors()->resetBottom(); +// setPropertyValue("y", modelNode().property("y").value()); +// setPropertyValue("height", modelNode().property("height").value()); +// } +// } +// +// +// if (anchors.hasAnchor(AnchorLine::HorizontalCenter)) { +// AnchorLine anchorLine(anchors.anchor(AnchorLine::HorizontalCenter)); +// NodeInstance instance(nodeInstanceView()->instanceForNode(anchorLine.modelNode())); +// +// if (instance.isQmlGraphicsItem()) { +// Pointer qmlGraphicsItemInstance(instance.QmlGraphicsItemNodeInstance()); +// qmlGraphicsItem()->anchors()->setProperty("horizontalCenter", anchorLineFor(qmlGraphicsItemInstance->qmlGraphicsItem(), anchorLine)); +// } +// } else { +// if (qmlGraphicsItem()->anchors()->usedAnchors().testFlag(QmlGraphicsAnchors::HasHCenterAnchor)) { +// qmlGraphicsItem()->anchors()->resetHorizontalCenter(); +// setPropertyValue("x", modelNode().property("x").value()); +// setPropertyValue("width", modelNode().property("width").value()); +// } +// } +// +// +// if (anchors.hasAnchor(AnchorLine::VerticalCenter)) { +// AnchorLine anchorLine(anchors.anchor(AnchorLine::VerticalCenter)); +// NodeInstance instance(nodeInstanceView()->instanceForNode(anchorLine.modelNode())); +// +// if (instance.isQmlGraphicsItem()) { +// Pointer qmlGraphicsItemInstance(instance.QmlGraphicsItemNodeInstance()); +// qmlGraphicsItem()->anchors()->setProperty("verticalCenter",anchorLineFor(qmlGraphicsItemInstance->qmlGraphicsItem(), anchorLine)); +// } +// } else { +// if (qmlGraphicsItem()->anchors()->usedAnchors().testFlag(QmlGraphicsAnchors::HasVCenterAnchor)) { +// qmlGraphicsItem()->anchors()->resetVerticalCenter(); +// setPropertyValue("y", modelNode().property("y").value()); +// setPropertyValue("height", modelNode().property("height").value()); +// } +// } +// +// +// qmlGraphicsItem()->anchors()->setTopMargin(anchors.margin(AnchorLine::Top)); +// qmlGraphicsItem()->anchors()->setLeftMargin(anchors.margin(AnchorLine::Left)); +// qmlGraphicsItem()->anchors()->setBottomMargin(anchors.margin(AnchorLine::Bottom)); +// qmlGraphicsItem()->anchors()->setRightMargin(anchors.margin(AnchorLine::Right)); +// qmlGraphicsItem()->anchors()->setHorizontalCenterOffset(anchors.margin(AnchorLine::HorizontalCenter)); +// qmlGraphicsItem()->anchors()->setVerticalCenterOffset(anchors.margin(AnchorLine::VerticalCenter)); +//} + +QmlGraphicsAnchors::UsedAnchor anchorLineFlagForName(const QString &name) +{ + if (name == "anchors.top") + return QmlGraphicsAnchors::HasTopAnchor; + + if (name == "anchors.left") + return QmlGraphicsAnchors::HasLeftAnchor; + + if (name == "anchors.bottom") + return QmlGraphicsAnchors::HasBottomAnchor; + + if (name == "anchors.right") + return QmlGraphicsAnchors::HasRightAnchor; + + if (name == "anchors.horizontalCenter") + return QmlGraphicsAnchors::HasHCenterAnchor; + + if (name == "anchors.verticalCenter") + return QmlGraphicsAnchors::HasVCenterAnchor; + + if (name == "anchors.baseline") + return QmlGraphicsAnchors::HasBaselineAnchor; + + + Q_ASSERT_X(false, Q_FUNC_INFO, "wrong anchor name - this should never happen"); + return QmlGraphicsAnchors::HasLeftAnchor; +} + +QString propertyNameForAnchorLine(const QmlGraphicsAnchorLine::AnchorLine &anchorLine) +{ + switch(anchorLine) { + case QmlGraphicsAnchorLine::Left: return "left"; + case QmlGraphicsAnchorLine::Right: return "right"; + case QmlGraphicsAnchorLine::Top: return "top"; + case QmlGraphicsAnchorLine::Bottom: return "bottom"; + case QmlGraphicsAnchorLine::HCenter: return "horizontalCenter"; + case QmlGraphicsAnchorLine::VCenter: return "verticalCenter"; + case QmlGraphicsAnchorLine::Baseline: return "baseline"; + case QmlGraphicsAnchorLine::Invalid: + default: return QString(); + } +} + +bool isValidAnchorName(const QString &name) +{ + static QStringList anchorNameList(QStringList() << "anchors.top" + << "anchors.left" + << "anchors.right" + << "anchors.bottom" + << "anchors.verticalCenter" + << "anchors.horizontalCenter" + << "anchors.fill" + << "anchors.centerIn" + << "anchors.baseline"); + + return anchorNameList.contains(name); +} + +QPair<QString, NodeInstance> QmlGraphicsItemNodeInstance::anchor(const QString &name) const +{ + if (!isValidAnchorName(name) || !hasAnchor(name)) + return GraphicsObjectNodeInstance::anchor(name); + + QObject *targetObject = 0; + QString targetName; + + if (name == "anchors.fill") { + targetObject = qmlGraphicsItem()->anchors()->fill(); + } else if (name == "anchors.centerIn") { + targetObject = qmlGraphicsItem()->anchors()->centerIn(); + } else { + QmlMetaProperty metaProperty = QmlMetaProperty::createProperty(object(), name, context()); + QmlGraphicsAnchorLine anchorLine = metaProperty.read().value<QmlGraphicsAnchorLine>(); + if (anchorLine.anchorLine != QmlGraphicsAnchorLine::Invalid) { + targetObject = anchorLine.item; + targetName = propertyNameForAnchorLine(anchorLine.anchorLine); + } + + } + + if (targetObject && nodeInstanceView()->hasInstanceForObject(targetObject)) { + return qMakePair(targetName, nodeInstanceView()->instanceForObject(targetObject)); + } else { + return GraphicsObjectNodeInstance::anchor(name); + } +} + +bool QmlGraphicsItemNodeInstance::hasAnchor(const QString &name) const +{ + if (!isValidAnchorName(name)) + return false; + + if (name == "anchors.fill") + return qmlGraphicsItem()->anchors()->fill() != 0; + + if (name == "anchors.centerIn") + return qmlGraphicsItem()->anchors()->centerIn() != 0; + + if (name == "anchors.right") + return qmlGraphicsItem()->anchors()->right().item != 0; + + if (name == "anchors.top") + return qmlGraphicsItem()->anchors()->top().item != 0; + + if (name == "anchors.left") + return qmlGraphicsItem()->anchors()->left().item != 0; + + if (name == "anchors.bottom") + return qmlGraphicsItem()->anchors()->bottom().item != 0; + + if (name == "anchors.horizontalCenter") + return qmlGraphicsItem()->anchors()->horizontalCenter().item != 0; + + if (name == "anchors.verticalCenter") + return qmlGraphicsItem()->anchors()->verticalCenter().item != 0; + + if (name == "anchors.baseline") + return qmlGraphicsItem()->anchors()->baseline().item != 0; + + return qmlGraphicsItem()->anchors()->usedAnchors().testFlag(anchorLineFlagForName(name)); +} + +bool isAnchoredTo(QmlGraphicsItem *fromItem, QmlGraphicsItem *toItem) +{ + return fromItem->anchors()->fill() == toItem + || fromItem->anchors()->centerIn() == toItem + || fromItem->anchors()->bottom().item == toItem + || fromItem->anchors()->top().item == toItem + || fromItem->anchors()->left().item == toItem + || fromItem->anchors()->right().item == toItem + || fromItem->anchors()->verticalCenter().item == toItem + || fromItem->anchors()->horizontalCenter().item == toItem + || fromItem->anchors()->baseline().item == toItem; +} + +bool areChildrenAnchoredTo(QmlGraphicsItem *fromItem, QmlGraphicsItem *toItem) +{ + foreach(QObject *childObject, fromItem->children()) { + QmlGraphicsItem *childItem = qobject_cast<QmlGraphicsItem*>(childObject); + if (childItem) { + if (isAnchoredTo(childItem, toItem)) + return true; + + if (areChildrenAnchoredTo(childItem, toItem)) + return true; + } + } + + return false; +} + +bool QmlGraphicsItemNodeInstance::isAnchoredBy() const +{ + if (areChildrenAnchoredTo(qmlGraphicsItem(), qmlGraphicsItem())) // search in children for a anchor to this item + return true; + + if (qmlGraphicsItem()->parent()) { + foreach(QObject *siblingObject, qmlGraphicsItem()->parent()->children()) { // search in siblings for a anchor to this item + QmlGraphicsItem *siblingItem = qobject_cast<QmlGraphicsItem*>(siblingObject); + if (siblingItem) { + if (isAnchoredTo(siblingItem, qmlGraphicsItem())) + return true; + + if (areChildrenAnchoredTo(siblingItem, qmlGraphicsItem())) + return true; + } + } + } + + return false; +} + + + +QmlGraphicsItem *QmlGraphicsItemNodeInstance::qmlGraphicsItem() const +{ + if (object() == 0) + return 0; + + Q_ASSERT(qobject_cast<QmlGraphicsItem*>(object())); + return static_cast<QmlGraphicsItem*>(object()); +} +} +} diff --git a/src/plugins/qmldesigner/core/instances/qmlgraphicsitemnodeinstance.h b/src/plugins/qmldesigner/core/instances/qmlgraphicsitemnodeinstance.h new file mode 100644 index 00000000000..da1275b282c --- /dev/null +++ b/src/plugins/qmldesigner/core/instances/qmlgraphicsitemnodeinstance.h @@ -0,0 +1,79 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef QMLGRAPHICSITEMNODEINSTANCE_H +#define QMLGRAPHICSITEMNODEINSTANCE_H + +#include "graphicsobjectnodeinstance.h" +#include <QmlGraphicsItem> +#include <QWeakPointer> + + +namespace QmlDesigner { +namespace Internal { + +class QmlGraphicsItemNodeInstance : public GraphicsObjectNodeInstance +{ +public: + typedef QSharedPointer<QmlGraphicsItemNodeInstance> Pointer; + typedef QWeakPointer<QmlGraphicsItemNodeInstance> WeakPointer; + + ~QmlGraphicsItemNodeInstance(); + + static Pointer create(const NodeMetaInfo &metaInfo, QmlContext *context, QObject *objectToBeWrapped); + + bool isQmlGraphicsItem() const; + + QSizeF size() const; + QRectF boundingRect() const; +// void updateAnchors(); + + void setPropertyVariant(const QString &name, const QVariant &value); + void setPropertyBinding(const QString &name, const QString &expression); + + QVariant property(const QString &name) const; + void resetProperty(const QString &name); + + bool hasAnchor(const QString &name) const; + QPair<QString, NodeInstance> anchor(const QString &name) const; + bool isAnchoredBy() const; +protected: + QmlGraphicsItemNodeInstance(QmlGraphicsItem *item, bool hasContent); + QmlGraphicsItem *qmlGraphicsItem() const; + void resetHorizontal(); + void resetVertical(); + +private: //variables + bool m_hasHeight; + bool m_hasWidth; +}; + +} +} +#endif // QMLGRAPHICSITEMNODEINSTANCE_H diff --git a/src/plugins/qmldesigner/core/instances/qmlpropertychangesnodeinstance.cpp b/src/plugins/qmldesigner/core/instances/qmlpropertychangesnodeinstance.cpp new file mode 100644 index 00000000000..35cb2322303 --- /dev/null +++ b/src/plugins/qmldesigner/core/instances/qmlpropertychangesnodeinstance.cpp @@ -0,0 +1,205 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "qmlpropertychangesnodeinstance.h" +#include "qmlstatenodeinstance.h" +#include <QmlEngine> +#include <QmlContext> +#include <QmlExpression> +#include <QmlBinding> +#include <metainfo.h> + +namespace QmlDesigner { +namespace Internal { + +QmlPropertyChangesObject::QmlPropertyChangesObject() : + QmlStateOperation(), + m_restoreEntryValues(true), + m_isExplicit(false) +{ +} + +QmlStateOperation::ActionList QmlPropertyChangesObject::actions() +{ + ActionList list; + + foreach (const QString &property, m_properties.keys()) { + Action a(m_targetObject.data(), property, m_properties.value(property)); + + if (a.property.isValid()) { + a.restore = restoreEntryValues(); + + if (a.property.propertyType() == QVariant::Url && + (a.toValue.type() == QVariant::String || a.toValue.type() == QVariant::ByteArray) && !a.toValue.isNull()) + a.toValue.setValue(qmlContext(this)->resolvedUrl(QUrl(a.toValue.toString()))); + + list << a; + } + } + +// for (int ii = 0; ii < d->signalReplacements.count(); ++ii) { +// +// QmlReplaceSignalHandler *handler = d->signalReplacements.at(ii); +// +// if (handler->property.isValid()) { +// Action a; +// a.event = handler; +// list << a; +// } +// } + + foreach (const QString &property, m_expressions.keys()) { + QmlMetaProperty mProperty = metaProperty(property); + + if (mProperty.isValid()) { + Action a; + a.restore = restoreEntryValues(); + a.property = mProperty; + a.fromValue = a.property.read(); + a.specifiedObject = m_targetObject.data(); + a.specifiedProperty = property; + + if (m_isExplicit) { + a.toValue = QmlExpression(qmlContext(object()), m_expressions.value(property), object()).value(); + } else { + QmlBinding *newBinding = new QmlBinding(m_expressions.value(property), object(), qmlContext(object())); + newBinding->setTarget(mProperty); + a.toBinding = newBinding; + a.deletableToBinding = true; + } + + list << a; + } + } + + return list; +} + +QmlMetaProperty QmlPropertyChangesObject::metaProperty(const QString &property) +{ + QmlMetaProperty prop = QmlMetaProperty::createProperty(m_targetObject.data(), property); + if (!prop.isValid()) { + qWarning() << "Cannot assign to non-existant property" << property; + return QmlMetaProperty(); + } else if (!prop.isWritable()) { + qWarning() << "Cannot assign to read-only property" << property; + return QmlMetaProperty(); + } + return prop; +} + +QmlPropertyChangesNodeInstance::QmlPropertyChangesNodeInstance(QmlPropertyChangesObject *propertyChangesObject) : + ObjectNodeInstance(propertyChangesObject) +{ +} + +QmlPropertyChangesNodeInstance::Pointer + QmlPropertyChangesNodeInstance::create(const NodeMetaInfo & /*metaInfo*/, + QmlContext *context, + QObject *objectToBeWrapped) +{ + Q_ASSERT(!objectToBeWrapped); + + QmlPropertyChangesObject *object = new QmlPropertyChangesObject; + QmlEngine::setContextForObject(object, context); + Pointer instance(new QmlPropertyChangesNodeInstance(object)); + return instance; +} + +void QmlPropertyChangesNodeInstance::setPropertyVariant(const QString &name, const QVariant &value) +{ + QmlMetaProperty metaProperty = QmlMetaProperty::createProperty(object(), name, context()); + if (metaProperty.isValid()) { // 'restoreEntryValues', 'explicit' + ObjectNodeInstance::setPropertyVariant(name, value); + return; + } + changesObject()->m_properties.insert(name, value); + + updateStateInstance(); +} + +void QmlPropertyChangesNodeInstance::setPropertyBinding(const QString &name, const QString &expression) +{ + QmlMetaProperty metaProperty = QmlMetaProperty::createProperty(object(), name, context()); + if (metaProperty.isValid()) { // 'target' + ObjectNodeInstance::setPropertyBinding(name, expression); + return; + } + changesObject()->m_expressions.insert(name, expression); + + updateStateInstance(); +} + +QVariant QmlPropertyChangesNodeInstance::property(const QString &name) const +{ + if (changesObject()->m_properties.contains(name)) + return changesObject()->m_properties.value(name); + if (changesObject()->m_expressions.contains(name)) + return changesObject()->m_expressions.value(name); + + return QVariant(); +} + +void QmlPropertyChangesNodeInstance::resetProperty(const QString &name) +{ + if (changesObject()->m_properties.contains(name)) + changesObject()->m_properties.remove(name); + else if (changesObject()->m_expressions.contains(name)) + changesObject()->m_expressions.remove(name); + // TODO: How to force states object to update? + + updateStateInstance(); +} + +QmlPropertyChangesObject *QmlPropertyChangesNodeInstance::changesObject() const +{ + Q_ASSERT(qobject_cast<QmlPropertyChangesObject*>(object())); + return static_cast<QmlPropertyChangesObject*>(object()); +} + +void QmlPropertyChangesNodeInstance::updateStateInstance() const +{ + if (!nodeInstanceView()->hasInstanceForNode(modelNode())) + return; + + NodeInstance myInstance = nodeInstanceView()->instanceForNode(modelNode()); + Q_ASSERT(myInstance.isValid()); + + NodeInstance qmlStateInstance = myInstance.parent(); + if (!qmlStateInstance.isValid() + || !qmlStateInstance.modelNode().isValid()) + return; + + qmlStateInstance.setPropertyVariant(PROPERTY_STATEACTIONSCHANGED, true); +} + +} // namespace Internal +} // namespace QmlDesigner + +QML_DEFINE_NOCREATE_TYPE(QmlDesigner::Internal::QmlPropertyChangesObject) diff --git a/src/plugins/qmldesigner/core/instances/qmlpropertychangesnodeinstance.h b/src/plugins/qmldesigner/core/instances/qmlpropertychangesnodeinstance.h new file mode 100644 index 00000000000..a928621ce68 --- /dev/null +++ b/src/plugins/qmldesigner/core/instances/qmlpropertychangesnodeinstance.h @@ -0,0 +1,103 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef QMLPROPERTYCHANGESNODEINSTANCE_H +#define QMLPROPERTYCHANGESNODEINSTANCE_H + +#include "objectnodeinstance.h" +#include <private/qmlstateoperations_p.h> + +namespace QmlDesigner { + +namespace Internal { + +class QmlPropertyChangesNodeInstance; + +// Original QmlPropertyChanges class requires a custom parser +// work around this by writing a replacement class +class QmlPropertyChangesObject : public QmlStateOperation +{ + Q_OBJECT + Q_PROPERTY(QObject *target READ object WRITE setObject) + Q_PROPERTY(bool restoreEntryValues READ restoreEntryValues WRITE setRestoreEntryValues) + Q_PROPERTY(bool explicit READ isExplicit WRITE setIsExplicit) + +public: + QObject *object() const { return m_targetObject.data(); } + void setObject(QObject *object) {m_targetObject = object; } + + bool restoreEntryValues() const { return m_restoreEntryValues; } + void setRestoreEntryValues(bool restore) { m_restoreEntryValues = restore; } + + bool isExplicit() const { return m_isExplicit; } + void setIsExplicit(bool isExplicit) { m_isExplicit = isExplicit; } + + virtual ActionList actions(); + +private: + friend class QmlPropertyChangesNodeInstance; + + QmlPropertyChangesObject(); + QmlMetaProperty metaProperty(const QString &property); + + QWeakPointer<QObject> m_targetObject; + bool m_restoreEntryValues; + bool m_isExplicit; + + QHash<QString, QVariant> m_properties; + QHash<QString, QString> m_expressions; +// QList<QmlReplaceSignalHandler*> signalReplacements; +}; + +class QmlPropertyChangesNodeInstance : public ObjectNodeInstance +{ +public: + typedef QSharedPointer<QmlPropertyChangesNodeInstance> Pointer; + typedef QWeakPointer<QmlPropertyChangesNodeInstance> WeakPointer; + + static Pointer create(const NodeMetaInfo &metaInfo, QmlContext *context, QObject *objectToBeWrapped); + + virtual void setPropertyVariant(const QString &name, const QVariant &value); + virtual void setPropertyBinding(const QString &name, const QString &expression); + virtual QVariant property(const QString &name) const; + virtual void resetProperty(const QString &name); + + void updateStateInstance() const; + +protected: + QmlPropertyChangesNodeInstance(QmlPropertyChangesObject *object); + QmlPropertyChangesObject *changesObject() const; +}; + +} // namespace Internal +} // namespace QmlDesigner + +QML_DECLARE_TYPE(QmlDesigner::Internal::QmlPropertyChangesObject) + +#endif // QMLPROPERTYCHANGESNODEINSTANCE_H diff --git a/src/plugins/qmldesigner/core/instances/qmlstatenodeinstance.cpp b/src/plugins/qmldesigner/core/instances/qmlstatenodeinstance.cpp new file mode 100644 index 00000000000..615b5f7be73 --- /dev/null +++ b/src/plugins/qmldesigner/core/instances/qmlstatenodeinstance.cpp @@ -0,0 +1,134 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "qmlstatenodeinstance.h" +#include <private/qmlstategroup_p.h> + +namespace QmlDesigner { +namespace Internal { + +const char * const ACTIVATESTATEPROPERTY = "__activateState"; +const char * const STATEACTIONSCHANGED = "__stateActionsChanged"; + +/** + \class QmlStateNodeInstance + + QmlStateNodeInstance manages a QmlState object. One can activate / deactivate a state + by setting/unsetting the special "__activateState" boolean property. + */ + +QmlStateNodeInstance::QmlStateNodeInstance(QmlState *object) : + ObjectNodeInstance(object) +{ +} + +QmlStateNodeInstance::Pointer + QmlStateNodeInstance::create(const NodeMetaInfo &metaInfo, + QmlContext *context, + QObject *objectToBeWrapped) +{ + Q_ASSERT(!objectToBeWrapped); + QObject *object = createObject(metaInfo, context); + QmlState *stateObject = qobject_cast<QmlState*>(object); + Q_ASSERT(stateObject); + + Pointer instance(new QmlStateNodeInstance(stateObject)); + + instance->populateResetValueHash(); + + return instance; +} + +void QmlStateNodeInstance::setPropertyVariant(const QString &name, const QVariant &value) +{ + if (name == ACTIVATESTATEPROPERTY) { + Q_ASSERT(value.type() == QVariant::Bool); + bool shouldActivate = value.toBool(); + + if (shouldActivate != isStateActive()) { + if (shouldActivate) { +// QmlState *currentState = stateGroup()->findState(stateGroup()->state()); +// stateObject()->apply(stateGroup(), 0, currentState); + + // TODO: Will this activate transitions???? + stateGroup()->setState(property("name").toString()); + } else { + resetProperty(name); + } + } + } else if (name == PROPERTY_STATEACTIONSCHANGED) { + if (isStateActive()) { + stateGroup()->setState(QString()); + stateGroup()->setState(property("name").toString()); + } + } else { + ObjectNodeInstance::setPropertyVariant(name, value); + } +} + +QVariant QmlStateNodeInstance::property(const QString &name) const +{ + if (name == ACTIVATESTATEPROPERTY) { + return isStateActive(); + } else { + return ObjectNodeInstance::property(name); + } +} + +void QmlStateNodeInstance::resetProperty(const QString &name) +{ + if (name == ACTIVATESTATEPROPERTY) { + if (isStateActive()) { + // TODO: Will this activate transitions???? + stateGroup()->setState(QString()); + } + } else { + ObjectNodeInstance::resetProperty(name); + } +} + +QmlState *QmlStateNodeInstance::stateObject() const +{ + Q_ASSERT(object()); + Q_ASSERT(qobject_cast<QmlState*>(object())); + return static_cast<QmlState*>(object()); +} + +QmlStateGroup *QmlStateNodeInstance::stateGroup() const +{ + return stateObject()->stateGroup(); +} + +bool QmlStateNodeInstance::isStateActive() const +{ + return (stateGroup()->state() == property("name")); +} + +} // namespace Internal +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/core/instances/qmlstatenodeinstance.h b/src/plugins/qmldesigner/core/instances/qmlstatenodeinstance.h new file mode 100644 index 00000000000..f5ef1a40bf8 --- /dev/null +++ b/src/plugins/qmldesigner/core/instances/qmlstatenodeinstance.h @@ -0,0 +1,66 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef QMLSTATENODEINSTANCE_H +#define QMLSTATENODEINSTANCE_H + +#include "objectnodeinstance.h" +#include <private/qmlstate_p.h> + +namespace QmlDesigner { + +namespace Internal { + +const char * const PROPERTY_STATEACTIONSCHANGED = "__stateActionsChanged"; + +class QmlStateNodeInstance : public ObjectNodeInstance +{ +public: + typedef QSharedPointer<QmlStateNodeInstance> Pointer; + typedef QWeakPointer<QmlStateNodeInstance> WeakPointer; + + static Pointer create(const NodeMetaInfo &metaInfo, QmlContext *context, QObject *objectToBeWrapped); + + virtual void setPropertyVariant(const QString &name, const QVariant &value); + virtual QVariant property(const QString &name) const; + virtual void resetProperty(const QString &name); + +protected: + QmlStateNodeInstance(QmlState *object); + + bool isStateActive() const; + + QmlState *stateObject() const; + QmlStateGroup *stateGroup() const; +}; + +} // namespace Internal +} // namespace QmlDesigner + +#endif // QMLSTATENODEINSTANCE_H diff --git a/src/plugins/qmldesigner/core/instances/qmltransitionnodeinstance.cpp b/src/plugins/qmldesigner/core/instances/qmltransitionnodeinstance.cpp new file mode 100644 index 00000000000..0cc5388156b --- /dev/null +++ b/src/plugins/qmldesigner/core/instances/qmltransitionnodeinstance.cpp @@ -0,0 +1,87 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "qmltransitionnodeinstance.h" +#include <private/qmltransition_p.h> +#include <nodemetainfo.h> +#include "invalidnodeinstanceexception.h" + +namespace QmlDesigner { +namespace Internal { + +QmlTransitionNodeInstance::QmlTransitionNodeInstance(QmlTransition *transition) + : ObjectNodeInstance(transition) +{ +} + +QmlTransitionNodeInstance::Pointer QmlTransitionNodeInstance::create(const NodeMetaInfo &nodeMetaInfo, QmlContext *context, QObject *objectToBeWrapped) +{ + QObject *object = 0; + if (objectToBeWrapped) + object = objectToBeWrapped; + else + object = createObject(nodeMetaInfo, context); + + QmlTransition *transition = qobject_cast<QmlTransition*>(object); + if (transition == 0) + throw InvalidNodeInstanceException(__LINE__, __FUNCTION__, __FILE__); + + Pointer instance(new QmlTransitionNodeInstance(transition)); + + if (objectToBeWrapped) + instance->setDeleteHeldInstance(false); // the object isn't owned + + instance->populateResetValueHash(); + + transition->setToState("invalidState"); + transition->setFromState("invalidState"); + + return instance; +} + +bool QmlTransitionNodeInstance::isTransition() const +{ + return true; +} + +void QmlTransitionNodeInstance::setPropertyVariant(const QString &name, const QVariant &value) +{ + if (name == "from" || name == "to") + return; + + ObjectNodeInstance::setPropertyVariant(name, value); +} + +QmlTransition *QmlTransitionNodeInstance::qmlTransition() const +{ + Q_ASSERT(qobject_cast<QmlTransition*>(object())); + return static_cast<QmlTransition*>(object()); +} +} +} diff --git a/src/plugins/qmldesigner/core/instances/qmltransitionnodeinstance.h b/src/plugins/qmldesigner/core/instances/qmltransitionnodeinstance.h new file mode 100644 index 00000000000..4202549efa3 --- /dev/null +++ b/src/plugins/qmldesigner/core/instances/qmltransitionnodeinstance.h @@ -0,0 +1,60 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef QMLTRANSITIONNODEINSTANCE_H +#define QMLTRANSITIONNODEINSTANCE_H + +#include "objectnodeinstance.h" + +class QmlTransition; + +namespace QmlDesigner { +namespace Internal { + +class QmlTransitionNodeInstance : public ObjectNodeInstance +{ +public: + typedef QSharedPointer<QmlTransitionNodeInstance> Pointer; + typedef QWeakPointer<QmlTransitionNodeInstance> WeakPointer; + + static Pointer create(const NodeMetaInfo &metaInfo, QmlContext *context, QObject *objectToBeWrapped); + + void setPropertyVariant(const QString &name, const QVariant &value); + + bool isTransition() const; + +protected: + QmlTransition *qmlTransition() const; + +private: + QmlTransitionNodeInstance(QmlTransition *transition); +}; +} +} +#endif // QMLTRANSITIONNODEINSTANCE_H diff --git a/src/plugins/qmldesigner/core/instances/qmlviewnodeinstance.cpp b/src/plugins/qmldesigner/core/instances/qmlviewnodeinstance.cpp new file mode 100644 index 00000000000..ca8b50b7163 --- /dev/null +++ b/src/plugins/qmldesigner/core/instances/qmlviewnodeinstance.cpp @@ -0,0 +1,88 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "qmlviewnodeinstance.h" + + +#include <QmlMetaType> +#include <QmlView> +#include <QmlGraphicsItem> + +#include <invalidnodeinstanceexception.h> + +namespace QmlDesigner { +namespace Internal { + +QmlViewNodeInstance::QmlViewNodeInstance(QmlView *view) + : GraphicsViewNodeInstance(view) +{ +} + + +QmlViewNodeInstance::Pointer QmlViewNodeInstance::create(const NodeMetaInfo &nodeMetaInfo, QmlContext *context, QObject *objectToBeWrapped) +{ + QObject *object = 0; + if (objectToBeWrapped) + object = objectToBeWrapped; + else + createObject(nodeMetaInfo, context); + + QmlView* view = qobject_cast<QmlView*>(object); + if (view == 0) + throw InvalidNodeInstanceException(__LINE__, __FUNCTION__, __FILE__); + + Pointer instance(new QmlViewNodeInstance(view)); + + if (objectToBeWrapped) + instance->setDeleteHeldInstance(false); // the object isn't owned + + instance->populateResetValueHash(); + + return instance; +} + +QmlView* QmlViewNodeInstance::view() const +{ + QmlView* view = qobject_cast<QmlView*>(widget()); + Q_ASSERT(view); + return view; +} + +bool QmlViewNodeInstance::isQmlView() const +{ + return true; +} + +void QmlViewNodeInstance::addItem(QmlGraphicsItem *item) +{ + item->setParent(view()->root()); +} + +} +} diff --git a/src/plugins/qmldesigner/core/instances/qmlviewnodeinstance.h b/src/plugins/qmldesigner/core/instances/qmlviewnodeinstance.h new file mode 100644 index 00000000000..42bfee1f424 --- /dev/null +++ b/src/plugins/qmldesigner/core/instances/qmlviewnodeinstance.h @@ -0,0 +1,61 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef QMLVIEWNODEINSTANCE_H +#define QMLVIEWNODEINSTANCE_H + +#include <QWeakPointer> +#include <QmlView> + +#include "graphicsviewnodeinstance.h" + +namespace QmlDesigner { +namespace Internal { + +class QmlViewNodeInstance : public GraphicsViewNodeInstance +{ +public: + typedef QSharedPointer<QmlViewNodeInstance> Pointer; + typedef QWeakPointer<QmlViewNodeInstance> WeakPointer; + + static Pointer create(const NodeMetaInfo &metaInfo, QmlContext *context, QObject *objectToBeWrapped); + + bool isQmlView() const; + + void addItem(QmlGraphicsItem *item); + +protected: + QmlViewNodeInstance(QmlView *view); + + QmlView* view() const; +}; + +} +} +#endif // QMLVIEWNODEINSTANCE_H diff --git a/src/plugins/qmldesigner/core/instances/widgetnodeinstance.cpp b/src/plugins/qmldesigner/core/instances/widgetnodeinstance.cpp new file mode 100644 index 00000000000..71863afa712 --- /dev/null +++ b/src/plugins/qmldesigner/core/instances/widgetnodeinstance.cpp @@ -0,0 +1,154 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "widgetnodeinstance.h" + +#include <QmlMetaType> + +#include "proxywidgetnodeinstance.h" +#include <invalidnodeinstanceexception.h> + +#include <propertymetainfo.h> + +namespace QmlDesigner { +namespace Internal { + +WidgetNodeInstance::WidgetNodeInstance(QWidget* widget) + : ObjectNodeInstance(widget) +{ +} + +WidgetNodeInstance::~WidgetNodeInstance() +{ +} + +WidgetNodeInstance::Pointer WidgetNodeInstance::create(const NodeMetaInfo &nodeMetaInfo, QmlContext *context, QObject *objectToBeWrapped) +{ + QObject *object = 0; + if (objectToBeWrapped) + object = objectToBeWrapped; + else + object = createObject(nodeMetaInfo, context); + + + QWidget* widget = qobject_cast<QWidget*>(object); + if (widget == 0) + throw InvalidNodeInstanceException(__LINE__, __FUNCTION__, __FILE__); + + Pointer instance(new WidgetNodeInstance(widget)); + + if (objectToBeWrapped) + instance->setDeleteHeldInstance(false); // the object isn't owned + + instance->populateResetValueHash(); + + return instance; +} + +void WidgetNodeInstance::paint(QPainter *painter) const +{ + Q_ASSERT(widget()); + + QWidget::RenderFlags flags; + if (!widget()->children().isEmpty()) + flags = QWidget::DrawChildren; + else + flags = 0; + + if (isTopLevel() && modelNode().isValid()) + widget()->render(painter, QPoint(), QRegion(), QWidget::DrawWindowBackground); + else + widget()->render(painter, QPoint(), QRegion(), flags); +} + +bool WidgetNodeInstance::isTopLevel() const +{ + Q_ASSERT(widget()); + return widget()->isTopLevel(); +} + +QWidget* WidgetNodeInstance::widget() const +{ + return static_cast<QWidget*>(object()); +} + +bool WidgetNodeInstance::isWidget() const +{ + return true; +} + +QRectF WidgetNodeInstance::boundingRect() const +{ + return widget()->frameGeometry(); +} + +void WidgetNodeInstance::setPropertyVariant(const QString &name, const QVariant &value) +{ + if (name == "x") + widget()->move(value.toInt(), widget()->y()); + else if (name == "y") + widget()->move(widget()->x(), value.toInt()); + else if (name == "width") + widget()->resize(value.toInt(), widget()->height()); + else if (name == "height") + widget()->resize(widget()->width(), value.toInt()); + else { + widget()->setProperty(name.toLatin1(), value); + } + + widget()->update(); +} + +QVariant WidgetNodeInstance::property(const QString &name) const +{ + return widget()->property(name.toLatin1()); +} + +bool WidgetNodeInstance::isVisible() const +{ + return widget()->isVisible(); +} + +void WidgetNodeInstance::setVisible(bool isVisible) +{ + widget()->setVisible(isVisible); +} + +QPointF WidgetNodeInstance::position() const +{ + return widget()->pos(); +} + +QSizeF WidgetNodeInstance::size() const +{ + return widget()->size(); +} + +} +} diff --git a/src/plugins/qmldesigner/core/instances/widgetnodeinstance.h b/src/plugins/qmldesigner/core/instances/widgetnodeinstance.h new file mode 100644 index 00000000000..88c6a6ced1b --- /dev/null +++ b/src/plugins/qmldesigner/core/instances/widgetnodeinstance.h @@ -0,0 +1,75 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef WIDGETNODEINSTANCE_H +#define WIDGETNODEINSTANCE_H + +#include "objectnodeinstance.h" +#include <QWeakPointer> + +namespace QmlDesigner { +namespace Internal { + +class WidgetNodeInstance : public ObjectNodeInstance +{ +public: + ~WidgetNodeInstance(); + typedef QSharedPointer<WidgetNodeInstance> Pointer; + typedef QWeakPointer<WidgetNodeInstance> WeakPointer; + + static Pointer create(const NodeMetaInfo &metaInfo, QmlContext *context, QObject *objectToBeWrapped); + + void paint(QPainter *painter) const; + + bool isTopLevel() const; + + bool isWidget() const; + + QRectF boundingRect() const; + QPointF position() const; + QSizeF size() const; + + void setPropertyVariant(const QString &name, const QVariant &value); + QVariant property(const QString &name) const; + + bool isVisible() const; + void setVisible(bool isVisible); + + QWidget *widget() const; + + void updateProperties(); + +protected: + WidgetNodeInstance(QWidget* widget); + +}; + +} +} +#endif // WIDGETNODEINSTANCE_H diff --git a/src/plugins/qmldesigner/core/iwidgetplugin.pri b/src/plugins/qmldesigner/core/iwidgetplugin.pri new file mode 100644 index 00000000000..8dceea3244c --- /dev/null +++ b/src/plugins/qmldesigner/core/iwidgetplugin.pri @@ -0,0 +1 @@ +INCLUDEPATH += $$PWD/include diff --git a/src/plugins/qmldesigner/core/metainfo/enumeratormetainfo.cpp b/src/plugins/qmldesigner/core/metainfo/enumeratormetainfo.cpp new file mode 100644 index 00000000000..35924bc30f9 --- /dev/null +++ b/src/plugins/qmldesigner/core/metainfo/enumeratormetainfo.cpp @@ -0,0 +1,153 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "enumeratormetainfo.h" + +#include <QSharedData> +#include <QString> +#include <QMap> +#include <QtDebug> + +namespace QmlDesigner { + +namespace Internal { + +class EnumeratorMetaInfoData : public QSharedData +{ +public: + QString name; + QString scope; + bool isFlagType; + bool isValid; + QMap<QString, int> elements; +}; + +} + +/*! +\class QmlDesigner::EnumeratorMetaInfo +\ingroup CoreModel +\brief The EnumeratorMetaInfo class provides meta information about an enumerator type. + +TODO + +\see QmlDesigner::MetaInfo, QmlDesigner::NodeMetaInfo, QmlDesigner::PropertyMetaInfo +*/ + +EnumeratorMetaInfo::EnumeratorMetaInfo() + : m_data(new Internal::EnumeratorMetaInfoData) +{ + m_data->isFlagType = false; + m_data->isValid = false; +} + +EnumeratorMetaInfo::~EnumeratorMetaInfo() +{} + +EnumeratorMetaInfo::EnumeratorMetaInfo(const EnumeratorMetaInfo &other) + : m_data(other.m_data) +{ +} + +EnumeratorMetaInfo& EnumeratorMetaInfo::operator=(const EnumeratorMetaInfo &other) +{ + if (this !=&other) + m_data = other.m_data; + + return *this; +} + +QString EnumeratorMetaInfo::name() const +{ + return m_data->name; +} + +QString EnumeratorMetaInfo::scope() const +{ + return m_data->scope; +} + +bool EnumeratorMetaInfo::isValid() const +{ + return m_data->isValid; +} + +QString EnumeratorMetaInfo::scopeAndName(const QString &combiner) const +{ + return m_data->scope + combiner + m_data->name; +} + +QList<QString> EnumeratorMetaInfo::elementNames() const +{ + return m_data->elements.keys(); +} + +int EnumeratorMetaInfo::elementValue(const QString &enumeratorName) const +{ + QString possibleScope = scope(); + if (!possibleScope.isEmpty()) + possibleScope.append("::"); + QString unscoped = enumeratorName; + unscoped.remove(possibleScope); + return m_data->elements.value(unscoped, -1); +} + +void EnumeratorMetaInfo::setScope(const QString &scope) +{ + Q_ASSERT(!scope.isEmpty()); + m_data->scope = scope; +} + +void EnumeratorMetaInfo::setName(const QString &name) +{ + Q_ASSERT(!name.isEmpty()); + m_data->name = name; +} + +void EnumeratorMetaInfo::addElement(const QString &enumeratorName, int enumeratorValue) +{ + m_data->elements.insert(enumeratorName, enumeratorValue); +} + +bool EnumeratorMetaInfo::isFlagType() const +{ + return m_data->isFlagType; +} + +void EnumeratorMetaInfo::setIsFlagType(bool isFlagType) +{ + m_data->isFlagType = isFlagType; +} + +void EnumeratorMetaInfo::setValid(bool valid) +{ + m_data->isValid = valid; +} + +} diff --git a/src/plugins/qmldesigner/core/metainfo/gui.metainfo b/src/plugins/qmldesigner/core/metainfo/gui.metainfo new file mode 100644 index 00000000000..7aa8673171a --- /dev/null +++ b/src/plugins/qmldesigner/core/metainfo/gui.metainfo @@ -0,0 +1,184 @@ +<!-- Metadata that is general to Qt and cannot be put into a separate plugin --> +<metainfo> + <enumerator name="Orientation" scope="Qt"> + <element name="Horizontal" value="1" /> + <element name="Vertical" value="2" /> + </enumerator> + <enumerator name="ContextMenuPolicy" scope="Qt"> + <element name="DefaultContextMenu" value="1" /> + <element name="PreventContextMenu" value="4" /> + <element name="ActionsContextMenu" value="2" /> + <element name="NoContextMenu" value="0" /> + <element name="CustomContextMenu" value="3" /> + </enumerator> + <enumerator name="LayoutDirection" scope="Qt"> + <element name="RightToLeft" value="1" /> + <element name="LeftToRight" value="0" /> + </enumerator> + <enumerator name="ArrowType" scope="Qt"> + <element name="RightArrow" value="4" /> + <element name="NoArrow" value="0" /> + <element name="UpArrow" value="1" /> + <element name="DownArrow" value="2" /> + <element name="LeftArrow" value="3" /> + </enumerator> + <enumerator name="PenStyle" scope="Qt"> + <element name="DashLine" value="2" /> + <element name="NoPen" value="0" /> + <element name="DashDotLine" value="4" /> + <element name="DashDotDotLine" value="5" /> + <element name="SolidLine" value="1" /> + <element name="DotLine" value="3" /> + <element name="CustomDashLine" value="6" /> + </enumerator> + <enumerator name="CaseSensitivity" scope="Qt"> + <element name="CaseSensitive" value="1" /> + <element name="CaseInsensitive" value="0" /> + </enumerator> + <enumerator name="WritingSystem" scope="QFontDatabase"> + <element name="Thaana" value="8" /> + <element name="Telugu" value="15" /> + <element name="Cyrillic" value="3" /> + <element name="Oriya" value="13" /> + <element name="Gujarati" value="12" /> + <element name="Other" value="30" /> + <element name="Any" value="0" /> + <element name="Korean" value="28" /> + <element name="Khmer" value="24" /> + <element name="Latin" value="1" /> + <element name="Georgian" value="23" /> + <element name="Vietnamese" value="29" /> + <element name="TraditionalChinese" value="26" /> + <element name="Armenian" value="4" /> + <element name="Devanagari" value="9" /> + <element name="Thai" value="19" /> + <element name="Tibetan" value="21" /> + <element name="Bengali" value="10" /> + <element name="Symbol" value="30" /> + <element name="WritingSystemsCount" value="33" /> + <element name="Greek" value="2" /> + <element name="Lao" value="20" /> + <element name="Gurmukhi" value="11" /> + <element name="Malayalam" value="17" /> + <element name="Ogham" value="31" /> + <element name="Myanmar" value="22" /> + <element name="Tamil" value="14" /> + <element name="Japanese" value="27" /> + <element name="SimplifiedChinese" value="25" /> + <element name="Kannada" value="16" /> + <element name="Syriac" value="7" /> + <element name="Runic" value="32" /> + <element name="Hebrew" value="5" /> + <element name="Arabic" value="6" /> + <element name="Sinhala" value="18" /> + </enumerator> + <enumerator name="ToolButtonStyle" scope="Qt"> + <element name="ToolButtonIconOnly" value="0" /> + <element name="ToolButtonTextBesideIcon" value="2" /> + <element name="ToolButtonTextOnly" value="1" /> + <element name="ToolButtonTextUnderIcon" value="3" /> + </enumerator> + <enumerator name="ItemSelectionMode" scope="Qt"> + <element name="ContainsItemBoundingRect" value="2" /> + <element name="IntersectsItemBoundingRect" value="3" /> + <element name="ContainsItemShape" value="0" /> + <element name="IntersectsItemShape" value="1" /> + </enumerator> + <enumerator name="TextElideMode" scope="Qt"> + <element name="ElideMiddle" value="2" /> + <element name="ElideRight" value="1" /> + <element name="ElideNone" value="3" /> + <element name="ElideLeft" value="0" /> + </enumerator> + <enumerator name="TimeSpec" scope="Qt"> + <element name="OffsetFromUTC" value="2" /> + <element name="UTC" value="1" /> + <element name="LocalTime" value="0" /> + </enumerator> + <enumerator name="FocusPolicy" scope="Qt"> + <element name="StrongFocus" value="11" /> + <element name="WheelFocus" value="15" /> + <element name="ClickFocus" value="2" /> + <element name="TabFocus" value="1" /> + <element name="NoFocus" value="0" /> + </enumerator> + <enumerator name="ScrollBarPolicy" scope="Qt"> + <element name="ScrollBarAsNeeded" value="0" /> + <element name="ScrollBarAlwaysOff" value="1" /> + <element name="ScrollBarAlwaysOn" value="2" /> + </enumerator> + <enumerator name="TextFormat" scope="Qt"> + <element name="PlainText" value="0" /> + <element name="AutoText" value="2" /> + <element name="LogText" value="3" /> + <element name="RichText" value="1" /> + </enumerator> + <enumerator name="DayOfWeek" scope="Qt"> + <element name="Monday" value="1" /> + <element name="Tuesday" value="2" /> + <element name="Friday" value="5" /> + <element name="Wednesday" value="3" /> + <element name="Thursday" value="4" /> + <element name="Sunday" value="7" /> + <element name="Saturday" value="6" /> + </enumerator> + <enumerator name="WindowModality" scope="Qt"> + <element name="WindowModal" value="1" /> + <element name="NonModal" value="0" /> + <element name="ApplicationModal" value="2" /> + </enumerator> + <flag name="Alignment" scope="Qt"> + <element name="AlignLeft" value="1" /> + <element name="AlignTrailing" value="2" /> + <element name="AlignAbsolute" value="16" /> + <element name="AlignHCenter" value="4" /> + <element name="AlignCenter" value="132" /> + <element name="AlignJustify" value="8" /> + <element name="AlignHorizontal_Mask" value="31" /> + <element name="AlignTop" value="32" /> + <element name="AlignRight" value="2" /> + <element name="AlignLeading" value="1" /> + <element name="AlignVCenter" value="128" /> + <element name="AlignBottom" value="64" /> + <element name="AlignVertical_Mask" value="224" /> + </flag> + <flag name="DockWidgetAreas" scope="Qt"> + <element name="AllDockWidgetAreas" value="15" /> + <element name="LeftDockWidgetArea" value="1" /> + <element name="DockWidgetArea_Mask" value="15" /> + <element name="NoDockWidgetArea" value="0" /> + <element name="TopDockWidgetArea" value="4" /> + <element name="RightDockWidgetArea" value="2" /> + <element name="BottomDockWidgetArea" value="8" /> + </flag> + <flag name="ToolBarAreas" scope="Qt"> + <element name="RightToolBarArea" value="2" /> + <element name="TopToolBarArea" value="4" /> + <element name="ToolBarArea_Mask" value="15" /> + <element name="NoToolBarArea" value="0" /> + <element name="LeftToolBarArea" value="1" /> + <element name="AllToolBarAreas" value="15" /> + <element name="BottomToolBarArea" value="8" /> + </flag> + <flag name="RenderHints" scope="QPainter"> + <element name="Antialiasing" value="1" /> + <element name="TextAntialiasing" value="2" /> + <element name="SmoothPixmapTransform" value="4" /> + <element name="HighQualityAntialiasing" value="8" /> + <element name="NonCosmeticDefaultPen" value="16" /> + </flag> + <flag name="TextInteractionFlags" scope="Qt"> + <element name="TextEditable" value="16" /> + <element name="TextSelectableByKeyboard" value="2" /> + <element name="NoTextInteraction" value="0" /> + <element name="TextSelectableByMouse" value="1" /> + <element name="TextBrowserInteraction" value="13" /> + <element name="LinksAccessibleByKeyboard" value="8" /> + <element name="LinksAccessibleByMouse" value="4" /> + <element name="TextEditorInteraction" value="19" /> + </flag> + + <node name="Object"> + <property index="0" isEnumType="False" isFlagType="False" isReadable="True" isResettable="False" isWritable="True" name="objectName" type="QString" showInPropertyEditor="true"/> + </node> +</metainfo> diff --git a/src/plugins/qmldesigner/core/metainfo/itemlibraryinfo.cpp b/src/plugins/qmldesigner/core/metainfo/itemlibraryinfo.cpp new file mode 100644 index 00000000000..e7981e99a87 --- /dev/null +++ b/src/plugins/qmldesigner/core/metainfo/itemlibraryinfo.cpp @@ -0,0 +1,208 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "itemlibraryinfo.h" +#include "model/internalproperty.h" + +#include <QSharedData> +#include <QString> +#include <QList> +#include <QtDebug> +#include <QIcon> + +namespace QmlDesigner { + +namespace Internal { + +class ItemLibraryInfoData : public QSharedData +{ +public: + ItemLibraryInfoData() : majorVersion(-1), minorVersion(-1) + { } + QString name; + QString typeName; + QString category; + int majorVersion; + int minorVersion; + QIcon icon; + QIcon dragIcon; + QList<PropertyContainer> properties; + QString qml; +}; +} + +ItemLibraryInfo::ItemLibraryInfo(const ItemLibraryInfo &other) + : m_data(other.m_data) +{ +} + +ItemLibraryInfo& ItemLibraryInfo::operator=(const ItemLibraryInfo &other) +{ + if (this !=&other) + m_data = other.m_data; + + return *this; +} + +void ItemLibraryInfo::setDragIcon(const QIcon &icon) +{ + m_data->dragIcon = icon; +} + +QIcon ItemLibraryInfo::dragIcon() const +{ + return m_data->dragIcon; +} + +void ItemLibraryInfo::addProperty(const Property &property) +{ + m_data->properties.append(property); +} + +QList<ItemLibraryInfo::Property> ItemLibraryInfo::properties() const +{ + return m_data->properties; +} + +ItemLibraryInfo::ItemLibraryInfo() : m_data(new Internal::ItemLibraryInfoData) +{ + m_data->name = QString(); +} + +ItemLibraryInfo::~ItemLibraryInfo() +{ +} + +QString ItemLibraryInfo::name() const +{ + return m_data->name; +} + +QString ItemLibraryInfo::typeName() const +{ + return m_data->typeName; +} + +QString ItemLibraryInfo::qml() const +{ + return m_data->qml; +} + +int ItemLibraryInfo::majorVersion() const +{ + return m_data->majorVersion; +} + +int ItemLibraryInfo::minorVersion() const +{ + return m_data->minorVersion; +} + +QString ItemLibraryInfo::category() const +{ + return m_data->category; +} + +void ItemLibraryInfo::setCategory(const QString &category) +{ + m_data->category = category; +} + +QIcon ItemLibraryInfo::icon() const +{ + return m_data->icon; +} + +void ItemLibraryInfo::setName(const QString &name) +{ + m_data->name = name; +} + +void ItemLibraryInfo::setTypeName(const QString &typeName) +{ + m_data->typeName = typeName; +} + +void ItemLibraryInfo::setIcon(const QIcon &icon) +{ + m_data->icon = icon; +} + +void ItemLibraryInfo::setMajorVersion(int majorVersion) +{ + m_data->majorVersion = majorVersion; +} + +void ItemLibraryInfo::setMinorVersion(int minorVersion) +{ + m_data->minorVersion = minorVersion; +} + +void ItemLibraryInfo::setQml(const QString &qml) +{ + m_data->qml = qml; +} + +void ItemLibraryInfo::addProperty(QString &name, QString &type, QString &value) +{ + Property property; + property.set(name, type, value); + addProperty(property); +} + +QDataStream& operator<<(QDataStream& stream, const ItemLibraryInfo& itemLibraryInfo) +{ + stream << itemLibraryInfo.name(); + stream << itemLibraryInfo.typeName(); + stream << itemLibraryInfo.majorVersion(); + stream << itemLibraryInfo.minorVersion(); + stream << itemLibraryInfo.icon(); + stream << itemLibraryInfo.category(); + stream << itemLibraryInfo.dragIcon(); + stream << itemLibraryInfo.m_data->properties; + + return stream; +} + +QDataStream& operator>>(QDataStream& stream, ItemLibraryInfo& itemLibraryInfo) +{ + stream >> itemLibraryInfo.m_data->name; + stream >> itemLibraryInfo.m_data->typeName; + stream >> itemLibraryInfo.m_data->majorVersion; + stream >> itemLibraryInfo.m_data->minorVersion; + stream >> itemLibraryInfo.m_data->icon; + stream >> itemLibraryInfo.m_data->category; + stream >> itemLibraryInfo.m_data->dragIcon; + stream >> itemLibraryInfo.m_data->properties; + + return stream; +} + + +} diff --git a/src/plugins/qmldesigner/core/metainfo/metainfo.cpp b/src/plugins/qmldesigner/core/metainfo/metainfo.cpp new file mode 100644 index 00000000000..71448ab3363 --- /dev/null +++ b/src/plugins/qmldesigner/core/metainfo/metainfo.cpp @@ -0,0 +1,678 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "metainfo.h" + +#include "abstractproperty.h" +#include "modelnode.h" +#include "invalidmodelnodeexception.h" +#include "invalidargumentexception.h" +#include "propertymetainfo.h" +#include "metainfoparser.h" +#include "iwidgetplugin.h" + +#include "model/variantparser.h" +#include "pluginmanager/widgetpluginmanager.h" + +#include <QtDebug> +#include <QPair> +#include <QtAlgorithms> +#include <QMetaProperty> +#include <QmlMetaType> +#include <private/qmlgraphicsanchors_p.h> + +enum { + debug = false +}; + +namespace QmlDesigner { +namespace Internal { + +class MetaInfoPrivate +{ + Q_DISABLE_COPY(MetaInfoPrivate) +public: + typedef QSharedPointer<MetaInfoPrivate> Pointer; + typedef QWeakPointer<MetaInfoPrivate> WeakPointer; + + + MetaInfoPrivate(MetaInfo *q); + void clear(); + + void initialize(); + void parseQmlTypes(); + void parseNonQmlTypes(); + void parseValueTypes(); + void parseNonQmlClassRecursively(const QMetaObject *qMetaObject); + void parseProperties(NodeMetaInfo &nodeMetaInfo, const QMetaObject *qMetaObject) const; + void parseClassInfo(NodeMetaInfo &nodeMetaInfo, const QMetaObject *qMetaObject) const; + + QString typeName(const QMetaObject *qMetaObject) const; + + void parseXmlFiles(); + + QMultiHash<QString, QString> m_superClassHash; // the list of direct superclasses + QHash<QString, NodeMetaInfo> m_nodeMetaInfoHash; + QHash<QString, EnumeratorMetaInfo> m_enumeratorMetaInfoHash; + QMultiHash<NodeMetaInfo, ItemLibraryInfo> m_itemLibraryInfoHash; + QHash<QString, ItemLibraryInfo> m_itemLibraryInfoHashAll; + QHash<QString, QString> m_QtTypesToQmlTypes; + + MetaInfo *m_q; + bool m_isInitialized; +}; + +MetaInfoPrivate::MetaInfoPrivate(MetaInfo *q) : + m_q(q), + m_isInitialized(false) +{ +} + +void MetaInfoPrivate::clear() +{ + m_superClassHash.clear(); + m_nodeMetaInfoHash.clear(); + m_enumeratorMetaInfoHash.clear(); + m_itemLibraryInfoHash.clear(); + m_itemLibraryInfoHashAll.clear(); + m_isInitialized = false; +} + +void MetaInfoPrivate::initialize() +{ + parseQmlTypes(); + parseNonQmlTypes(); + parseValueTypes(); + parseXmlFiles(); + + m_isInitialized = true; +} + + + +void MetaInfoPrivate::parseProperties(NodeMetaInfo &nodeMetaInfo, const QMetaObject *qMetaObject) const +{ + Q_ASSERT_X(qMetaObject, Q_FUNC_INFO, "invalid QMetaObject"); + Q_ASSERT_X(nodeMetaInfo.isValid(), Q_FUNC_INFO, "invalid NodeMetaInfo"); + + for (int i = qMetaObject->propertyOffset(); i < qMetaObject->propertyCount(); ++i) { + QMetaProperty qProperty = qMetaObject->property(i); + + PropertyMetaInfo propertyInfo; + + propertyInfo.setName(QLatin1String(qProperty.name())); + + QString typeName(qProperty.typeName()); + QString noStar = typeName; + bool star = false; + while (noStar.contains('*')) {//strip star + noStar.chop(1); + star = true; + } + if (m_QtTypesToQmlTypes.contains(noStar)) { + typeName = star ? m_QtTypesToQmlTypes.value(noStar) + '*' : m_QtTypesToQmlTypes.value(noStar); + //### versions + } + propertyInfo.setType(typeName); + propertyInfo.setValid(true); + propertyInfo.setReadable(qProperty.isReadable()); + propertyInfo.setWritable(qProperty.isWritable()); + propertyInfo.setResettable(qProperty.isResettable()); + propertyInfo.setEnumType(qProperty.isEnumType()); + propertyInfo.setFlagType(qProperty.isFlagType()); + + if (propertyInfo.isEnumType()) { + EnumeratorMetaInfo enumerator; + + QMetaEnum qEnumerator = qProperty.enumerator(); + enumerator.setValid(qEnumerator.isValid()); + enumerator.setIsFlagType(qEnumerator.isFlag()); + enumerator.setScope(qEnumerator.scope()); + enumerator.setName(qEnumerator.name()); + for (int i = 0 ;i < qEnumerator.keyCount(); i++) + { + enumerator.addElement(qEnumerator.valueToKey(i), i); + } + + propertyInfo.setEnumerator(enumerator); + } + + nodeMetaInfo.addProperty(propertyInfo); + } +} + +void MetaInfoPrivate::parseClassInfo(NodeMetaInfo &nodeMetaInfo, const QMetaObject *qMetaObject) const +{ + Q_ASSERT_X(qMetaObject, Q_FUNC_INFO, "invalid QMetaObject"); + Q_ASSERT_X(nodeMetaInfo.isValid(), Q_FUNC_INFO, "invalid NodeMetaInfo"); + for (int index = qMetaObject->classInfoCount() - 1 ; index >= 0 ; --index) { + QMetaClassInfo classInfo = qMetaObject->classInfo(index); + if (QLatin1String(classInfo.name()) == QLatin1String("DefaultProperty")) { + nodeMetaInfo.setDefaultProperty(classInfo.value()); + return; + } + } +} + +void MetaInfoPrivate::parseNonQmlClassRecursively(const QMetaObject *qMetaObject) +{ + Q_ASSERT_X(qMetaObject, Q_FUNC_INFO, "invalid QMetaObject"); + const QString className = qMetaObject->className(); + if ( !m_q->hasNodeMetaInfo(className) + && !QmlMetaType::qmlTypeNames().contains(typeName(qMetaObject).toAscii()) ) { + NodeMetaInfo nodeMetaInfo(*m_q); + nodeMetaInfo.setTypeName(typeName(qMetaObject)); + parseProperties(nodeMetaInfo, qMetaObject); + parseClassInfo(nodeMetaInfo, qMetaObject); + + if (debug) + qDebug() << "adding non qml type" << className << typeName(qMetaObject) << ", parent type" << typeName(qMetaObject->superClass()); + m_q->addNodeInfo(nodeMetaInfo, typeName(qMetaObject->superClass())); + } + + if (const QMetaObject *superClass = qMetaObject->superClass()) { + parseNonQmlClassRecursively(superClass); + } +} + + +QString MetaInfoPrivate::typeName(const QMetaObject *qMetaObject) const +{ + if (!qMetaObject) + return QString(); + QString className = qMetaObject->className(); + if (QmlType *qmlType = QmlMetaType::qmlType(qMetaObject)) { + QString qmlClassName(qmlType->qmlTypeName()); + if (!qmlClassName.isEmpty()) + className = qmlType->qmlTypeName(); // Ensure that we always use the qml name, + // if available. + } + return className; +} + +void MetaInfoPrivate::parseValueTypes() +{ + QStringList valueTypes; + //there is no global list of all supported value types + valueTypes << "QFont" + << "QPoint" + << "QPointF" + << "QRect" + << "QRectF" + << "QSize" + << "QSizeF" + << "QVector3D"; + + foreach (const QString &type, valueTypes) { + NodeMetaInfo nodeMetaInfo(*m_q); + nodeMetaInfo.setTypeName(type); + foreach (const QString &propertyName, VariantParser::create(type).properties()) { + PropertyMetaInfo propertyInfo; + propertyInfo.setName(propertyName); + propertyInfo.setType("real"); + if (type == ("QFont")) { + if (propertyName == "bold") + propertyInfo.setType("bool"); + else if (propertyName == "italic") + propertyInfo.setType("bool"); + else if (propertyName == "family") + propertyInfo.setType("string"); + else if (propertyName == "pixelSize") + propertyInfo.setType("int"); + } else if (type == ("QPoint")) { + propertyInfo.setType("int"); + } else if (type == ("QSize")) { + propertyInfo.setType("int"); + } else if (type == ("QRect")) { + propertyInfo.setType("int"); + } + propertyInfo.setValid(true); + propertyInfo.setReadable(true); + propertyInfo.setWritable(true); + nodeMetaInfo.addProperty(propertyInfo); + } + if (debug) + qDebug() << "adding value type" << nodeMetaInfo.typeName(); + m_q->addNodeInfo(nodeMetaInfo, QString()); + } +} + +void MetaInfoPrivate::parseQmlTypes() +{ + foreach (QmlType *qmlType, QmlMetaType::qmlTypes()) { + const QString qtTypeName(qmlType->typeName()); + const QString qmlTypeName(qmlType->qmlTypeName()); + m_QtTypesToQmlTypes.insert(qtTypeName, qmlTypeName); + } + foreach (QmlType *qmlType, QmlMetaType::qmlTypes()) { + const QMetaObject *qMetaObject = qmlType->metaObject(); + + // parseQmlTypes is called iteratively e.g. when plugins are loaded + if (m_q->hasNodeMetaInfo(qmlType->qmlTypeName(), qmlType->majorVersion(), qmlType->minorVersion())) + continue; + + NodeMetaInfo nodeMetaInfo(*m_q); + nodeMetaInfo.setTypeName(qmlType->qmlTypeName()); + nodeMetaInfo.setMajorVersion(qmlType->majorVersion()); + nodeMetaInfo.setMinorVersion(qmlType->minorVersion()); + + parseProperties(nodeMetaInfo, qMetaObject); + parseClassInfo(nodeMetaInfo, qMetaObject); + + QString superTypeName = typeName(qMetaObject->superClass()); + if (qmlType->baseMetaObject() != qMetaObject) { + // type is declared with Q_DECLARE_EXTENDED_TYPE + // also parse properties of original type + parseProperties(nodeMetaInfo, qmlType->baseMetaObject()); + superTypeName = typeName(qmlType->baseMetaObject()->superClass()); + } + + m_q->addNodeInfo(nodeMetaInfo, superTypeName); + } +} + +void MetaInfoPrivate::parseNonQmlTypes() +{ + foreach (QmlType *qmlType, QmlMetaType::qmlTypes()) { + parseNonQmlClassRecursively(qmlType->metaObject()); + } + + parseNonQmlClassRecursively(&QmlGraphicsAnchors::staticMetaObject); +} + + +void MetaInfoPrivate::parseXmlFiles() +{ + Internal::MetaInfoParser(*m_q).parseFile(":/metainfo/gui.metainfo"); + + Internal::WidgetPluginManager pluginManager; + foreach (const QString &pluginDir, m_q->s_pluginDirs) + pluginManager.addPath(pluginDir); + QList<IWidgetPlugin *> widgetPluginList = pluginManager.instances(); + foreach (IWidgetPlugin *plugin, widgetPluginList) { + parseQmlTypes(); + Internal::MetaInfoParser parser(*m_q); + parser.parseFile(plugin->metaInfo()); + } +} + +} // namespace Internal + +using QmlDesigner::Internal::MetaInfoPrivate; + +MetaInfo MetaInfo::s_global; +QStringList MetaInfo::s_pluginDirs; + + +/*! +\class QmlDesigner::MetaInfo +\ingroup CoreModel +\brief The MetaInfo class provides meta information about qml types and properties. + +The MetaInfo, NodeMetaInfo, PropertyMetaInfo and EnumeratorMetaInfo +classes provide information about the (static and dynamic) qml types available in +a specific model. Just like their Model, ModelNode and AbstractProperty counterparts, +objects of these classes are handles - that means, they are implicitly shared, and +should be created on the stack. + +The MetaInfo object should always be accessed via the model (see Model::metaInfo()). +Otherwise types specific to a model (like sub components) might +be missed. + +\see Model::metaInfo(), QmlDesigner::NodeMetaInfo, QmlDesigner::PropertyMetaInfo, QmlDesigner::EnumeratorMetaInfo +*/ + +/*! + \brief Constructs a copy of the given meta info. + */ +MetaInfo::MetaInfo(const MetaInfo &metaInfo) : + m_p(metaInfo.m_p) +{ +} + +/*! + \brief Creates a meta information object with just the qml types registered statically. + You almost always want to use Model::metaInfo() instead! + + You almost certainly want to access the meta information for the model. + + \see Model::metaInfo() + */ +MetaInfo::MetaInfo() : + m_p(new MetaInfoPrivate(this)) +{ +} + +MetaInfo::~MetaInfo() +{ +} + +/*! + \brief Assigns other to this meta information and returns a reference to this meta information. + */ +MetaInfo& MetaInfo::operator=(const MetaInfo &other) +{ + m_p = other.m_p; + return *this; +} + +/*! + \brief Returns whether a type with the given name is registered in the meta system. + */ +bool MetaInfo::hasNodeMetaInfo(const QString &typeName, int /*majorVersion*/, int /*minorVersion*/) const +{ + if (m_p->m_nodeMetaInfoHash.contains(typeName)) + return true; + if (!isGlobal()) + return global().hasNodeMetaInfo(typeName); + return false; +} + +/*! + \brief Returns meta information for a qml type. An invalid NodeMetaInfo object if the type is unknown. + */ +NodeMetaInfo MetaInfo::nodeMetaInfo(const QString &typeName, int /*majorVersion*/, int /*minorVersion*/) const +{ + if (m_p->m_nodeMetaInfoHash.contains(typeName)) + return m_p->m_nodeMetaInfoHash.value(typeName, NodeMetaInfo()); + if (!isGlobal()) + return global().nodeMetaInfo(typeName); + + return NodeMetaInfo(); +} + +QStringList MetaInfo::superClasses(const QString &className) const +{ + QStringList ancestorList = m_p->m_superClassHash.values(className); + foreach (const QString &ancestor, ancestorList) { + QStringList superClassList = superClasses(ancestor); + if (!superClassList.isEmpty()) + ancestorList += superClassList; + } + if (!isGlobal()) + ancestorList += global().superClasses(className); + return ancestorList; +} + +QStringList MetaInfo::directSuperClasses(const QString &className) const +{ + QStringList directAncestorList = m_p->m_superClassHash.values(className); + if (!isGlobal()) + directAncestorList += global().directSuperClasses(className); + return directAncestorList; +} + +QList<NodeMetaInfo> MetaInfo::superClasses(const NodeMetaInfo &nodeInfo) const +{ + if (!nodeInfo.isValid()) { + Q_ASSERT_X(nodeInfo.isValid(), Q_FUNC_INFO, "Invalid nodeInfo argument"); + throw InvalidArgumentException(__LINE__, __FUNCTION__, __FILE__, "nodeInfo"); + } + + QList<NodeMetaInfo> superClassList; + + foreach (const QString &typeName, superClasses(nodeInfo.typeName())) { + if (!hasNodeMetaInfo(typeName)) + continue; + const NodeMetaInfo superClass = nodeMetaInfo(typeName); + if (!superClassList.contains(superClass)) + superClassList.append(superClass); + } + return superClassList; +} + +QList<NodeMetaInfo> MetaInfo::directSuperClasses(const NodeMetaInfo &nodeInfo) const +{ + if (!nodeInfo.isValid()) { + Q_ASSERT_X(nodeInfo.isValid(), Q_FUNC_INFO, "Invalid nodeInfo argument"); + throw InvalidArgumentException(__LINE__, __FUNCTION__, __FILE__, "nodeInfo"); + } + + QList<NodeMetaInfo> superClassList; + + foreach (const QString &typeName, directSuperClasses(nodeInfo.typeName())) { + if (!hasNodeMetaInfo(typeName)) + continue; + const NodeMetaInfo superClass = nodeMetaInfo(typeName); + if (!superClassList.contains(superClass)) + superClassList.append(superClass); + } + return superClassList; +} + +QList<ItemLibraryInfo> MetaInfo::itemLibraryRepresentations(const NodeMetaInfo &nodeMetaInfo) const +{ + QList<ItemLibraryInfo> itemLibraryItems = m_p->m_itemLibraryInfoHash.values(nodeMetaInfo); + if (!isGlobal()) + itemLibraryItems += global().itemLibraryRepresentations(nodeMetaInfo); + return itemLibraryItems; +} + +ItemLibraryInfo MetaInfo::itemLibraryRepresentation(const QString &name) const +{ + if (m_p->m_itemLibraryInfoHashAll.contains(name)) + return m_p->m_itemLibraryInfoHashAll.value(name); + if (!isGlobal()) + return global().itemLibraryRepresentation(name); + return ItemLibraryInfo(); +} + +QStringList MetaInfo::itemLibraryItems() const +{ + QStringList completeList = m_p->m_nodeMetaInfoHash.keys(); + QStringList finalList; + foreach (const QString &name, completeList) { + if (nodeMetaInfo(name).isVisibleToItemLibrary()) + finalList.append(name); + } + + if (!isGlobal()) + finalList += global().itemLibraryItems(); + + return finalList; +} + +/*! + \brief Returns whether className is the same type or a type derived from superClassName. + */ +bool MetaInfo::isSubclassOf(const QString &className, const QString &superClassName) const +{ + return (className == superClassName) || superClasses(className).contains(superClassName); +} + +/*! + \brief Returns whether the type of modelNode is the same type or a type derived from superClassName. + \throws InvalidModelNode if !modelNode.isValid() + */ +bool MetaInfo::isSubclassOf(const ModelNode &modelNode, const QString &superClassName) const +{ + if (!modelNode.isValid()) { + Q_ASSERT_X(modelNode.isValid(), Q_FUNC_INFO, "Invalid modelNode argument"); + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + } + return (modelNode.type() == superClassName) || isSubclassOf(modelNode.type(), superClassName); +} + +/*! + \brief Returns whether an enumerator is registered in the meta type system. + */ +bool MetaInfo::hasEnumerator(const QString &enumeratorName) const +{ + return m_p->m_enumeratorMetaInfoHash.contains(enumeratorName) + || (!isGlobal() ? global().hasEnumerator(enumeratorName) : false); +} + +/*! + \brief Returns meta information about an enumerator. An invalid EnumeratorMetaInfo object if the enumerator is not known. + */ +EnumeratorMetaInfo MetaInfo::enumerator(const QString &enumeratorName) const +{ + if (m_p->m_enumeratorMetaInfoHash.contains(enumeratorName)) + return m_p->m_enumeratorMetaInfoHash.value(enumeratorName); + if (!isGlobal()) + return global().enumerator(enumeratorName); + return EnumeratorMetaInfo(); +} + +/*! + \brief Access to the global meta information object. + You almost always want to use Model::metaInfo() instead. + + Internally all meta information objects share this "global" object + where static qml type information is stored. + */ +MetaInfo MetaInfo::global() +{ + if (!s_global.m_p->m_isInitialized) { + s_global.m_p = QSharedPointer<MetaInfoPrivate>(new MetaInfoPrivate(&s_global)); + s_global.m_p->initialize(); + } + return s_global; +} + +/*! + \brief Clears the global meta information object. + + This method should be called once on application shutdown to free static data structures. + */ +void MetaInfo::clearGlobal() +{ + MetaInfo::global().m_p->clear(); +} + +void MetaInfo::setPluginPaths(const QStringList &paths) +{ + s_pluginDirs = paths; +} + +/*! + This bypasses the notifications to the model that the metatype has changed. + Use MetaInfo::addNodeInfo() instead + */ +void MetaInfo::addSuperClassRelationship(const QString &superClassName, const QString &className) +{ + m_p->m_superClassHash.insert(className, superClassName); +} + +void MetaInfo::addNodeInfo(NodeMetaInfo &nodeInfo, const QString &baseType) +{ + if (nodeInfo.typeName().isEmpty() || nodeInfo.metaInfo() != *this) + throw new InvalidArgumentException(__LINE__, __FUNCTION__, __FILE__, QLatin1String("nodeInfo")); + + if (nodeInfo.typeName() == baseType) // prevent simple recursion + throw new InvalidArgumentException(__LINE__, __FUNCTION__, __FILE__, QLatin1String("baseType")); + + m_p->m_nodeMetaInfoHash.insert(nodeInfo.typeName(), nodeInfo); + + if (!baseType.isEmpty()) { + m_p->m_superClassHash.insert(nodeInfo.typeName(), baseType); + } +} + +void MetaInfo::removeNodeInfo(NodeMetaInfo &info) +{ + Q_ASSERT(info.isValid()); + + if (m_p->m_nodeMetaInfoHash.contains(info.typeName())) { + m_p->m_nodeMetaInfoHash.remove(info.typeName()); + + m_p->m_superClassHash.remove(info.typeName()); + // TODO: Other types might specify type as parent type + m_p->m_itemLibraryInfoHash.remove(info); + m_p->m_itemLibraryInfoHashAll.remove(info.typeName()); + + } else if (!isGlobal()) { + global().removeNodeInfo(info); + } else { + Q_ASSERT_X(0, Q_FUNC_INFO, "Node meta info not in db"); + } + + info.setInvalid(); +} + +void MetaInfo::replaceNodeInfo(NodeMetaInfo & /*oldInfo*/, NodeMetaInfo & /*newInfo*/, const QString & /*baseType*/) +{ + // TODO +} + +EnumeratorMetaInfo MetaInfo::addEnumerator(const QString &enumeratorScope, const QString &enumeratorName) +{ + Q_ASSERT(!enumeratorName.isEmpty()); + + EnumeratorMetaInfo enumeratorMetaInfo; + enumeratorMetaInfo.setName(enumeratorName); + enumeratorMetaInfo.setScope(enumeratorScope); + enumeratorMetaInfo.setIsFlagType(false); + enumeratorMetaInfo.setValid(true); + + m_p->m_enumeratorMetaInfoHash.insert(enumeratorMetaInfo.scopeAndName(), enumeratorMetaInfo); + + return enumeratorMetaInfo; +} + +EnumeratorMetaInfo MetaInfo::addFlag(const QString &enumeratorScope, const QString &enumeratorName) +{ + Q_ASSERT(!enumeratorName.isEmpty()); + + EnumeratorMetaInfo enumeratorMetaInfo; + enumeratorMetaInfo.setName(enumeratorName); + enumeratorMetaInfo.setScope(enumeratorScope); + enumeratorMetaInfo.setIsFlagType(true); + m_p->m_enumeratorMetaInfoHash.insert(enumeratorMetaInfo.scopeAndName(), enumeratorMetaInfo); + + return enumeratorMetaInfo; +} + +ItemLibraryInfo MetaInfo::addItemLibraryInfo(const NodeMetaInfo &nodeMetaInfo, const QString &itemLibraryRepresentationName) +{ + ItemLibraryInfo itemLibraryInfo; + itemLibraryInfo.setName(itemLibraryRepresentationName); + itemLibraryInfo.setTypeName(nodeMetaInfo.typeName()); + itemLibraryInfo.setMajorVersion(nodeMetaInfo.majorVersion()); + itemLibraryInfo.setMinorVersion(nodeMetaInfo.minorVersion()); + m_p->m_itemLibraryInfoHash.insert(nodeMetaInfo, itemLibraryInfo); + m_p->m_itemLibraryInfoHashAll.insert(itemLibraryRepresentationName, itemLibraryInfo); + return itemLibraryInfo; +} + +bool MetaInfo::isGlobal() const +{ + return (this->m_p == s_global.m_p); +} + +bool operator==(const MetaInfo &first, const MetaInfo &second) +{ + return first.m_p == second.m_p; +} + +bool operator!=(const MetaInfo &first, const MetaInfo &second) +{ + return !(first == second); +} +} //namespace QmlDesigner diff --git a/src/plugins/qmldesigner/core/metainfo/metainfoparser.cpp b/src/plugins/qmldesigner/core/metainfo/metainfoparser.cpp new file mode 100644 index 00000000000..ae1930cbdae --- /dev/null +++ b/src/plugins/qmldesigner/core/metainfo/metainfoparser.cpp @@ -0,0 +1,386 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "metainfoparser.h" +#include "metainfo.h" + +#include "propertymetainfo.h" +#include "model/propertyparser.h" +#include <QXmlStreamReader> +#include <QString> +#include <QFile> +#include <QtDebug> +#include <QIcon> + +namespace QmlDesigner { +namespace Internal { + +static bool stringToBool(const QString &boolString) +{ + QString lowerString(boolString.toLower()); + if (lowerString == "true" || lowerString == "1") + return true; + + return false; +} + +MetaInfoParser::MetaInfoParser(const MetaInfo &metaInfo) + : m_metaInfo(metaInfo) +{ +} + +void MetaInfoParser::parseFile(const QString &path) +{ + QFile file; + file.setFileName(path); + if (!file.open(QIODevice::ReadOnly)) + throw new InvalidMetaInfoException(__LINE__, __FUNCTION__, __FILE__); + + QXmlStreamReader reader; + reader.setDevice(&file); + + while (!reader.atEnd()) { + reader.readNext(); + tokenHandler(reader); + } + errorHandling(reader, file); +} + +void MetaInfoParser::tokenHandler(QXmlStreamReader &reader) +{ + if (reader.isStartElement() && reader.name() == "metainfo") + handleMetaInfoElement(reader); +} + +void MetaInfoParser::handleMetaInfoElement(QXmlStreamReader &reader) +{ + while (!reader.atEnd() && !(reader.isEndElement() && reader.name() == "metainfo")) { + reader.readNext(); + metaInfoHandler(reader); + } +} + +void MetaInfoParser::metaInfoHandler(QXmlStreamReader &reader) +{ + if (reader.isStartElement()) + { + if (reader.name() == "enumerator") + handleEnumElement(reader); + + if (reader.name() == "flag") + handleFlagElement(reader); + + if (reader.name() == "node") + handleNodeElement(reader); + } +} + +void MetaInfoParser::handleEnumElement(QXmlStreamReader &reader) +{ + QString enumeratorName = reader.attributes().value("name").toString(); + QString enumeratorScope = reader.attributes().value("scope").toString(); + EnumeratorMetaInfo enumeratorMetaInfo; + if (m_metaInfo.hasEnumerator(enumeratorName)) { + enumeratorMetaInfo = m_metaInfo.enumerator(enumeratorName); + } else { + enumeratorMetaInfo = m_metaInfo.addEnumerator(enumeratorScope, enumeratorName); + } + + while (!reader.atEnd() && !(reader.isEndElement() && reader.name() == "enumerator")) { + + reader.readNext(); + handleEnumElementElement(reader, enumeratorMetaInfo); + } +} + +void MetaInfoParser::handleFlagElement(QXmlStreamReader &reader) +{ + QString enumeratorName = reader.attributes().value("name").toString(); + QString enumeratorScope = reader.attributes().value("scope").toString(); + EnumeratorMetaInfo enumeratorMetaInfo = m_metaInfo.addFlag(enumeratorScope, enumeratorName); + + while (!reader.atEnd() && !(reader.isEndElement() && reader.name() == "flag")) { + + reader.readNext(); + handleFlagElementElement(reader, enumeratorMetaInfo); + } +} + +void MetaInfoParser::handleEnumElementElement(QXmlStreamReader &reader, EnumeratorMetaInfo &enumeratorMetaInfo) +{ + if (reader.isStartElement() && reader.name() == "element") + { + bool isIntType; + enumeratorMetaInfo.addElement(reader.attributes().value("name").toString(), + reader.attributes().value("value").toString().toInt(&isIntType)); + Q_ASSERT(isIntType); + } +} + +void MetaInfoParser::handleFlagElementElement(QXmlStreamReader &reader, EnumeratorMetaInfo &enumeratorMetaInfo) +{ + if (reader.isStartElement() && reader.name() == "element") + { + bool isIntType; + enumeratorMetaInfo.addElement(reader.attributes().value("name").toString(), + reader.attributes().value("value").toString().toInt(&isIntType)); + Q_ASSERT(isIntType); + } +} + +void MetaInfoParser::handleNodeElement(QXmlStreamReader &reader) +{ + const QXmlStreamAttributes attributes = reader.attributes(); + + const QString className = attributes.value("name").toString(); + if (className.isEmpty()) { + reader.raiseError("Invalid element 'node' - mandatory attribute 'name' is missing"); + return; + } + + NodeMetaInfo nodeMetaInfo; + if (m_metaInfo.hasNodeMetaInfo(className)) { + nodeMetaInfo = m_metaInfo.nodeMetaInfo(className); + } else { + qWarning() << "Metainfo: " << className << " does not exist"; + while (!reader.atEnd() && !(reader.isEndElement() && reader.name() == "node")) + reader.readNext(); + return; + } + + if (attributes.hasAttribute("isContainer")) { + const QString isContainer = attributes.value("isContainer").toString(); + nodeMetaInfo.setIsContainer(stringToBool(isContainer)); + } + + if (attributes.hasAttribute("showInItemLibrary")) { + const QString showInItemLibrary = attributes.value("showInItemLibrary").toString(); + nodeMetaInfo.setIsVisibleToItemLibrary(stringToBool(showInItemLibrary)); + } + + if (attributes.hasAttribute("category")) { + const QString category = attributes.value("category").toString(); + nodeMetaInfo.setCategory(category); + } + + if (attributes.hasAttribute("icon")) { + const QString iconPath = reader.attributes().value("icon").toString(); + nodeMetaInfo.setIcon(QIcon(iconPath)); + } + + while (!reader.atEnd() && !(reader.isEndElement() && reader.name() == "node")) { + reader.readNext(); + + handleNodeInheritElement(reader, className); + handleAbstractPropertyElement(reader, nodeMetaInfo); + handleAbstractPropertyDefaultValueElement(reader, nodeMetaInfo); + handleNodeItemLibraryRepresentationElement(reader, className); + } +} + +void MetaInfoParser::handleNodeItemLibraryRepresentationElement(QXmlStreamReader &reader, const QString & className) +{ + if (reader.isStartElement() && reader.name() == "itemlibraryrepresentation") + { + QString name = reader.attributes().value("name").toString(); + ItemLibraryInfo ItemLibraryInfo = m_metaInfo.addItemLibraryInfo(m_metaInfo.nodeMetaInfo(className), name); + + QString iconPath = reader.attributes().value("icon").toString(); + if (!iconPath.isEmpty()) + ItemLibraryInfo.setIcon(QIcon(iconPath)); + + while (!reader.atEnd() && !(reader.isEndElement() && reader.name() == "itemlibraryrepresentation")) { + reader.readNext(); + handleItemLibraryInfoPropertyElement(reader, ItemLibraryInfo); + } + } +} + +void MetaInfoParser::handleNodeInheritElement(QXmlStreamReader &reader, const QString & className) +{ + if (reader.isStartElement() && reader.name() == "inherits") + { + QString superClassName = reader.attributes().value("name").toString(); + Q_ASSERT(!superClassName.isEmpty()); + m_metaInfo.addSuperClassRelationship(superClassName, className); + reader.readNext(); + } +} + +void MetaInfoParser::handleItemLibraryInfoPropertyElement(QXmlStreamReader &reader, ItemLibraryInfo &ItemLibraryInfo) +{ + if (reader.isStartElement() && reader.name() == "property") + { + QXmlStreamAttributes attributes(reader.attributes()); + QString name = attributes.value("name").toString(); + QString type = attributes.value("type").toString(); + QString value = attributes.value("value").toString(); + ItemLibraryInfo.addProperty(name, type, value); + + reader.readNext(); + } +} + +void MetaInfoParser::handleAbstractPropertyDefaultValueElement(QXmlStreamReader &reader, NodeMetaInfo &nodeMetaInfoArg) +{ + if (reader.isStartElement() && reader.name() == "propertyDefaultValue") + { + const QXmlStreamAttributes attributes(reader.attributes()); + Q_ASSERT(attributes.hasAttribute("name")); + Q_ASSERT(attributes.hasAttribute("type")); + Q_ASSERT(attributes.hasAttribute("defaultValue")); + const QString propertyName = attributes.value("name").toString(); + const QString propertyType = attributes.value("type").toString(); + const QString defaultValueString = attributes.value("defaultValue").toString(); + QVariant defaultValue = Internal::PropertyParser::read(propertyType, + defaultValueString, + m_metaInfo); + + QList<NodeMetaInfo> nodeMetaInfoList(nodeMetaInfoArg.superClasses()); + nodeMetaInfoList.prepend(nodeMetaInfoArg); + foreach(const NodeMetaInfo &nodeMetaInfo, nodeMetaInfoList) { + if (nodeMetaInfo.hasLocalProperty(propertyName)) { + nodeMetaInfo.property(propertyName).setDefaultValue(nodeMetaInfoArg, defaultValue); + break; + } + } + + reader.readNext(); + } +} + +void MetaInfoParser::handleAbstractPropertyElement(QXmlStreamReader &reader, NodeMetaInfo &nodeMetaInfo) +{ + if (reader.isStartElement() && reader.name() == "property") + { + const QXmlStreamAttributes attributes(reader.attributes()); + + + const QString propertyName = attributes.value("name").toString(); + + if (propertyName.isEmpty()) { + reader.raiseError("Invalid element 'property' - attribute 'name' is missing or empty"); + return; + } + + PropertyMetaInfo propertyMetaInfo; + if (nodeMetaInfo.hasLocalProperty(propertyName)) { + propertyMetaInfo = nodeMetaInfo.property(propertyName); + } else { + propertyMetaInfo.setName(propertyName); + } + propertyMetaInfo.setValid(true); + + //propertyMetaInfo.setReadable(stringToBool(attributes.value("isReadable").toString())); + //propertyMetaInfo.setWritable(stringToBool(attributes.value("isWritable").toString())); + //propertyMetaInfo.setResettable(stringToBool(attributes.value("isResetable").toString())); + if (attributes.hasAttribute("isEnumType")) + propertyMetaInfo.setEnumType(stringToBool(attributes.value("isEnumType").toString())); + if (attributes.hasAttribute("isFlagType")) + propertyMetaInfo.setFlagType(stringToBool(attributes.value("isFlagType").toString())); + if (attributes.hasAttribute("showInPropertyEditor")) + propertyMetaInfo.setIsVisibleToPropertyEditor(stringToBool(attributes.value("showInPropertyEditor").toString())); + + if (propertyMetaInfo.isEnumType()) { + propertyMetaInfo.setType(QString("%1::%2").arg(attributes.value("enumeratorScope").toString(), + attributes.value("enumeratorName").toString())); + propertyMetaInfo.setEnumerator(m_metaInfo.enumerator(propertyMetaInfo.type())); + } else { + const QString type = attributes.value("type").toString(); + if (type.isEmpty()) { + reader.raiseError("Invalid element 'property' - attribute 'type' is missing or empty"); + return; + } + propertyMetaInfo.setType(attributes.value("type").toString()); + } + + if (attributes.hasAttribute("defaultValue")) { + QVariant defaultValue = Internal::PropertyParser::read(propertyMetaInfo.type(), + attributes.value("defaultValue").toString(), + m_metaInfo); + + propertyMetaInfo.setDefaultValue(nodeMetaInfo, defaultValue); + } + + nodeMetaInfo.addProperty(propertyMetaInfo); + + reader.readNext(); + } +} + +void MetaInfoParser::errorHandling(QXmlStreamReader &reader, QFile &file) +{ + if (!reader.hasError()) + return; + + qDebug() << QString("Error at %1, %2:%3: %4") + .arg(file.fileName()) + .arg(reader.lineNumber()) + .arg(reader.columnNumber()) + .arg(reader.errorString()); + + file.reset(); + + QString fileString = file.readAll(); + QString snippetString; + int lineCount = 0; + int position = reader.characterOffset(); + while (position >= 0) + { + if (fileString[position] == '\n') { + if (lineCount > 3) + break; + lineCount++; + } + + snippetString.prepend(fileString[position]); + position--; + } + + lineCount = 0; + position = reader.characterOffset(); + while (position >= 0) + { + position++; + if (fileString[position] == '\n') { + if (lineCount > 1) + break; + lineCount++; + } + + snippetString.append(fileString[position]); + } + + qDebug() << snippetString; + +} + + +} +} diff --git a/src/plugins/qmldesigner/core/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/core/metainfo/nodemetainfo.cpp new file mode 100644 index 00000000000..3498b5c4190 --- /dev/null +++ b/src/plugins/qmldesigner/core/metainfo/nodemetainfo.cpp @@ -0,0 +1,728 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "nodemetainfo.h" +#include "model.h" +#include "widgetqueryview.h" +#include "nodeinstance.h" +#include "invalidargumentexception.h" + +#include "metainfo.h" +#include "propertymetainfo.h" + +#include <QtCore/QSharedData> +#include <QtCore/QtDebug> +#include <QtGui/QIcon> +#include <QtDeclarative/QmlMetaType> +#include <QtDeclarative/QmlContext> +#include <QtDeclarative/QmlEngine> +#include <QtDeclarative/QmlComponent> +#include <private/qmlvaluetype_p.h> + +namespace QmlDesigner { + +namespace Internal { + +class NodeMetaInfoData : public QSharedData +{ +public: + typedef enum { + No = -1, + Unknown = 0, + Yes = 1, + } TristateBoolean; + +public: + NodeMetaInfoData(const MetaInfo &metaInfo) : + metaInfo(metaInfo), + isContainer(false), + isVisibleToItemLibrary(false), + isFXItem(Unknown), + icon(), + category("misc") + { } + + MetaInfo metaInfo; + QString typeName; + bool isContainer; + bool isVisibleToItemLibrary; + TristateBoolean isFXItem; + QHash<QString, PropertyMetaInfo> propertyMetaInfoHash; + QIcon icon; + QString category; + QString qmlFile; + QString defaultProperty; + int majorVersion; + int minorVersion; +}; + +} // namespace Internal + +/*! +\class QmlDesigner::NodeMetaInfo +\ingroup CoreModel +\brief The NodeMetaInfo class provides meta information about a qml type. + +A NodeMetaInfo object can be created via ModelNode::metaInfo, or MetaInfo::nodeMetaInfo. + +The object can be invalid - you can check this by calling isValid(). +The object is invalid if you ask for meta information for +an non-existing qml property. Also the node meta info can become invalid +if the enclosing type is deregistered from the meta type system (e.g. +a sub component qml file is deleted). Trying to call any accessor methods on an invalid +NodeMetaInfo object will result in an InvalidMetaInfoException being thrown. + +\see QmlDesigner::MetaInfo, QmlDesigner::PropertyMetaInfo, QmlDesigner::EnumeratorMetaInfo +*/ + +NodeMetaInfo::NodeMetaInfo() + : m_data(0) +{ + // create invalid node +} + +NodeMetaInfo::NodeMetaInfo(const MetaInfo &metaInfo) + : m_data(new Internal::NodeMetaInfoData(metaInfo)) +{ +} + +NodeMetaInfo::~NodeMetaInfo() +{ +} + +/*! + \brief Creates a copy of the handle. + */ +NodeMetaInfo::NodeMetaInfo(const NodeMetaInfo &other) + : m_data(other.m_data) +{ +} + +/*! + \brief Copies the handle. + */ +NodeMetaInfo &NodeMetaInfo::operator=(const NodeMetaInfo &other) +{ + if (this != &other) + this->m_data = other.m_data; + + return *this; +} + +/*! + \brief Returns whether the meta information system knows about this type. + */ +bool NodeMetaInfo::isValid() const +{ + return (m_data.data() != 0); +} + +MetaInfo NodeMetaInfo::metaInfo() const +{ + if (!isValid()) { + Q_ASSERT_X(0, Q_FUNC_INFO, "Invalid NodeMetaInfo object"); + throw InvalidMetaInfoException(__LINE__, __FUNCTION__, __FILE__); + } + + return m_data->metaInfo; +} + +/*! + \brief Creates an instance of the qml type in the given qml context. + + \throws InvalidArgumentException when the context argument is a null pointer + \throws InvalidMetaInfoException if the object is not valid + */ +QObject *NodeMetaInfo::createInstance(QmlContext *parentContext) const +{ + if (!parentContext) { + Q_ASSERT_X(0, Q_FUNC_INFO, "Context cannot be null"); + throw InvalidArgumentException(__LINE__, __FUNCTION__, __FILE__, "context"); + } + + if (!isValid()) { + Q_ASSERT_X(0, Q_FUNC_INFO, "Invalid NodeMetaInfo object"); + throw InvalidMetaInfoException(__LINE__, __FUNCTION__, __FILE__); + } + + QObject *object = 0; + if (isComponent()) { + // qml component + // TODO: This is maybe expensive ... + QmlComponent component(parentContext->engine(), QUrl::fromLocalFile(m_data->qmlFile)); + object = component.create(parentContext); + } else { + // primitive + object = QmlMetaType::qmlType(typeName().toAscii(), 4, 6)->create(); + if (object && parentContext) + QmlEngine::setContextForObject(object, new QmlContext(parentContext, object)); + } + return object; +} + +/*! + \brief Returns all (direct and indirect) ancestor types. + + \throws InvalidMetaInfoException if the object is not valid + */ +QList<NodeMetaInfo> NodeMetaInfo::superClasses() const +{ + if (!isValid()) { + Q_ASSERT_X(0, Q_FUNC_INFO, "Invalid NodeMetaInfo object"); + throw InvalidMetaInfoException(__LINE__, __FUNCTION__, __FILE__); + } + + return m_data->metaInfo.superClasses(*this); +} + +/*! + \brief Returns direct ancestor types. + + \throws InvalidMetaInfoException if the object is not valid + */ +QList<NodeMetaInfo> NodeMetaInfo::directSuperClasses() const +{ + if (!isValid()) { + Q_ASSERT_X(0, Q_FUNC_INFO, "Invalid NodeMetaInfo object"); + throw InvalidMetaInfoException(__LINE__, __FUNCTION__, __FILE__); + } + + return m_data->metaInfo.directSuperClasses(*this); +} + +/*! + \brief Returns meta information for all properties, including properties inherited from base types. + + Returns a Hash with the name of the property as key and property meta information as value. Node + In case there are multiple properties with the same name in the hierarchy the property defined + in the more concrete subclass is chosen. + + \throws InvalidMetaInfoException if the object is not valid + */ +QHash<QString,PropertyMetaInfo> NodeMetaInfo::properties(bool resolveDotSyntax ) const +{ + if (!isValid()) { + Q_ASSERT_X(0, Q_FUNC_INFO, "Invalid NodeMetaInfo object"); + throw InvalidMetaInfoException(__LINE__, __FUNCTION__, __FILE__); + } + + QHash<QString,PropertyMetaInfo> propertiesInfo; + propertiesInfo = m_data->propertyMetaInfoHash; + foreach (const NodeMetaInfo &nodeInfo, directSuperClasses()) { + QHash<QString,PropertyMetaInfo> superClassProperties = nodeInfo.properties(); + QHashIterator<QString,PropertyMetaInfo> iter(superClassProperties); + while (iter.hasNext()) { + iter.next(); + if (!propertiesInfo.contains(iter.key())) + propertiesInfo.insert(iter.key(), iter.value()); + } + } + if (resolveDotSyntax) { + QHashIterator<QString,PropertyMetaInfo> iter(dotProperties()); + while (iter.hasNext()) { + iter.next(); + if (!propertiesInfo.contains(iter.key())) + propertiesInfo.insert(iter.key(), iter.value()); + } + } + return propertiesInfo; +} + +/*! + \brief Returns meta information for all dot properties, including properties inherited from base types. + + */ +QHash<QString,PropertyMetaInfo> NodeMetaInfo::dotProperties() const +{ + if (!isValid()) { + Q_ASSERT_X(0, Q_FUNC_INFO, "Invalid NodeMetaInfo object"); + throw InvalidMetaInfoException(__LINE__, __FUNCTION__, __FILE__); + } + + QHash<QString,PropertyMetaInfo> propertiesInfo; + + foreach (const QString &propertyName, properties().keys()) { + if (property(propertyName).hasDotSubProperties()) { + QString propertyType = property(propertyName).type(); + if (propertyType.right(1) == "*") + propertyType = propertyType.left(propertyType.size() - 1).trimmed(); + NodeMetaInfo nodeInfo(m_data->metaInfo.nodeMetaInfo(propertyType, majorVersion(), minorVersion())); + if (nodeInfo.isValid()) { + QHashIterator<QString,PropertyMetaInfo> iter(nodeInfo.properties()); + while (iter.hasNext()) { + iter.next(); + if (!propertiesInfo.contains(iter.key()) && iter.key() != "objectName") + propertiesInfo.insert(propertyName + "." + iter.key(), iter.value()); + } + } + } + } + return propertiesInfo; +} + +/*! + \brief Returns meta information for a property. An invalid PropertyMetaInfo object if the given property name is unknown. + + \throws InvalidMetaInfoException if the object is not valid + */ +PropertyMetaInfo NodeMetaInfo::property(const QString &propertyName, bool resolveDotSyntax) const +{ + if (!isValid()) { + Q_ASSERT_X(0, Q_FUNC_INFO, "Invalid NodeMetaInfo object"); + throw InvalidMetaInfoException(__LINE__, __FUNCTION__, __FILE__); + } + + if (resolveDotSyntax && propertyName.contains('.')) { + const QStringList nameParts = propertyName.split('.'); + NodeMetaInfo nodeInfo = *this; + const int partCount = nameParts.size(); + for (int i = 0; i < partCount; ++i) { + const QString namePart(nameParts.at(i)); + const PropertyMetaInfo propInfo = nodeInfo.property(namePart, false); + + if (!propInfo.isValid()) + break; + + if (i + 1 == partCount) + return propInfo; + + QString propertyType = propInfo.type(); + if (propertyType.right(1) == "*") + propertyType = propertyType.left(propertyType.size() - 1).trimmed(); + nodeInfo = m_data->metaInfo.nodeMetaInfo(propertyType, majorVersion(), minorVersion()); + if (!nodeInfo.isValid()) { + qDebug() << "no type info available for" << propertyType; + break; + } + } + + return PropertyMetaInfo(); + } else { + PropertyMetaInfo propertyMetaInfo; + + if (hasLocalProperty(propertyName)) { + propertyMetaInfo = m_data->propertyMetaInfoHash.value(propertyName, PropertyMetaInfo()); + } else { + foreach (const NodeMetaInfo &superTypeMetaInfo, directSuperClasses()) { + Q_ASSERT(superTypeMetaInfo.isValid()); + propertyMetaInfo = superTypeMetaInfo.property(propertyName); + if (propertyMetaInfo.isValid()) + break; + } + } + + return propertyMetaInfo; + } +} + +/*! + \brief Returns whether the type has a (not inherited) property. + + \throws InvalidMetaInfoException if the object is not valid + */ +bool NodeMetaInfo::hasLocalProperty(const QString &propertyName, bool resolveDotSyntax) const +{ + if (!isValid()) { + Q_ASSERT_X(0, Q_FUNC_INFO, "Invalid NodeMetaInfo object"); + throw InvalidMetaInfoException(__LINE__, __FUNCTION__, __FILE__); + } + + if (resolveDotSyntax && propertyName.contains('.')) { + const QStringList nameParts = propertyName.split('.'); + NodeMetaInfo nodeInfo = *this; + const int partCount = nameParts.size(); + for (int i = 0; i < partCount; ++i) { + QString namePart(nameParts.at(i)); + const PropertyMetaInfo propInfo = nodeInfo.property(namePart, false); + + if (!propInfo.isValid()) + break; + + if (i + 1 == partCount) + return true; + + QString propertyType = propInfo.type(); + if (propertyType.right(1) == "*") + propertyType = propertyType.left(propertyType.size() - 1).trimmed(); + nodeInfo = m_data->metaInfo.nodeMetaInfo(propertyType, majorVersion(), minorVersion()); + if (!nodeInfo.isValid()) { + qDebug() << "no type info available for" << propertyType; + break; + } + } + + return false; + } else { + return m_data->propertyMetaInfoHash.contains(propertyName); + } +} + +/*! + \brief Returns whether the type has a (inherited or not inherited) property. + + \throws InvalidMetaInfoException if the object is not valid + */ +bool NodeMetaInfo::hasProperty(const QString &propertyName, bool resolveDotSyntax) const +{ + if (!isValid()) { + Q_ASSERT_X(0, Q_FUNC_INFO, "Invalid NodeMetaInfo object"); + throw InvalidMetaInfoException(__LINE__, __FUNCTION__, __FILE__); + } + + if (hasLocalProperty(propertyName, resolveDotSyntax)) + return true; + + foreach (const NodeMetaInfo &nodeMetaInfo, directSuperClasses()) { + if (nodeMetaInfo.hasProperty(propertyName, resolveDotSyntax)) + return true; + } + + return false; +} + +void NodeMetaInfo::addProperty(const PropertyMetaInfo &property) +{ + if (!isValid()) { + Q_ASSERT_X(0, Q_FUNC_INFO, "Invalid NodeMetaInfo object"); + throw InvalidMetaInfoException(__LINE__, __FUNCTION__, __FILE__); + } + m_data->propertyMetaInfoHash.insert(property.name(), property); +} + +/*! + \brief Returns the name of the qml type. + + This is not necessarily the class name: E.g. the class defining "Item" is QmlGraphicsItem. + + \throws InvalidMetaInfoException if the object is not valid + */ +QString NodeMetaInfo::typeName() const +{ + if (!isValid()) { + Q_ASSERT_X(0, Q_FUNC_INFO, "Invalid NodeMetaInfo object"); + throw InvalidMetaInfoException(__LINE__, __FUNCTION__, __FILE__); + } + return m_data->typeName; +} + +/*! + \brief Returns the name of the major number of the qml type. + + \throws InvalidMetaInfoException if the object is not valid +*/ +int NodeMetaInfo::majorVersion() const +{ + if (!isValid()) { + Q_ASSERT_X(0, Q_FUNC_INFO, "Invalid NodeMetaInfo object"); + throw InvalidMetaInfoException(__LINE__, __FUNCTION__, __FILE__); + } + + return 4; +} + + +/*! + \brief Returns the name of the minor number of the qml type to which the type is used. + + \throws InvalidMetaInfoException if the object is not valid +*/ +int NodeMetaInfo::minorVersion() const +{ + if (!isValid()) { + Q_ASSERT_X(0, Q_FUNC_INFO, "Invalid NodeMetaInfo object"); + throw InvalidMetaInfoException(__LINE__, __FUNCTION__, __FILE__); + } + + return m_data->minorVersion; +} + +bool NodeMetaInfo::hasDefaultProperty() const +{ + if (!isValid()) { + Q_ASSERT_X(0, Q_FUNC_INFO, "Invalid NodeMetaInfo object"); + throw InvalidMetaInfoException(__LINE__, __FUNCTION__, __FILE__); + } + + return m_data->defaultProperty.isNull(); +} + +QString NodeMetaInfo::defaultProperty() const +{ + if (!isValid()) { + Q_ASSERT_X(0, Q_FUNC_INFO, "Invalid NodeMetaInfo object"); + throw InvalidMetaInfoException(__LINE__, __FUNCTION__, __FILE__); + } + + return m_data->defaultProperty; +} + +void NodeMetaInfo::setDefaultProperty(const QString &defaultProperty) +{ + if (!isValid()) { + Q_ASSERT_X(0, Q_FUNC_INFO, "Invalid NodeMetaInfo object"); + throw InvalidMetaInfoException(__LINE__, __FUNCTION__, __FILE__); + } + + m_data->defaultProperty = defaultProperty; +} + +void NodeMetaInfo::setMajorVersion(int version) +{ + m_data->majorVersion = version; +} + +void NodeMetaInfo::setMinorVersion(int version) +{ + m_data->minorVersion = version; +} + +void NodeMetaInfo::setInvalid() +{ + if (!isValid()) + return; + + m_data = 0; +} + +void NodeMetaInfo::setTypeName(const QString &typeName) +{ + if (!isValid()) { + Q_ASSERT_X(0, Q_FUNC_INFO, "Invalid NodeMetaInfo object"); + throw InvalidMetaInfoException(__LINE__, __FUNCTION__, __FILE__); + } + m_data->typeName = typeName; +} + +uint qHash(const NodeMetaInfo &nodeMetaInfo) +{ + if (!nodeMetaInfo.isValid()) + return 0; + return qHash(nodeMetaInfo.m_data->typeName); +} + +bool operator==(const NodeMetaInfo &firstNodeInfo, + const NodeMetaInfo &secondNodeInfo) +{ + if (!firstNodeInfo.isValid() || !secondNodeInfo.isValid()) + return false; + return firstNodeInfo.m_data->typeName == secondNodeInfo.m_data->typeName; +} + +/*! + \brief Returns whether objects of these type can have children. + + \throws InvalidMetaInfoException if the object is not valid + */ +bool NodeMetaInfo::isContainer() const +{ + // TODO KAI: Is this too generic? + if (!isValid()) { + Q_ASSERT_X(0, Q_FUNC_INFO, "Invalid NodeMetaInfo object"); + throw InvalidMetaInfoException(__LINE__, __FUNCTION__, __FILE__); + } + return m_data->isContainer; +} + +bool NodeMetaInfo::isVisibleToItemLibrary() const +{ + if (!isValid()) { + Q_ASSERT_X(0, Q_FUNC_INFO, "Invalid NodeMetaInfo object"); + throw InvalidMetaInfoException(__LINE__, __FUNCTION__, __FILE__); + } + return m_data->isVisibleToItemLibrary; +} + +void NodeMetaInfo::setIsContainer(bool isContainer) +{ + if (!isValid()) { + Q_ASSERT_X(0, Q_FUNC_INFO, "Invalid NodeMetaInfo object"); + throw InvalidMetaInfoException(__LINE__, __FUNCTION__, __FILE__); + } + m_data->isContainer = isContainer; +} + +void NodeMetaInfo::setIsVisibleToItemLibrary(bool isVisibleToItemLibrary) +{ + if (!isValid()) { + Q_ASSERT_X(0, Q_FUNC_INFO, "Invalid NodeMetaInfo object"); + throw InvalidMetaInfoException(__LINE__, __FUNCTION__, __FILE__); + } + m_data->isVisibleToItemLibrary = isVisibleToItemLibrary; +} + +QIcon NodeMetaInfo::icon() const +{ + if (!isValid()) { + Q_ASSERT_X(0, Q_FUNC_INFO, "Invalid NodeMetaInfo object"); + throw InvalidMetaInfoException(__LINE__, __FUNCTION__, __FILE__); + } + return m_data->icon; +} + +QString NodeMetaInfo::category() const +{ + if (!isValid()) { + Q_ASSERT_X(0, Q_FUNC_INFO, "Invalid NodeMetaInfo object"); + throw InvalidMetaInfoException(__LINE__, __FUNCTION__, __FILE__); + } + return m_data->category; +} + +void NodeMetaInfo::setIcon(const QIcon &icon) +{ + if (!isValid()) { + Q_ASSERT_X(0, Q_FUNC_INFO, "Invalid NodeMetaInfo object"); + throw InvalidMetaInfoException(__LINE__, __FUNCTION__, __FILE__); + } + m_data->icon = icon; +} + +void NodeMetaInfo::setCategory(const QString &category) +{ + if (!isValid()) { + Q_ASSERT_X(0, Q_FUNC_INFO, "Invalid NodeMetaInfo object"); + throw InvalidMetaInfoException(__LINE__, __FUNCTION__, __FILE__); + } + m_data->category = category; +} + +/*! + \brief Returns whether the type inherits from "QWidget". + + \throws InvalidMetaInfoException if the object is not valid + */ +bool NodeMetaInfo::isWidget() const +{ + if (!isValid()) { + Q_ASSERT_X(0, Q_FUNC_INFO, "Invalid NodeMetaInfo object"); + throw InvalidMetaInfoException(__LINE__, __FUNCTION__, __FILE__); + } + return m_data->metaInfo.isSubclassOf(m_data->typeName, "Qt/QWidget"); +} + +/*! + \brief Returns whether the type inherits from "QGraphicsWidget". + + \throws InvalidMetaInfoException if the object is not valid + */ +bool NodeMetaInfo::isGraphicsWidget() const +{ + if (!isValid()) { + Q_ASSERT_X(0, Q_FUNC_INFO, "Invalid NodeMetaInfo object"); + throw InvalidMetaInfoException(__LINE__, __FUNCTION__, __FILE__); + } + return m_data->metaInfo.isSubclassOf(m_data->typeName, "Qt/QGraphicsWidget"); +} + +/*! + \brief Returns whether the type inherits from "QGraphicsObject". + + \throws InvalidMetaInfoException if the object is not valid + */ +bool NodeMetaInfo::isGraphicsObject() const +{ + if (!isValid()) { + Q_ASSERT_X(0, Q_FUNC_INFO, "Invalid NodeMetaInfo object"); + throw InvalidMetaInfoException(__LINE__, __FUNCTION__, __FILE__); + } + return m_data->metaInfo.isSubclassOf(m_data->typeName, "QGraphicsObject"); +} + +/*! + \brief Returns whether the type inherits from "Item/QmlGraphicsItem". + + \throws InvalidMetaInfoException if the object is not valid + */ +bool NodeMetaInfo::isQmlGraphicsItem() const +{ + if (!isValid()) { + Q_ASSERT_X(0, Q_FUNC_INFO, "Invalid NodeMetaInfo object"); + throw InvalidMetaInfoException(__LINE__, __FUNCTION__, __FILE__); + } + + if (m_data->isFXItem == Internal::NodeMetaInfoData::Unknown) { + m_data->isFXItem = m_data->metaInfo.isSubclassOf(m_data->typeName, "Qt/Item") ? Internal::NodeMetaInfoData::Yes : Internal::NodeMetaInfoData::No; + } + + return m_data->isFXItem == Internal::NodeMetaInfoData::Yes; +} + +bool NodeMetaInfo::isComponent() const +{ + return !m_data->qmlFile.isEmpty(); +} + +/*! + \brief Returns whether the type inherits from a type. + + \throws InvalidMetaInfoException if the object is not valid + */ +bool NodeMetaInfo::isSubclassOf(const QString &type, int /*majorVersion*/, int /*minorVersion*/) const +{ + if (!isValid()) { + Q_ASSERT_X(0, Q_FUNC_INFO, "Invalid NodeMetaInfo object"); + throw InvalidMetaInfoException(__LINE__, __FUNCTION__, __FILE__); + } + return m_data->metaInfo.isSubclassOf(m_data->typeName, type); +} + +void NodeMetaInfo::setQmlFile(const QString &filePath) +{ + if (!isValid()) { + Q_ASSERT_X(0, Q_FUNC_INFO, "Invalid NodeMetaInfo object"); + throw InvalidMetaInfoException(__LINE__, __FUNCTION__, __FILE__); + } + m_data->qmlFile = filePath; +} + +//QDataStream& operator<<(QDataStream& stream, const NodeMetaInfo& nodeMetaInfo) +//{ +// stream << nodeMetaInfo.typeName(); +// stream << nodeMetaInfo.majorVersion(); +// stream << nodeMetaInfo.minorVersionTo(); +// +// return stream; +//} +// +//QDataStream& operator>>(QDataStream& stream, NodeMetaInfo& nodeMetaInfo) +//{ +// QString typeName; +// int minorVersion; +// int majorVersion; +// +// stream >> minorVersion; +// stream >> majorVersion; +// stream >> typeName; +// +// nodeMetaInfo = MetaInfo::global().nodeMetaInfo(typeName/*, majorVersion ,minorVersion*/); +// +// return stream; +//} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/core/metainfo/propertymetainfo.cpp b/src/plugins/qmldesigner/core/metainfo/propertymetainfo.cpp new file mode 100644 index 00000000000..5ad6995bffb --- /dev/null +++ b/src/plugins/qmldesigner/core/metainfo/propertymetainfo.cpp @@ -0,0 +1,441 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "propertymetainfo.h" + +#include <QSharedData> + +#include "invalidmetainfoexception.h" +#include "metainfo.h" +#include "modelnode.h" +#include <private/qmlvaluetype_p.h> +#include <private/qmlstringconverters_p.h> + +namespace QmlDesigner { + +namespace Internal +{ + +class PropertyMetaInfoData : public QSharedData +{ +public: + PropertyMetaInfoData() + : QSharedData(), + isValid(false), + readable(false), + writeable(false), + resettable(false), + enumType(false), + flagType(false), + isVisible(false) + {} + + EnumeratorMetaInfo enumerator; + + QString name; + QString type; + bool isValid; + + bool readable; + bool writeable; + bool resettable; + + bool enumType; + bool flagType; + bool isVisible; + + QHash<QString, QVariant> defaultValueHash; +}; + +} + +/*! +\class QmlDesigner::PropertyMetaInfo +\ingroup CoreModel +\brief The PropertyMetaInfo class provides meta information about a qml type property. + +A PropertyMetaInfo object can be NodeMetaInfo, or AbstractProperty::metaInfo. + +The object can be invalid - you can check this by calling isValid(). +The object is invalid if you ask for meta information for +an non-existing qml type. Also the node meta info can become invalid +if the type is deregistered from the meta type system (e.g. +a sub component qml file is deleted). Trying to call any accessor methods on an invalid +PropertyMetaInfo object will result in an InvalidMetaInfoException being thrown. + + +\see QmlDesigner::MetaInfo, QmlDesigner::NodeMetaInfo, QmlDesigner::EnumeratorMetaInfo +*/ + +PropertyMetaInfo::PropertyMetaInfo() + : m_data(new Internal::PropertyMetaInfoData) +{ +} + +PropertyMetaInfo::~PropertyMetaInfo() +{ +} + +/*! + \brief Creates a copy of the handle. + */ +PropertyMetaInfo::PropertyMetaInfo(const PropertyMetaInfo &other) + : m_data(other.m_data) +{ +} + +/*! + \brief Copies the handle. + */ +PropertyMetaInfo &PropertyMetaInfo::operator=(const PropertyMetaInfo &other) +{ + if (this != &other) + m_data = other.m_data; + + return *this; +} + +/*! + \brief Returns whether the meta information system knows about this property. + */ +bool PropertyMetaInfo::isValid() const +{ + return m_data->isValid; +} + +/*! + \brief Returns the name of the property. + */ +QString PropertyMetaInfo::name() const +{ + if (!isValid()) { + Q_ASSERT_X(isValid(), Q_FUNC_INFO, ""); + throw InvalidMetaInfoException(__LINE__, Q_FUNC_INFO, __FILE__); + } + return m_data->name; +} + +/*! + \brief Returns the type name of the property. + */ +QString PropertyMetaInfo::type() const +{ + if (!isValid()) { + Q_ASSERT_X(isValid(), Q_FUNC_INFO, ""); + throw InvalidMetaInfoException(__LINE__, Q_FUNC_INFO, __FILE__); + } + return m_data->type; +} + +bool PropertyMetaInfo::isVisibleToPropertyEditor() const +{ + if (!isValid()) { + Q_ASSERT_X(isValid(), Q_FUNC_INFO, ""); + throw InvalidMetaInfoException(__LINE__, Q_FUNC_INFO, __FILE__); + } + return m_data->isVisible; +} + +void PropertyMetaInfo::setIsVisibleToPropertyEditor(bool isVisible) +{ + m_data->isVisible = isVisible; +} + +/*! + \brief Returns the QVariant type of the property. + */ +QVariant::Type PropertyMetaInfo::variantTypeId() const +{ + if (!isValid()) { + Q_ASSERT_X(isValid(), Q_FUNC_INFO, ""); + throw InvalidMetaInfoException(__LINE__, Q_FUNC_INFO, __FILE__); + } + Q_ASSERT(!m_data->type.isEmpty()); + + if (m_data->type == "string") + return QVariant::String; + + if (m_data->type == "color") + return QVariant::Color; + + if (m_data->type == "int") + return QVariant::Int; + + if (m_data->type == "url") + return QVariant::Url; + + if (m_data->type == "real") + return QVariant::Double; + + if (m_data->type == "bool") + return QVariant::Bool; + + if (m_data->type == "date") + return QVariant::Date; + + if (m_data->type == "alias") + return QVariant::UserType; + + if (m_data->type == "var") + return QVariant::UserType; + + + return QVariant::nameToType(m_data->type.toLatin1().data()); +} + +/*! + \brief Returns whether the propery is readable. + */ +bool PropertyMetaInfo::isReadable() const +{ + if (!isValid()) { + Q_ASSERT_X(isValid(), Q_FUNC_INFO, ""); + throw InvalidMetaInfoException(__LINE__, Q_FUNC_INFO, __FILE__); + } + return m_data->readable; +} + +/*! + \brief Returns whether the propery is writeable. + */ +bool PropertyMetaInfo::isWriteable() const +{ + if (!isValid()) { + Q_ASSERT_X(isValid(), Q_FUNC_INFO, ""); + throw InvalidMetaInfoException(__LINE__, Q_FUNC_INFO, __FILE__); + } + return m_data->writeable; +} + +/*! + \brief Returns whether the propery is resettable. + */ +bool PropertyMetaInfo::isResettable() const +{ + if (!isValid()) { + Q_ASSERT_X(isValid(), Q_FUNC_INFO, ""); + throw InvalidMetaInfoException(__LINE__, Q_FUNC_INFO, __FILE__); + } + return m_data->resettable; +} + +/*! + \brief Returns whether the propery is complex value type. + */ +bool PropertyMetaInfo::isValueType() const +{ + if (!isValid()) { + Q_ASSERT_X(isValid(), Q_FUNC_INFO, ""); + throw InvalidMetaInfoException(__LINE__, Q_FUNC_INFO, __FILE__); + } + + QmlValueType *valueType(QmlValueTypeFactory::valueType(variantTypeId())); + return valueType; +} + +/*! + \brief Returns whether the propery is a QmlList. + */ +bool PropertyMetaInfo::isListProperty() const +{ + if (!isValid()) { + Q_ASSERT_X(isValid(), Q_FUNC_INFO, ""); + throw InvalidMetaInfoException(__LINE__, Q_FUNC_INFO, __FILE__); + } + + return type().contains("QmlList"); +} + +/*! + \brief Returns whether the propery has sub properties with "." syntax e.g. font + */ +bool PropertyMetaInfo::hasDotSubProperties() const +{ + if (!isValid()) { + Q_ASSERT_X(isValid(), Q_FUNC_INFO, ""); + throw InvalidMetaInfoException(__LINE__, Q_FUNC_INFO, __FILE__); + } + + return isValueType() || !isWriteable(); +} + +/*! + \brief Returns whether the propery stores an enum value. + */ +bool PropertyMetaInfo::isEnumType() const +{ + if (!isValid()) { + Q_ASSERT_X(isValid(), Q_FUNC_INFO, ""); + throw InvalidMetaInfoException(__LINE__, Q_FUNC_INFO, __FILE__); + } + return m_data->enumType; +} + +/*! + \brief Returns whether the propery stores a flag value. + */ +bool PropertyMetaInfo::isFlagType() const +{ + if (!isValid()) { + Q_ASSERT_X(isValid(), Q_FUNC_INFO, ""); + throw InvalidMetaInfoException(__LINE__, Q_FUNC_INFO, __FILE__); + } + return m_data->flagType; +} + +/*! + \brief Returns a default value if there is one specified, an invalid QVariant otherwise. + */ +QVariant PropertyMetaInfo::defaultValue(const NodeMetaInfo &nodeMetaInfoArg) const +{ + if (!isValid()) { + Q_ASSERT_X(isValid(), Q_FUNC_INFO, ""); + throw InvalidMetaInfoException(__LINE__, Q_FUNC_INFO, __FILE__); + } + + QList<NodeMetaInfo> nodeMetaInfoList(nodeMetaInfoArg.superClasses()); + nodeMetaInfoList.prepend(nodeMetaInfoArg); + foreach (const NodeMetaInfo &nodeMetaInfo, nodeMetaInfoList) { + if (m_data->defaultValueHash.contains(nodeMetaInfo.typeName())) + return m_data->defaultValueHash.value(nodeMetaInfo.typeName()); + } + + return QVariant(); +} + +void PropertyMetaInfo::setName(const QString &name) +{ + m_data->name = name; +} + +void PropertyMetaInfo::setType(const QString &type) +{ + m_data->type = type; +} + +void PropertyMetaInfo::setValid(bool isValid) +{ + m_data->isValid = isValid; +} + +void PropertyMetaInfo::setReadable(bool isReadable) +{ + m_data->readable = isReadable; +} + +void PropertyMetaInfo::setWritable(bool isWritable) +{ + m_data->writeable = isWritable; +} + +void PropertyMetaInfo::setResettable(bool isRessetable) +{ + m_data->resettable = isRessetable; +} + +void PropertyMetaInfo::setEnumType(bool isEnumType) +{ + m_data->enumType = isEnumType; +} + +void PropertyMetaInfo::setFlagType(bool isFlagType) +{ + m_data->flagType = isFlagType; +} + +void PropertyMetaInfo::setDefaultValue(const NodeMetaInfo &nodeMetaInfo, const QVariant &value) +{ + m_data->defaultValueHash.insert(nodeMetaInfo.typeName(), value); +} + +void PropertyMetaInfo::setEnumerator(const EnumeratorMetaInfo &info) +{ + m_data->enumerator = info; +} + +/*! + \brief Returns meta information about the enumerator type, an invalid EnumeratorMetaInfo object if the property does not store enumerator values. + */ +const EnumeratorMetaInfo PropertyMetaInfo::enumerator() const +{ + if (!isValid()) { + Q_ASSERT_X(isValid(), Q_FUNC_INFO, ""); + throw InvalidMetaInfoException(__LINE__, Q_FUNC_INFO, __FILE__); + } + return m_data->enumerator; +} + +/*! + \brief cast value type of QVariant parameter + + If the type of the passed variant does not correspond to type(), the method tries to convert + the value according to QVariant::convert(). Returns a new QVariant with casted value type + if successful, an invalid QVariant otherwise. + + \param variant the QVariant to take the value from + \returns QVariant with aligned value type, or invalid QVariant + */ +QVariant PropertyMetaInfo::castedValue(const QVariant &originalVariant) const +{ + if (!isValid()) { + Q_ASSERT_X(isValid(), Q_FUNC_INFO, ""); + throw InvalidMetaInfoException(__LINE__, Q_FUNC_INFO, __FILE__); + } + + QVariant variant = originalVariant; + if (m_data->enumType) { + return variant; + } + + QVariant::Type typeId = variantTypeId(); + + if (variant.type() == QVariant::UserType && variant.userType() == ModelNode::variantUserType()) { + return variant; + } else if (typeId == QVariant::UserType && m_data->type == QLatin1String("QVariant")) { + return variant; + } else if (typeId == QVariant::UserType && m_data->type == QLatin1String("variant")) { + return variant; + } else if (typeId == QVariant::UserType && m_data->type == QLatin1String("var")) { + return variant; + } else if (variant.type() == QVariant::List && variant.type() == QVariant::List) { + // TODO: check the contents of the list + return variant; + } else if (type() == "var" || type() == "variant") { + return variant; + } else if (type() == "alias") { + // TODO: The Qml compiler resolves the alias type. We probably should do the same. + return variant; + } else if (variant.convert(typeId)) { + return variant; + } + + return QmlStringConverters::variantFromString(variant.toString()); +} + +} diff --git a/src/plugins/qmldesigner/core/metainfo/subcomponentmanager.cpp b/src/plugins/qmldesigner/core/metainfo/subcomponentmanager.cpp new file mode 100644 index 00000000000..2f0de684a3b --- /dev/null +++ b/src/plugins/qmldesigner/core/metainfo/subcomponentmanager.cpp @@ -0,0 +1,442 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "subcomponentmanager.h" +#include "metainfo.h" + +#include <QDir> +#include <QMetaType> +#include <QUrl> +#include <QmlDomDocument> +#include <QmlEngine> +#include <QmlMetaType> +#include <QFileSystemWatcher> + +enum { debug = false }; + + +// Allow usage of QFileInfo in hash / qSort + +static bool operator<(const QFileInfo &file1, const QFileInfo &file2) +{ + return file1.filePath() < file2.filePath(); +} + +static uint qHash(const QFileInfo &fileInfo) +{ + return qHash(fileInfo.filePath()); +} + + +namespace QmlDesigner { + +namespace Internal { + +static const QString QMLFILEPATTERN = QString(QLatin1String("*.qml")); + + +class SubComponentManagerPrivate : QObject { + Q_OBJECT +public: + SubComponentManagerPrivate(MetaInfo metaInfo, SubComponentManager *q); + + void addImport(int pos, const QmlDomImport &import); + void removeImport(int pos); + void parseDirectories(); + +public slots: + void parseDirectory(const QString &dirPath); + void parseFile(const QString &filePath); + +public: + QList<QFileInfo> watchedFiles(const QDir &dirInfo); + void unregisterQmlFile(const QFileInfo &fileInfo, const QString &qualifier); + void registerQmlFile(const QFileInfo &fileInfo, const QString &qualifier, const QmlDomDocument &document); + + SubComponentManager *m_q; + + MetaInfo m_metaInfo; + QmlEngine m_engine; + + QFileSystemWatcher m_watcher; + + QMultiHash<QFileInfo,QString> m_dirToQualifier; + + QUrl m_filePath; + + QList<QmlDomImport> m_imports; +}; + +SubComponentManagerPrivate::SubComponentManagerPrivate(MetaInfo metaInfo, SubComponentManager *q) : + m_q(q), + m_metaInfo(metaInfo) +{ + connect(&m_watcher, SIGNAL(directoryChanged(QString)), this, SLOT(parseDirectory(QString))); + connect(&m_watcher, SIGNAL(fileChanged(QString)), this, SLOT(parseFile(QString))); +} + +void SubComponentManagerPrivate::addImport(int pos, const QmlDomImport &import) +{ + if (debug) + qDebug() << Q_FUNC_INFO << pos << import.uri(); + + if (import.type() == QmlDomImport::File) { + QFileInfo dirInfo = QFileInfo(m_filePath.resolved(import.uri()).toLocalFile()); + if (dirInfo.exists() && dirInfo.isDir()) { + m_watcher.addPath(dirInfo.filePath()); + m_dirToQualifier.insertMulti(dirInfo, import.qualifier()); + } + } else { + // TODO: QmlDomImport::Library + } + + m_imports.insert(pos, import); +} + +void SubComponentManagerPrivate::removeImport(int pos) +{ + const QmlDomImport import = m_imports.takeAt(pos); + + if (import.type() == QmlDomImport::File) { + QFileInfo dirInfo = QFileInfo(m_filePath.resolved(import.uri()).toLocalFile()); + + m_dirToQualifier.remove(dirInfo, import.qualifier()); + + if (!m_dirToQualifier.contains(dirInfo)) + m_watcher.removePath(dirInfo.filePath()); + + foreach (const QFileInfo &monitoredFile, watchedFiles(dirInfo.filePath())) { + if (!m_dirToQualifier.contains(dirInfo)) + m_watcher.removePath(monitoredFile.filePath()); + unregisterQmlFile(monitoredFile, import.qualifier()); + } + } else { + // TODO: QmlDomImport::Library + } +} + +void SubComponentManagerPrivate::parseDirectories() +{ + if (!m_filePath.isEmpty()) { + const QString file = m_filePath.toLocalFile(); + QFileInfo dirInfo = QFileInfo(QFileInfo(file).path()); + if (dirInfo.exists() && dirInfo.isDir()) + parseDirectory(dirInfo.filePath()); + } + + foreach (const QmlDomImport &import, m_imports) { + if (import.type() == QmlDomImport::File) { + QFileInfo dirInfo = QFileInfo(m_filePath.resolved(import.uri()).toLocalFile()); + if (dirInfo.exists() && dirInfo.isDir()) { + parseDirectory(dirInfo.filePath()); + } + } + } +} + +void SubComponentManagerPrivate::parseDirectory(const QString &dirPath) +{ + if (debug) + qDebug() << Q_FUNC_INFO << dirPath; + + QDir dir(dirPath); + + dir.setNameFilters(QStringList(QMLFILEPATTERN)); + dir.setFilter(QDir::Files | QDir::Readable | QDir::CaseSensitive); + + QList<QFileInfo> monitoredList = watchedFiles(dir); + QList<QFileInfo> newList; + foreach (const QFileInfo &qmlFile, dir.entryInfoList()) { + if (QFileInfo(m_filePath.toLocalFile()) == qmlFile) { + // do not parse main file + continue; + } + if (!qmlFile.fileName().at(0).isUpper()) { + // Qml sub components must be upper case + continue; + } + newList << qmlFile; + } + + qSort(monitoredList); + qSort(newList); + + if (debug) + qDebug() << "monitored list " << monitoredList.size() << "new list " << newList.size(); + QList<QFileInfo>::const_iterator oldIter = monitoredList.constBegin(); + QList<QFileInfo>::const_iterator newIter = newList.constBegin(); + + while (oldIter != monitoredList.constEnd() && newIter != newList.constEnd()) { + const QFileInfo oldFileInfo = *oldIter; + const QFileInfo newFileInfo = *newIter; + if (oldFileInfo == newFileInfo) { + ++oldIter; + ++newIter; + continue; + } + if (oldFileInfo < newFileInfo) { + foreach (const QString &qualifier, m_dirToQualifier.value(dirPath)) + unregisterQmlFile(oldFileInfo, qualifier); + m_watcher.removePath(oldFileInfo.filePath()); + ++oldIter; + continue; + } + // oldFileInfo > newFileInfo + parseFile(newFileInfo.filePath()); + m_watcher.addPath(oldFileInfo.filePath()); + ++newIter; + } + + while (oldIter != monitoredList.constEnd()) { + foreach (const QString &qualifier, m_dirToQualifier.value(dirPath)) + unregisterQmlFile(*oldIter, qualifier); + m_watcher.removePath(oldIter->filePath()); + ++oldIter; + } + + while (newIter != newList.constEnd()) { + parseFile(newIter->filePath()); + if (debug) + qDebug() << "m_watcher.addPath(" << newIter->filePath() << ")"; + m_watcher.addPath(newIter->filePath()); + ++newIter; + } +} + +void SubComponentManagerPrivate::parseFile(const QString &filePath) +{ + if (debug) + qDebug() << Q_FUNC_INFO << filePath; + + QFile file(filePath); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + return; + } + + QmlDomDocument document; + if (!document.load(&m_engine, file.readAll(), QUrl::fromLocalFile(filePath))) { + // TODO: Put the errors somewhere? + qWarning() << "Could not load qml file " << filePath; + return; + } + + QString dir = QFileInfo(filePath).dir().path(); + Q_ASSERT(m_dirToQualifier.contains(dir)); + foreach (const QString &qualifier, m_dirToQualifier.values(dir)) { + registerQmlFile(filePath, qualifier, document); + } +} + +QList<QFileInfo> SubComponentManagerPrivate::watchedFiles(const QDir &dirInfo) +{ + QList<QFileInfo> files; + + const QString dirPath = dirInfo.absolutePath(); + foreach (const QString &monitoredFile, m_watcher.files()) { + QFileInfo fileInfo(monitoredFile); + if (fileInfo.dir().absolutePath() == dirPath) { + files.append(fileInfo); + } + } + return files; +} + +void SubComponentManagerPrivate::unregisterQmlFile(const QFileInfo &fileInfo, const QString &qualifier) +{ + QString componentName = fileInfo.baseName(); + if (!qualifier.isEmpty()) + componentName = qualifier + '/' + componentName; + + if (m_metaInfo.hasNodeMetaInfo(componentName)) { + NodeMetaInfo nodeInfo = m_metaInfo.nodeMetaInfo(componentName); + m_metaInfo.removeNodeInfo(nodeInfo); + } +} + +void SubComponentManagerPrivate::registerQmlFile(const QFileInfo &fileInfo, const QString &qualifier, const QmlDomDocument &document) +{ + QString componentName = fileInfo.baseName(); + if (!qualifier.isEmpty()) + componentName = qualifier + '/' + componentName; + + if (debug) + qDebug() << "SubComponentManager" << __FUNCTION__ << componentName; + + if (m_metaInfo.hasNodeMetaInfo(componentName)) { + NodeMetaInfo nodeInfo = m_metaInfo.nodeMetaInfo(componentName); + m_metaInfo.removeNodeInfo(nodeInfo); + } + + const QmlDomObject rootObject = document.rootObject(); + + const QString baseType = document.rootObject().objectType(); + Q_ASSERT_X(!baseType.isEmpty(), Q_FUNC_INFO, "Type of root object cannot be empty"); + + NodeMetaInfo nodeInfo(m_metaInfo); + nodeInfo.setTypeName(componentName); + nodeInfo.setQmlFile(fileInfo.filePath()); + + // Add file components to the library + nodeInfo.setCategory(tr("Qml Component")); + nodeInfo.setIsVisibleToItemLibrary(true); + + m_metaInfo.addItemLibraryInfo(nodeInfo, componentName); + m_metaInfo.addNodeInfo(nodeInfo, baseType); + + foreach (const QmlDomDynamicProperty &dynamicProperty, document.rootObject().dynamicProperties()) { + Q_ASSERT(!dynamicProperty.propertyName().isEmpty()); + Q_ASSERT(!dynamicProperty.propertyTypeName().isEmpty()); + + PropertyMetaInfo propertyMetaInfo; + propertyMetaInfo.setName(dynamicProperty.propertyName()); + propertyMetaInfo.setType(dynamicProperty.propertyTypeName()); + propertyMetaInfo.setValid(true); + propertyMetaInfo.setReadable(true); + propertyMetaInfo.setWritable(true); + + QmlDomProperty defaultValue = dynamicProperty.defaultValue(); + if (defaultValue.value().isLiteral()) { + QVariant defaultValueVariant(defaultValue.value().toLiteral().literal()); + defaultValueVariant.convert((QVariant::Type) dynamicProperty.propertyType()); + propertyMetaInfo.setDefaultValue(nodeInfo, defaultValueVariant); + } + + nodeInfo.addProperty(propertyMetaInfo); + } +} + +} // namespace Internal + +/*! + \class SubComponentManager + + Detects & monitors (potential) component files in a list of directories, and registers + these in the metatype system. +*/ + +SubComponentManager::SubComponentManager(MetaInfo metaInfo, QObject *parent) : + QObject(parent), + m_d(new Internal::SubComponentManagerPrivate(metaInfo, this)) +{ +} + +SubComponentManager::~SubComponentManager() +{ + delete m_d; +} + +QStringList SubComponentManager::directories() const +{ + return m_d->m_watcher.directories(); +} + +QStringList SubComponentManager::qmlFiles() const +{ + return m_d->m_watcher.files(); +} + +static bool importEqual(const QmlDomImport &import1, const QmlDomImport &import2) +{ + return import1.type() == import2.type() + && import1.uri() == import2.uri() + && import1.version() == import2.version() + && import1.qualifier() == import2.qualifier(); +} + +void SubComponentManager::update(const QUrl &filePath, const QByteArray &data) +{ + QmlEngine engine; + QmlDomDocument document; + + QList<QmlDomImport> imports; + if (document.load(&engine, data, filePath)) + imports = document.imports(); + + update(filePath, imports); +} + +void SubComponentManager::update(const QUrl &filePath, const QList<QmlDomImport> &imports) +{ + if (debug) + qDebug() << Q_FUNC_INFO << filePath << imports.size(); + + QFileInfo oldDir, newDir; + + if (!m_d->m_filePath.isEmpty()) { + const QString file = m_d->m_filePath.toLocalFile(); + oldDir = QFileInfo(QFileInfo(file).path()); + } + if (!filePath.isEmpty()) { + const QString file = filePath.toLocalFile(); + newDir = QFileInfo(QFileInfo(file).path()); + } + + m_d->m_filePath = filePath; + + // + // (implicit) import of local directory + // + if (oldDir != newDir) { + if (!oldDir.filePath().isEmpty()) { + m_d->m_dirToQualifier.remove(oldDir, QString()); + if (!m_d->m_dirToQualifier.contains(oldDir)) + m_d->m_watcher.removePath(oldDir.filePath()); + } + + if (!newDir.filePath().isEmpty()) { + m_d->m_watcher.addPath(newDir.filePath()); + m_d->m_dirToQualifier.insertMulti(newDir, QString()); + } + } + + // + // Imports + // + + // skip first list items until the lists differ + int i = 0; + while (i < qMin(imports.size(), m_d->m_imports.size())) { + if (!importEqual(imports.at(i), m_d->m_imports.at(i))) + break; + ++i; + } + + for (int ii = m_d->m_imports.size() - 1; ii >= i; --ii) + m_d->removeImport(ii); + + for (int ii = i; ii < imports.size(); ++ii) { + m_d->addImport(ii, imports.at(ii)); + } + + m_d->parseDirectories(); +} + +} // namespace QmlDesigner + +#include "subcomponentmanager.moc" diff --git a/src/plugins/qmldesigner/core/model/abstractproperty.cpp b/src/plugins/qmldesigner/core/model/abstractproperty.cpp new file mode 100644 index 00000000000..2d7eec926a5 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/abstractproperty.cpp @@ -0,0 +1,429 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "abstractproperty.h" +#include "internalnode_p.h" +#include "internalproperty.h" +#include <model.h> +#include "model_p.h" +#include <modelnode.h> +#include <metainfo.h> +#include "invalidpropertyexception.h" +#include "variantproperty.h" +#include "bindingproperty.h" +#include "nodeproperty.h" +#include "nodeabstractproperty.h" +#include "nodelistproperty.h" +#include <QTextStream> +#include <qmlobjectnode.h> +#include <qmlview.h> + +namespace QmlDesigner { + + /*! +\class QmlDesigner::AbstractProperty +\ingroup CoreModel +\brief AbstractProperty is a value holder for a property +*/ + +AbstractProperty::AbstractProperty(): + m_internalNode(new Internal::InternalNode) +{ +} + +AbstractProperty::AbstractProperty(const QString &propertyName, const Internal::InternalNodePointer &internalNode, Model* model, AbstractView *view) + : m_propertyName(propertyName), + m_internalNode(internalNode), + m_model(model), + m_view(view) +{ + Q_ASSERT(!m_model || m_view); +} + +AbstractProperty::AbstractProperty(const Internal::InternalPropertyPointer &property, Model* model, AbstractView *view) + : m_propertyName(property->name()), + m_internalNode(property->propertyOwner()), + m_model(model), + m_view(view) +{ + Q_ASSERT(!m_model || m_view); +} + +AbstractProperty::AbstractProperty(const AbstractProperty &property, AbstractView *view) + : m_propertyName(property.name()), + m_internalNode(property.internalNode()), + m_model(property.model()), + m_view(view) +{ + +} + +AbstractProperty::~AbstractProperty() +{ +} + +AbstractProperty::AbstractProperty(const AbstractProperty &other) + : m_propertyName(other.m_propertyName), + m_internalNode(other.m_internalNode), + m_model(other.m_model), + m_view(other.m_view) +{ +} + +AbstractProperty& AbstractProperty::operator=(const AbstractProperty &other) +{ + m_propertyName = other.m_propertyName; + m_internalNode = other.m_internalNode; + m_model = other.m_model; + m_view = other.m_view; + + return *this; +} + +Internal::InternalNodePointer AbstractProperty::internalNode() const +{ + return m_internalNode; +} + +Model *AbstractProperty::model() const +{ + return m_model.data(); +} + +AbstractView *AbstractProperty::view() const +{ + return m_view.data(); +} + + /*! + \brief AbstractProperty is a value holder for a property + \return the value of the property + + The QVariant is null if the property doesn't exists. +*/ +QString AbstractProperty::name() const +{ + if (m_propertyName == "id") { // the ID for a node is independent of the state, so it has to be set with ModelNode::setId + Q_ASSERT_X(0, Q_FUNC_INFO, "id is not a property in the model"); + throw InvalidPropertyException(__LINE__, __FUNCTION__, __FILE__, name()); + } + return m_propertyName; +} + + /*! + \brief Checks if the property is valid. + + A prooperty is valid if the belonging ModelNode + is valid. This function is not overloaded for + subclasses. + + \return the property is valid for true return value +*/ +bool AbstractProperty::isValid() const +{ + return !m_internalNode.isNull() && + !m_model.isNull() && + m_internalNode->isValid() && + !m_propertyName.isEmpty(); +} + +//bool AbstractProperty::isValueAPropertyBinding() const +//{ +// const QVariant propertyValue = value(); +// +// return propertyValue.type() == QVariant::UserType && propertyValue.userType() == qMetaTypeId<QmlDesigner::PropertyBinding>(); +//} +// +//PropertyBinding AbstractProperty::valueToPropertyBinding() const +//{ +// if (isValueAPropertyBinding()) +// return value().value<PropertyBinding>(); +// else +// return PropertyBinding(); +//} +// +//bool AbstractProperty::isValueAModelNode() const +//{ +// const QVariant propertyValue = value(); +// +// return propertyValue.type() == QVariant::UserType && propertyValue.userType() == ModelNode::variantUserType(); +//} +// +//ModelNode AbstractProperty::valueToModelNode() const +//{ +// if (isValueAModelNode()) +// return value().value<ModelNode>(); +// else +// return ModelNode(); +//} +// +//bool AbstractProperty::isValueAList() const +//{ +// const QVariant propertyValue = value(); +// +// return propertyValue.type() == QVariant::List; +//} +// +//QVariantList AbstractProperty::valueToList() const +//{ +// if (isValueAList()) +// return value().toList(); +// else +// return QVariantList(); +//} +// +//ModelNode AbstractProperty::addModelNodeToValueList(const QString &type, int majorVersion, int minorVersion, const QList<QPair<QString, QVariant> > &propertyList) +//{ +// //if (isValueAList()) +// // return m_model->addModelNode(state(), m_propertyName, type, majorVersion, minorVersion, propertyList); +// //else +// // throw InvalidPropertyException(__LINE__, __FUNCTION__, __FILE__, m_propertyName); +// return ModelNode(); +//} + + /*! + \brief returns the ModelNode to which the property belongs + \return node to which the property belongs +*/ +ModelNode AbstractProperty::parentModelNode() const +{ + return ModelNode(m_internalNode, m_model.data(), view()); +} + +/*! +\brief returns the QmlObjectNode to which the property belongs +\return node to which the property belongs +*/ +QmlObjectNode AbstractProperty::parentQmlObjectNode() const +{ + return QmlObjectNode(parentModelNode()); +} +/*! + \brief returns the metainfo instance for this property + \return metainfo instance for this property +*/ +PropertyMetaInfo AbstractProperty::metaInfo() const +{ + return ModelNode(m_internalNode, m_model.data(), view()).metaInfo().property(m_propertyName); +} + +/*! + \brief returns wether the property is the default property for the model node. +*/ +bool AbstractProperty::isDefaultProperty() const +{ + return ModelNode(m_internalNode, m_model.data(), view()).metaInfo().defaultProperty() == m_propertyName; +} + +VariantProperty AbstractProperty::toVariantProperty() const +{ + if (!isValid()) + throw InvalidPropertyException(__LINE__, __FUNCTION__, __FILE__, m_propertyName); + + VariantProperty propertyVariant(name(), internalNode(), model(), view()); + + if (propertyVariant.isVariantProperty()) + return propertyVariant; + + return VariantProperty(); +} + +NodeProperty AbstractProperty::toNodeProperty() const +{ + if (!isValid()) + throw InvalidPropertyException(__LINE__, __FUNCTION__, __FILE__, m_propertyName); + + NodeProperty propertyNode(name(), internalNode(), model(), view()); + + if (propertyNode.isNodeProperty()) + return propertyNode; + + return NodeProperty(); +} + +NodeListProperty AbstractProperty::toNodeListProperty() const +{ + if (!isValid()) + throw InvalidPropertyException(__LINE__, __FUNCTION__, __FILE__, m_propertyName); + + NodeListProperty propertyNodeList(name(), internalNode(), model(), view()); + + if (propertyNodeList.isNodeListProperty()) + return propertyNodeList; + + return NodeListProperty(); +} + +NodeAbstractProperty AbstractProperty::toNodeAbstractProperty() const +{ + if (!isValid()) + throw InvalidPropertyException(__LINE__, __FUNCTION__, __FILE__, m_propertyName); + + NodeAbstractProperty propertyNodeAbstract(name(), internalNode(), model(), view()); + + if (propertyNodeAbstract.isNodeAbstractProperty()) + return propertyNodeAbstract; + + return NodeAbstractProperty(); +} + +BindingProperty AbstractProperty::toBindingProperty() const +{ + if (!isValid()) + throw InvalidPropertyException(__LINE__, __FUNCTION__, __FILE__, m_propertyName); + + BindingProperty propertyBinding(name(), internalNode(), model(), view()); + + if (propertyBinding.isBindingProperty()) + return propertyBinding; + + return BindingProperty(); +} + +bool AbstractProperty::isVariantProperty() const +{ + if (!isValid()) + throw InvalidPropertyException(__LINE__, __FUNCTION__, __FILE__, m_propertyName); + + if (internalNode()->hasProperty(name())) { + Q_ASSERT(internalNode()->property(name())); + return internalNode()->property(name())->isVariantProperty(); + } + + return false; +} + +bool AbstractProperty::isNodeAbstractProperty() const +{ + if (!isValid()) + throw InvalidPropertyException(__LINE__, __FUNCTION__, __FILE__, m_propertyName); + + if (internalNode()->hasProperty(name())) { + Q_ASSERT(internalNode()->property(name())); + return internalNode()->property(name())->isNodeAbstractProperty(); + } + + return false; +} + +bool AbstractProperty::isNodeListProperty() const +{ + if (!isValid()) + throw InvalidPropertyException(__LINE__, __FUNCTION__, __FILE__, m_propertyName); + + if (internalNode()->hasProperty(name())) { + Q_ASSERT(internalNode()->property(name())); + return internalNode()->property(name())->isNodeListProperty(); + } + + return false; +} + +bool AbstractProperty::isNodeProperty() const +{ + if (!isValid()) + throw InvalidPropertyException(__LINE__, __FUNCTION__, __FILE__, m_propertyName); + + if (internalNode()->hasProperty(name())) { + Q_ASSERT(internalNode()->property(name())); + return internalNode()->property(name())->isNodeProperty(); + } + + return false; +} + + +bool AbstractProperty::isBindingProperty() const +{ + if (!isValid()) + throw InvalidPropertyException(__LINE__, __FUNCTION__, __FILE__, m_propertyName); + + if (internalNode()->hasProperty(name())) { + Q_ASSERT(internalNode()->property(name())); + return internalNode()->property(name())->isBindingProperty(); + } + + return false; +} + +bool AbstractProperty::isDynamic() const +{ + return !dynamicTypeName().isEmpty(); +} + +QString AbstractProperty::dynamicTypeName() const +{ + if (!isValid()) + throw InvalidPropertyException(__LINE__, __FUNCTION__, __FILE__, m_propertyName); + + if (internalNode()->hasProperty(name())) + return internalNode()->property(name())->dynamicTypeName(); + + return QString(); +} + +/*! + \brief Returns if the the two property handles reference the same property in the same node +*/ +bool operator ==(const AbstractProperty &property1, const AbstractProperty &property2) +{ + return (property1.m_model == property2.m_model) + && (property1.m_internalNode == property2.m_internalNode) + && (property1.m_propertyName == property2.m_propertyName); +} + +/*! + \brief Returns if the the two property handles do not reference the same property in the same node + */ +bool operator !=(const AbstractProperty &property1, const AbstractProperty &property2) +{ + return !(property1 == property2); +} + +uint qHash(const AbstractProperty &property) +{ + //### to do + return ::qHash(property.m_internalNode.data()) + ^ ::qHash(property.m_propertyName); +} + +QDebug operator<<(QDebug debug, const AbstractProperty &property) +{ + return debug.nospace() << "AbstractProperty(" << (property.isValid() ? property.name() : QLatin1String("invalid")) << ")"; +} + +QTextStream& operator<<(QTextStream &stream, const AbstractProperty &property) +{ + stream << "AbstractProperty(" << property.name() << ")"; + + return stream; +} + +} // namespace QmlDesigner + diff --git a/src/plugins/qmldesigner/core/model/abstractview.cpp b/src/plugins/qmldesigner/core/model/abstractview.cpp new file mode 100644 index 00000000000..ff1c3c185fa --- /dev/null +++ b/src/plugins/qmldesigner/core/model/abstractview.cpp @@ -0,0 +1,354 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "abstractview.h" + +#include "model.h" +#include "model_p.h" +#include "nodeproperty.h" +#include "bindingproperty.h" +#include "internalnode_p.h" +#include <qmlmodelview.h> + +namespace QmlDesigner { + + +/*! +\class QmlDesigner::AbstractView +\ingroup CoreModel +\brief An abstract interface views and editors can implement to be notified about model changes. + +\see QmlDesigner::WidgetQueryView, QmlDesigner::NodeInstanceView +*/ + +AbstractView::~AbstractView() +{ + if (m_model) + m_model.data()->detachView(this, Model::DoNotNotifyView); +} + +/*! +\brief sets the view of the model. this is handled automatically by AbstractView::modelAttached. +\param model new Model +*/ +void AbstractView::setModel(Model *model) +{ + Q_ASSERT(model != 0); + if (model == m_model.data()) + return; + + if (m_model) + m_model.data()->detachView(this); + + m_model = model; +} + +RewriterTransaction AbstractView::beginRewriterTransaction() +{ + return RewriterTransaction(this); +} + +ModelNode AbstractView::createModelNode(const QString &typeString, + int majorVersion, + int minorVersion, + const QList<QPair<QString, QVariant> > &propertyList) +{ + return ModelNode(model()->m_d->createNode(typeString, majorVersion, minorVersion, propertyList), model(), this); +} + + +/*! \brief returns the root model node +\return constant root model node + +*/ + +const ModelNode AbstractView::rootModelNode() const +{ + Q_ASSERT(model()); + return ModelNode(model()->m_d->rootNode(), model(), const_cast<AbstractView*>(this)); +} + + +/*! \brief returns the root model node +\return root model node + +*/ + +ModelNode AbstractView::rootModelNode() +{ + Q_ASSERT(model()); + return ModelNode(model()->m_d->rootNode(), model(), this); +} + +/*! +\brief sets the reference to model to a null pointer + +*/ +void AbstractView::removeModel() +{ + m_model.clear(); +} + +/*! +\name Model related functions +\{ +*/ + +/*! +\brief returns the model +\return the model of the view +*/ +Model* AbstractView::model() const +{ + return m_model.data(); +} + +/*! +\brief is called if a view is being attached to a model +\param model which is being attached +The default implementation is setting the reference of the model to the view. +\see Model::attachView +*/ +void AbstractView::modelAttached(Model *model) +{ + setModel(model); +} + +/*! +\brief is called before a view is being detached from a model +\param model which is being detached + +This is not called if Model::detachViewWithOutNotification is used! The default implementation +is removing the reference to the model from the view. + +\see Model::detachView +*/ +void AbstractView::modelAboutToBeDetached(Model *) +{ + removeModel(); +} + +//\} + + +/*! +\name Property related functions +\{ + */ + +/*! +\fn void QmlDesigner::AbstractView::propertyAdded(const ModelNode &, const AbstractProperty &) +\brief node notifies about that this property is added +\param node node to which the property is added +\param property added property +*/ + + +/*! +\fn void AbstractView::propertyValueChanged(const ModelNode &, const AbstractProperty& , const QVariant& , const QVariant& ) +\brief this notifies about that the value of this proeprty will be changes +\param node node of the property +\param property changed property +\param newValue the variant of the new value +\param oldValue the variant of the old value +*/ +//\} + +/*! +\name Node related functions +\{ + */ + +/*! +\fn void AbstractView::nodeCreated(const ModelNode &) +\brief this function is called if a new node was created +\param createdNode created node +*/ + + +/*! +\fn AbstractView::fileUrlChanged(const QUrl &oldBaseUrl, const QUrl &newBaseUrl) +\brief Called when the file url (e.g. needed to to resolve relative paths against) has changed +\param oldBaseUrl old search path +\param newBaseUrl new search path +*/ +void AbstractView::fileUrlChanged(const QUrl &/*oldUrl*/, const QUrl &/*newUrl*/) +{ +} + +/*! +\fn AbstractView::importsChanged() +\brief Called when the imports for the model change. +*/ +void AbstractView::importsChanged() +{ +} + +/*! +\fn void AbstractView::nodeAboutToBeRemoved(const ModelNode &) +\brief this is called if a node will be removed +\param removedNode to be removed node +*/ + +/*! +\brief this is called after a propererty was removed +\param propertyList removed property list +*/ +void AbstractView::propertiesRemoved(const QList<AbstractProperty>& /*propertyList*/) +{ +} + +/*! +\fn void AbstractView::nodeReparented(const ModelNode &, const ModelNode &, const ModelNode &) +\brief this is called if a node was reparented +\param node the parent for this node will be changed +\param oldParent old parent of the node +\param newParent new parent of the node +*/ + +/*! +\fn void QmlDesigner::AbstractView::selectedNodesChanged(const QList< ModelNode > &, const QList< ModelNode > &) +\brief this function is called if the selection was changed +\param selectedNodeList the new selection list +\param lastSelectedNodeList the old selection list +*/ +//\} + +void AbstractView::auxiliaryDataChanged(const ModelNode &/*node*/, const QString &/*name*/, const QVariant &/*data*/) +{ + +} + +void AbstractView::customNotification(const AbstractView * /*view*/, const QString & /*identifier*/, const QList<ModelNode> & /*nodeList*/, const QList<QVariant> & /*data*/) +{ +} + +QList<ModelNode> AbstractView::toModelNodeList(const QList<Internal::InternalNode::Pointer> &nodeList) const +{ + return QmlDesigner::toModelNodeList(nodeList, const_cast<AbstractView*>(this)); +} + +QList<ModelNode> toModelNodeList(const QList<Internal::InternalNode::Pointer> &nodeList, AbstractView *view) +{ + QList<ModelNode> newNodeList; + foreach(const Internal::InternalNode::Pointer &node, nodeList) + newNodeList.append(ModelNode(node, view->model(), view)); + + return newNodeList; +} + +QList<Internal::InternalNode::Pointer> toInternalNodeList(const QList<ModelNode> &nodeList) +{ + QList<Internal::InternalNode::Pointer> newNodeList; + foreach(const ModelNode &node, nodeList) + newNodeList.append(node.internalNode()); + + return newNodeList; +} + +/*! +\brief set this list nodes to the actual selected nodes +\param focusNodeList list the selected nodes +*/ +void AbstractView::setSelectedModelNodes(const QList<ModelNode> &selectedNodeList) +{ + model()->m_d->setSelectedNodes(toInternalNodeList(selectedNodeList)); +} + +/*! +\brief clears the selection +*/ +void AbstractView::clearSelectedModelNodes() +{ + model()->m_d->clearSelectedNodes(); +} + +/*! +\brief set this list nodes to the actual selected nodes +\return list the selected nodes +*/ +QList<ModelNode> AbstractView::selectedModelNodes() const +{ + return toModelNodeList(model()->m_d->selectedNodes()); +} + +/*! +\brief adds a node to the selection list +\param node to be added to the selection list +*/ +void AbstractView::selectModelNode(const ModelNode &node) +{ + model()->m_d->selectNode(node.internalNode()); +} + +/*! +\brief removes a node from the selection list +\param node to be removed from the selection list +*/ +void AbstractView::deselectModelNode(const ModelNode &node) +{ + model()->m_d->deselectNode(node.internalNode()); +} + +ModelNode AbstractView::modelNodeForId(const QString &id) +{ + return ModelNode(model()->m_d->nodeForId(id), model(), this); +} + +bool AbstractView::hasId(const QString &id) const +{ + return model()->m_d->hasId(id); +} + +QmlModelView *AbstractView::toQmlModelView() +{ + return qobject_cast<QmlModelView*>(this); +} + +QList<ModelNode> AbstractView::allModelNodes() +{ + return toModelNodeList(model()->m_d->allNodes()); +} + +void AbstractView::emitCustomNotification(const QString &identifier) +{ + emitCustomNotification(identifier, QList<ModelNode>()); +} + +void AbstractView::emitCustomNotification(const QString &identifier, const QList<ModelNode> &nodeList) +{ + emitCustomNotification(identifier, nodeList, QList<QVariant>()); +} + +void AbstractView::emitCustomNotification(const QString &identifier, const QList<ModelNode> &nodeList, const QList<QVariant> &data) +{ + model()->m_d->notifyCustomNotification(this, identifier, nodeList, data); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/core/model/anchorline.cpp b/src/plugins/qmldesigner/core/model/anchorline.cpp new file mode 100644 index 00000000000..c2f36be59cc --- /dev/null +++ b/src/plugins/qmldesigner/core/model/anchorline.cpp @@ -0,0 +1,130 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "anchorline.h" +#include "model.h" +#include "internalnode_p.h" +#include "modelnode.h" + +namespace QmlDesigner { + +AnchorLine::AnchorLine() + : m_anchorType(Invalid) +{ +} + +AnchorLine::AnchorLine(const Internal::InternalNodeStatePointer &internalNodeState, + const Internal::InternalNodePointer &internalNode, + Model* model, + Type type) + : m_internalNodeState(internalNodeState), + m_internalNode(internalNode), + m_model(model), + m_anchorType(type) +{ +} + +AnchorLine::AnchorLine(const NodeState &nodeState, + Type type) + : m_internalNodeState(nodeState.internalNodeState()), + m_internalNode(nodeState.internalNode()), + m_model(nodeState.model()), + m_anchorType(type) +{ + +} + +AnchorLine::~AnchorLine() +{ +} + +AnchorLine::AnchorLine(const AnchorLine &other) + : m_internalNodeState(other.m_internalNodeState), + m_internalNode(other.m_internalNode), + m_model(other.m_model), + m_anchorType(other.m_anchorType) +{ +} + +AnchorLine &AnchorLine::operator =(const AnchorLine &other) +{ + m_internalNodeState = other.m_internalNodeState; + m_internalNode = other.m_internalNode; + m_model = other.m_model; + m_anchorType = other.m_anchorType; + + return *this; +} + +ModelNode AnchorLine::modelNode() const +{ + if (m_internalNode.isNull() || m_internalNodeState.isNull() || m_model.isNull()) { + return ModelNode(); + } + return ModelNode(m_internalNode, m_model.data()); +} + + +AnchorLine::Type AnchorLine::type() const +{ + return m_anchorType; +} + +bool AnchorLine::isValid() const +{ + return m_anchorType != Invalid && + !m_model.isNull() && + !m_internalNode.isNull() && + !m_internalNodeState.isNull() && + m_internalNode.toStrongRef()->isValid() && + m_internalNodeState.toStrongRef()->isValid(); +} + +QVariant AnchorLine::toVariant() const +{ + return QVariant::fromValue(*this); +} + +Internal::InternalNodeStatePointer AnchorLine::internalNodeState() const +{ + if (m_internalNodeState.isNull()) + return Internal::InternalNodeStatePointer(new Internal::InternalNodeState); + + return m_internalNodeState.toStrongRef(); +} + +Internal::InternalNodePointer AnchorLine::internalNode() const +{ + if (m_internalNode.isNull()) + return Internal::InternalNodePointer(new Internal::InternalNode); + + return m_internalNode.toStrongRef(); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/core/model/bindingproperty.cpp b/src/plugins/qmldesigner/core/model/bindingproperty.cpp new file mode 100644 index 00000000000..b258bb2cdd9 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/bindingproperty.cpp @@ -0,0 +1,200 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "bindingproperty.h" +#include "nodeabstractproperty.h" +#include "nodeproperty.h" +#include "internalproperty.h" +#include "internalbindingproperty.h" +#include "invalidmodelnodeexception.h" +#include "invalidpropertyexception.h" +#include "invalidargumentexception.h" +#include "internalnode_p.h" +#include "model.h" +#include "model_p.h" +namespace QmlDesigner { + +BindingProperty::BindingProperty() +{ +} + +BindingProperty::BindingProperty(const BindingProperty &property, AbstractView *view) + : AbstractProperty(property.name(), property.internalNode(), property.model(), view) +{ +} + + +BindingProperty::BindingProperty(const QString &propertyName, const Internal::InternalNodePointer &internalNode, Model* model, AbstractView *view) + : AbstractProperty(propertyName, internalNode, model, view) +{ +} + + +void BindingProperty::setExpression(const QString &expression) +{ + if (!isValid()) + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + if (name() == "id") { // the ID for a node is independent of the state, so it has to be set with ModelNode::setId + throw InvalidPropertyException(__LINE__, __FUNCTION__, __FILE__, name()); + } + + if (expression.isEmpty()) + throw InvalidArgumentException(__LINE__, __FUNCTION__, __FILE__, name()); + + if (internalNode()->hasProperty(name())) { //check if oldValue != value + Internal::InternalProperty::Pointer internalProperty = internalNode()->property(name()); + if (internalProperty->isBindingProperty() + && internalProperty->toBindingProperty()->expression() == expression) + + return; + } + + if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isBindingProperty()) + model()->m_d->removeProperty(internalNode()->property(name())); + + model()->m_d->setBindingProperty(internalNode(), name(), expression); +} + +QString BindingProperty::expression() const +{ + if (internalNode()->hasProperty(name()) + && internalNode()->property(name())->isBindingProperty()) + return internalNode()->bindingProperty(name())->expression(); + + return QString(); +} + +BindingProperty& BindingProperty::operator= (const QString &expression) +{ + setExpression(expression); + + return *this; +} + +static ModelNode resolveBinding(const QString &binding, ModelNode currentNode, AbstractView* view) +{ + int i = 0; + QString element = binding.split(".").at(0); + while (!element.isEmpty()) + { + if (element == "parent") { + if (currentNode.hasParentProperty()) + currentNode = currentNode.parentProperty().toNodeAbstractProperty().parentModelNode(); + else + return ModelNode(); //binding not valid + } else if (currentNode.hasProperty(element)) { + if (currentNode.property(element).isNodeProperty()) + currentNode = currentNode.nodeProperty(element).modelNode(); + else + return ModelNode(); //binding not valid + } else { + currentNode = view->modelNodeForId(element); //id + } + i++; + if (i < binding.split(".").count()) + element = binding.split(".").at(i); + else + element = ""; + } + return currentNode; + +} + +ModelNode BindingProperty::resolveToModelNode() const +{ + if (!isValid()) + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + return resolveBinding(expression(), parentModelNode(), view()); +} + +AbstractProperty BindingProperty::resolveToProperty() const +{ + if (!isValid()) + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + QString binding = expression(); + ModelNode node = parentModelNode(); + QString element; + if (binding.contains(".")) { + element = binding.split(".").last(); + QString nodeBinding = binding; + nodeBinding.chop(element.length()); + node = resolveBinding(nodeBinding, parentModelNode(), view()); + } else { + element = binding; + } + + if (node.isValid()) + return node.property(element); + else + return AbstractProperty(); +} + +void BindingProperty::setDynamicTypeNameAndExpression(const QString &typeName, const QString &expression) + +{ + if (!isValid()) + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + if (name() == "id") { // the ID for a node is independent of the state, so it has to be set with ModelNode::setId + throw InvalidPropertyException(__LINE__, __FUNCTION__, __FILE__, name()); + } + + if (expression.isEmpty()) + throw InvalidArgumentException(__LINE__, __FUNCTION__, __FILE__, name()); + + if (typeName.isEmpty()) { + throw InvalidArgumentException(__LINE__, __FUNCTION__, __FILE__, name()); + } + + if (internalNode()->hasProperty(name())) { //check if oldValue != value + Internal::InternalProperty::Pointer internalProperty = internalNode()->property(name()); + if (internalProperty->isBindingProperty() + && internalProperty->toBindingProperty()->expression() == expression + && internalProperty->toBindingProperty()->dynamicTypeName() == typeName) + + return; + } + + if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isBindingProperty()) + model()->m_d->removeProperty(internalNode()->property(name())); + + model()->m_d->setDynamicBindingProperty(internalNode(), name(), typeName, expression); +} + +BindingProperty& BindingProperty::operator= (const QPair<QString, QString> &typeExpressionPair) +{ + setDynamicTypeNameAndExpression(typeExpressionPair.first, typeExpressionPair.second); + + return *this; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/core/model/bytearraymodifier.cpp b/src/plugins/qmldesigner/core/model/bytearraymodifier.cpp new file mode 100644 index 00000000000..33f0fe905f0 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/bytearraymodifier.cpp @@ -0,0 +1,68 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "bytearraymodifier.h" + +namespace QmlDesigner { + +ByteArrayModifier::ByteArrayModifier(QPlainTextEdit* textEdit): + PlainTextEditModifier(textEdit), + m_textEdit(textEdit) +{ +} + +ByteArrayModifier::~ByteArrayModifier() +{ + delete m_textEdit; +} + +void ByteArrayModifier::undo() +{ + m_textEdit->undo(); +} + +void ByteArrayModifier::redo() +{ + m_textEdit->redo(); +} + +ByteArrayModifier* ByteArrayModifier::create(const QString& data) +{ + QPlainTextEdit* edit = new QPlainTextEdit; + edit->setPlainText(data); + return new ByteArrayModifier(edit); +} + +void ByteArrayModifier::setText(const QString& text) +{ + m_textEdit->setPlainText(text); + emit textChanged(); +} + +} diff --git a/src/plugins/qmldesigner/core/model/componenttextmodifier.cpp b/src/plugins/qmldesigner/core/model/componenttextmodifier.cpp new file mode 100644 index 00000000000..98a78bd9557 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/componenttextmodifier.cpp @@ -0,0 +1,127 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "componenttextmodifier.h" + +namespace QmlDesigner { + +ComponentTextModifier::ComponentTextModifier(TextModifier *originalModifier, int componentStartOffset, int componentEndOffset, int rootStartOffset) : + m_originalModifier(originalModifier), + m_componentStartOffset(componentStartOffset), + m_componentEndOffset(componentEndOffset), + m_rootStartOffset(rootStartOffset) +{ + connect(m_originalModifier->textDocument(), SIGNAL(contentsChange(int,int,int)), this, SLOT(contentsChange(int,int,int))); + connect(m_originalModifier, SIGNAL(textChanged()), this, SIGNAL(textChanged())); + + connect(m_originalModifier, SIGNAL(replaced(int, int, int)), this, SIGNAL(replaced(int, int, int))); + connect(m_originalModifier, SIGNAL(moved(const TextModifier::MoveInfo &)), this, SIGNAL(moved(const TextModifier::MoveInfo &))); +} + +ComponentTextModifier::~ComponentTextModifier() +{ +} + +void ComponentTextModifier::replace(int offset, int length, const QString& replacement) +{ + m_originalModifier->replace(offset, length, replacement); +} + +void ComponentTextModifier::move(const MoveInfo &moveInfo) +{ + m_originalModifier->move(moveInfo); +} + +void ComponentTextModifier::startGroup() +{ + m_originalModifier->startGroup(); +} + +void ComponentTextModifier::flushGroup() +{ + m_originalModifier->flushGroup(); +} + +void ComponentTextModifier::commitGroup() +{ + m_originalModifier->commitGroup(); +} + +QTextDocument *ComponentTextModifier::textDocument() const +{ + return m_originalModifier->textDocument(); +} + +QString ComponentTextModifier::text() const +{ + QString txt(m_originalModifier->text()); + + const int leader = m_componentStartOffset - m_rootStartOffset; + txt.replace(m_rootStartOffset, leader, QString(leader, ' ')); + + const int textLength = txt.size(); + const int trailer = textLength - m_componentEndOffset; + txt.replace(m_componentEndOffset, trailer, QString(trailer, ' ')); + + return txt; +} + +QTextCursor ComponentTextModifier::textCursor() const +{ + return m_originalModifier->textCursor(); +} + +void ComponentTextModifier::deactivateChangeSignals() +{ + m_originalModifier->deactivateChangeSignals(); +} + +void ComponentTextModifier::reactivateChangeSignals() +{ + m_originalModifier->reactivateChangeSignals(); +} + +void ComponentTextModifier::contentsChange(int position, int charsRemoved, int charsAdded) +{ + const int diff = charsAdded - charsRemoved; + + if (position < m_rootStartOffset) { + m_rootStartOffset += diff; + } + + if (position < m_componentStartOffset) { + m_componentStartOffset += diff; + } + + if (position < m_componentEndOffset) { + m_componentEndOffset += diff; + } +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/core/model/copyhelper.cpp b/src/plugins/qmldesigner/core/model/copyhelper.cpp new file mode 100644 index 00000000000..0d4f467f1a3 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/copyhelper.cpp @@ -0,0 +1,89 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include <QtCore/QBuffer> +#include <QtCore/QDataStream> + +#include "copyhelper.h" +#include "internalnode_p.h" + +namespace QmlDesigner { +namespace Internal { + +CopyHelper::CopyHelper(const QString &source): + m_source(source) +{ +} + +QMimeData *CopyHelper::createMimeData(const QList<InternalNodeState::Pointer> &nodeStates) +{ + if (nodeStates.isEmpty()) + return 0; + + m_locations.clear(); + + foreach (const InternalNodeState::Pointer &nodeState, nodeStates) { + collectLocations(nodeState); + } + + if (m_locations.isEmpty()) + return 0; + + QMimeData *mimeData = new QMimeData; + mimeData->setData("application/x-qt-bauhaus", contentsAsByteArray()); + + return mimeData; +} + +void CopyHelper::collectLocations(const InternalNodeState::Pointer &nodeState) +{ + // TODO: copy way more here. + m_locations.insert(nodeState->modelNode()->baseNodeState()->location()); +} + +QByteArray CopyHelper::contentsAsByteArray() const +{ + QBuffer result; + result.open(QIODevice::WriteOnly); + QDataStream os(&result); + + os << m_locations.size(); + foreach (const TextLocation &location, m_locations) { + os << location.position(); + os << location.length(); + } + + os << m_source; + + result.close(); + return result.buffer(); +} + +} // namespace Internal +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/core/model/copyhelper.h b/src/plugins/qmldesigner/core/model/copyhelper.h new file mode 100644 index 00000000000..6e143fb7dbc --- /dev/null +++ b/src/plugins/qmldesigner/core/model/copyhelper.h @@ -0,0 +1,63 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef COPYHELPER_H +#define COPYHELPER_H + +#include <QtCore/QByteArray> +#include <QtCore/QMimeData> +#include <QtCore/QSet> +#include <QtCore/QString> + +#include "internalnodestate.h" +#include "textlocation.h" + +namespace QmlDesigner { +namespace Internal { + +class CopyHelper +{ +public: + CopyHelper(const QString &source); + + QMimeData *createMimeData(const QList<InternalNodeState::Pointer> &nodeStates); + +private: + void collectLocations(const InternalNodeState::Pointer &nodeState); + QByteArray contentsAsByteArray() const; + +private: + QString m_source; + QSet<TextLocation> m_locations; +}; + +} // namespace Internal +} // namespace QmlDesigner + +#endif // COPYHELPER_H diff --git a/src/plugins/qmldesigner/core/model/copypasteutil.cpp b/src/plugins/qmldesigner/core/model/copypasteutil.cpp new file mode 100644 index 00000000000..c51c0b3d67b --- /dev/null +++ b/src/plugins/qmldesigner/core/model/copypasteutil.cpp @@ -0,0 +1,61 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include <qmljsast_p.h> +#include <qmljsengine_p.h> + +#include "copypasteutil.h" + +using namespace QmlJS; +using namespace QmlJS::AST; + +namespace QmlDesigner { +namespace Internal { + +Import CopyPasteUtil::createImport(QmlJS::AST::UiImport *ast) +{ + QString version; + if (ast->versionToken.isValid()) + version = textAt(ast->versionToken); + + QString alias; + if (ast->importId) + alias = ast->importId->asString(); + + if (ast->fileName) + return Import::createFileImport(ast->fileName->asString(), version, alias); + + if (ast->importUri && ast->importUri->name) + return Import::createLibraryImport(ast->importUri->name->asString(), version, alias); + + return Import::empty(); +} + +} // namespace Internal +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/core/model/copypasteutil.h b/src/plugins/qmldesigner/core/model/copypasteutil.h new file mode 100644 index 00000000000..d44783e2670 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/copypasteutil.h @@ -0,0 +1,63 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef COPYPASTEUTIL_H +#define COPYPASTEUTIL_H + +#include <qmljsastfwd_p.h> + +#include "import.h" + +namespace QmlDesigner { +namespace Internal { + +class CopyPasteUtil { +public: + CopyPasteUtil(const QString &originalSource): m_originalSource(originalSource) + {} + + Import createImport(QmlJS::AST::UiImport *ast); + +protected: + QString textAt(const QmlJS::AST::SourceLocation &loc) const + { return m_originalSource.mid(loc.offset, loc.length); } + + QString textAt(const QmlJS::AST::SourceLocation &firstSourceLocation, const QmlJS::AST::SourceLocation &lastSourceLocation) const + { return m_originalSource.mid(firstSourceLocation.offset, lastSourceLocation.end() - firstSourceLocation.offset); } + + QString originalSource() const { return m_originalSource; } + +private: + QString m_originalSource; +}; + +} // namespace Internal +} // namespace QmlDesigner + +#endif // COPYPASTEUTIL_H diff --git a/src/plugins/qmldesigner/core/model/import.cpp b/src/plugins/qmldesigner/core/model/import.cpp new file mode 100644 index 00000000000..c7c7abf4eb9 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/import.cpp @@ -0,0 +1,92 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include <QtCore/QHash> + +#include "import.h" + +namespace QmlDesigner { + +Import Import::createLibraryImport(const QUrl &url, const QString &version, const QString &alias) +{ + return Import(url, QString(), version, alias); +} + +Import Import::createFileImport(const QString &file, const QString &version, const QString &alias) +{ + return Import(QUrl(), file, version, alias); +} + +Import Import::empty() +{ + return Import(QUrl(), QString(), QString(), QString()); +} + +Import::Import(const QUrl &url, const QString &file, const QString &version, const QString &alias): + m_url(url), + m_file(file), + m_version(version), + m_alias(alias) +{ +} + +QString Import::toString(bool addSemicolon) const +{ + QString result = QLatin1String("import "); + + if (isFileImport()) + result += '"' + file() + '"'; + else if (isLibraryImport()) + result += url().toString(); + else + return QString(); + + if (hasVersion()) + result += ' ' + version(); + + if (hasAlias()) + result += " as " + alias(); + + if (addSemicolon) + result += ';'; + + return result; +} + +bool Import::operator==(const Import &other) const +{ + return url() == other.url() && file() == other.file() && version() == other.version() && alias() == other.alias(); +} + +uint qHash(const Import &import) +{ + return ::qHash(import.url().toString()) ^ ::qHash(import.file()) ^ ::qHash(import.version()) ^ ::qHash(import.alias()); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/core/model/internalbindingproperty.cpp b/src/plugins/qmldesigner/core/model/internalbindingproperty.cpp new file mode 100644 index 00000000000..e590c8e00bf --- /dev/null +++ b/src/plugins/qmldesigner/core/model/internalbindingproperty.cpp @@ -0,0 +1,77 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "internalbindingproperty.h" + +namespace QmlDesigner { +namespace Internal { + +InternalBindingProperty::InternalBindingProperty(const QString &name, const InternalNodePointer &propertyOwner) + : InternalProperty(name, propertyOwner) +{ +} + + +InternalBindingProperty::Pointer InternalBindingProperty::create(const QString &name, const InternalNodePointer &propertyOwner) +{ + InternalBindingProperty *newPointer(new InternalBindingProperty(name, propertyOwner)); + InternalBindingProperty::Pointer smartPointer(newPointer); + + newPointer->setInternalWeakPointer(smartPointer); + + return smartPointer; +} + +bool InternalBindingProperty::isValid() const +{ + return InternalProperty::isValid() && isBindingProperty(); +} + +QString InternalBindingProperty::expression() const +{ + return m_expression; +} +void InternalBindingProperty::setExpression(const QString &expression) +{ + m_expression = expression; +} + +bool InternalBindingProperty::isBindingProperty() const +{ + return true; +} + +void InternalBindingProperty::setDynamicExpression(const QString &type, const QString &expression) +{ + setExpression(expression); + setDynamicTypeName(type); +} + +} // namespace Internal +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/core/model/internalbindingproperty.h b/src/plugins/qmldesigner/core/model/internalbindingproperty.h new file mode 100644 index 00000000000..1fe8fd08bc5 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/internalbindingproperty.h @@ -0,0 +1,65 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef INTERNALBINDINGPROPERTY_H +#define INTERNALBINDINGPROPERTY_H + +#include "internalproperty.h" + +namespace QmlDesigner { +namespace Internal { + +class InternalBindingProperty : public InternalProperty +{ +public: + typedef QSharedPointer<InternalBindingProperty> Pointer; + + static Pointer create(const QString &name, const InternalNodePointer &propertyOwner); + + bool isValid() const; + + QString expression() const; + void setExpression(const QString &expression); + + void setDynamicExpression(const QString &type, const QString &expression); + + + bool isBindingProperty() const; + +protected: + InternalBindingProperty(const QString &name, const InternalNodePointer &propertyOwner); + +private: + QString m_expression; +}; + +} // namespace Internal +} // namespace QmlDesigner + +#endif // INTERNALBINDINGPROPERTY_H diff --git a/src/plugins/qmldesigner/core/model/internalnode.cpp b/src/plugins/qmldesigner/core/model/internalnode.cpp new file mode 100644 index 00000000000..a18a1bad9c3 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/internalnode.cpp @@ -0,0 +1,315 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "internalnode_p.h" +#include "internalproperty.h" +#include "internalvariantproperty.h" +#include "internalnodeproperty.h" +#include "internalnodelistproperty.h" +#include "invalidmodelnodeexception.h" +#include "invalidpropertyexception.h" + +#include <QtDebug> +#include <corelib_global.h> + +namespace QmlDesigner { +namespace Internal { + +/*! + \class QmlDesigner::Internal::InternalNode + + Represents one XML element. + */ + +InternalNode::InternalNode() : + m_valid(false) +{ +} + +InternalNode::InternalNode(const QString &typeName,int majorVersion, int minorVersion): + m_typeName(typeName), + m_majorVersion(majorVersion), + m_minorVersion(minorVersion), + m_valid(true) +{ + +} + +InternalNode::Pointer InternalNode::create(const QString &type,int majorVersion, int minorVersion) +{ + InternalNode *newPointer(new InternalNode(type, majorVersion, minorVersion)); + InternalNode::Pointer smartPointer(newPointer); + + newPointer->setInternalWeakPointer(smartPointer); + + return smartPointer; +} + +InternalNode::Pointer InternalNode::internalPointer() const +{ + return m_internalPointer.toStrongRef(); +} +void InternalNode::setInternalWeakPointer(const Pointer &pointer) +{ + m_internalPointer = pointer; +} + +QString InternalNode::type() const +{ + return m_typeName; +} + +void InternalNode::setType(const QString &newType) +{ + m_typeName = newType; +} + +int InternalNode::minorVersion() const +{ + return m_minorVersion; +} + +int InternalNode::majorVersion() const +{ + return m_majorVersion; +} + +void InternalNode::setMinorVersion(int number) +{ + m_minorVersion = number; +} + +void InternalNode::setMajorVersion(int number) +{ + m_majorVersion = number; +} + +bool InternalNode::isValid() const +{ + return m_valid; +} + +void InternalNode::setValid(bool valid) +{ + m_valid = valid; +} + +InternalNodeAbstractProperty::Pointer InternalNode::parentProperty() const +{ + return m_parentProperty; +} +void InternalNode::setParentProperty(const InternalNodeAbstractProperty::Pointer &parent) +{ + if (m_parentProperty) + m_parentProperty->remove(internalPointer()); + + Q_ASSERT(parent && parent->isValid()); + m_parentProperty = parent; + + parent->add(internalPointer()); +} + +void InternalNode::resetParentProperty() +{ + if (m_parentProperty) + m_parentProperty->remove(internalPointer()); + + m_parentProperty.clear(); +} + +QString InternalNode::id() const +{ + return m_id; +} + +void InternalNode::setId(const QString& id) +{ + m_id = id; +} + + +uint qHash(const InternalNodePointer& node) +{ + return ::qHash(node.data()); +} + +QVariant InternalNode::auxiliaryData(const QString &name) const +{ + return m_auxiliaryDataHash.value(name); +} + +void InternalNode::setAuxiliaryData(const QString &name, const QVariant &data) +{ + m_auxiliaryDataHash.insert(name, data); +} + +bool InternalNode::hasAuxiliaryData(const QString &name) const +{ + return m_auxiliaryDataHash.contains(name); +} + +InternalProperty::Pointer InternalNode::property(const QString &name) const +{ + return m_namePropertyHash.value(name); +} + +InternalBindingProperty::Pointer InternalNode::bindingProperty(const QString &name) const +{ + InternalProperty::Pointer property = m_namePropertyHash.value(name); + if (property->isBindingProperty()) + return property.staticCast<InternalBindingProperty>(); + + return InternalBindingProperty::Pointer(); +} + +InternalVariantProperty::Pointer InternalNode::variantProperty(const QString &name) const +{ + InternalProperty::Pointer property = m_namePropertyHash.value(name); + if (property->isVariantProperty()) + return property.staticCast<InternalVariantProperty>(); + + return InternalVariantProperty::Pointer(); +} + +void InternalNode::addBindingProperty(const QString &name) +{ + InternalProperty::Pointer newProperty(InternalBindingProperty::create(name, internalPointer())); + m_namePropertyHash.insert(name, newProperty); +} + +InternalNodeListProperty::Pointer InternalNode::nodeListProperty(const QString &name) const +{ + InternalProperty::Pointer property = m_namePropertyHash.value(name); + if (property && property->isNodeListProperty()) + return property.staticCast<InternalNodeListProperty>(); + + return InternalNodeListProperty::Pointer(); +} + +InternalNodeAbstractProperty::Pointer InternalNode::nodeAbstractProperty(const QString &name) const +{ + InternalProperty::Pointer property = m_namePropertyHash.value(name); + if (property && property->isNodeAbstractProperty()) + return property.staticCast<InternalNodeAbstractProperty>(); + + return InternalNodeProperty::Pointer(); +} + +InternalNodeProperty::Pointer InternalNode::nodeProperty(const QString &name) const +{ + InternalProperty::Pointer property = m_namePropertyHash.value(name); + if (property->isNodeProperty()) + return property.staticCast<InternalNodeProperty>(); + + return InternalNodeProperty::Pointer(); +} + +void InternalNode::addVariantProperty(const QString &name) +{ + InternalProperty::Pointer newProperty(InternalVariantProperty::create(name, internalPointer())); + m_namePropertyHash.insert(name, newProperty); +} + +void InternalNode::addNodeProperty(const QString &name) +{ + InternalProperty::Pointer newProperty(InternalNodeProperty::create(name, internalPointer())); + m_namePropertyHash.insert(name, newProperty); +} + +void InternalNode::addNodeListProperty(const QString &name) +{ + InternalProperty::Pointer newProperty(InternalNodeListProperty::create(name, internalPointer())); + m_namePropertyHash.insert(name, newProperty); +} + +void InternalNode::removeProperty(const QString &name) +{ + InternalProperty::Pointer property = m_namePropertyHash.take(name); + Q_ASSERT(!property.isNull()); +} + +QList<QString> InternalNode::propertyNameList() const +{ + return m_namePropertyHash.keys(); +} + +bool InternalNode::hasProperties() const +{ + return !m_namePropertyHash.isEmpty(); +} + +bool InternalNode::hasProperty(const QString &name) const +{ + return m_namePropertyHash.contains(name); +} + +QList<InternalProperty::Pointer> InternalNode::propertyList() const +{ + return m_namePropertyHash.values(); +} + +QList<InternalNodeAbstractProperty::Pointer> InternalNode::nodeAbstractPropertyList() const +{ + QList<InternalNodeAbstractProperty::Pointer> abstractPropertyList; + foreach(const InternalProperty::Pointer &property, propertyList()) { + if (property->isNodeAbstractProperty()) + abstractPropertyList.append(property->toNodeAbstractProperty()); + } + + return abstractPropertyList; +} + + +QList<InternalNode::Pointer> InternalNode::allSubNodes() const +{ + QList<InternalNode::Pointer> nodeList; + foreach(const InternalNodeAbstractProperty::Pointer &property, nodeAbstractPropertyList()) { + nodeList.append(property->allSubNodes()); + } + + return nodeList; +} + +QList<InternalNode::Pointer> InternalNode::allDirectSubNodes() const +{ + QList<InternalNode::Pointer> nodeList; + foreach(const InternalNodeAbstractProperty::Pointer &property, nodeAbstractPropertyList()) { + nodeList.append(property->allDirectSubNodes()); + } + + return nodeList; +} + +bool operator <(const InternalNode::Pointer &firstNode, const InternalNode::Pointer &secondNode) +{ + return firstNode.data() < secondNode.data(); +} + +} +} diff --git a/src/plugins/qmldesigner/core/model/internalnode_p.h b/src/plugins/qmldesigner/core/model/internalnode_p.h new file mode 100644 index 00000000000..417d58f9207 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/internalnode_p.h @@ -0,0 +1,144 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef INTERNALNODE_H +#define INTERNALNODE_H + +#include <QMap> +#include <QHash> +#include <QSharedPointer> +#include <QWeakPointer> +#include "internalproperty.h" +#include "internalvariantproperty.h" +#include "internalbindingproperty.h" +#include "internalnodelistproperty.h" +#include "internalnodeproperty.h" +#include "internalnodeabstractproperty.h" + +namespace QmlDesigner { + +class Model; + +namespace Internal { + +class InternalProperty; +class InternalNode; + +typedef QSharedPointer<InternalNode> InternalNodePointer; +typedef QSharedPointer<InternalProperty> InternalPropertyPointer; +typedef QWeakPointer<InternalNode> InternalNodeWeakPointer; + +class InternalNode +{ + friend class InternalProperty; +public: + typedef QSharedPointer<InternalNode> Pointer; + typedef QWeakPointer<InternalNode> WeakPointer; + + explicit InternalNode(); + + static Pointer create(const QString &typeName, int majorVersion, int minorVersion); + + QString type() const; + void setType(const QString &newType); + + int minorVersion() const; + int majorVersion() const; + void setMinorVersion(int number); + void setMajorVersion(int number); + + bool isValid() const; + void setValid(bool valid); + + InternalNodeAbstractProperty::Pointer parentProperty() const; + + // Reparent within model + void setParentProperty(const InternalNodeAbstractProperty::Pointer &parent); + void resetParentProperty(); + + QString id() const; + void setId(const QString& id); + + QVariant auxiliaryData(const QString &name) const; + void setAuxiliaryData(const QString &name, const QVariant &data); + bool hasAuxiliaryData(const QString &name) const; + + InternalProperty::Pointer property(const QString &name) const; + InternalBindingProperty::Pointer bindingProperty(const QString &name) const; + InternalVariantProperty::Pointer variantProperty(const QString &name) const; + InternalNodeListProperty::Pointer nodeListProperty(const QString &name) const; + InternalNodeAbstractProperty::Pointer nodeAbstractProperty(const QString &name) const; + InternalNodeProperty::Pointer nodeProperty(const QString &name) const; + + void addBindingProperty(const QString &name); + void addNodeListProperty(const QString &name); + void addVariantProperty(const QString &name); + void addNodeProperty(const QString &name); + + + QList<QString> propertyNameList() const; + + bool hasProperties() const; + bool hasProperty(const QString &name) const; + + QList<InternalProperty::Pointer> propertyList() const; + QList<InternalNodeAbstractProperty::Pointer> nodeAbstractPropertyList() const; + QList<InternalNode::Pointer> allSubNodes() const; + QList<InternalNode::Pointer> allDirectSubNodes() const; + +protected: + Pointer internalPointer() const; + void setInternalWeakPointer(const Pointer &pointer); + void removeProperty(const QString &name); + explicit InternalNode(const QString &type, int majorVersion, int minorVersion); + +private: + QString m_typeName; + QString m_id; + + QHash<QString, QVariant> m_auxiliaryDataHash; + + InternalNodeAbstractProperty::WeakPointer m_parentProperty; + WeakPointer m_internalPointer; + + int m_majorVersion; + int m_minorVersion; + + bool m_valid; + + QHash<QString, InternalPropertyPointer> m_namePropertyHash; +}; + +uint qHash(const InternalNodePointer& node); +bool operator <(const InternalNodePointer &firstNode, const InternalNodePointer &secondNode); + +} // Internal +} // QtQmlDesigner + +#endif // INTERNALNODE_H diff --git a/src/plugins/qmldesigner/core/model/internalnodeabstractproperty.cpp b/src/plugins/qmldesigner/core/model/internalnodeabstractproperty.cpp new file mode 100644 index 00000000000..848cc578b8d --- /dev/null +++ b/src/plugins/qmldesigner/core/model/internalnodeabstractproperty.cpp @@ -0,0 +1,52 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "internalnodeabstractproperty.h" +#include "internalnode_p.h" + +namespace QmlDesigner { +namespace Internal { + +InternalNodeAbstractProperty::InternalNodeAbstractProperty(const QString &name, const InternalNode::Pointer &propertyOwner) + : InternalProperty(name, propertyOwner) +{ +} + +bool InternalNodeAbstractProperty::isNodeAbstractProperty() const +{ + return true; +} + +bool InternalNodeAbstractProperty::isValid() const +{ + return InternalProperty::isValid() && isNodeAbstractProperty(); +} + +} // namespace Internal +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/core/model/internalnodeabstractproperty.h b/src/plugins/qmldesigner/core/model/internalnodeabstractproperty.h new file mode 100644 index 00000000000..29b99c03c95 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/internalnodeabstractproperty.h @@ -0,0 +1,64 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef INTERNALNODEABSTRACTPROPERTY_H +#define INTERNALNODEABSTRACTPROPERTY_H + +#include "internalproperty.h" + + +namespace QmlDesigner { +namespace Internal { + +class InternalNodeAbstractProperty : public InternalProperty +{ + friend class InternalNode; + +public: + typedef QSharedPointer<InternalNodeAbstractProperty> Pointer; + typedef QWeakPointer<InternalNodeAbstractProperty> WeakPointer; + + bool isNodeAbstractProperty() const; + + virtual QList<InternalNodePointer> allSubNodes() const = 0; + virtual QList<InternalNodePointer> allDirectSubNodes() const = 0; + + virtual bool isEmpty() const = 0; + + virtual bool isValid() const; +protected: + InternalNodeAbstractProperty(const QString &name, const InternalNodePointer &propertyOwner); + virtual void remove(const InternalNodePointer &node) = 0; + virtual void add(const InternalNodePointer &node) = 0; +}; + +} // namespace Internal +} // namespace QmlDesigner + +#endif // INTERNALNODEABSTRACTPROPERTY_H diff --git a/src/plugins/qmldesigner/core/model/internalnodelistproperty.cpp b/src/plugins/qmldesigner/core/model/internalnodelistproperty.cpp new file mode 100644 index 00000000000..38b3b567808 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/internalnodelistproperty.cpp @@ -0,0 +1,107 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "internalnodelistproperty.h" +#include "internalnode_p.h" +#include <QList> + +namespace QmlDesigner { +namespace Internal { + +InternalNodeListProperty::InternalNodeListProperty(const QString &name, const InternalNodePointer &propertyOwner) + : InternalNodeAbstractProperty(name, propertyOwner) +{ +} + +InternalNodeListProperty::Pointer InternalNodeListProperty::create(const QString &name, const InternalNodePointer &propertyOwner) +{ + InternalNodeListProperty *newPointer(new InternalNodeListProperty(name, propertyOwner)); + InternalProperty::Pointer smartPointer(newPointer); + + newPointer->setInternalWeakPointer(smartPointer.toWeakRef()); + + return smartPointer.staticCast<InternalNodeListProperty>(); +} + +bool InternalNodeListProperty::isValid() const +{ + return InternalProperty::isValid() && isNodeListProperty(); +} + +bool InternalNodeListProperty::isEmpty() const +{ + return m_nodeList.isEmpty(); +} + +bool InternalNodeListProperty::isNodeListProperty() const +{ + return true; +} + +void InternalNodeListProperty::add(const InternalNode::Pointer &internalNode) +{ + Q_ASSERT(!m_nodeList.contains(internalNode)); + m_nodeList.append(internalNode); +} + +void InternalNodeListProperty::remove(const InternalNodePointer &internalNode) +{ + Q_ASSERT(m_nodeList.contains(internalNode)); + m_nodeList.removeAll(internalNode); +} + +const QList<InternalNode::Pointer> &InternalNodeListProperty::nodeList() const +{ + return m_nodeList; +} + +void InternalNodeListProperty::slide(int from, int to) +{ + InternalNode::Pointer internalNode = m_nodeList.takeAt(from); + m_nodeList.insert(to, internalNode); +} + +QList<InternalNode::Pointer> InternalNodeListProperty::allSubNodes() const +{ + QList<InternalNode::Pointer> nodeList; + foreach(const InternalNode::Pointer &childNode, m_nodeList) { + nodeList.append(childNode->allSubNodes()); + nodeList.append(childNode); + } + + return nodeList; +} + +QList<InternalNodePointer> InternalNodeListProperty::allDirectSubNodes() const +{ + return nodeList(); +} + +} // namespace Internal +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/core/model/internalnodelistproperty.h b/src/plugins/qmldesigner/core/model/internalnodelistproperty.h new file mode 100644 index 00000000000..41827a3fcea --- /dev/null +++ b/src/plugins/qmldesigner/core/model/internalnodelistproperty.h @@ -0,0 +1,71 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef INTERNALNODELISTPROPERTY_H +#define INTERNALNODELISTPROPERTY_H + +#include "internalnodeabstractproperty.h" + +#include <QList> + +namespace QmlDesigner { + +namespace Internal { + +class InternalNodeListProperty : public InternalNodeAbstractProperty +{ +public: + typedef QSharedPointer<InternalNodeListProperty> Pointer; + + static Pointer create(const QString &name, const InternalNodePointer &propertyOwner); + + bool isValid() const; + + bool isEmpty() const; + + bool isNodeListProperty() const; + + QList<InternalNodePointer> allSubNodes() const; + QList<InternalNodePointer> allDirectSubNodes() const; + const QList<InternalNodePointer> &nodeList() const; + void slide(int from, int to); + +protected: + InternalNodeListProperty(const QString &name, const InternalNodePointer &propertyOwner); + void add(const InternalNodePointer &node); + void remove(const InternalNodePointer &node); + +private: + QList<InternalNodePointer> m_nodeList; +}; + +} // namespace Internal +} // namespace QmlDesigner + +#endif // INTERNALNODELISTPROPERTY_H diff --git a/src/plugins/qmldesigner/core/model/internalnodeproperty.cpp b/src/plugins/qmldesigner/core/model/internalnodeproperty.cpp new file mode 100644 index 00000000000..128a02dd8f5 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/internalnodeproperty.cpp @@ -0,0 +1,109 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "internalnodeproperty.h" +#include "internalnode_p.h" + +namespace QmlDesigner { +namespace Internal { + +InternalNodeProperty::InternalNodeProperty(const QString &name, const InternalNode::Pointer &propertyOwner) + : InternalNodeAbstractProperty(name, propertyOwner) +{ +} + +InternalNodeProperty::Pointer InternalNodeProperty::create(const QString &name, const InternalNode::Pointer &propertyOwner) +{ + InternalNodeProperty *newPointer = new InternalNodeProperty(name, propertyOwner); + InternalNodeProperty::Pointer smartPointer(newPointer); + + newPointer->setInternalWeakPointer(smartPointer); + + return smartPointer; +} + +bool InternalNodeProperty::isEmpty() const +{ + return m_node.isNull(); +} + +bool InternalNodeProperty::isValid() const +{ + return InternalProperty::isValid() && isNodeProperty(); +} + +bool InternalNodeProperty::isNodeProperty() const +{ + return true; +} + +InternalNode::Pointer InternalNodeProperty::node() const +{ + return m_node; +} + +void InternalNodeProperty::remove(const InternalNode::Pointer &node) +{ + Q_ASSERT(m_node == node); + m_node.clear(); + +} + +void InternalNodeProperty::add(const InternalNode::Pointer &node) +{ + Q_ASSERT(node); + Q_ASSERT(node->parentProperty()); + m_node = node; +} + +QList<InternalNode::Pointer> InternalNodeProperty::allSubNodes() const +{ + QList<InternalNode::Pointer> nodeList; + + if (node()) { + nodeList.append(node()->allSubNodes()); + nodeList.append(node()); + } + + return nodeList; +} + +QList<InternalNodePointer> InternalNodeProperty::allDirectSubNodes() const +{ + QList<InternalNode::Pointer> nodeList; + + if (node()) { + nodeList.append(node()); + } + + return nodeList; +} + +} // namespace Internal +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/core/model/internalnodeproperty.h b/src/plugins/qmldesigner/core/model/internalnodeproperty.h new file mode 100644 index 00000000000..d1eec719e9c --- /dev/null +++ b/src/plugins/qmldesigner/core/model/internalnodeproperty.h @@ -0,0 +1,67 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef INTERNALNODEPROPERTY_H +#define INTERNALNODEPROPERTY_H + +#include "internalnodeabstractproperty.h" + +namespace QmlDesigner { +namespace Internal { + +class InternalNodeProperty : public InternalNodeAbstractProperty +{ +public: + typedef QSharedPointer<InternalNodeProperty> Pointer; + + static Pointer create(const QString &name, const InternalNodePointer &propertyOwner); + + bool isValid() const; + bool isEmpty() const; + bool isNodeProperty() const; + + QList<InternalNodePointer> allSubNodes() const; + QList<InternalNodePointer> allDirectSubNodes() const; + + InternalNodePointer node() const; + + +protected: + InternalNodeProperty(const QString &name, const InternalNodePointer &node); + void add(const InternalNodePointer &node); + void remove(const InternalNodePointer &node); + +private: + InternalNodePointer m_node; +}; + +} // namespace Internal +} // namespace QmlDesigner + +#endif // INTERNALNODEPROPERTY_H diff --git a/src/plugins/qmldesigner/core/model/internalproperty.cpp b/src/plugins/qmldesigner/core/model/internalproperty.cpp new file mode 100644 index 00000000000..afb1831dfe8 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/internalproperty.cpp @@ -0,0 +1,175 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "internalproperty.h" +#include "internalbindingproperty.h" +#include "internalvariantproperty.h" +#include "internalnodelistproperty.h" +#include "internalnodeproperty.h" +#include "propertyparser.h" +#include "internalnode_p.h" +#include <QVariant> +#include <QString> +#include <QRegExp> +#include <QSize> +#include <QSizeF> +#include <QPoint> +#include <QPointF> + +namespace QmlDesigner { + +namespace Internal { + +// Creates invalid InternalProperty +InternalProperty::InternalProperty() +{ +} + +InternalProperty::~InternalProperty() +{ +} + +InternalProperty::InternalProperty(const QString &name, const InternalNode::Pointer &propertyOwner) + : m_name(name), + m_propertyOwner(propertyOwner) +{ + Q_ASSERT_X(!name.isEmpty(), Q_FUNC_INFO, "Name of property cannot be empty"); +} + +InternalProperty::Pointer InternalProperty::internalPointer() const +{ + Q_ASSERT(!m_internalPointer.isNull()); + return m_internalPointer.toStrongRef(); +} + +void InternalProperty::setInternalWeakPointer(const Pointer &pointer) +{ + Q_ASSERT(!pointer.isNull()); + m_internalPointer = pointer; +} + + +bool InternalProperty::isValid() const +{ + return m_propertyOwner && !m_name.isEmpty(); +} + +QString InternalProperty::name() const +{ + return m_name; +} + +bool InternalProperty::isBindingProperty() const +{ + return false; +} + +bool InternalProperty::isVariantProperty() const +{ + return false; +} + +QSharedPointer<InternalBindingProperty> InternalProperty::toBindingProperty() const +{ + Q_ASSERT(internalPointer().dynamicCast<InternalBindingProperty>()); + return internalPointer().staticCast<InternalBindingProperty>(); +} + + +bool InternalProperty::isNodeListProperty() const +{ + return false; +} + +bool InternalProperty::isNodeProperty() const +{ + return false; +} + +bool InternalProperty::isNodeAbstractProperty() const +{ + return false; +} + +QSharedPointer<InternalVariantProperty> InternalProperty::toVariantProperty() const + +{ + Q_ASSERT(internalPointer().dynamicCast<InternalVariantProperty>()); + return internalPointer().staticCast<InternalVariantProperty>(); +} + +InternalNode::Pointer InternalProperty::propertyOwner() const +{ + return m_propertyOwner.toStrongRef(); +} + +QSharedPointer<InternalNodeListProperty> InternalProperty::toNodeListProperty() const +{ + Q_ASSERT(internalPointer().dynamicCast<InternalNodeListProperty>()); + return internalPointer().staticCast<InternalNodeListProperty>(); +} + +QSharedPointer<InternalNodeProperty> InternalProperty::toNodeProperty() const +{ + Q_ASSERT(internalPointer().dynamicCast<InternalNodeProperty>()); + return internalPointer().staticCast<InternalNodeProperty>(); +} + +QSharedPointer<InternalNodeAbstractProperty> InternalProperty::toNodeAbstractProperty() const +{ + Q_ASSERT(internalPointer().dynamicCast<InternalNodeAbstractProperty>()); + return internalPointer().staticCast<InternalNodeAbstractProperty>(); +} + +void InternalProperty::remove() +{ + propertyOwner()->removeProperty(name()); + m_propertyOwner.clear(); +} + +QString InternalProperty::dynamicTypeName() const +{ + return m_dynamicType; +} + +void InternalProperty::setDynamicTypeName(const QString &name) +{ + m_dynamicType = name; +} + + +void InternalProperty::resetDynamicTypeName() +{ + m_dynamicType = QString(); +} + + +} //namespace Internal +} //namespace QmlDesigner + diff --git a/src/plugins/qmldesigner/core/model/internalproperty.h b/src/plugins/qmldesigner/core/model/internalproperty.h new file mode 100644 index 00000000000..f694569abd0 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/internalproperty.h @@ -0,0 +1,109 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef INTERNALPROPERTY_H +#define INTERNALPROPERTY_H + +#include "corelib_global.h" + +#include <QVariant> +#include <QString> +#include <QRegExp> +#include <QSize> +#include <QSizeF> +#include <QPoint> +#include <QPointF> +#include <QSharedPointer> + +namespace QmlDesigner { + +namespace Internal { + +class InternalBindingProperty; +class InternalVariantProperty; +class InternalNodeListProperty; +class InternalNodeProperty; +class InternalNodeAbstractProperty; +class InternalNode; + +typedef QSharedPointer<InternalNode> InternalNodePointer; +typedef QWeakPointer<InternalNode> InternalNodeWeakPointer; + +class CORESHARED_EXPORT InternalProperty +{ +public: + typedef QSharedPointer<InternalProperty> Pointer; + typedef QWeakPointer<InternalProperty> WeakPointer; + + + InternalProperty(); + virtual ~InternalProperty(); + + virtual bool isValid() const; + + QString name() const; + + virtual bool isBindingProperty() const; + virtual bool isVariantProperty() const; + virtual bool isNodeListProperty() const; + virtual bool isNodeProperty() const; + virtual bool isNodeAbstractProperty() const; + + QSharedPointer<InternalBindingProperty> toBindingProperty() const; + QSharedPointer<InternalVariantProperty> toVariantProperty() const; + QSharedPointer<InternalNodeListProperty> toNodeListProperty() const; + QSharedPointer<InternalNodeProperty> toNodeProperty() const; + QSharedPointer<InternalNodeAbstractProperty> toNodeAbstractProperty() const; + + InternalNodePointer propertyOwner() const; + + virtual void remove(); + + + QString dynamicTypeName() const; + + void resetDynamicTypeName(); + +protected: // functions + InternalProperty(const QString &name, const InternalNodePointer &propertyOwner); + Pointer internalPointer() const; + void setInternalWeakPointer(const Pointer &pointer); + void setDynamicTypeName(const QString &name); +private: + WeakPointer m_internalPointer; + QString m_name; + QString m_dynamicType; + InternalNodeWeakPointer m_propertyOwner; + +}; + +} // namespace Internal +} // namespace QmlDesigner + +#endif // INTERNALPROPERTY_H diff --git a/src/plugins/qmldesigner/core/model/internalvariantproperty.cpp b/src/plugins/qmldesigner/core/model/internalvariantproperty.cpp new file mode 100644 index 00000000000..95446dcd5b5 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/internalvariantproperty.cpp @@ -0,0 +1,77 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "internalvariantproperty.h" + +namespace QmlDesigner { +namespace Internal { + +InternalVariantProperty::InternalVariantProperty(const QString &name, const InternalNodePointer &node) + : InternalProperty(name, node) +{ +} + +InternalVariantProperty::Pointer InternalVariantProperty::create(const QString &name, const InternalNodePointer &propertyOwner) +{ + InternalVariantProperty *newPointer(new InternalVariantProperty(name, propertyOwner)); + InternalVariantProperty::Pointer smartPointer(newPointer); + + newPointer->setInternalWeakPointer(smartPointer); + + return smartPointer; +} + +QVariant InternalVariantProperty::value() const +{ + return m_value; +} + +void InternalVariantProperty::setValue(const QVariant &value) +{ + m_value = value; +} + +bool InternalVariantProperty::isVariantProperty() const +{ + return true; +} + +void InternalVariantProperty::setDynamicValue(const QString &type, const QVariant &value) +{ + setValue(value); + setDynamicTypeName(type); +} + +bool InternalVariantProperty::isValid() const +{ + return InternalProperty::isValid() && isVariantProperty(); +} + +} // namespace Internal +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/core/model/internalvariantproperty.h b/src/plugins/qmldesigner/core/model/internalvariantproperty.h new file mode 100644 index 00000000000..e881bb1db0b --- /dev/null +++ b/src/plugins/qmldesigner/core/model/internalvariantproperty.h @@ -0,0 +1,64 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef INTERNALVARIANTPROPERTY_H +#define INTERNALVARIANTPROPERTY_H + +#include "internalproperty.h" + +namespace QmlDesigner { +namespace Internal { + +class InternalVariantProperty : public InternalProperty +{ +public: + typedef QSharedPointer<InternalVariantProperty> Pointer; + + static Pointer create(const QString &name, const InternalNodePointer &propertyOwner); + + bool isValid() const; + + QVariant value() const; + void setValue(const QVariant &value); + + void setDynamicValue(const QString &type, const QVariant &value); + + bool isVariantProperty() const; + +protected: + InternalVariantProperty(const QString &name, const InternalNodePointer &propertyOwner); + +private: + QVariant m_value; +}; + +} // namespace Internal +} // namespace QmlDesigner + +#endif // INTERNALVARIANTPROPERTY_H diff --git a/src/plugins/qmldesigner/core/model/model.cpp b/src/plugins/qmldesigner/core/model/model.cpp new file mode 100644 index 00000000000..a837a811e4e --- /dev/null +++ b/src/plugins/qmldesigner/core/model/model.cpp @@ -0,0 +1,918 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include <model.h> +#include <modelnode.h> +#include "internalnode_p.h" +#include "invalidpropertyexception.h" +#include "invalidargumentexception.h" + +#include <QtCore/QFile> +#include <QtCore/QByteArray> +#include <QWeakPointer> +#include <QtCore/QFileInfo> + +#include <QtGui/QUndoStack> +#include <QtXml/QXmlStreamReader> +#include <QtCore/QDebug> +#include <QPlainTextEdit> + +#include "abstractview.h" +#include "widgetqueryview.h" +#include "metainfo.h" +#include "model_p.h" +#include "modelutilities.h" +#include "subcomponentmanager.h" +#include "variantparser.h" +#include "internalproperty.h" +#include "internalnodelistproperty.h" +#include "internalnodeabstractproperty.h" +#include "invalidmodelnodeexception.h" +#include "invalidmodelstateexception.h" +#include "invalidslideindexexception.h" + +#include "abstractproperty.h" +#include "variantproperty.h" +#include "bindingproperty.h" +#include "nodeabstractproperty.h" +#include "nodelistproperty.h" +#include "rewritertransaction.h" + +/*! +\defgroup CoreModel +*/ +/*! +\class QmlDesigner::Model +\ingroup CoreModel +\brief This is the facade for the abstract model data. + All write access is running through this interface + +The Model is the central place to access a qml files data (see e.g. rootNode() ) and meta data (see metaInfo() ). + +Components that want to be informed about changes in the model can register a subclass of AbstractView via attachView(). + +\see QmlDesigner::ModelNode, QmlDesigner::AbstractProperty, QmlDesigner::AbstractView +*/ + +namespace QmlDesigner { +namespace Internal { + +ModelPrivate::ModelPrivate(Model *model) : + m_q(model), + m_rootInternalNode(createNode("Qt/Rectangle", 4, 6, PropertyListType())) +{ +} + +ModelPrivate::~ModelPrivate() +{ + + detachAllViews(); +} + +void ModelPrivate::detachAllViews() +{ + foreach (const QWeakPointer<AbstractView> &view, m_viewList) + detachView(view.data(), true); + + m_viewList.clear(); +} + +Model *ModelPrivate::create(QString type, int major, int minor) +{ + Model *model = new Model; + + model->m_d->rootNode()->setType(type); + model->m_d->rootNode()->setMajorVersion(major); + model->m_d->rootNode()->setMinorVersion(minor); + + return model; +} + +void ModelPrivate::setImports(const QSet<Import> &imports) +{ + QList<Import> added = QSet<Import>(imports).subtract(m_imports).toList(); + QList<Import> removed = QSet<Import>(m_imports).subtract(imports).toList(); + + if (added.isEmpty() && removed.isEmpty()) + return; + + m_imports = imports; + + notifyImportsChanged(); +} + +void ModelPrivate::addImport(const Import &import) +{ + if (m_imports.contains(import)) + return; + + m_imports.insert(import); + notifyImportsChanged(); +} + +void ModelPrivate::removeImport(const Import &import) +{ + if (!m_imports.remove(import)) + return; + + notifyImportsChanged(); +} + +void ModelPrivate::notifyImportsChanged() const +{ + foreach (const QWeakPointer<AbstractView> &view, m_viewList) + view->importsChanged(); +} + +QUrl ModelPrivate::fileUrl() const +{ + return m_fileUrl; +} + +void ModelPrivate::setFileUrl(const QUrl &fileUrl) +{ + QUrl oldPath = m_fileUrl; + + if (oldPath != fileUrl) { + m_fileUrl = fileUrl; + + foreach (const QWeakPointer<AbstractView> &view, m_viewList) + view->fileUrlChanged(oldPath, fileUrl); + } +} + +InternalNode::Pointer ModelPrivate::createNode(const QString &typeString, + int majorVersion, + int minorVersion, + const QList<QPair<QString, QVariant> > &propertyList) +{ + if (typeString.isEmpty()) + throw InvalidArgumentException(__LINE__, __FUNCTION__, __FILE__, "typeString"); + if (!m_metaInfo.nodeMetaInfo(typeString).isValid()) + throw InvalidArgumentException(__LINE__, __FUNCTION__, __FILE__, "typeString"); + + InternalNode::Pointer newInternalNodePointer = InternalNode::create(typeString, majorVersion, minorVersion); + + typedef QPair<QString, QVariant> PropertyPair; + + foreach (const PropertyPair &propertyPair, propertyList) { + newInternalNodePointer->addVariantProperty(propertyPair.first); + newInternalNodePointer->variantProperty(propertyPair.first)->setValue(propertyPair.second); + } + + m_nodeSet.insert(newInternalNodePointer); + + notifyNodeCreated(newInternalNodePointer); + + return newInternalNodePointer; +} + +void ModelPrivate::removeNodeFromModel(const InternalNodePointer &node) +{ + Q_ASSERT(!node.isNull()); + + node->resetParentProperty(); + + if (!node->id().isEmpty()) + m_idNodeHash.remove(node->id()); + node->setValid(false); + m_nodeSet.remove(node); +} + +void ModelPrivate::removeAllSubNodes(const InternalNode::Pointer &node) +{ + foreach (const InternalNodePointer &subNode, node->allSubNodes()) { + removeNodeFromModel(subNode); + } +} + +void ModelPrivate::removeNode(const InternalNode::Pointer &node) +{ + Q_ASSERT(!node.isNull()); + + AbstractView::PropertyChangeFlags propertyChangeFlags = AbstractView::NoAdditionalChanges; + + notifyNodeAboutToBeRemoved(node); + + InternalNodeAbstractProperty::Pointer oldParentProperty(node->parentProperty()); + + removeAllSubNodes(node); + removeNodeFromModel(node); + + InternalNode::Pointer parentNode; + QString parentPropertyName; + if (oldParentProperty) { + parentNode = oldParentProperty->propertyOwner(); + parentPropertyName = oldParentProperty->name(); + } + + if (oldParentProperty && oldParentProperty->isEmpty()) { + removePropertyWithoutNotification(oldParentProperty); + + propertyChangeFlags |= AbstractView::EmptyPropertiesRemoved; + } + + notifyNodeRemoved(node, parentNode, parentPropertyName, propertyChangeFlags); +} + +InternalNode::Pointer ModelPrivate::rootNode() const +{ + return m_rootInternalNode; +} + +MetaInfo ModelPrivate::metaInfo() const +{ + return m_metaInfo; +} + +void ModelPrivate::changeNodeId(const InternalNode::Pointer& internalNodePointer, const QString &id) +{ + const QString oldId = internalNodePointer->id(); + + internalNodePointer->setId(id); + if (!oldId.isEmpty()) + m_idNodeHash.remove(oldId); + if (!id.isEmpty()) + m_idNodeHash.insert(id, internalNodePointer); + notifyNodeIdChanged(internalNodePointer, id, oldId); +} + +void ModelPrivate::checkPropertyName(const QString &propertyName) +{ + if (propertyName.isEmpty()) { + Q_ASSERT_X(propertyName.isEmpty(), Q_FUNC_INFO, "empty property name"); + throw InvalidPropertyException(__LINE__, __FUNCTION__, __FILE__, "<empty property name>"); + } + + if (propertyName == "id") { + Q_ASSERT_X(propertyName != "id", Q_FUNC_INFO, "cannot add property id"); + throw InvalidPropertyException(__LINE__, __FUNCTION__, __FILE__, propertyName); + } +} + +void ModelPrivate::notifyAuxiliaryDataChanged(const InternalNodePointer &internalNode, const QString &name, const QVariant &data) +{ + foreach (const QWeakPointer<AbstractView> &view, m_viewList) { + Q_ASSERT(view != 0); + ModelNode node(internalNode, model(), view.data()); + view->auxiliaryDataChanged(node, name, data); + } +} + +void ModelPrivate::notifyNodeTypeChanged(const InternalNodePointer &internalNode, const QString &type, int majorVersion, int minorVersion) +{ + foreach (const QWeakPointer<AbstractView> &view, m_viewList) { + Q_ASSERT(view != 0); + ModelNode node(internalNode, model(), view.data()); + view->nodeTypeChanged(node, type, majorVersion, minorVersion); + } +} + +void ModelPrivate::notifyCustomNotification(const AbstractView *senderView, const QString &identifier, const QList<ModelNode> &nodeList, const QList<QVariant> &data) +{ + QList<Internal::InternalNode::Pointer> internalList(toInternalNodeList(nodeList)); + foreach (const QWeakPointer<AbstractView> &view, m_viewList) { + Q_ASSERT(view != 0); + view->customNotification(senderView, identifier, toModelNodeList(internalList, view.data()), data); + } +} + + +void ModelPrivate::notifyPropertiesRemoved(const QList<PropertyPair> &propertyPairList) +{ + foreach (const QWeakPointer<AbstractView> &view, m_viewList) { + QList<AbstractProperty> propertyList; + Q_ASSERT(view != 0); + foreach (const PropertyPair &propertyPair, propertyPairList) { + AbstractProperty newProperty(propertyPair.second, propertyPair.first, model(), view.data()); + propertyList.append(newProperty); + } + view->propertiesRemoved(propertyList); + } +} + +void ModelPrivate::notifyPropertiesAboutToBeRemoved(const QList<InternalProperty::Pointer> &internalPropertyList) +{ + foreach (const QWeakPointer<AbstractView> &view, m_viewList) { + QList<AbstractProperty> propertyList; + Q_ASSERT(view != 0); + foreach (const InternalProperty::Pointer &property, internalPropertyList) { + AbstractProperty newProperty(property->name(), property->propertyOwner(), model(), view.data()); + propertyList.append(newProperty); + } + view->propertiesAboutToBeRemoved(propertyList); + } +} + +void ModelPrivate::setAuxiliaryData(const InternalNode::Pointer& node, const QString &name, const QVariant &data) +{ + node->setAuxiliaryData(name, data); + notifyAuxiliaryDataChanged(node, name,data); +} + + +void ModelPrivate::attachView(AbstractView *view) +{ + if (m_viewList.contains(view)) + return; + + m_viewList.append(view); + + view->modelAttached(m_q); +} + +void ModelPrivate::detachView(AbstractView *view, bool notifyView) +{ + if (notifyView) + view->modelAboutToBeDetached(m_q); + m_viewList.removeOne(view); +} + +void ModelPrivate::notifyNodeCreated(const InternalNode::Pointer &newInternalNodePointer) +{ + Q_ASSERT(newInternalNodePointer->isValid()); + + foreach (const QWeakPointer<AbstractView> &view, m_viewList) { + Q_ASSERT(view != 0); + ModelNode createdNode(newInternalNodePointer, model(), view.data()); + view->nodeCreated(createdNode); + } +} + +void ModelPrivate::notifyNodeAboutToBeRemoved(const InternalNode::Pointer &nodePointer) +{ + foreach (const QWeakPointer<AbstractView> &view, m_viewList) { + Q_ASSERT(view != 0); + ModelNode node(nodePointer, model(), view.data()); + view->nodeAboutToBeRemoved(node); + } +} + +void ModelPrivate::notifyNodeRemoved(const InternalNodePointer &nodePointer, const InternalNodePointer &parentNodePointer, const QString &parentPropertyName, AbstractView::PropertyChangeFlags propertyChange) +{ + foreach (const QWeakPointer<AbstractView> &view, m_viewList) { + Q_ASSERT(view != 0); + ModelNode node(nodePointer, model(), view.data()); + NodeAbstractProperty parentProperty(parentPropertyName, parentNodePointer, model(), view.data()); + view->nodeRemoved(node, parentProperty, propertyChange); + } +} + +void ModelPrivate::notifyNodeIdChanged(const InternalNode::Pointer& nodePointer, const QString& newId, const QString& oldId) +{ + foreach (const QWeakPointer<AbstractView> &view, m_viewList) { + Q_ASSERT(view != 0); + ModelNode node(nodePointer, model(), view.data()); + view->nodeIdChanged(node, newId, oldId); + } +} + +void ModelPrivate::notifyBindingPropertiesChanged(const QList<InternalBindingPropertyPointer> &internalBropertyList, AbstractView::PropertyChangeFlags propertyChange) +{ + foreach (const QWeakPointer<AbstractView> &view, m_viewList) { + QList<BindingProperty> propertyList; + Q_ASSERT(view != 0); + foreach (const InternalBindingPropertyPointer &bindingProperty, internalBropertyList) { + propertyList.append(BindingProperty(bindingProperty->name(), bindingProperty->propertyOwner(), model(), view.data())); + } + view->bindingPropertiesChanged(propertyList, propertyChange); + } +} + +void ModelPrivate::notifyVariantPropertiesChanged(const InternalNodePointer &internalNodePointer, const QStringList& propertyNameList, AbstractView::PropertyChangeFlags propertyChange) +{ + foreach (const QWeakPointer<AbstractView> &view, m_viewList) { + QList<VariantProperty> propertyList; + Q_ASSERT(view != 0); + foreach (const QString &propertyName, propertyNameList) { + Q_ASSERT(internalNodePointer->hasProperty(propertyName)); + Q_ASSERT(internalNodePointer->property(propertyName)->isVariantProperty()); + VariantProperty property(propertyName, internalNodePointer, model(), view.data()); + propertyList.append(property); + } + ModelNode node(internalNodePointer, model(), view.data()); + view->variantPropertiesChanged(propertyList, propertyChange); + } +} + +void ModelPrivate::notifyNodeReparent(const InternalNode::Pointer &internalNodePointer, const InternalNodeAbstractProperty::Pointer &newPropertyParent, const InternalNodePointer &oldParent, const QString &oldPropertyName, AbstractView::PropertyChangeFlags propertyChange) +{ + foreach (const QWeakPointer<AbstractView> &view, m_viewList) { + NodeAbstractProperty newProperty; + NodeAbstractProperty oldProperty; + + Q_ASSERT(!view.isNull()); + if (!oldPropertyName.isEmpty() && oldParent->isValid()) + oldProperty = NodeAbstractProperty(oldPropertyName, oldParent, model(), view.data()); + + if (!newPropertyParent.isNull()) + newProperty = NodeAbstractProperty(newPropertyParent, model(), view.data()); + ModelNode node(internalNodePointer, model(), view.data()); + view->nodeReparented(node, newProperty, oldProperty, propertyChange); + } + } + +void ModelPrivate::notifyNodeSlidedToIndex(const InternalNodeListProperty::Pointer &internalNodeListproperty, int newIndex, int oldIndex) +{ + foreach (const QWeakPointer<AbstractView> &view, m_viewList) { + Q_ASSERT(view != 0); + NodeListProperty nodeListproperty(internalNodeListproperty, model(), view.data()); + view->nodeSlidedToIndex(nodeListproperty, newIndex, oldIndex); + } +} + +void ModelPrivate::setSelectedNodes(const QList<InternalNode::Pointer> &selectedNodeList) +{ + + QList<InternalNode::Pointer> sortedSelectedList(selectedNodeList); + QMutableListIterator<InternalNode::Pointer> iterator(sortedSelectedList); + while (iterator.hasNext()) { + InternalNode::Pointer node(iterator.next()); + if (!node->isValid()) + iterator.remove(); + } + + sortedSelectedList = sortedSelectedList.toSet().toList(); + qSort(sortedSelectedList); + + if (sortedSelectedList == m_selectedNodeList) + return; + + + const QList<InternalNode::Pointer> lastSelectedNodeList = m_selectedNodeList; + m_selectedNodeList = sortedSelectedList; + + changeSelectedNodes(sortedSelectedList, lastSelectedNodeList); +} + + +void ModelPrivate::clearSelectedNodes() +{ + const QList<InternalNode::Pointer> lastSelectedNodeList = m_selectedNodeList; + m_selectedNodeList.clear(); + changeSelectedNodes(m_selectedNodeList, lastSelectedNodeList); +} + +QList<ModelNode> ModelPrivate::toModelNodeList(const QList<InternalNode::Pointer> &nodeList, AbstractView *view) const +{ + QList<ModelNode> newNodeList; + foreach (const Internal::InternalNode::Pointer &node, nodeList) + newNodeList.append(ModelNode(node, model(), view)); + + return newNodeList; +} + +QList<Internal::InternalNode::Pointer> ModelPrivate::toInternalNodeList(const QList<ModelNode> &nodeList) const +{ + QList<Internal::InternalNode::Pointer> newNodeList; + foreach (const ModelNode &node, nodeList) + newNodeList.append(node.internalNode()); + + return newNodeList; +} + +void ModelPrivate::changeSelectedNodes(const QList<InternalNode::Pointer> &newSelectedNodeList, + const QList<InternalNode::Pointer> &oldSelectedNodeList) +{ + foreach (const QWeakPointer<AbstractView> &view, m_viewList) { + Q_ASSERT(view != 0); + view->selectedNodesChanged(toModelNodeList(newSelectedNodeList, view.data()), toModelNodeList(oldSelectedNodeList, view.data())); + } +} + +QList<InternalNode::Pointer> ModelPrivate::selectedNodes() const +{ + foreach (const InternalNode::Pointer &node, m_selectedNodeList) { + if (!node->isValid()) + throw new InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + } + + return m_selectedNodeList; +} + +void ModelPrivate::selectNode(const InternalNode::Pointer &node) +{ + if (selectedNodes().contains(node)) + return; + + QList<InternalNode::Pointer> selectedNodeList(selectedNodes()); + selectedNodeList += node; + setSelectedNodes(selectedNodeList); +} + +void ModelPrivate::deselectNode(const InternalNode::Pointer &node) +{ + QList<InternalNode::Pointer> selectedNodeList(selectedNodes()); + bool isRemoved = selectedNodeList.removeOne(node); + + if (!isRemoved) + return; + + setSelectedNodes(selectedNodeList); +} + +void ModelPrivate::removePropertyWithoutNotification(const InternalPropertyPointer &property) +{ + if (property->isNodeAbstractProperty()) { + foreach (const InternalNode::Pointer & internalNode, property->toNodeAbstractProperty()->allSubNodes()) + removeNodeFromModel(internalNode); + } + + property->remove(); +} + +static QList<PropertyPair> toPropertyPairList(const QList<InternalProperty::Pointer> &propertyList) +{ + QList<PropertyPair> propertyPairList; + + foreach (const InternalProperty::Pointer &property, propertyList) + propertyPairList.append(qMakePair(property->propertyOwner(), property->name())); + + return propertyPairList; + +} + +void ModelPrivate::removeProperty(const InternalProperty::Pointer &property) +{ + notifyPropertiesAboutToBeRemoved(QList<InternalProperty::Pointer>() << property); + + QList<PropertyPair> propertyPairList = toPropertyPairList(QList<InternalProperty::Pointer>() << property); + + removePropertyWithoutNotification(property); + + notifyPropertiesRemoved(propertyPairList); +} + +void ModelPrivate::setBindingProperty(const InternalNode::Pointer &internalNode, const QString &name, const QString &expression) +{ + AbstractView::PropertyChangeFlags propertyChange = AbstractView::NoAdditionalChanges; + if (!internalNode->hasProperty(name)) { + internalNode->addBindingProperty(name); + propertyChange = AbstractView::PropertiesAdded; + } + + InternalBindingProperty::Pointer bindingProperty = internalNode->bindingProperty(name); + bindingProperty->setExpression(expression); + notifyBindingPropertiesChanged(QList<InternalBindingPropertyPointer>() << bindingProperty, propertyChange); +} + +void ModelPrivate::setVariantProperty(const InternalNode::Pointer &internalNode, const QString &name, const QVariant &value) +{ + AbstractView::PropertyChangeFlags propertyChange = AbstractView::NoAdditionalChanges; + if (!internalNode->hasProperty(name)) { + internalNode->addVariantProperty(name); + propertyChange = AbstractView::PropertiesAdded; + } + + internalNode->variantProperty(name)->setValue(value); + internalNode->variantProperty(name)->resetDynamicTypeName(); + notifyVariantPropertiesChanged(internalNode, QStringList() << name, propertyChange); +} + +void ModelPrivate::setDynamicVariantProperty(const InternalNodePointer &internalNode, const QString &name, const QString &dynamicPropertyType, const QVariant &value) +{ + AbstractView::PropertyChangeFlags propertyChange = AbstractView::NoAdditionalChanges; + if (!internalNode->hasProperty(name)) { + internalNode->addVariantProperty(name); + propertyChange = AbstractView::PropertiesAdded; + } + + internalNode->variantProperty(name)->setDynamicValue(dynamicPropertyType, value); + notifyVariantPropertiesChanged(internalNode, QStringList() << name, propertyChange); +} + +void ModelPrivate::setDynamicBindingProperty(const InternalNodePointer &internalNode, const QString &name, const QString &dynamicPropertyType, const QString &expression) +{ + AbstractView::PropertyChangeFlags propertyChange = AbstractView::NoAdditionalChanges; + if (!internalNode->hasProperty(name)) { + internalNode->addBindingProperty(name); + propertyChange = AbstractView::PropertiesAdded; + } + + InternalBindingProperty::Pointer bindingProperty = internalNode->bindingProperty(name); + bindingProperty->setDynamicExpression(dynamicPropertyType, expression); + notifyBindingPropertiesChanged(QList<InternalBindingPropertyPointer>() << bindingProperty, propertyChange); +} + +void ModelPrivate::reparentNode(const InternalNode::Pointer &newParentNode, const QString &name, const InternalNode::Pointer &node, bool list) +{ + AbstractView::PropertyChangeFlags propertyChange = AbstractView::NoAdditionalChanges; + if (!newParentNode->hasProperty(name)) { + if (list) + newParentNode->addNodeListProperty(name); + else + newParentNode->addNodeProperty(name); + propertyChange |= AbstractView::PropertiesAdded; + } + + InternalNodeAbstractProperty::Pointer oldParentProperty(node->parentProperty()); + InternalNode::Pointer oldParentNode; + QString oldParentPropertyName; + if (oldParentProperty && oldParentProperty->isValid()) { + oldParentNode = node->parentProperty()->propertyOwner(); + oldParentPropertyName = node->parentProperty()->name(); + } + + InternalNodeAbstractProperty::Pointer newParentProperty(newParentNode->nodeAbstractProperty(name)); + Q_ASSERT(!newParentProperty.isNull()); + if (newParentProperty) + node->setParentProperty(newParentProperty); + + + if (oldParentProperty && oldParentProperty->isValid() && oldParentProperty->isEmpty()) { + removePropertyWithoutNotification(oldParentProperty); + + propertyChange |= AbstractView::EmptyPropertiesRemoved; + } + + notifyNodeReparent(node, newParentProperty, oldParentNode, oldParentPropertyName, propertyChange); +} + +void ModelPrivate::clearParent(const InternalNodePointer &node) +{ + + InternalNodeAbstractProperty::Pointer oldParentProperty(node->parentProperty()); + InternalNode::Pointer oldParentNode; + QString oldParentPropertyName; + if (oldParentProperty->isValid()) { + oldParentNode = node->parentProperty()->propertyOwner(); + oldParentPropertyName = node->parentProperty()->name(); + } + + node->resetParentProperty(); + notifyNodeReparent(node, InternalNodeAbstractProperty::Pointer(), oldParentNode, oldParentPropertyName, AbstractView::NoAdditionalChanges); +} + +void ModelPrivate::changeType(const InternalNodePointer &internalNode, const QString &type, int majorVersion, int minorVersion) +{ + Q_ASSERT(!internalNode.isNull()); + internalNode->setType(type); + internalNode->setMajorVersion(majorVersion); + internalNode->setMinorVersion(minorVersion); + notifyNodeTypeChanged(internalNode, type, majorVersion, minorVersion); +} + +void ModelPrivate::slideNodeList(const InternalNode::Pointer &internalNode, const QString &name, int from, int to) +{ + InternalNodeListProperty::Pointer nodeList(internalNode->nodeListProperty(name)); + Q_ASSERT(!nodeList.isNull()); + nodeList->slide(from, to); + notifyNodeSlidedToIndex(nodeList, to, from); +} + + +void ModelPrivate::setRootNode(const InternalNode::Pointer& newRootNode) +{ + removeNode(m_rootInternalNode); + m_rootInternalNode = newRootNode; + + if (!m_rootInternalNode.isNull() && m_rootInternalNode->isValid()) + notifyNodeCreated(m_rootInternalNode); +} + + +InternalNodePointer ModelPrivate::nodeForId(const QString &id) const +{ + return m_idNodeHash.value(id); +} + +bool ModelPrivate::hasId(const QString &id) const +{ + return m_idNodeHash.contains(id); +} + +QList<InternalNodePointer> ModelPrivate::allNodes() const +{ + return m_nodeSet.toList(); +} + +//static QString anchorLinePropertyValue(const InternalNode::Pointer &sourceNode, const InternalNode::Pointer &targetNode, const AnchorLine::Type &targetType) +//{ +// if (targetNode.isNull() || !targetNode->isValid()) +// return QString(); +// +// if (sourceNode.isNull() || !sourceNode->isValid()) +// return QString(); +// +// if (targetNode == sourceNode) +// return QString(); +// +// QString anchorLineType = InternalNodeAnchors::lineTypeToString(targetType); +// +// // handle "parent" and "parent.something" +// if (targetNode == sourceNode->parentNode()) { +// if (anchorLineType.isNull()) +// return QLatin1String("parent"); +// else +// return QString("parent.%1").arg(anchorLineType); +// } +// +// if (anchorLineType.isNull()) +// return QString(); // for sibling reference, the type cannot be empty anymore. +// +// foreach (const InternalNode::Pointer &sibling, sourceNode->parentNode()->childNodes()) { +// if (sibling == targetNode) { +// return QString("%1.%2").arg(sibling->id(), anchorLineType); +// } +// } +// +// return QString(); +//} + +} + + +Model::Model() : + QObject(), + m_d(new Internal::ModelPrivate(this)) +{ +} + + +Model::~Model() +{ + delete m_d; +} + + +Model *Model::create(QString type, int major, int minor) +{ + return Internal::ModelPrivate::create(type, major, minor); +} + + +/*! + \brief Creates a model for a component definition + + Creates a model containing the content of a component node. The component node itself is + not part of the newly created model; it's the first item defined after "Component {" + that is the root node. + + \arg componentNode must be valid & of type "Qt/Component" + \return the newly created model. The caller takes ownership of the object life time. + */ +//Model *Model::createComponentModel(const ModelNode &componentNode) +//{ +// +// if (!componentNode.isValid() || componentNode.type() != "Qt/Component") { +// throw new InvalidArgumentException(__LINE__, __FUNCTION__, __FILE__, QString("componentNode")); +// } +// +// // the data property does not include the Component element itself +// const TextLocation componentRootLocation +// = componentNode.baseNodeState().m_internalNodeState->propertyLocation("data"); +// +// TextModifier *textModifier +// = new Internal::ComponentTextModifier(m_d->m_rewriter->textModifier(), +// componentRootLocation, +// m_d->m_rootInternalNode->baseNodeState()->location()); +// +// QList<QmlError> errors; +// Model *subModel = create(textModifier, m_d->m_fileUrl, &errors); +// +// Q_ASSERT(subModel); +// Q_ASSERT(errors.size() == 0); // should never happen, after all it was already compiled! +// +// textModifier->setParent(subModel); +// +// return subModel; +//} + +QSet<Import> Model::imports() const +{ + return m_d->imports(); +} + +void Model::addImport(const Import &import) +{ + m_d->addImport(import); +} + +void Model::removeImport(const Import &import) +{ + m_d->removeImport(import); +} + +#if 0 +/*! + \brief Creates a new empty model + \param uiFilePath path to the ui file + \param[out] errorMessage returns a error message + \return new created model +*/ +Model *Model::create(const QString &rootType) +{ + return Internal::ModelPrivate::create(rootType); +} +#endif + +Model *Model::masterModel() const +{ + return m_d->m_masterModel.data(); +} + +void Model::setMasterModel(Model *model) +{ + m_d->m_masterModel = model; +} + +/*! + \brief Returns the URL against which relative URLs within the model should be resolved. + \return The base URL. + */ +QUrl Model::fileUrl() const +{ + return m_d->fileUrl(); +} + +/*! + \brief Sets the URL against which relative URLs within the model should be resolved. + \param url the base URL, i.e. the qml file path. + */ +void Model::setFileUrl(const QUrl &url) +{ + m_d->setFileUrl(url); +} + +/*! + \brief Returns list of Qml types available within the model. + */ +const MetaInfo Model::metaInfo() const +{ + return m_d->metaInfo(); +} + +/*! + \brief Returns list of Qml types available within the model. + */ +MetaInfo Model::metaInfo() +{ + return m_d->metaInfo(); +} + +/*! \name Undo Redo Interface + here you can find a facade to the internal undo redo framework +*/ + + +/*! \name View related functions +*/ +//\{ +/*! +\brief Attaches a view to the model + +Registers a "view" that from then on will be informed about changes to the model. Different views +will always be informed in the order in which they registered to the model. + +The view is informed that it has been registered within the model by a call to AbstractView::modelAttached . + +\param view The view object to register. Must be not null. +\see detachView +*/ +void Model::attachView(AbstractView *view) +{ + m_d->attachView(view); +} + +/*! +\brief Detaches a view to the model + +\param view The view to unregister. Must be not null. +\param emitDetachNotify If set to NotifyView (the default), AbstractView::modelAboutToBeDetached() will be called + +\see attachView +*/ +void Model::detachView(AbstractView *view, ViewNotification emitDetachNotify) +{ + bool emitNotify = (emitDetachNotify == NotifyView); + m_d->detachView(view, emitNotify); +} + + +} diff --git a/src/plugins/qmldesigner/core/model/model_p.h b/src/plugins/qmldesigner/core/model/model_p.h new file mode 100644 index 00000000000..184d10ca3ee --- /dev/null +++ b/src/plugins/qmldesigner/core/model/model_p.h @@ -0,0 +1,195 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef MODEL_P_H +#define MODEL_P_H + +#include <QtCore/QList> +#include <QWeakPointer> +#include <QtCore/QSet> +#include <QtCore/QUrl> + +#include "modelnode.h" +#include "abstractview.h" +#include "metainfo.h" + +#include "corelib_global.h" + +QT_BEGIN_NAMESPACE +class QPlainTextEdit; +QT_END_NAMESPACE + +namespace QmlDesigner { + +class AbstractProperty; + + +namespace Internal { + +class InternalNode; +class InternalProperty; +class InternalBindingProperty; +class InternalVariantProperty; +class InternalNodeAbstractProperty; +class InternalNodeListProperty; + +typedef QSharedPointer<InternalNode> InternalNodePointer; +typedef QSharedPointer<InternalProperty> InternalPropertyPointer; +typedef QSharedPointer<InternalBindingProperty> InternalBindingPropertyPointer; +typedef QSharedPointer<InternalVariantProperty> InternalVariantPropertyPointer; +typedef QSharedPointer<InternalNodeAbstractProperty> InternalNodeAbstractPropertyPointer; +typedef QSharedPointer<InternalNodeListProperty> InternalNodeListPropertyPointer; +typedef QPair<InternalNodePointer, QString> PropertyPair; + +class ModelPrivate : QObject { + Q_OBJECT + + Q_DISABLE_COPY(ModelPrivate) + + friend class QmlDesigner::Model; + +public: + ModelPrivate(Model *model); + ~ModelPrivate(); + + static Model *create(QString type, int major, int minor); + + QUrl fileUrl() const; + void setFileUrl(const QUrl &url); + + InternalNodePointer createNode(const QString &typeString, + int majorVersion, + int minorVersion, + const QList<QPair<QString, QVariant> > &propertyList); + + + /*factory methods for internal use in model and rewriter*/ + + void removeNode(const InternalNodePointer &node); + void changeNodeId(const InternalNodePointer& internalNodePointer, const QString& id); + + InternalNodePointer rootNode() const; + InternalNodePointer findNode(const QString &id) const; + + MetaInfo metaInfo() const; + + void attachView(AbstractView *view); + void detachView(AbstractView *view, bool notifyView); + void detachAllViews(); + + + Model *model() const { return m_q; } + void setModel(Model *q) { m_q = q; } + + void notifyNodeCreated(const InternalNodePointer &newInternalNodePointer); + void notifyNodeReparent(const InternalNodePointer &internalNodePointer, const InternalNodeAbstractPropertyPointer &newPropertyParent, const InternalNodePointer &oldParent, const QString &oldPropertyName, AbstractView::PropertyChangeFlags propertyChange); + void notifyNodeAboutToBeRemoved(const InternalNodePointer &nodePointer); + void notifyNodeRemoved(const InternalNodePointer &nodePointer, const InternalNodePointer &parentNodePointer, const QString &parentPropertyName, AbstractView::PropertyChangeFlags propertyChange); + void notifyNodeIdChanged(const InternalNodePointer& nodePointer, const QString& newId, const QString& oldId); + + void notifyPropertiesRemoved(const QList<PropertyPair> &propertyList); + void notifyPropertiesAboutToBeRemoved(const QList<InternalPropertyPointer> &propertyList); + void notifyBindingPropertiesChanged(const QList<InternalBindingPropertyPointer> &propertyList, AbstractView::PropertyChangeFlags propertyChange); + void notifyVariantPropertiesChanged(const InternalNodePointer &internalNodePointer, const QStringList& propertyNameList, AbstractView::PropertyChangeFlags propertyChange); + + void notifyNodeSlidedToIndex(const InternalNodeListPropertyPointer &internalNode, int newIndex, int oldIndex); + void notifyAuxiliaryDataChanged(const InternalNodePointer &internalNode, const QString &name, const QVariant &data); + + void notifyNodeTypeChanged(const InternalNodePointer &internalNode, const QString &type, int majorVersion, int minorVersion); + + void notifyCustomNotification(const AbstractView *senderView, const QString &identifier, const QList<ModelNode> &nodeList, const QList<QVariant> &data); + + void setSelectedNodes(const QList<InternalNodePointer> &selectedNodeList); + void clearSelectedNodes(); + QList<InternalNodePointer> selectedNodes() const; + void selectNode(const InternalNodePointer &node); + void deselectNode(const InternalNodePointer &node); + void changeSelectedNodes(const QList<InternalNodePointer> &newSelectedsNodeList, + const QList<InternalNodePointer> &oldSelectedsNodeList); + + void setRootNode(const InternalNodePointer& newRootNode); + void setAuxiliaryData(const InternalNodePointer& node, const QString &name, const QVariant &data); + + + // Imports: + QSet<Import> imports() const { return m_imports; } + void setImports(const QSet<Import> &imports); + void addImport(const Import &import); + void removeImport(const Import &import); + void notifyImportsChanged() const; + + + //node state property manipulation + + void addProperty(const InternalNodePointer &node, const QString &name); + void setPropertyValue(const InternalNodePointer &node,const QString &name, const QVariant &value); + void removeProperty(const InternalPropertyPointer &property); + + void setBindingProperty(const InternalNodePointer &internalNode, const QString &name, const QString &expression); + void setVariantProperty(const InternalNodePointer &internalNode, const QString &name, const QVariant &value); + void setDynamicVariantProperty(const InternalNodePointer &internalNode, const QString &name, const QString &propertyType, const QVariant &value); + void setDynamicBindingProperty(const InternalNodePointer &internalNode, const QString &name, const QString &dynamicPropertyType, const QString &expression); + void reparentNode(const InternalNodePointer &internalNode, const QString &name, const InternalNodePointer &internalNodeToBeAppended, bool list = true); + void slideNodeList(const InternalNodePointer &internalNode, const QString &name, int from, int to); + void checkPropertyName(const QString &propertyName); + void clearParent(const InternalNodePointer &internalNode); + void changeType(const InternalNodePointer & internalNode, const QString &type, int majorVersion, int minorVersion); + + InternalNodePointer nodeForId(const QString &id) const; + bool hasId(const QString &id) const; + + QList<InternalNodePointer> allNodes() const; + +private: //functions + void removePropertyWithoutNotification(const InternalPropertyPointer &property); + void removeAllSubNodes(const InternalNodePointer &node); + void removeNodeFromModel(const InternalNodePointer &node); + QList<InternalNodePointer> toInternalNodeList(const QList<ModelNode> &nodeList) const; + QList<ModelNode> toModelNodeList(const QList<InternalNodePointer> &nodeList, AbstractView *view) const; + +private: + Model *m_q; + MetaInfo m_metaInfo; + + QSet<Import> m_imports; + QList<QWeakPointer<AbstractView> > m_viewList; + QList<InternalNodePointer> m_selectedNodeList; + QHash<QString,InternalNodePointer> m_idNodeHash; + QSet<InternalNodePointer> m_nodeSet; + + InternalNodePointer m_rootInternalNode; + + QUrl m_fileUrl; + + QWeakPointer<Model> m_masterModel; +}; + +} +} +#endif // MODEL_P_H diff --git a/src/plugins/qmldesigner/core/model/modelmerger.cpp b/src/plugins/qmldesigner/core/model/modelmerger.cpp new file mode 100644 index 00000000000..133fb417e74 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/modelmerger.cpp @@ -0,0 +1,168 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "modelmerger.h" + +#include "modelmerger.h" +#include "modelnode.h" +#include "abstractview.h" +#include "model.h" +#include "nodeproperty.h" +#include "nodelistproperty.h" +#include "bindingproperty.h" +#include "variantproperty.h" +#include "rewritertransaction.h" + +#include <QSet> +#include <QStringList> + +namespace QmlDesigner { + +static ModelNode createNodeFromNode(const ModelNode &modelNode,const QHash<QString, QString> &idRenamingHash, AbstractView *view); + +static QString fixExpression(const QString &expression, const QHash<QString, QString> &idRenamingHash) +{ + QString newExpression = expression; + foreach (const QString &id, idRenamingHash.keys()) { + if (newExpression.contains(id)) + newExpression = newExpression.replace(id, idRenamingHash.value(id)); + } + return newExpression; +} + +static void syncVariantProperties(ModelNode &outputNode, const ModelNode &inputNode) +{ + foreach (const VariantProperty &variantProperty, inputNode.variantProperties()) { + outputNode.variantProperty(variantProperty.name()) = variantProperty.value(); + } +} + +static void syncBindingProperties(ModelNode &outputNode, const ModelNode &inputNode, const QHash<QString, QString> &idRenamingHash) +{ + foreach (const BindingProperty &bindingProperty, inputNode.bindingProperties()) { + outputNode.bindingProperty(bindingProperty.name()).setExpression(fixExpression(bindingProperty.expression(), idRenamingHash)); + } +} + +static void syncId(ModelNode &outputNode, const ModelNode &inputNode, const QHash<QString, QString> &idRenamingHash) +{ + if (!inputNode.id().isNull() && inputNode.id() != "") { + outputNode.setId(idRenamingHash.value(inputNode.id())); + } +} + +static void setupIdRenamingHash(const ModelNode &modelNode, QHash<QString, QString> &idRenamingHash, AbstractView *view) +{ + QList<ModelNode> allNodes(modelNode.allSubModelNodes()); + allNodes.append(modelNode); + foreach (const ModelNode &node, allNodes) { + if (!node.id().isNull() && node.id() != "") { + QString newId = node.id(); + int i = 1; + while (view->hasId(newId) || idRenamingHash.contains(newId)) { + newId = node.id() + QString::number(i).toLower(); + i++; + } + idRenamingHash.insert(node.id(), newId); + } + } +} + +static void syncNodeProperties(ModelNode &outputNode, const ModelNode &inputNode, const QHash<QString, QString> &idRenamingHash, AbstractView *view) +{ + foreach (NodeProperty nodeProperty, inputNode.nodeProperties()) { + ModelNode newNode = createNodeFromNode(nodeProperty.modelNode(), idRenamingHash, view); + outputNode.nodeProperty(nodeProperty.name()).reparentHere(newNode); + } +} + +static void syncNodeListProperties(ModelNode &outputNode, const ModelNode &inputNode, const QHash<QString, QString> &idRenamingHash, AbstractView *view) +{ + foreach (NodeListProperty nodeListProperty, inputNode.nodeListProperties()) { + foreach (const ModelNode &node, nodeListProperty.toModelNodeList()) { + ModelNode newNode = createNodeFromNode(node, idRenamingHash, view); + outputNode.nodeListProperty(nodeListProperty.name()).reparentHere(newNode); + } + } +} + +static ModelNode createNodeFromNode(const ModelNode &modelNode,const QHash<QString, QString> &idRenamingHash, AbstractView *view) +{ + QList<QPair<QString, QVariant> > propertyList; + foreach (const VariantProperty &variantProperty, modelNode.variantProperties()) { + propertyList.append(QPair<QString, QVariant>(variantProperty.name(), variantProperty.value())); + } + ModelNode newNode(view->createModelNode(modelNode.type(),modelNode.majorVersion(),modelNode.minorVersion(), propertyList)); + syncBindingProperties(newNode, modelNode, idRenamingHash); + syncId(newNode, modelNode, idRenamingHash); + syncNodeProperties(newNode, modelNode, idRenamingHash, view); + syncNodeListProperties(newNode, modelNode, idRenamingHash, view); + + return newNode; +} + +ModelNode ModelMerger::insertModel(const ModelNode &modelNode) +{ + RewriterTransaction transaction(view()->beginRewriterTransaction()); + + foreach (const Import &import, modelNode.model()->imports()) + view()->model()->addImport(import); + + QHash<QString, QString> idRenamingHash; + setupIdRenamingHash(modelNode, idRenamingHash, view()); + ModelNode newNode(createNodeFromNode(modelNode, idRenamingHash, view())); + + return newNode; +} + +void ModelMerger::replaceModel(const ModelNode &modelNode) +{ + RewriterTransaction transaction(view()->beginRewriterTransaction()); + + foreach (const Import &import, modelNode.model()->imports()) + view()->model()->addImport(import); + + ModelNode rootNode(view()->rootModelNode()); + + foreach (const QString &propertyName, rootNode.propertyNames()) + rootNode.removeProperty(propertyName); + + QHash<QString, QString> idRenamingHash; + setupIdRenamingHash(modelNode, idRenamingHash, view()); + + syncVariantProperties(rootNode, modelNode); + syncBindingProperties(rootNode, modelNode, idRenamingHash); + syncId(rootNode, modelNode, idRenamingHash); + syncNodeProperties(rootNode, modelNode, idRenamingHash, view()); + syncNodeListProperties(rootNode, modelNode, idRenamingHash, view()); + rootNode.changeType(modelNode.type(), modelNode.majorVersion(), modelNode.minorVersion()); +} + +} //namespace QmlDesigner + diff --git a/src/plugins/qmldesigner/core/model/modelnode.cpp b/src/plugins/qmldesigner/core/model/modelnode.cpp new file mode 100644 index 00000000000..600977f606d --- /dev/null +++ b/src/plugins/qmldesigner/core/model/modelnode.cpp @@ -0,0 +1,943 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include <modelnode.h> +#include <abstractproperty.h> +#include <abstractview.h> +#include <model.h> +#include <metainfo.h> +#include "internalnode_p.h" +#include <QHash> +#include <QTextStream> +#include "invalidargumentexception.h" +#include "invalididexception.h" +#include "invalidmodelnodeexception.h" +#include "invalidpropertyexception.h" +#include "invalidslideindexexception.h" +#include "model_p.h" +#include "abstractview.h" +#include "abstractproperty.h" +#include "variantproperty.h" +#include "bindingproperty.h" +#include "nodeabstractproperty.h" +#include "nodelistproperty.h" +#include "nodeproperty.h" + +namespace QmlDesigner { +using namespace QmlDesigner::Internal; + +/*! +\class QmlDesigner::ModelNode +\ingroup CoreModel +\brief The central class to access the node which can represent a widget, layout + or other items. A Node is a part of a tree and has properties. + +Conceptually ModelNode is an opaque handle to the internal data structures. + +There is always a root model node in every QmlDesigner::Model: +\code +QmlDesigner::Model *model = QmlDesigner::Model::create(); +QmlDesigner::ModelNode rootNode = model->rootNode(); +QmlDesigner::ModelNode childNode = rootNode.addChildNode("QLineEdit"); +\endcode + +You can add a property to a node: +\code +childNode.addProperty("pos", QPoint(2, 12)); +\endcode + +All the manipulation functions are generating undo commands internally. +*/ + + + +/*! \brief internal constructor + +*/ +ModelNode::ModelNode(const InternalNodePointer &internalNode, Model *model, AbstractView *view): + m_internalNode(internalNode), + m_model(model), + m_view(view) +{ + Q_ASSERT(!m_model || m_view); +} + +ModelNode::ModelNode(const ModelNode modelNode, AbstractView *view) + : m_internalNode(modelNode.m_internalNode), + m_model(modelNode.model()), + m_view(view) +{ + +} + +/*! \brief contructs a invalid model node +\return invalid node +\see invalid +*/ +ModelNode::ModelNode(): + m_internalNode(new InternalNode) +{ + +} + +ModelNode::ModelNode(const ModelNode &other): + m_internalNode(other.m_internalNode), + m_model(other.m_model), + m_view(other.m_view) +{ +} + +ModelNode& ModelNode::operator=(const ModelNode &other) +{ + this->m_model = other.m_model; + this->m_internalNode = other.m_internalNode; + this->m_view = other.m_view; + + return *this; +} + +/*! \brief does nothing +*/ +ModelNode::~ModelNode() +{ +} + +QString ModelNode::generateNewId() const +{ + int counter = 1; + QString newId = QString("%1%2").arg(simplifiedTypeName().toLower()).arg(counter); + + while(view()->hasId(newId)) { + counter += 1; + newId = QString("%1%2").arg(simplifiedTypeName().toLower()).arg(counter); + } + + return newId; +} + +/*! \brief returns the name of node which is a short cut to a property like objectName +\return name of the node +*/ +QString ModelNode::id() const +{ + if (!isValid()) { + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + } + + return m_internalNode->id(); +} + +QString ModelNode::validId() +{ + if (id().isEmpty()) + setId(generateNewId()); + + return id(); +} + +static bool idContainsWrongLetter(const QString& id) +{ + static QRegExp idExpr(QLatin1String("[a-zA-Z][a-zA-Z0-9_]*")); + return !idExpr.exactMatch(id); +} + +bool ModelNode::isValidId(const QString &id) +{ + return !(id.isEmpty() || idContainsWrongLetter(id)); +} + +void ModelNode::setId(const QString& id) +{ + if (!isValid()) { + Q_ASSERT_X(isValid(), Q_FUNC_INFO, "model node is invalid"); + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + } + + if (!isValidId(id)) + throw InvalidIdException(__LINE__, __FUNCTION__, __FILE__); + + if (id == ModelNode::id()) + return; + + if (view()->hasId(id)) + throw InvalidIdException(__LINE__, __FUNCTION__, __FILE__); + + m_model.data()->m_d->changeNodeId(internalNode(), id); +} + +/*! \brief the fully-qualified type name of the node is represented as string +\return type of the node as a string +*/ +QString ModelNode::type() const +{ + if (!isValid()) { + Q_ASSERT_X(isValid(), Q_FUNC_INFO, "model node is invalid"); + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + } + return m_internalNode->type(); +} + +/*! \brief set the fully-qualified type name of the node +*/ +void ModelNode::changeType(const QString &type, int majorVersion, int minorVersion) +{ + if (!isValid()) { + Q_ASSERT_X(isValid(), Q_FUNC_INFO, "model node is invalid"); + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + } + + m_model.data()->m_d->changeType(internalNode(), type, majorVersion, minorVersion); +} + +/*! \brief minor number of the Qml Type +\return minor number +*/ +int ModelNode::minorVersion() const +{ + if (!isValid()) { + Q_ASSERT_X(isValid(), Q_FUNC_INFO, "model node is invalid"); + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + } + return m_internalNode->minorVersion(); +} + +/*! \brief major number of the Qml Type +\return major number +*/ +int ModelNode::majorVersion() const +{ + if (!isValid()) { + Q_ASSERT_X(isValid(), Q_FUNC_INFO, "model node is invalid"); + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + } + return m_internalNode->majorVersion(); +} + + +/*! \return the short-hand type name of the node. */ +QString ModelNode::simplifiedTypeName() const +{ + if (!isValid()) { + Q_ASSERT_X(isValid(), Q_FUNC_INFO, "model node is invalid"); + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + } + + return type().split("/").last(); +} + +/*! \brief Returns whether the node is valid + +A node is valid if its model still exists, and contains this node. +Also, the current state must be a valid one. + +A node might become invalid if e.g. it or one of its ancestors is deleted. + +\return is a node valid(true) or invalid(false) +*/ +bool ModelNode::isValid() const +{ + return !m_model.isNull() && !m_view.isNull() && m_internalNode &&m_internalNode->isValid(); +} + +/*! + \brief Returns whether the root node of the model is one of the anchestors of this node. + + Will return true also for the root node itself. + */ +bool ModelNode::isInHierarchy() const +{ + if (!isValid()) { + Q_ASSERT_X(isValid(), Q_FUNC_INFO, "model node is invalid"); + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + } + if (isRootNode()) + return true; + if (!hasParentProperty()) + return false; + return parentProperty().parentModelNode().isInHierarchy(); +} + +/*! + \brief Returns the property containing this node + + The NodeAbstractProperty is invalid if this ModelNode has no parent. + NodeAbstractProperty can be a NodeProperty containing a single ModelNode, or + a NodeListProperty. + + \return the property containing this ModelNode + */ +NodeAbstractProperty ModelNode::parentProperty() const +{ + if (!isValid()) { + Q_ASSERT_X(isValid(), Q_FUNC_INFO, "model node is invalid"); + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + } + if (m_internalNode->parentProperty().isNull()) + return NodeAbstractProperty(); + + return NodeAbstractProperty(m_internalNode->parentProperty()->name(), m_internalNode->parentProperty()->propertyOwner(), m_model.data(), view()); +} + + +/*! \brief the command id is used to compress the some commands together. +\param newParentNode parent of this node will be set to this node +\param commandId integer which is used to descripe commands which should compressed together to one command + +For example: +\code +node.setParentNode(parentNode1); +node.setParentNode(parentNode2, 212); +node.setParentNode(parentNode3, 212); +model->undoStack()->undo(); +ModelNode parentNode4 = node.parentProperty().parentModelNode(); +parentNode4 == parentNode1; -> true +\endcode + +\see parentNode childNodes addChildNode hasChildNodes Model::undo + +*/ + +void ModelNode::setParentProperty(NodeAbstractProperty parent) +{ + if (!isValid()) { + Q_ASSERT_X(isValid(), Q_FUNC_INFO, "model node is invalid"); + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + } + + if (!parent.parentModelNode().isValid()) { + throw InvalidArgumentException(__LINE__, __FUNCTION__, __FILE__, "newParentNode"); + } + + if (*this == parent.parentModelNode()) { + Q_ASSERT_X(*this != parent.parentModelNode(), Q_FUNC_INFO, "cannot set parent to itself"); + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + } + + if (parent == parentProperty()) + return; + + parent.reparentHere(*this); +} + +void ModelNode::setParentProperty(const ModelNode &newParentNode, const QString &propertyName) +{ + setParentProperty(newParentNode.nodeAbstractProperty(propertyName)); +} + +/*! \brief test if there is a parent for this node +\return true is this node has a parent +\see childNodes parentNode setParentNode addChildNode hasChildNodes Model::undo +*/ +bool ModelNode::hasParentProperty() const +{ + if (!isValid()) { + Q_ASSERT_X(isValid(), Q_FUNC_INFO, "model node is invalid"); + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + } + + if (m_internalNode->parentProperty().isNull()) + return false; + + return true; +} + +/*! + \brief Returns a BindingProperty + + Note that a valid BindingProperty is returned, if the ModelNode is valid, + even if this property does not exist or is not a BindingProperty. + Assigning an expression to this BindingProperty will create the property. + + \return BindingProperty named name + */ + +BindingProperty ModelNode::bindingProperty(const QString &name) const +{ + if (!isValid()) + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + return BindingProperty(name, m_internalNode, model(), view()); +} + + +/*! + \brief Returns a NodeProperty + + Note that a valid NodeProperty is returned, if the ModelNode is valid, + even if this property does not exist or is not a NodeProperty. + Assigning a ModelNode to this NodeProperty will create the property. + + \return NodeProperty named name + */ + +NodeProperty ModelNode::nodeProperty(const QString &name) const +{ + if (!isValid()) + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + return NodeProperty(name, m_internalNode, model(), view()); +} + + +/*! + \brief Returns a NodeListProperty + + Note that a valid NodeListProperty is returned, if the ModelNode is valid, + even if this property does not exist or is not a NodeListProperty. + Assigning a ModelNode to this NodeListProperty will create the property. + + \return NodeListProperty named name + */ + +NodeListProperty ModelNode::nodeListProperty(const QString &name) const +{ + if (!isValid()) + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + return NodeListProperty(name, m_internalNode, model(), view()); +} + +NodeAbstractProperty ModelNode::nodeAbstractProperty(const QString &name) const +{ + if (!isValid()) + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + return NodeAbstractProperty(name, m_internalNode, model(), view()); +} + + +/*! + \brief Returns a VariantProperty + + Note that a valid VariantProperty is returned, if the ModelNode is valid, + even if this property does not exist or is not a VariantProperty. + Assigning a value to this VariantProperty will create the property. + + \return VariantProperty named name + */ + +VariantProperty ModelNode::variantProperty(const QString &name) const +{ + if (!isValid()) + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + return VariantProperty(name, m_internalNode, model(), view()); +} + +AbstractProperty ModelNode::property(const QString &name) const +{ + if (!isValid()) + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + return AbstractProperty(name, m_internalNode, model(), view()); +} + +/*! \brief returns a property +\param name name of the property +\return returns a node property handle. If the property is not set yet, the node property is still valid (lazy reference). + +It is searching only in the local Property. + +\see addProperty changePropertyValue removeProperty properties hasProperties +*/ + +/*! \brief returns a list of all properties +\return list of all properties + +The list of properties + +*/ +QList<AbstractProperty> ModelNode::properties() const +{ + if (!isValid()) + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + QList<AbstractProperty> propertyList; + + foreach (const QString &propertyName, internalNode()->propertyNameList()) { + AbstractProperty property(propertyName, internalNode(), model(), view()); + propertyList.append(property); + } + + return propertyList; +} + + +/*! \brief returns a list of all VariantProperties +\return list of all VariantProperties + +The list of all properties containing just an atomic value. + +*/ +QList<VariantProperty> ModelNode::variantProperties() const +{ + QList<VariantProperty> propertyList; + + foreach (const AbstractProperty &abstractProperty, properties()) + if (abstractProperty.isVariantProperty()) + propertyList.append(abstractProperty.toVariantProperty()); + return propertyList; +} + +QList<NodeAbstractProperty> ModelNode::nodeAbstractProperties() const +{ + QList<NodeAbstractProperty> propertyList; + + foreach (const AbstractProperty &nodeAbstractProperty, properties()) + if (nodeAbstractProperty.isNodeAbstractProperty()) + propertyList.append(nodeAbstractProperty.toNodeAbstractProperty()); + return propertyList; +} + +QList<NodeProperty> ModelNode::nodeProperties() const +{ + QList<NodeProperty> propertyList; + + foreach (const AbstractProperty &nodeProperty, properties()) + if (nodeProperty.isNodeProperty()) + propertyList.append(nodeProperty.toNodeProperty()); + return propertyList; +} + +QList<NodeListProperty> ModelNode::nodeListProperties() const +{ + QList<NodeListProperty> propertyList; + + foreach (const AbstractProperty &nodeListProperty, properties()) + if (nodeListProperty.isNodeListProperty()) + propertyList.append(nodeListProperty.toNodeListProperty()); + return propertyList; +} + + +/*! \brief returns a list of all BindingProperties +\return list of all BindingProperties + +The list of all properties containing an expression. + +*/ +QList<BindingProperty> ModelNode::bindingProperties() const +{ + QList<BindingProperty> propertyList; + + foreach (const AbstractProperty &bindingProperty, properties()) + if (bindingProperty.isBindingProperty()) + propertyList.append(bindingProperty.toBindingProperty()); + return propertyList; +} + +/*! +\brief removes a property from this node +\param name name of the property + +Does nothing if the node state does not set this property. + +\see addProperty property properties hasProperties +*/ +void ModelNode::removeProperty(const QString &name) +{ + if (!isValid()) + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + model()->m_d->checkPropertyName(name); + + if (internalNode()->hasProperty(name)) + model()->m_d->removeProperty(internalNode()->property(name)); +} + + + +/*! \brief creates a new node and add it as child to this node +\param nodeTypeString type name of the child node +\param propertyList list of properties +\return the new created model node + +*/ +ModelNode ModelNode::addChildNode(const QString &nodeTypeString, int majorVersion, int minorVersion, const QString &propertyName, const PropertyListType &propertyList) +{ + if (!isValid()) { + Q_ASSERT_X(isValid(), Q_FUNC_INFO, "model node is invalid"); + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + } + + ModelNode newNode(view()->createModelNode(nodeTypeString, majorVersion, minorVersion, propertyList)); + + newNode.setParentProperty(nodeListProperty(propertyName)); + + return newNode; +} + +/*! \brief removes this node from the node tree +*/ + +static QList<ModelNode> descendantNodes(const ModelNode &parent) +{ + QList<ModelNode> descendants(parent.allDirectSubModelNodes()); + foreach (const ModelNode &child, parent.allDirectSubModelNodes()) { + descendants += descendantNodes(child); + } + return descendants; +} + +static void removeModelNodeFromSelection(const ModelNode &node) +{ + { // remove nodes from the active selection: + QList<ModelNode> selectedList = node.view()->selectedModelNodes(); + + foreach (const ModelNode &childModelNode, descendantNodes(node)) + selectedList.removeAll(childModelNode); + selectedList.removeAll(node); + + node.view()->setSelectedModelNodes(selectedList); + } +} + + +/*! \brief complete removes this ModelNode from the Model + +*/ +void ModelNode::destroy() +{ + if (!isValid()) { + Q_ASSERT_X(isValid(), Q_FUNC_INFO, "model node is invalid"); + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + } + + if (isRootNode()) { + throw InvalidArgumentException(__LINE__, __FUNCTION__, __FILE__, "rootNode"); + } + + removeModelNodeFromSelection(*this); + model()->m_d->removeNode(internalNode()); +} +//\} + +/*! \name Property Manipulation + * This methodes interact with properties. + */ + + +/*! + \brief Returns if the the two nodes reference the same entity in the same model + */ +bool operator ==(const ModelNode &firstNode, const ModelNode &secondNode) +{ + if (firstNode.m_internalNode.isNull() || secondNode.m_internalNode.isNull()) { + Q_ASSERT_X(0, Q_FUNC_INFO, "model node is invalid"); + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + } + + return firstNode.m_internalNode.data() == secondNode.m_internalNode.data(); +} + +/*! + \brief Returns if the the two nodes do not reference the same entity in the same model + */ +bool operator !=(const ModelNode &firstNode, const ModelNode &secondNode) +{ + if (firstNode.m_internalNode.isNull() || secondNode.m_internalNode.isNull()) { + Q_ASSERT_X(0, Q_FUNC_INFO, "model node is invalid"); + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + } + + return firstNode.m_internalNode != secondNode.m_internalNode; +} + +bool operator <(const ModelNode &firstNode, const ModelNode &secondNode) +{ + if (firstNode.m_internalNode.isNull() || secondNode.m_internalNode.isNull()) { + Q_ASSERT_X(0, Q_FUNC_INFO, "model node is invalid"); + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + } + + return firstNode.internalNode().data() < secondNode.internalNode().data(); +} + + +Internal::InternalNodePointer ModelNode::internalNode() const +{ + if (!isValid()) { + Q_ASSERT_X(isValid(), Q_FUNC_INFO, "model node is invalid"); + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + } + return m_internalNode; +} + + +uint qHash(const ModelNode &node) +{ +// if (!node.isValid()) { +// Q_ASSERT_X(node.isValid(), Q_FUNC_INFO, "model node is invalid"); +// throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); +// } + return ::qHash(node.m_internalNode.data()); +} + +/*! +\brief returns the model of the node +\return returns the model of the node +*/ +Model *ModelNode::model() const +{ + return m_model.data(); +} + +/*! +\brief returns the view of the node +Each ModelNode belongs to one specific view. +\return view of the node +*/ +AbstractView *ModelNode::view() const +{ + return m_view.data(); +} + + +/*! +\brief returns all ModelNodes that are direct children of this ModelNode +The list contains every ModelNode that belongs to one of this ModelNodes +properties. +\return a list of all ModelNodes that are direct children +*/ +const QList<ModelNode> ModelNode::allDirectSubModelNodes() const +{ + return toModelNodeList(internalNode()->allDirectSubNodes(), view()); +} + + +/*! +\brief returns all ModelNodes that are direct or indirect children of this ModelNode +The list contains every ModelNode that is a direct or indirect child of this ModelNode. +All children in this list will be implicitly removed if this ModelNode is destroyed. +\return a list of all ModelNodes that are direct or indirect children +*/ + +const QList<ModelNode> ModelNode::allSubModelNodes() const +{ + return toModelNodeList(internalNode()->allSubNodes(), view()); +} + +/*! +\brief returns if this ModelNode has any child ModelNodes. + +\return if this ModelNode has any child ModelNodes +*/ + +bool ModelNode::hasAnySubModelNodes() const +{ + return !nodeAbstractProperties().isEmpty(); +} + +/*! \brief returns the meta info of the node +\return meta info of the node +*/ +const NodeMetaInfo ModelNode::metaInfo() const +{ + if (!isValid()) { + Q_ASSERT_X(isValid(), Q_FUNC_INFO, "model node is invalid"); + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + } + return model()->metaInfo().nodeMetaInfo(type(), majorVersion(), minorVersion()); +} + +/*! \brief has a node the selection of the model +\return true if the node his selection +*/ +bool ModelNode::isSelected() const +{ + if (!isValid()) { + Q_ASSERT_X(isValid(), Q_FUNC_INFO, "model node is invalid"); + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + } + return view()->selectedModelNodes().contains(ModelNode(m_internalNode, m_model.data(), view())); +} + +/*! \briefis this node the root node of the model +\return true if it is the root node +*/ +bool ModelNode::isRootNode() const +{ + if (!isValid()) { + Q_ASSERT_X(isValid(), Q_FUNC_INFO, "model node is invalid"); + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + } + return view()->rootModelNode() == *this; +} + +/*! \brief returns the list of all property names +\return list of all property names set in this state. + +The list of properties set in this state. + +\see addProperty property changePropertyValue removeProperty hasProperties +*/ +QStringList ModelNode::propertyNames() const +{ + if (!isValid()) + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + return internalNode()->propertyNameList(); +} + +/*! \brief test a if a property is set for this node +\return true if property a property ins this or a ancestor state exists +*/ +bool ModelNode::hasProperty(const QString &name) const +{ + if (!isValid()) + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + return internalNode()->hasProperty(name); +} + +bool ModelNode::hasVariantProperty(const QString &name) const +{ + return hasProperty(name) && internalNode()->property(name)->isVariantProperty(); +} + +bool ModelNode::hasBindingProperty(const QString &name) const +{ + return hasProperty(name) && internalNode()->property(name)->isBindingProperty(); +} + +bool ModelNode::hasNodeAbstracProperty(const QString &name) const +{ + return hasProperty(name) && internalNode()->property(name)->isNodeAbstractProperty(); +} + +bool ModelNode::hasNodeProperty(const QString &name) const +{ + return hasProperty(name) && internalNode()->property(name)->isNodeProperty(); +} + +bool ModelNode::hasNodeListProperty(const QString &name) const +{ + return hasProperty(name) && internalNode()->property(name)->isNodeListProperty(); +} + +static bool recursiveAncestor(const ModelNode &possibleAncestor, const ModelNode &node) +{ + if (node.hasParentProperty()) { + if (node.parentProperty().parentModelNode() == possibleAncestor) + return true; + return recursiveAncestor(possibleAncestor, node.parentProperty().parentModelNode()); + } + + return false; +} + +bool ModelNode::isAncestorOf(const ModelNode &node) const +{ + return recursiveAncestor(*this, node); +} + +QDebug operator<<(QDebug debug, const ModelNode &modelNode) +{ + if (modelNode.isValid()) { + debug.nospace() << "ModelNode(" + << modelNode.internalNode().data() << ", " + << modelNode.type() << ", " + << modelNode.id() <<")"; + } else { + debug.nospace() << "ModelNode(invalid)"; + } + + return debug.space(); +} + +QTextStream& operator<<(QTextStream &stream, const ModelNode &modelNode) +{ + if (modelNode.isValid()) { + stream << "ModelNode(" + << "type: " << modelNode.type() << ", " + << "id: " << modelNode.id() <<")"; + } else { + stream << "ModelNode(invalid)"; + } + + return stream; +} + +void ModelNode::selectNode() +{ + if (!isValid()) { + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + } + + QList<ModelNode> selectedNodeList; + selectedNodeList.append(*this); + + view()->setSelectedModelNodes(selectedNodeList); +} + +void ModelNode::deselectNode() +{ + if (!isValid()) { + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + } + + QList<ModelNode> selectedNodeList(view()->selectedModelNodes()); + selectedNodeList.removeAll(*this); + + view()->setSelectedModelNodes(selectedNodeList); +} + +int ModelNode::variantUserType() +{ + return qMetaTypeId<ModelNode>(); +} + +QVariant ModelNode::toVariant() const +{ + return QVariant::fromValue(*this); +} + +QVariant ModelNode::auxiliaryData(const QString &name) const +{ + if (!isValid()) { + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + } + + return internalNode()->auxiliaryData(name); +} + +void ModelNode::setAuxiliaryData(const QString &name, const QVariant &data) +{ + m_model.data()->m_d->setAuxiliaryData(internalNode(), name, data); +} + +bool ModelNode::hasAuxiliaryData(const QString &name) const +{ + if (!isValid()) { + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + } + + return internalNode()->hasAuxiliaryData(name); +} + +} diff --git a/src/plugins/qmldesigner/core/model/modelnodepositionrecalculator.cpp b/src/plugins/qmldesigner/core/model/modelnodepositionrecalculator.cpp new file mode 100644 index 00000000000..172409b06a9 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/modelnodepositionrecalculator.cpp @@ -0,0 +1,100 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include <QDebug> +#include "modelnodepositionrecalculator.h" + +using namespace QmlDesigner; +using namespace QmlDesigner::Internal; + +void ModelNodePositionRecalculator::connectTo(TextModifier *textModifier) +{ + Q_ASSERT(textModifier); + + connect(textModifier, SIGNAL(moved(const TextModifier::MoveInfo &)), this, SLOT(moved(const TextModifier::MoveInfo &))); + connect(textModifier, SIGNAL(replaced(int,int,int)), this, SLOT(replaced(int,int,int))); +} + +void ModelNodePositionRecalculator::moved(const TextModifier::MoveInfo &moveInfo) +{ + const int from = moveInfo.objectStart; + const int to = moveInfo.destination; + const int length = moveInfo.objectEnd - moveInfo.objectStart; + const int prefixLength = moveInfo.prefixToInsert.length(); + const int suffixLength = moveInfo.suffixToInsert.length(); + + foreach (const ModelNode &node, m_nodesToTrack) { + const int nodeLocation = m_positionStore->nodeOffset(node); + + if (nodeLocation == ModelNodePositionStorage::INVALID_LOCATION) + continue; + + if (from <= nodeLocation && moveInfo.objectEnd > nodeLocation) { + if (to > from) + if (length == (to - from)) + m_positionStore->setNodeOffset(node, nodeLocation + prefixLength - moveInfo.leadingCharsToRemove); + else + m_positionStore->setNodeOffset(node, to - length + nodeLocation - from + prefixLength - moveInfo.leadingCharsToRemove - moveInfo.trailingCharsToRemove); + else + m_positionStore->setNodeOffset(node, to + nodeLocation - from + prefixLength); + } else if (from < nodeLocation && to > nodeLocation) { + m_positionStore->setNodeOffset(node, nodeLocation - length - moveInfo.leadingCharsToRemove - moveInfo.trailingCharsToRemove); + } else if (from > nodeLocation && to <= nodeLocation) { + m_positionStore->setNodeOffset(node, nodeLocation + length + prefixLength + suffixLength); + } else if (from < nodeLocation && to <= nodeLocation) { + m_positionStore->setNodeOffset(node, nodeLocation + prefixLength + suffixLength - moveInfo.leadingCharsToRemove - moveInfo.trailingCharsToRemove); + } + } + + m_dirtyAreas.insert(to - prefixLength, length + prefixLength + suffixLength); +} + +void ModelNodePositionRecalculator::replaced(int offset, int oldLength, int newLength) +{ + Q_ASSERT(offset >= 0); + + const int growth = newLength - oldLength; + if (growth == 0) + return; + + foreach (const ModelNode &node, m_nodesToTrack) { + const int nodeLocation = m_positionStore->nodeOffset(node); + + if (nodeLocation == ModelNodePositionStorage::INVALID_LOCATION) + continue; + + if (offset < nodeLocation || (offset == nodeLocation && oldLength == 0)) { + const int newPosition = nodeLocation + growth; + if (newPosition < 0) + m_positionStore->removeNodeOffset(node); + else + m_positionStore->setNodeOffset(node, newPosition); + } + } +} diff --git a/src/plugins/qmldesigner/core/model/modelnodepositionrecalculator.h b/src/plugins/qmldesigner/core/model/modelnodepositionrecalculator.h new file mode 100644 index 00000000000..abee72afefa --- /dev/null +++ b/src/plugins/qmldesigner/core/model/modelnodepositionrecalculator.h @@ -0,0 +1,71 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef MODELNODEPOSITIONRECALCULATOR_H +#define MODELNODEPOSITIONRECALCULATOR_H + +#include <QMap> +#include <QObject> +#include <QSet> + +#include "modelnode.h" +#include "modelnodepositionstorage.h" +#include "textmodifier.h" + +namespace QmlDesigner { +namespace Internal { + +class ModelNodePositionRecalculator: public QObject +{ + Q_OBJECT + +public: + ModelNodePositionRecalculator(ModelNodePositionStorage *positionStore, const QList<ModelNode> &nodesToTrack): + m_positionStore(positionStore), m_nodesToTrack(nodesToTrack) + { Q_ASSERT(positionStore); } + + void connectTo(TextModifier *textModifier); + + QMap<int,int> dirtyAreas() const + { return m_dirtyAreas; } + +public slots: + void replaced(int offset, int oldLength, int newLength); + void moved(const TextModifier::MoveInfo &moveInfo); + +private: + ModelNodePositionStorage *m_positionStore; + QList<ModelNode> m_nodesToTrack; + QMap<int, int> m_dirtyAreas; +}; + +} // namespace Internal +} // namespace QmlDesigner + +#endif // MODELNODEPOSITIONRECALCULATOR_H diff --git a/src/plugins/qmldesigner/core/model/modelnodepositionstorage.cpp b/src/plugins/qmldesigner/core/model/modelnodepositionstorage.cpp new file mode 100644 index 00000000000..5c800158050 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/modelnodepositionstorage.cpp @@ -0,0 +1,67 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "invalidmodelnodeexception.h" +#include "modelnodepositionstorage.h" + +using namespace QmlDesigner; +using namespace QmlDesigner::Internal; + +void ModelNodePositionStorage::setNodeOffset(const ModelNode &modelNode, int fileOffset) +{ + m_rewriterData[modelNode].setOffset(fileOffset); +} + +void ModelNodePositionStorage::cleanupInvalidOffsets() +{ + QHash<ModelNode, RewriterData> validModelNodes; + + QHashIterator<ModelNode, RewriterData> iter(m_rewriterData); + while (iter.hasNext()) { + iter.next(); + const ModelNode modelNode = iter.key(); + if (modelNode.isValid()) + validModelNodes.insert(modelNode, iter.value()); + } + m_rewriterData = validModelNodes; +} + +int ModelNodePositionStorage::nodeOffset(const ModelNode &modelNode) +{ + QHash<ModelNode, RewriterData>::const_iterator iter = m_rewriterData.find(modelNode); + if (iter == m_rewriterData.end()) + return INVALID_LOCATION; + else + return iter.value().offset(); +} + +QList<ModelNode> ModelNodePositionStorage::modelNodes() const +{ + return m_rewriterData.keys(); +} diff --git a/src/plugins/qmldesigner/core/model/modelrewriter.cpp b/src/plugins/qmldesigner/core/model/modelrewriter.cpp new file mode 100644 index 00000000000..725d45cd69b --- /dev/null +++ b/src/plugins/qmldesigner/core/model/modelrewriter.cpp @@ -0,0 +1,510 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include <QCoreApplication> + +#include "copyhelper.h" +#include "invalidmodelnodeexception.h" +#include "invalidpropertyexception.h" +#include "internalnode_p.h" +#include "modelrewriter.h" +#include "modificationgroupexception.h" +#include "nodeproperty.h" +#include "paster.h" +#include "textmodifier.h" +#include "textlocation.h" + +/*! +\defgroup Rewriting +*/ +/*! +\class QmlDesigner::Internal::ModelRewriter +\ingroup Rewriting +\brief The ModelRewriter class is the facade interface to all rewriting classes for QML files. + +The rewriting consists of two phases. First, any changes are applied to the QML source, as held by the TextModifier. This is done by the +TextToModelMerger. After that, the QTextDocument of the TextModifier will fire a textChanged() signal, which triggers the second phase. In this +phase, the ModelRewriter will use the ModelToTextMerger to parse the QML (using QmlDocDocument) and change the Model according to the new input. + +The strategy of the Model is to first apply the changes to the datastructures itself, and then tell the ModelRewriter to apply the changes to the QML +source file. By doing it in this order, the second phase of the rewriting will usually not make any changes to the Model. + +However, as with any good rule, there are two exceptions. The most common one is that the QTextDocument of the TextModifier is also used by a text +editor. When the user changes anything in the QML source text using that editor, reparsing will be triggered too, and the Model will be changed to +reflect the user's changes. The second exception to the rule is that an error can occur while re-parsing the QML. (Note that this can also happen +due to a change by a text editor.) In this case, the TextToModelMerger will try to revert the model to the last known correct state. + +The ModelRewriter has one requirement to do the application of changes to the text correctly. It assumes that every change is coming in separately. +This means, when, for example, addNode for a rectangle is called, only "Rectangle {}" is added to the QML source text. If the node has properties set, +the rewriting will not magically decide that these need to be added. So to add a rectangle on a certain position with a certain size, the +appropriate calls to addProperty will have to be made too. If these changes need to be made in one "bigger" change to the QML source text, then a +modification group can be created. All the changes in the modification group are applied to the QML source text in such a way that the underlying +QTextDocument will see it as one single change. + +\see QmlDesigner::Model +*/ + +namespace QmlDesigner { +namespace Internal { + +ModelRewriter::ModelRewriter(Model* model, TextModifier* textModifier): + m_subcomponentManager(model->metaInfo()), + m_textModifier(textModifier), + m_modelToTextMerger(model), + m_textToModelMerger(model, &m_subcomponentManager), + m_lastRewriteFailed(false) +{ + Q_ASSERT(model); + Q_ASSERT(textModifier); + + connect(m_textModifier, SIGNAL(textChanged()), this, SLOT(mergeChanges())); +} + +/*! + Starting point for the second phase of the rewriting: this will parse the QML source text, and apply any changes to the Model. + + \param errors A pointer to a list which can hold the QmlError objects which describe errors while parsing the QML source text. Passing + <code>null</code> will leave all errors unreported. + */ +bool ModelRewriter::reloadAndMerge(QList<QmlError> *errors) +{ + if (QCoreApplication::arguments().contains("--no-resync")) + return false; + + QByteArray newData = m_textModifier->text().toUtf8(); + + if (m_textToModelMerger.reloadAndMerge(newData, errors)) { + m_lastCorrectData = newData; + return true; + } else { + m_textToModelMerger.reloadAndMerge(m_lastCorrectData, 0); + return false; + } +} + +/*! + Starting point for the second phase of the rewriting: this will parse the QML source text, and apply any changes to the Model. + + \param url The URL of the input data, used to load any sub-components, and used for reporting parser errors. + \param errors A pointer to a list which can hold the QmlError objects which describe errors while parsing the QML source text. Passing + <code>null</code> will leave all errors unreported. + */ +bool ModelRewriter::load(const QUrl &url, QList<QmlError> *errors) +{ + QByteArray newData = m_textModifier->text().toUtf8(); + + if (m_textToModelMerger.load(newData, url, errors)) { + m_lastCorrectData = newData; + return true; + } else { + return false; + } +} + +/*! + Sets the URL of the input file, which is used to report parser errors, and to load sub-components. + + \note Changing the file URL will force a re-load of all sub-components. + */ +void ModelRewriter::setFileUrl(const QUrl& url) +{ + m_textToModelMerger.setFileUrl(url); +} + +/** + Changes the value of a property in the given state in the QML source text. + + \param state The state for which the property is changed. + \param name The name of the property to change. + \param value The new value for the property. + + \note Changing a property value can only be done after the property has been added to that state. + \see {@link addProperty} + */ +void ModelRewriter::changePropertyValue(const InternalNodeState::Pointer& state, const QString& name, const QVariant& value) +{ + if (state->modelNode()->isReadOnly()) + return; //the rewriter ignores read only nodes + if (state.isNull() || !state->isValid()) + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + if (!modificationGroupActive() && !state->location().isValid()) + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + m_modelToTextMerger.changePropertyValue(state, name, value); + + if (!modificationGroupActive()) + m_modelToTextMerger.applyChanges(*m_textModifier); +} + +/** + Adds a property with the given value to the given state in the QML source text. + + \param state The state to which the property is added. + \param name The name of the property. + \param value The value for the new property. + + \see {@link changePropertyValue} + */ +void ModelRewriter::addProperty(const InternalNodeState::Pointer& state, const QString& name, const QVariant& value) +{ + if (state.isNull() || !state->isValid()) + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + +// qDebug() << "add property" << name << "in node" << state->modelNode()->id() << "for state" << state->modelState()->name(); + + m_modelToTextMerger.addProperty(state, name, value); + + if (!modificationGroupActive()) + m_modelToTextMerger.applyChanges(*m_textModifier); +} + +/** + Removes a property from the given state in the QML source text. + + \param state The state from which to remove the property. + \param name The name of the property to remove. + */ +void ModelRewriter::removeProperty(const InternalNodeState::Pointer& state, const QString& name) +{ + if (state.isNull() || !state->isValid()) + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + +// qDebug() << "remove property" << name << "in node" << state->modelNode()->id() << "for state" << state->modelState()->name(); + + m_modelToTextMerger.removeProperty(state, name); + + if (!modificationGroupActive()) + m_modelToTextMerger.applyChanges(*m_textModifier); +} + +void ModelRewriter::changeNodeId(const InternalNodePointer& node, const QString& id) +{ + if (node.isNull() || !node->isValid()) + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + m_modelToTextMerger.changeNodeId(node, id); + + if (!modificationGroupActive()) + m_modelToTextMerger.applyChanges(*m_textModifier); +} + +/** + Move the node after the given afterNode. + + \param node the node to move + \param afterNode the node before which the node is put. Pass in InternalNodePointer() to move the node to the end. + */ +void ModelRewriter::moveNodeBefore(const InternalNodePointer& node, const InternalNodePointer& beforeNode) +{ + if (node.isNull() || !node->isValid()) + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + if (!modificationGroupActive() && !node->baseNodeState()->location().isValid()) + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + if (!beforeNode.isNull() && beforeNode->isValid()) + if (!modificationGroupActive() && !beforeNode->baseNodeState()->location().isValid()) + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + m_modelToTextMerger.moveNodeBefore(node, beforeNode); + + if (!modificationGroupActive()) + m_modelToTextMerger.applyChanges(*m_textModifier); +} + +void ModelRewriter::addModelState(const InternalModelState::Pointer &state) +{ + if (state.isNull() || !state->isValid()) + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + m_modelToTextMerger.addModelState(state); + + if (!modificationGroupActive()) + m_modelToTextMerger.applyChanges(*m_textModifier); +} + +void ModelRewriter::removeModelState(const InternalModelState::Pointer &state) +{ + m_modelToTextMerger.removeModelState(state); + + if (!modificationGroupActive()) + m_modelToTextMerger.applyChanges(*m_textModifier); +} + +void ModelRewriter::setStateName(const InternalModelStatePointer &state, const QString &name) +{ + if (state.isNull() || !state->isValid()) + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + if (!modificationGroupActive() && !state->location().isValid()) + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + m_modelToTextMerger.setStateName(state, name); + + if (!modificationGroupActive()) + m_modelToTextMerger.applyChanges(*m_textModifier); +} + +void ModelRewriter::setStateWhenCondition(const InternalModelStatePointer &state, const QString &whenCondition) +{ + if (state.isNull() || !state->isValid()) + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + if (!modificationGroupActive() && !state->location().isValid()) + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + m_modelToTextMerger.setStateWhenCondition(state, whenCondition); + + if (!modificationGroupActive()) + m_modelToTextMerger.applyChanges(*m_textModifier); +} + +void ModelRewriter::setAnchor(const InternalNodeState::Pointer &state, const QString &propertyName, const QVariant &value) +{ + if (state.isNull() || !state->isValid()) + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + if (state->propertyLocation(propertyName).isValid()) + m_modelToTextMerger.changePropertyValue(state, propertyName, value); + else + m_modelToTextMerger.addProperty(state, propertyName, value); + + if (!modificationGroupActive()) + m_modelToTextMerger.applyChanges(*m_textModifier); +} + +void ModelRewriter::removeAnchor(const InternalNodeState::Pointer &state, const QString &propertyName) +{ + if (state.isNull() || !state->isValid()) + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + m_modelToTextMerger.removeProperty(state, propertyName); + + if (!modificationGroupActive()) + m_modelToTextMerger.applyChanges(*m_textModifier); +} + +void ModelRewriter::setAnchorMargin(const InternalNodeState::Pointer &state, const QString &propertyName, const QVariant &value) +{ + if (state.isNull() || !state->isValid()) + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + if (state->propertyLocation(propertyName).isValid()) { + if (value.toDouble() == 0.0 && state->isBaseState()) + m_modelToTextMerger.removeProperty(state, propertyName); + else + m_modelToTextMerger.changePropertyValue(state, propertyName, value); + } else { + if (value.toDouble() == 0.0 && state->isBaseState()) { + // do nothing + } else { + m_modelToTextMerger.addProperty(state, propertyName, value); + } + } + + if (!modificationGroupActive()) + m_modelToTextMerger.applyChanges(*m_textModifier); +} + +void ModelRewriter::addNode(const InternalNodePointer& newNode, const InternalNodePointer& parentNode) +{ + if (newNode.isNull() || !newNode->isValid()) + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + if (!modificationGroupActive() && !parentNode->baseNodeState()->location().isValid()) + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + m_modelToTextMerger.addNode(newNode, parentNode); + + if (!modificationGroupActive()) + m_modelToTextMerger.applyChanges(*m_textModifier); +} + +void ModelRewriter::removeNode(const InternalNodePointer &node) +{ + if (node.isNull() || !node->isValid()) + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + if (!modificationGroupActive() && !node->baseNodeState()->location().isValid()) + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + m_modelToTextMerger.removeNode(node); + + if (!modificationGroupActive()) + m_modelToTextMerger.applyChanges(*m_textModifier); +} + +void ModelRewriter::reparentNode(const InternalNodePointer &child, const InternalNodePointer &newParent) +{ + if (child.isNull() || !child->isValid()) + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + if (newParent.isNull() || !newParent->isValid()) + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + if (!modificationGroupActive() && !newParent->baseNodeState()->location().isValid()) + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + m_modelToTextMerger.reparentNode(child, newParent); + + if (!modificationGroupActive()) + m_modelToTextMerger.applyChanges(*m_textModifier); +} + +ModificationGroupToken ModelRewriter::beginModificationGroup() +{ + ModificationGroupToken newToken(m_activeModificationGroups.size()); + m_activeModificationGroups.append(newToken); + + return newToken; +} + +void ModelRewriter::endModificationGroup(const ModificationGroupToken& token) +{ + if (m_activeModificationGroups.isEmpty()) { + throw ModificationGroupException(__LINE__, Q_FUNC_INFO, __FILE__); + } + + if (m_activeModificationGroups.last() != token) { + throw ModificationGroupException(__LINE__, Q_FUNC_INFO, __FILE__); + } + + if (!m_activeModificationGroups.removeAll(token)) { + throw ModificationGroupException(__LINE__, Q_FUNC_INFO, __FILE__); + } + + if (!modificationGroupActive()) { + m_modelToTextMerger.applyChanges(*m_textModifier); + } +} + +bool ModelRewriter::lastRewriteFailed() const +{ + return m_lastRewriteFailed; +} + +QMimeData *ModelRewriter::copy(const QList<InternalNodeState::Pointer> &nodeStates) const +{ + if (modificationGroupActive()) + throw ModificationGroupException(__LINE__, Q_FUNC_INFO, __FILE__); + + const QString sourceCode = m_textModifier->text(); + CopyHelper copyHelper(sourceCode); + return copyHelper.createMimeData(nodeStates); +} + +/*! + Inserts the copied data into the given node. + + \note The mime-type for the data must be <tt>"application/x-qt-bauhaus"</tt>, otherwise the paste will fail. + + \param transferData The clip-board contents to paste. + \param intoNode The node into which the clip-board contents are to be pasted. + \return true if successful, false otherwise. + */ +bool ModelRewriter::paste(QMimeData *transferData, const InternalNode::Pointer &intoNode) +{ + if (!transferData) + return false; + + if (intoNode.isNull() || !intoNode->isValid()) + throw InvalidModelNodeException(__LINE__, Q_FUNC_INFO, __FILE__); + + Paster paster(transferData, intoNode); + if (paster.doPaste(m_modelToTextMerger)) { + if (!modificationGroupActive()) { + m_modelToTextMerger.applyChanges(*m_textModifier); + } + return true; + } else { + m_modelToTextMerger.clear(); + return false; + } +} + +/*! + Adds the given import to the QML file, if it doesn't already exist. + + \param import The import to add. + */ +void ModelRewriter::addImport(const Import &import) +{ + m_modelToTextMerger.addImport(import); + + if (!modificationGroupActive()) + m_modelToTextMerger.applyChanges(*m_textModifier); +} + +/*! + Removes the given import from the QML file. + + \param import The import to remove. + */ +void ModelRewriter::removeImport(const Import &import) +{ + m_modelToTextMerger.removeImport(import); + + if (!modificationGroupActive()) + m_modelToTextMerger.applyChanges(*m_textModifier); +} + +/*! + Slot connected to the textChanged() signal of the underlying QTextDocument. This slot will start the second part of the rewriting phase, being + the part where the QML document is parsed and any changes being merged into the Model. + + Any errors that occur during this phase will be reported by emitting the errorDuringRewrite() signal. + */ +void ModelRewriter::mergeChanges() +{ +// qDebug() << "*** Running rewrite..."; + + m_lastRewriteFailed = false; + + QList<QmlError> errors; + if (reloadAndMerge(&errors)) { +// qDebug() << "===> Roundtrip done"; + } else { + qDebug() << "===> Error doing round-trip"; + qDebug() << "new data:" << m_textModifier->text().toUtf8(); + qDebug() << "old data:" << m_lastCorrectData; + m_lastRewriteFailed = true; + emit errorDuringRewrite(errors); + } +} + +/*! + \fn void ModelRewriter::errorDuringRewrite(const QList<QmlError> &errors) + Emitted when an error when reading or merging changes back after the underlying text (-document) changed. + + \note This signal can be emitted after a modification by from within the application, or by a change done by a text editor which uses the same + QTextDocument. + */ + +} +} diff --git a/src/plugins/qmldesigner/core/model/modelrewriter.h b/src/plugins/qmldesigner/core/model/modelrewriter.h new file mode 100644 index 00000000000..8cb34efd38e --- /dev/null +++ b/src/plugins/qmldesigner/core/model/modelrewriter.h @@ -0,0 +1,125 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef MODELREWRITER_H +#define MODELREWRITER_H + +#include <QtCore/QByteArray> +#include <QtCore/QMimeData> +#include <QtCore/QObject> +#include <QtCore/QUrl> + +#include "anchorline.h" +#include "import.h" +#include "modeltotextmerger.h" +#include "model.h" +#include "modelnode.h" +#include "modificationgrouptoken.h" +#include "subcomponentmanager.h" +#include "texttomodelmerger.h" + +namespace QmlDesigner { +class TextModifier; + +namespace Internal { + +class ModelPrivate; + +class ModelRewriter: public QObject +{ + Q_OBJECT + +public: + ModelRewriter(Model* model, TextModifier* textModifier); + + TextModifier *textModifier() const { return m_textModifier; } + + bool load(const QUrl &url, QList<QmlError> *errors = 0); + bool reloadAndMerge(QList<QmlError> *errors = 0); + void setFileUrl(const QUrl& url); + + ModificationGroupToken beginModificationGroup(); + void endModificationGroup(const ModificationGroupToken& token); + + // Property change actions: + void addProperty(const InternalNodeState::Pointer& state, const QString& name, const QVariant& value); + void changePropertyValue(const InternalNodeState::Pointer& state, const QString& name, const QVariant& value); + void removeProperty(const InternalNodeState::Pointer& state, const QString& name); + + // Node change actions: + void addNode(const InternalNodePointer& newNode, const InternalNodePointer& parentNode); + void reparentNode(const InternalNodePointer &child, const InternalNodePointer &newParent); + void removeNode(const InternalNodePointer &node); + void changeNodeId(const InternalNodePointer& node, const QString& id); + void moveNodeBefore(const InternalNodePointer& node, const InternalNodePointer& beforeNode); + + // State change actions: + void addModelState(const InternalModelState::Pointer &state); + void removeModelState(const InternalModelState::Pointer &state); + void setStateName(const InternalModelStatePointer &state, const QString &name); + void setStateWhenCondition(const InternalModelStatePointer &state, const QString &whenCondition); + + // Anchors: + void setAnchor(const InternalNodeState::Pointer &state, const QString &propertyName, const QVariant &value); + void removeAnchor(const InternalNodeState::Pointer &state, const QString &propertyName); + void setAnchorMargin(const InternalNodeState::Pointer &state, const QString &propertyName, const QVariant &value); + + bool lastRewriteFailed() const; + + // Copy & Paste: + QMimeData* copy(const QList<InternalNodeState::Pointer> &nodeStates) const; + bool paste(QMimeData *transferData, const InternalNode::Pointer &intoNode); + + // Imports: + void addImport(const Import &import); + void removeImport(const Import &import); + +public slots: + void mergeChanges(); + +signals: + void errorDuringRewrite(const QList<QmlError> &errors); + +private: + bool modificationGroupActive() const { return !m_activeModificationGroups.isEmpty(); } + +private: + SubComponentManager m_subcomponentManager; + QByteArray m_lastCorrectData; + QList<ModificationGroupToken> m_activeModificationGroups; + TextModifier* m_textModifier; + ModelToTextMerger m_modelToTextMerger; + TextToModelMerger m_textToModelMerger; + bool m_lastRewriteFailed; +}; + +} +} + +#endif //MODELREWRITER_H diff --git a/src/plugins/qmldesigner/core/model/modeltotextmerger.cpp b/src/plugins/qmldesigner/core/model/modeltotextmerger.cpp new file mode 100644 index 00000000000..204c7b211e5 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/modeltotextmerger.cpp @@ -0,0 +1,319 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "modelnodepositionrecalculator.h" +#include "modeltotextmerger.h" +#include "qmltextgenerator.h" +#include "rewriteactioncompressor.h" +#include "rewriterview.h" +#include <filemanager/qmldocument.h> +#include <QDebug> +#include <variantproperty.h> +#include <nodelistproperty.h> +#include <nodeproperty.h> +#include <textmodifier.h> + +#define INDENT_DEPTH 4 +#undef DUMP_REWRITE_ACTIONS + +using namespace QmlDesigner; +using namespace QmlDesigner::Internal; + +ModelToTextMerger::ModelToTextMerger(RewriterView *reWriterView): + m_rewriterView(reWriterView) +{ +} + +void ModelToTextMerger::nodeCreated(const ModelNode &/*createdNode*/) +{ + //the rewriter ignores model nodes outside of the hierachy +} + +void ModelToTextMerger::nodeRemoved(const ModelNode &removedNode, const NodeAbstractProperty &parentProperty, PropertyChangeFlags propertyChange) +{ + if (!isInHierarchy(parentProperty)) + return; + + if (parentProperty.isDefaultProperty()) { + schedule(new RemoveNodeRewriteAction(removedNode)); + } else if (AbstractView::EmptyPropertiesRemoved == propertyChange) { + schedule(new RemovePropertyRewriteAction(parentProperty)); + } else if (parentProperty.isNodeListProperty()) { + schedule(new RemoveNodeRewriteAction(removedNode)); + } +} + +void ModelToTextMerger::propertiesRemoved(const QList<AbstractProperty>& propertyList) +{ + foreach (const AbstractProperty &property, propertyList) { + if (isInHierarchy(property) && !property.isDefaultProperty()) + schedule(new RemovePropertyRewriteAction(property)); + } +} + +void ModelToTextMerger::propertiesChanged(const QList<AbstractProperty>& propertyList, PropertyChangeFlags propertyChange) +{ + foreach (const AbstractProperty &property, propertyList) { + if (!isInHierarchy(property)) + continue; + + ModelNode containedModelNode; + + switch (propertyChange) { + case AbstractView::PropertiesAdded: + if (property.isNodeProperty()) + containedModelNode = property.toNodeProperty().modelNode(); + + schedule(new AddPropertyRewriteAction(property, + QmlTextGenerator(getPropertyOrder(), INDENT_DEPTH)(property), + propertyType(property), + containedModelNode)); + break; + + case AbstractView::NoAdditionalChanges: + if (property.isNodeProperty()) + containedModelNode = property.toNodeProperty().modelNode(); + + schedule(new ChangePropertyRewriteAction(property, + QmlTextGenerator(getPropertyOrder(), INDENT_DEPTH)(property), + propertyType(property), + containedModelNode)); + break; + + case AbstractView::EmptyPropertiesRemoved: + break; + + default: + Q_ASSERT(!"Unknown PropertyChangeFlags"); + } + } +} + +void ModelToTextMerger::nodeTypeChanged(const ModelNode &node,const QString &/*type*/, int /*majorVersion*/, int /*minorVersion*/) +{ + if (!node.isInHierarchy()) + return; + + // TODO: handle the majorVersion and the minorVersion + + schedule(new ChangeTypeRewriteAction(node)); +} + +void ModelToTextMerger::nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent, const NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags propertyChange) +{ + if (isInHierarchy(oldPropertyParent) && isInHierarchy(newPropertyParent)) { // the node is moved + schedule(new ReparentNodeRewriteAction(node, newPropertyParent, propertyType(newPropertyParent))); + } else if (isInHierarchy(oldPropertyParent) && !isInHierarchy(newPropertyParent)) { // the node is removed from hierarchy + if (oldPropertyParent.isNodeProperty()) { + // ignore, the subsequent remove property will take care of all + } else if (oldPropertyParent.isNodeListProperty()) { + if (!oldPropertyParent.isDefaultProperty() && oldPropertyParent.toNodeListProperty().toModelNodeList().size() == 0) { + schedule(new RemovePropertyRewriteAction(oldPropertyParent)); + } else { + schedule(new RemoveNodeRewriteAction(node)); + } + } else { + schedule(new RemoveNodeRewriteAction(node)); + } + } else if (!isInHierarchy(oldPropertyParent) && isInHierarchy(newPropertyParent)) { // the node is inserted into to hierarchy + switch (propertyChange) { + case AbstractView::PropertiesAdded: + schedule(new AddPropertyRewriteAction(newPropertyParent, QmlTextGenerator(getPropertyOrder())(node), propertyType(newPropertyParent), node)); + break; + + case AbstractView::NoAdditionalChanges: + schedule(new ChangePropertyRewriteAction(newPropertyParent, QmlTextGenerator(getPropertyOrder())(node), propertyType(newPropertyParent), node)); + break; + + case AbstractView::EmptyPropertiesRemoved: + break; + + default: + Q_ASSERT(!"Unknown PropertyChange value"); + } + } else { + // old is outside of hierarchy, new is outside of hierarchy, so who cares? + } +} + +void ModelToTextMerger::nodeIdChanged(const ModelNode& node, const QString& newId, const QString& oldId) +{ + if (!node.isInHierarchy()) + return; + + schedule(new ChangeIdRewriteAction(node, oldId, newId)); +} + +void ModelToTextMerger::nodeSlidAround(const ModelNode &movingNode, const ModelNode &inFrontOfNode) +{ + if (!inFrontOfNode.isValid() || movingNode.parentProperty() == inFrontOfNode.parentProperty()) + schedule(new MoveNodeRewriteAction(movingNode, inFrontOfNode)); + else + Q_ASSERT(!"Nodes do not belong to the same containing property"); +} + +RewriterView *ModelToTextMerger::view() +{ + return m_rewriterView; +} + +void ModelToTextMerger::applyChanges() +{ + dumpRewriteActions(QLatin1String("Before compression")); + RewriteActionCompressor compress(getPropertyOrder()); + compress(m_rewriteActions); + dumpRewriteActions(QLatin1String("After compression")); + + if (m_rewriteActions.isEmpty()) + return; + + QmlEditor::QmlDocument::Ptr tmpDocument(QmlEditor::QmlDocument::create(QLatin1String("<ModelToTextMerger>"))); + tmpDocument->setSource(m_rewriterView->textModifier()->text()); + if (!tmpDocument->parse()) { + qDebug() << "*** Possible problem: QML file wasn't parsed correctly."; + qDebug() << "*** QML text:" << m_rewriterView->textModifier()->text(); + return; + } + + TextModifier *textModifier = m_rewriterView->textModifier(); + + try { + ModelNodePositionRecalculator positionRecalculator(m_rewriterView->positionStorage(), m_rewriterView->positionStorage()->modelNodes()); + positionRecalculator.connectTo(textModifier); + + QmlEditor::QmlRefactoring refactoring(tmpDocument, *textModifier, getPropertyOrder()); + + textModifier->deactivateChangeSignals(); + textModifier->startGroup(); + + for (int i = 0; i < m_rewriteActions.size(); ++i) { + if (i != 0) { + textModifier->flushGroup(); + refactoring.reparseDocument(); + } + + RewriteAction* action = m_rewriteActions.at(i); +#ifdef DUMP_REWRITE_ACTIONS + action->dump("Next rewrite action:"); +#endif // DUMP_REWRITE_ACTIONS + ModelNodePositionStorage *positionStore = m_rewriterView->positionStorage(); + const bool success = action->execute(refactoring, *positionStore); + Q_ASSERT(success); + } + qDeleteAll(m_rewriteActions); + m_rewriteActions.clear(); + + textModifier->commitGroup(); + + reindent(positionRecalculator.dirtyAreas()); + + textModifier->reactivateChangeSignals(); + } catch (...) { + textModifier->reactivateChangeSignals(); + + throw; + } +} + +void ModelToTextMerger::reindent(const QMap<int, int> &dirtyAreas) const +{ +// QList<int> offsets = dirtyAreas.keys(); +// qSort(offsets); +// +// foreach (const int offset, offsets) { +// const int length = dirtyAreas[offset]; +// xxxx +// } +} + +void ModelToTextMerger::schedule(RewriteAction *action) +{ + Q_ASSERT(action); + + m_rewriteActions.append(action); +} + +QmlEditor::QmlRefactoring::PropertyType ModelToTextMerger::propertyType(const AbstractProperty &property) +{ + if (property.isBindingProperty()) + return QmlEditor::QmlRefactoring::ObjectBinding; + else if (property.isNodeListProperty()) + return QmlEditor::QmlRefactoring::ArrayBinding; + else if (property.isNodeProperty()) + return QmlEditor::QmlRefactoring::ObjectBinding; + else if (property.isVariantProperty()) + return QmlEditor::QmlRefactoring::ScriptBinding; + + Q_ASSERT(!"cannot convert property type"); + return (QmlEditor::QmlRefactoring::PropertyType) -1; +} + +QStringList ModelToTextMerger::m_propertyOrder; + +QStringList ModelToTextMerger::getPropertyOrder() +{ + if (m_propertyOrder.isEmpty()) { + m_propertyOrder + << QLatin1String("property") + << QLatin1String("id") + << QLatin1String("x") + << QLatin1String("y") + << QLatin1String("width") + << QLatin1String("height") + << QLatin1String("position") + << QLatin1String("color") + << QLatin1String("radius") + << QLatin1String("text") + << QString::null + << QLatin1String("states") + << QLatin1String("transitions") + ; + } + + return m_propertyOrder; +} + +bool ModelToTextMerger::isInHierarchy(const AbstractProperty &property) { + return property.isValid() && property.parentModelNode().isInHierarchy(); +} + +void ModelToTextMerger::dumpRewriteActions(const QString &msg) +{ +#ifdef DUMP_REWRITE_ACTIONS + qDebug() << "---->" << qPrintable(msg); + + foreach (RewriteAction *action, m_rewriteActions) { + action->dump("-----"); + } + + qDebug() << "<----" << qPrintable(msg); +#else // DUMP_REWRITE_ACTIONS + Q_UNUSED(msg); +#endif // DUMP_REWRITE_ACTIONS +} diff --git a/src/plugins/qmldesigner/core/model/modeltotextmerger.h b/src/plugins/qmldesigner/core/model/modeltotextmerger.h new file mode 100644 index 00000000000..3511625a78c --- /dev/null +++ b/src/plugins/qmldesigner/core/model/modeltotextmerger.h @@ -0,0 +1,95 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef MODELTOTEXTMERGER_H +#define MODELTOTEXTMERGER_H + +#include "corelib_global.h" +#include <modelnode.h> +#include "abstractview.h" +#include "nodeabstractproperty.h" +#include "variantproperty.h" +#include "nodelistproperty.h" +#include "bindingproperty.h" +#include "rewriteaction.h" +#include <filemanager/qmlrefactoring.h> +#include <QMap> +#include <QSet> +#include <QHash> +#include <QVariant> + +namespace QmlDesigner { + +class CORESHARED_EXPORT RewriterView; + +namespace Internal { + +class ModelToTextMerger +{ + typedef AbstractView::PropertyChangeFlags PropertyChangeFlags; + static QStringList m_propertyOrder; + +public: + ModelToTextMerger(RewriterView *reWriterView); + void applyChanges(); + + void nodeCreated(const ModelNode &createdNode); + void nodeRemoved(const ModelNode &removedNode, const NodeAbstractProperty &parentProperty, PropertyChangeFlags propertyChange); + void propertiesRemoved(const QList<AbstractProperty>& propertyList); + void propertiesChanged(const QList<AbstractProperty>& propertyList, PropertyChangeFlags propertyChange); + void nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent, const NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags propertyChange); + void nodeIdChanged(const ModelNode& node, const QString& newId, const QString& oldId); + void nodeSlidAround(const ModelNode &movingNode, const ModelNode &inFrontOfNode); + void nodeTypeChanged(const ModelNode &node,const QString &type, int majorVersion, int minorVersion); + +protected: + RewriterView *view(); + + void reindent(const QMap<int,int> &dirtyAreas) const; + + void schedule(RewriteAction *action); + QList<RewriteAction *> scheduledRewriteActions() const + { return m_rewriteActions; } + + static QmlEditor::QmlRefactoring::PropertyType propertyType(const AbstractProperty &property); + static QStringList getPropertyOrder(); + + static bool isInHierarchy(const AbstractProperty &property); + + void dumpRewriteActions(const QString &msg); + +private: + RewriterView *m_rewriterView; + QList<RewriteAction *> m_rewriteActions; +}; + +} //Internal +} //QmlDesigner + +#endif // MODELTOTEXTMERGER_H diff --git a/src/plugins/qmldesigner/core/model/modelutilities.cpp b/src/plugins/qmldesigner/core/model/modelutilities.cpp new file mode 100644 index 00000000000..884d697097c --- /dev/null +++ b/src/plugins/qmldesigner/core/model/modelutilities.cpp @@ -0,0 +1,125 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "modelutilities.h" +#include <metainfo.h> +#include <model.h> +#include "model/propertyparser.h" +#include <QtCore/QDebug> +#include <QtDeclarative/QmlMetaType> +#include <QtCore/QMetaProperty> +#include <widgetqueryview.h> + +namespace QmlDesigner { +using namespace Internal; +namespace ModelUtilities { + + + +bool mustBeProxied(const ModelNode &child, const ModelNode &parent) +{ + return child.metaInfo().isSubclassOf("QWidget") && + (parent.metaInfo().isSubclassOf("QGraphicsItem") || + parent.metaInfo().isSubclassOf("QGraphicsScene")); + //if child widget is a QWidget and the target is either + //QGraphicsItem or QGraphicsScene return true +} + +//ModelNode reparentAndProxymize(const ModelNode &child, const ModelNode &parent) +//{ +// ModelNode oldProxy; +// ModelNode oldParent(parent); +// if (child.parentNode().type() == "QGraphicsProxyWidget") +// oldProxy = child.parentNode(); +// if (mustBeProxied(child, parent)) { +// if (oldProxy.isValid()) { +// oldProxy.setParentNode(parent); +// oldProxy = ModelNode(); +// } else { +// ModelNode proxy = oldParent.addChildNode("QGraphicsProxyWidget"); +// const QString id = child.id() + "_proxy"; +// proxy.setPropertyValue("objectName", id); +// proxy.setId(id); +// return proxy; +// } +// } else { +// return parent; +// } +// if (oldProxy.isValid()) +// oldProxy.remove(); +// +// return ModelNode(); +//} + +/* \brief Returns the QGraphicsScene for a QGraphicsView, + if node is not derived from QGraphicsView it returns node +*/ + +QVariant parseProperty(const QString &className, const QString &propertyName, const QString &value) +{ + const QMetaObject *metaObject = QmlMetaType::qmlType(className.toAscii().constData(), 4, 6)->metaObject(); + if (!metaObject) { + qWarning() << "Type " << className << "is unknown to the Qml type system"; + return QVariant(); + } + const int propertyIndex = metaObject->indexOfProperty(propertyName.toAscii().constData()); + if (propertyIndex < 0) { + return QVariant(); + } + + const QString typeName = QString(metaObject->property(propertyIndex).typeName()); + Q_ASSERT_X(!typeName.isEmpty(), Q_FUNC_INFO, + QString("Type name for class %1 and property %2 is empty").arg(className, propertyName).toAscii().constData()); +// NodeMetaInfo metaInfo = MetaInfo::global().create(className); +// PropertyMetaInfo property = metaInfo.property(propertyName); +// QString type = property.type(); + + return PropertyParser::read(typeName, value); +} + +//void setAbsolutePosition(ModelNode node, QPointF position) +//{ +// QPointF newPos = position; +// ModelNode parentNode; +// parentNode = node.parentNode(); +// if (parentNode.isValid()) { +//// NodeInstance nodeInstance = instanceForNode(parentNode); +//// newPos = nodeInstance.mapFromGlobal(position); +// Q_ASSERT(false); +// } +// node.setPropertyValue("x", newPos.x()); +// node.setPropertyValue("y", newPos.y()); +//} + + + + +} //namespace ModelUtilities +} //namespace QmlDesigner + diff --git a/src/plugins/qmldesigner/core/model/modificationgrouptoken.cpp b/src/plugins/qmldesigner/core/model/modificationgrouptoken.cpp new file mode 100644 index 00000000000..c22408b2f04 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/modificationgrouptoken.cpp @@ -0,0 +1,42 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "modificationgrouptoken.h" + +namespace QmlDesigner { + +long ModificationGroupToken::uniqueNumberCounter = 0; + +ModificationGroupToken::ModificationGroupToken(unsigned depth): + m_depth(depth), + m_uniqueNumber(++uniqueNumberCounter) +{ +} + +} diff --git a/src/plugins/qmldesigner/core/model/nodeabstractproperty.cpp b/src/plugins/qmldesigner/core/model/nodeabstractproperty.cpp new file mode 100644 index 00000000000..69c9fd64292 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/nodeabstractproperty.cpp @@ -0,0 +1,120 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "nodeabstractproperty.h" +#include "nodeproperty.h" +#include "internalproperty.h" +#include "internalnodelistproperty.h" +#include "invalidmodelnodeexception.h" +#include "invalidpropertyexception.h" +#include "invalidreparentingexception.h" +#include "internalnode_p.h" +#include "model.h" +#include "model_p.h" + +namespace QmlDesigner { + +NodeAbstractProperty::NodeAbstractProperty() +{ +} + +NodeAbstractProperty::NodeAbstractProperty(const NodeAbstractProperty &property, AbstractView *view) + : AbstractProperty(property.name(), property.internalNode(), property.model(), view) +{ +} + +NodeAbstractProperty::NodeAbstractProperty(const QString &propertyName, const Internal::InternalNodePointer &internalNode, Model *model, AbstractView *view) + : AbstractProperty(propertyName, internalNode, model, view) +{ +} + +NodeAbstractProperty::NodeAbstractProperty(const Internal::InternalNodeAbstractProperty::Pointer &property, Model *model, AbstractView *view) + : AbstractProperty(property, model, view) +{} + +void NodeAbstractProperty::reparentHere(const ModelNode &modelNode) +{ + if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isNodeAbstractProperty()) + reparentHere(modelNode, isNodeListProperty()); + else + reparentHere(modelNode, metaInfo().isListProperty()); //we could use the metasystem instead? +} + +void NodeAbstractProperty::reparentHere(const ModelNode &modelNode, bool isNodeList) +{ + if (!isValid()) + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + if (isNodeProperty()) { + NodeProperty nodeProperty(toNodeProperty()); + if (nodeProperty.modelNode().isValid()) + throw InvalidReparentingException(__LINE__, __FUNCTION__, __FILE__); + } + + if (modelNode.isAncestorOf(parentModelNode())) + throw InvalidReparentingException(__LINE__, __FUNCTION__, __FILE__); + + if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isNodeAbstractProperty()) + model()->m_d->removeProperty(internalNode()->property(name())); + + if (modelNode.hasParentProperty()) { + Internal::InternalNodeAbstractProperty::Pointer oldParentProperty = modelNode.internalNode()->parentProperty(); + + model()->m_d->reparentNode(internalNode(), name(), modelNode.internalNode(), isNodeList); + + Q_ASSERT(!oldParentProperty.isNull()); + + + } else { + model()->m_d->reparentNode(internalNode(), name(), modelNode.internalNode(), isNodeList); + } +} + +bool NodeAbstractProperty::isEmpty() const +{ + Internal::InternalNodeAbstractProperty::Pointer property = internalNode()->nodeAbstractProperty(name()); + if (property.isNull()) + return true; + else + return property->isEmpty(); +} + +QList<ModelNode> NodeAbstractProperty::allSubNodes() +{ + if (!internalNode() + || !internalNode()->isValid() + || !internalNode()->hasProperty(name()) + || !internalNode()->property(name())->isNodeAbstractProperty()) + return QList<ModelNode>(); + + Internal::InternalNodeAbstractProperty::Pointer property = internalNode()->nodeAbstractProperty(name()); + return toModelNodeList(property->allSubNodes(), view()); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/core/model/nodeanchors.cpp b/src/plugins/qmldesigner/core/model/nodeanchors.cpp new file mode 100644 index 00000000000..4cb4d7afea6 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/nodeanchors.cpp @@ -0,0 +1,373 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +//#include "nodeanchors.h" +// +//#include <model.h> +//#include <modelnode.h> +// +//#include "internalnode_p.h" +//#include "internalnodeanchors.h" +//#include "internalnodestate.h" +//#include "invalidargumentexception.h" +// +//using namespace QmlDesigner::Internal; +// +//namespace QmlDesigner { +// +///*! +//\class QmlDesigner::NodeAnchors +//\ingroup CoreModel +//\brief NodeAnchors is a value holder for an anchor +//*/ +// +//NodeAnchors::NodeAnchors(const NodeState &nodeState): +// m_internalNode(nodeState.m_internalNode), +// m_internalNodeState(nodeState.m_internalNodeState), +// m_model(nodeState.m_model) +//{ +//} +// +//NodeAnchors::~NodeAnchors() +//{ +//} +// +//NodeAnchors::NodeAnchors(const NodeAnchors &other) +// :m_internalNode(other.m_internalNode), +// m_model(other.m_model) +//{ +// +//} +// +//NodeAnchors::NodeAnchors(const Internal::InternalNodeStatePointer &internalNodeState, Model *model): +// m_internalNode(internalNodeState->modelNode()), +// m_internalNodeState(internalNodeState), +// m_model(model) +//{ +//} +// +//NodeAnchors &NodeAnchors::operator=(const NodeAnchors &other) +//{ +// m_internalNode = other.m_internalNode; +// m_internalNodeState = other.m_internalNodeState; +// m_model = other.m_model; +// +// return *this; +//} +// +//ModelNode NodeAnchors::modelNode() const +//{ +// return ModelNode(m_internalNode, m_model.data()); +//} +// +//bool NodeAnchors::isValid() const +//{ +// return m_internalNode->isValid() +// && m_internalNodeState->isValid() && +// m_model; +//} +// +//NodeState NodeAnchors::nodeState() const +//{ +// return NodeState(m_internalNodeState, m_internalNode, m_model.data()); +//} +// +//void NodeAnchors::setAnchor(AnchorLine::Type sourceAnchorLineType, +// const ModelNode &targetModelNode, +// AnchorLine::Type targetAnchorLineType) +//{ +// Q_ASSERT(m_internalNode->isValid()); +// Q_ASSERT(m_internalNodeState->isValid()); +// Q_ASSERT(modelNode().isValid()); +// +// m_model->setAnchor(AnchorLine(nodeState(), sourceAnchorLineType), +// AnchorLine(targetModelNode.baseNodeState(), targetAnchorLineType)); +//} +// +//bool NodeAnchors::canAnchor(AnchorLine::Type sourceAnchorLineType, +// const ModelNode & targetModelNode, +// AnchorLine::Type targetAnchorLineType) const +//{ +// if (modelNode() == targetModelNode) +// return false; +// +// return InternalNodeAnchors(m_internalNodeState).canAnchor(sourceAnchorLineType, targetModelNode.baseNodeState().internalNodeState(), targetAnchorLineType); +//} +// +//bool NodeAnchors::canAnchor(const ModelNode & targetModelNode) const +//{ +// if (modelNode() == targetModelNode) +// return false; +// +// if (possibleAnchorLines(AnchorLine::Left, targetModelNode) != AnchorLine::NoAnchor) +// return true; +// else if (possibleAnchorLines(AnchorLine::Top, targetModelNode) != AnchorLine::NoAnchor) +// return true; +// else if (possibleAnchorLines(AnchorLine::Right, targetModelNode) != AnchorLine::NoAnchor) +// return true; +// else if (possibleAnchorLines(AnchorLine::Bottom, targetModelNode) != AnchorLine::NoAnchor) +// return true; +// else if (possibleAnchorLines(AnchorLine::HorizontalCenter, targetModelNode) != AnchorLine::NoAnchor) +// return true; +// else +// return possibleAnchorLines(AnchorLine::VerticalCenter, targetModelNode) != AnchorLine::NoAnchor; +//} +// +//AnchorLine::Type NodeAnchors::possibleAnchorLines(AnchorLine::Type sourceAnchorLineType, +// const ModelNode & targetModelNode) const +//{ +// if (modelNode() == targetModelNode) +// return AnchorLine::NoAnchor; +// +// int anchorTypes = AnchorLine::NoAnchor; +// const InternalNodeAnchors anchors(m_internalNodeState); +// +// if (sourceAnchorLineType & AnchorLine::HorizontalMask) { +// if (anchors.canAnchor(sourceAnchorLineType, targetModelNode.baseNodeState().internalNodeState(), AnchorLine::Left)) +// anchorTypes |= AnchorLine::Left; +// if (anchors.canAnchor(sourceAnchorLineType, targetModelNode.baseNodeState().internalNodeState(), AnchorLine::Right)) +// anchorTypes |= AnchorLine::Right; +// if (anchors.canAnchor(sourceAnchorLineType, targetModelNode.baseNodeState().internalNodeState(), AnchorLine::HorizontalCenter)) +// anchorTypes |= AnchorLine::HorizontalCenter; +// } else if (sourceAnchorLineType & AnchorLine::VerticalMask) { +// if (anchors.canAnchor(sourceAnchorLineType, targetModelNode.baseNodeState().internalNodeState(), AnchorLine::Top)) +// anchorTypes |= AnchorLine::Top; +// if (anchors.canAnchor(sourceAnchorLineType, targetModelNode.baseNodeState().internalNodeState(), AnchorLine::Bottom)) +// anchorTypes |= AnchorLine::Bottom; +// if (anchors.canAnchor(sourceAnchorLineType, targetModelNode.baseNodeState().internalNodeState(), AnchorLine::VerticalCenter)) +// anchorTypes |= AnchorLine::VerticalCenter; +// } +// +// return (AnchorLine::Type) anchorTypes; +//} +// +//AnchorLine NodeAnchors::localAnchor(AnchorLine::Type anchorLineType) const +//{ +// return InternalNodeAnchors(m_internalNodeState).anchor(anchorLineType); +//} +// +//AnchorLine NodeAnchors::anchor(AnchorLine::Type anchorLineType) const +//{ +// Internal::InternalNodeState::Pointer statePointer(m_internalNodeState); +// AnchorLine anchorLine = InternalNodeAnchors(statePointer).anchor(anchorLineType); +// +// while (!anchorLine.isValid() && statePointer->hasParentState()) { +// statePointer = statePointer->parentState(); +// anchorLine = InternalNodeAnchors(statePointer).anchor(anchorLineType); +// } +// +// return anchorLine; +//} +// +//bool NodeAnchors::hasAnchor(AnchorLine::Type sourceAnchorLineType) const +//{ +// Internal::InternalNodeState::Pointer statePointer(m_internalNodeState); +// InternalNodeAnchors internalNodeAnchors(statePointer); +// while (!internalNodeAnchors.hasAnchor(sourceAnchorLineType) && +// statePointer->hasParentState()) { +// statePointer = statePointer->parentState(); +// internalNodeAnchors = InternalNodeAnchors(statePointer); +// } +// +// return internalNodeAnchors.hasAnchor(sourceAnchorLineType); +//} +// +//void NodeAnchors::removeAnchor(AnchorLine::Type sourceAnchorLineType) +//{ +// if (hasLocalAnchor(sourceAnchorLineType)) +// m_model->removeAnchor(AnchorLine(nodeState(), sourceAnchorLineType)); +//} +// +//void NodeAnchors::removeMargins() +//{ +// removeMargin(AnchorLine::Left); +// removeMargin(AnchorLine::Right); +// removeMargin(AnchorLine::Top); +// removeMargin(AnchorLine::Bottom); +// removeMargin(AnchorLine::HorizontalCenter); +// removeMargin(AnchorLine::VerticalCenter); +// removeMargin(AnchorLine::Baseline); +//} +// +//void NodeAnchors::removeAnchors() +//{ +// removeAnchor(AnchorLine::Left); +// removeAnchor(AnchorLine::Right); +// removeAnchor(AnchorLine::Top); +// removeAnchor(AnchorLine::Bottom); +// removeAnchor(AnchorLine::HorizontalCenter); +// removeAnchor(AnchorLine::VerticalCenter); +// removeAnchor(AnchorLine::Baseline); +//} +// +//bool NodeAnchors::hasLocalAnchor(AnchorLine::Type sourceAnchorLineType) const +//{ +// return InternalNodeAnchors(m_internalNodeState).hasAnchor(sourceAnchorLineType); +//} +// +//bool NodeAnchors::hasLocalAnchors() const +//{ +// return hasLocalAnchor(AnchorLine::Top) || +// hasLocalAnchor(AnchorLine::Bottom) || +// hasLocalAnchor(AnchorLine::Left) || +// hasLocalAnchor(AnchorLine::Right) || +// hasLocalAnchor(AnchorLine::VerticalCenter) || +// hasLocalAnchor(AnchorLine::HorizontalCenter) || +// hasLocalAnchor(AnchorLine::Baseline); +//} +// +//bool NodeAnchors::hasAnchors() const +//{ +// return hasAnchor(AnchorLine::Top) || +// hasAnchor(AnchorLine::Bottom) || +// hasAnchor(AnchorLine::Left) || +// hasAnchor(AnchorLine::Right) || +// hasAnchor(AnchorLine::VerticalCenter) || +// hasAnchor(AnchorLine::HorizontalCenter) || +// hasAnchor(AnchorLine::Baseline); +//} +// +//void NodeAnchors::setMargin(AnchorLine::Type sourceAnchorLineType, double margin) const +//{ +// m_model->setAnchorMargin(AnchorLine(nodeState(), sourceAnchorLineType), margin); +//} +// +//bool NodeAnchors::hasMargin(AnchorLine::Type sourceAnchorLineType) const +//{ +// return InternalNodeAnchors(m_internalNodeState).hasMargin(sourceAnchorLineType); +//} +// +//double NodeAnchors::localMargin(AnchorLine::Type sourceAnchorLineType) const +//{ +// return InternalNodeAnchors(m_internalNodeState).margin(sourceAnchorLineType); +//} +// +//double NodeAnchors::margin(AnchorLine::Type sourceAnchorLineType) const +//{ +// Internal::InternalNodeState::Pointer statePointer(m_internalNodeState); +// InternalNodeAnchors internalNodeAnchors(statePointer); +// while (!internalNodeAnchors.hasMargin(sourceAnchorLineType) && +// statePointer->hasParentState()) { +// statePointer = statePointer->parentState(); +// internalNodeAnchors = InternalNodeAnchors(statePointer); +// } +// +// return internalNodeAnchors.margin(sourceAnchorLineType); +//} +// +//void NodeAnchors::removeMargin(AnchorLine::Type sourceAnchorLineType) +//{ +// m_model->removeAnchorMargin(AnchorLine(nodeState(), sourceAnchorLineType)); +// +//} +// +//QDebug operator<<(QDebug debug, const NodeAnchors &anchors) +//{ +// debug.nospace() << "NodeAnchors("; +// if (anchors.isValid()) { +// if (anchors.hasAnchor(AnchorLine::Top)) +// debug << "top"; +// if (anchors.hasMargin(AnchorLine::Top)) +// debug.nospace() << "(" << anchors.margin(AnchorLine::Top) << ")"; +// if (anchors.hasAnchor(AnchorLine::Bottom)) +// debug << "bottom"; +// if (anchors.hasMargin(AnchorLine::Bottom)) +// debug.nospace() << "(" << anchors.margin(AnchorLine::Bottom) << ")"; +// if (anchors.hasAnchor(AnchorLine::Left)) +// debug << "left"; +// if (anchors.hasMargin(AnchorLine::Left)) +// debug.nospace() << "(" << anchors.margin(AnchorLine::Left) << ")"; +// if (anchors.hasAnchor(AnchorLine::Right)) +// debug << "right"; +// if (anchors.hasMargin(AnchorLine::Right)) +// debug.nospace() << "(" << anchors.margin(AnchorLine::Right) << ")"; +// if (anchors.hasAnchor(AnchorLine::VerticalCenter)) +// debug << "verticalCenter"; +// if (anchors.hasMargin(AnchorLine::VerticalCenter)) +// debug.nospace() << "(" << anchors.margin(AnchorLine::VerticalCenter) << ")"; +// if (anchors.hasAnchor(AnchorLine::HorizontalCenter)) +// debug << "horizontalCenter"; +// if (anchors.hasMargin(AnchorLine::HorizontalCenter)) +// debug.nospace() << "(" << anchors.margin(AnchorLine::HorizontalCenter) << ")"; +// if (anchors.hasAnchor(AnchorLine::Baseline)) +// debug << "baseline"; +// if (anchors.hasMargin(AnchorLine::Baseline)) +// debug.nospace() << "(" << anchors.margin(AnchorLine::Baseline) << ")"; +// } else { +// debug.nospace() << "invalid"; +// } +// +// debug.nospace() << ")"; +// +// return debug.space(); +//} +// +//QTextStream& operator<<(QTextStream &stream, const NodeAnchors &anchors) +//{ +// stream << "NodeAnchors("; +// if (anchors.isValid()) { +// if (anchors.hasAnchor(AnchorLine::Top)) +// stream << "top"; +// if (anchors.hasMargin(AnchorLine::Top)) +// stream << "(" << anchors.margin(AnchorLine::Top) << ")"; +// if (anchors.hasAnchor(AnchorLine::Bottom)) +// stream << "bottom"; +// if (anchors.hasMargin(AnchorLine::Bottom)) +// stream << "(" << anchors.margin(AnchorLine::Bottom) << ")"; +// if (anchors.hasAnchor(AnchorLine::Left)) +// stream << "left"; +// if (anchors.hasMargin(AnchorLine::Left)) +// stream << "(" << anchors.margin(AnchorLine::Left) << ")"; +// if (anchors.hasAnchor(AnchorLine::Right)) +// stream << "right"; +// if (anchors.hasMargin(AnchorLine::Right)) +// stream << "(" << anchors.margin(AnchorLine::Right) << ")"; +// if (anchors.hasAnchor(AnchorLine::VerticalCenter)) +// stream << "verticalCenter"; +// if (anchors.hasMargin(AnchorLine::VerticalCenter)) +// stream << "(" << anchors.margin(AnchorLine::VerticalCenter) << ")"; +// if (anchors.hasAnchor(AnchorLine::HorizontalCenter)) +// stream << "horizontalCenter"; +// if (anchors.hasMargin(AnchorLine::HorizontalCenter)) +// stream << "(" << anchors.margin(AnchorLine::HorizontalCenter) << ")"; +// if (anchors.hasAnchor(AnchorLine::Baseline)) +// stream << "baseline"; +// if (anchors.hasMargin(AnchorLine::Baseline)) +// stream << "(" << anchors.margin(AnchorLine::Baseline) << ")"; +// } else { +// stream << "invalid"; +// } +// +// stream << ")"; +// +// return stream; +//} +// +// +//} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/core/model/nodelistproperty.cpp b/src/plugins/qmldesigner/core/model/nodelistproperty.cpp new file mode 100644 index 00000000000..70e3826968b --- /dev/null +++ b/src/plugins/qmldesigner/core/model/nodelistproperty.cpp @@ -0,0 +1,125 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "nodelistproperty.h" +#include "internalproperty.h" +#include "internalnodelistproperty.h" +#include "invalidmodelnodeexception.h" +#include "invalidpropertyexception.h" +#include "internalnode_p.h" +#include "model.h" +#include "model_p.h" +#include <qmlobjectnode.h> + + + +namespace QmlDesigner { + +NodeListProperty::NodeListProperty() +{} + +NodeListProperty::NodeListProperty(const NodeListProperty &property, AbstractView *view) + : NodeAbstractProperty(property.name(), property.internalNode(), property.model(), view) +{ + +} + +NodeListProperty::NodeListProperty(const QString &propertyName, const Internal::InternalNodePointer &internalNode, Model* model, AbstractView *view) : + NodeAbstractProperty(propertyName, internalNode, model, view) +{ +} + +NodeListProperty::NodeListProperty(const Internal::InternalNodeListProperty::Pointer &internalNodeListProperty, Model* model, AbstractView *view) + : NodeAbstractProperty(internalNodeListProperty, model, view) +{ +} + +static QList<ModelNode> internalNodesToModelNodes(const QList<Internal::InternalNode::Pointer> &inputList, Model* model, AbstractView *view) +{ + QList<ModelNode> modelNodeList; + foreach (const Internal::InternalNode::Pointer &internalNode, inputList) { + modelNodeList.append(ModelNode(internalNode, model, view)); + } + return modelNodeList; +} + +const QList<ModelNode> NodeListProperty::toModelNodeList() const +{ + if (!isValid()) + throw InvalidPropertyException(__LINE__, __FUNCTION__, __FILE__, "<invalid node list property>"); + + if (internalNode()->hasProperty(name())) { + Internal::InternalProperty::Pointer internalProperty = internalNode()->property(name()); + if (internalProperty->isNodeListProperty()) { + return internalNodesToModelNodes(internalProperty->toNodeListProperty()->nodeList(), model(), view()); + } + } + + return QList<ModelNode>(); +} + +const QList<QmlObjectNode> NodeListProperty::toQmlObjectNodeList() const +{ + QmlModelView *fxView = view()->toQmlModelView(); + + if (fxView == 0) + return QList<QmlObjectNode>(); + + QList<QmlObjectNode> fxObjectNodeList; + + foreach (const ModelNode &modelNode, toModelNodeList()) + fxObjectNodeList.append(QmlObjectNode(modelNode)); + + return fxObjectNodeList; +} + +void NodeListProperty::slide(int from, int to) const +{ + if (!isValid()) + throw InvalidPropertyException(__LINE__, __FUNCTION__, __FILE__, "<invalid node list property>"); + if (to > toModelNodeList().count()) + throw InvalidPropertyException(__LINE__, __FUNCTION__, __FILE__, "<invalid node list sliding>"); + + model()->m_d->slideNodeList(internalNode(), name(), from, to); +} + +void NodeListProperty::reparentHere(const ModelNode &modelNode) +{ + NodeAbstractProperty::reparentHere(modelNode, true); +} + +bool NodeListProperty::isEmpty() const +{ + if (!isValid()) + throw InvalidPropertyException(__LINE__, __FUNCTION__, __FILE__, "<invalid node list property>"); + return toModelNodeList().empty(); +} + + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/core/model/nodeproperty.cpp b/src/plugins/qmldesigner/core/model/nodeproperty.cpp new file mode 100644 index 00000000000..35b7398c2f2 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/nodeproperty.cpp @@ -0,0 +1,91 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "nodeproperty.h" +#include "internalnodeproperty.h" +#include "invalidmodelnodeexception.h" +#include "invalidpropertyexception.h" +#include "invalidargumentexception.h" +#include "internalnode_p.h" +#include "model.h" +#include "model_p.h" + +namespace QmlDesigner { + +NodeProperty::NodeProperty() +{ +} + +NodeProperty::NodeProperty(const QString &propertyName, const Internal::InternalNodePointer &internalNode, Model* model, AbstractView *view) + : NodeAbstractProperty(propertyName, internalNode, model, view) +{ + +} + +void NodeProperty::setModelNode(const ModelNode &modelNode) +{ + if (!isValid()) + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + if (!modelNode.isValid()) + throw InvalidArgumentException(__LINE__, __FUNCTION__, __FILE__, name()); + + if (internalNode()->hasProperty(name())) { //check if oldValue != value + Internal::InternalProperty::Pointer internalProperty = internalNode()->property(name()); + if (internalProperty->isNodeProperty() + && internalProperty->toNodeProperty()->node() == modelNode.internalNode()) + return; + } + + if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isNodeProperty()) + model()->m_d->removeProperty(internalNode()->property(name())); + + model()->m_d->reparentNode(internalNode(), name(), modelNode.internalNode(), false); //### we have to add a flag that this is not a list +} + +ModelNode NodeProperty::modelNode() const +{ + if (!isValid()) + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + if (internalNode()->hasProperty(name())) { //check if oldValue != value + Internal::InternalProperty::Pointer internalProperty = internalNode()->property(name()); + if (internalProperty->isNodeProperty()) + return ModelNode(internalProperty->toNodeProperty()->node(), model(), view()); + } + + return ModelNode(); +} + +void NodeProperty::reparentHere(const ModelNode &modelNode) +{ + NodeAbstractProperty::reparentHere(modelNode, false); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/core/model/objectpropertybinding.cpp b/src/plugins/qmldesigner/core/model/objectpropertybinding.cpp new file mode 100644 index 00000000000..6358e832818 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/objectpropertybinding.cpp @@ -0,0 +1,55 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "objectpropertybinding.h" + +namespace QmlDesigner { + +ObjectPropertyBinding::ObjectPropertyBinding() +{ +} + +ObjectPropertyBinding::ObjectPropertyBinding(const ModelNode &node) + : m_node(node) +{ + +} + +ModelNode ObjectPropertyBinding::modelNode() const +{ + return m_node; +} + +bool ObjectPropertyBinding::isValid() const +{ + return m_node.isValid(); +} + + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/core/model/painteventfilter.cpp b/src/plugins/qmldesigner/core/model/painteventfilter.cpp new file mode 100644 index 00000000000..c5fc4ec542c --- /dev/null +++ b/src/plugins/qmldesigner/core/model/painteventfilter.cpp @@ -0,0 +1,73 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "painteventfilter_p.h" + + +#include <QTimer> +#include <QtDebug> +#include <QEvent> + +namespace QmlDesigner { +namespace Internal { + +PaintEventFilter::PaintEventFilter(QObject *parent) + : QObject(parent), m_pushObjectTimer(new QTimer(this)) +{ + m_pushObjectTimer->start(20); + connect(m_pushObjectTimer, SIGNAL(timeout()), SLOT(emitPaintedObjects())); +} + +bool PaintEventFilter::eventFilter(QObject *object, QEvent *event) +{ + switch(event->type()) + { + case QEvent::Paint : + { + m_paintedObjects.append(object); + } + + default: return false; + + } + + return false; +} + +void PaintEventFilter::emitPaintedObjects() +{ + if (m_paintedObjects.isEmpty()) + return; + + emit paintedObjects(m_paintedObjects); + m_paintedObjects.clear(); +} + +} +} diff --git a/src/plugins/qmldesigner/core/model/painteventfilter_p.h b/src/plugins/qmldesigner/core/model/painteventfilter_p.h new file mode 100644 index 00000000000..183ade321b4 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/painteventfilter_p.h @@ -0,0 +1,65 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef PAINTEVENTFILTER_H +#define PAINTEVENTFILTER_H + +#include <QObject> +#include <QList> + +class QTimer; + +namespace QmlDesigner { +namespace Internal { + +class PaintEventFilter : public QObject +{ + Q_OBJECT +public: + PaintEventFilter(QObject *parent); + +signals: + void paintedObjects(const QList<QObject*> &paintedObjects); + +protected: + bool eventFilter(QObject *object, QEvent *event); + +private slots: + void emitPaintedObjects(); + +private: + QList<QObject*> m_paintedObjects; + QTimer *m_pushObjectTimer; +}; + +} + +} + +#endif // PAINTEVENTFILTER_H diff --git a/src/plugins/qmldesigner/core/model/parsedqml.cpp b/src/plugins/qmldesigner/core/model/parsedqml.cpp new file mode 100644 index 00000000000..5d42307bcb9 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/parsedqml.cpp @@ -0,0 +1,53 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "parsedqml.h" + +namespace QmlDesigner { +namespace Internal { + +ParsedQML::ParsedQML(const QString &sourceCode, const QString &fileName): + m_sourceCode(sourceCode), + m_engine(), + m_nodePool(fileName, &m_engine), + m_ast(0) +{ + QmlJS::Lexer lexer(&m_engine); + QmlJS::Parser parser(&m_engine); + + const QString code = sourceCode; + lexer.setCode(code, 1); + if (parser.parse()) + m_ast = parser.ast(); + else + m_ast = 0; +} + +} // namespace Internal +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/core/model/parsedqml.h b/src/plugins/qmldesigner/core/model/parsedqml.h new file mode 100644 index 00000000000..25cc4950bf8 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/parsedqml.h @@ -0,0 +1,64 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef PARSEDQML_H +#define PARSEDQML_H + +#include <QtCore/QString> + +#include <qmljsengine_p.h> +#include <qmljslexer_p.h> +#include <qmljsparser_p.h> +#include <qmljsnodepool_p.h> +#include <qmljsast_p.h> + +namespace QmlDesigner { +namespace Internal { + +class ParsedQML +{ +public: + ParsedQML(const QString &sourceCode, const QString &fileName = QString()); + + bool isValid() const { return m_ast != 0; } + QmlJS::AST::UiProgram *ast() const { return m_ast; } + + QString sourceCode() const { return m_sourceCode; } + +private: + QString m_sourceCode; + QmlJS::Engine m_engine; + QmlJS::NodePool m_nodePool; + QmlJS::AST::UiProgram* m_ast; +}; + +} // namespace Internal +} // namespace QmlDesigner + +#endif // PARSEDQML_H diff --git a/src/plugins/qmldesigner/core/model/paster.cpp b/src/plugins/qmldesigner/core/model/paster.cpp new file mode 100644 index 00000000000..1ad0315a495 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/paster.cpp @@ -0,0 +1,307 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include <QtCore/QByteArray> +#include <QtCore/QDataStream> +#include <QtCore/QSet> + +#include "changeimportsvisitor.h" +#include "copypasteutil.h" +#include "parsedqml.h" +#include "paster.h" +#include "qmlrewriter.h" + +using namespace QmlJS; +using namespace QmlJS::AST; + +namespace QmlDesigner { +namespace Internal { + +class ASTExtractor: public AST::Visitor, protected CopyPasteUtil +{ +private: + Paster *m_paster; + QSet<TextLocation> m_locations; + bool m_inStates; + QString m_stateName; + +private: + TextLocation toLocation(quint32 pos, quint32 len) const { return TextLocation((int) pos, (int) len); } + TextLocation toLocation(const SourceLocation &firstSourceLocation, const SourceLocation &lastSourceLocation) const + { return toLocation(firstSourceLocation.offset, lastSourceLocation.end() - firstSourceLocation.offset); } + +public: + ASTExtractor(Paster *paster, const QSet<TextLocation> &locations, const QString &sourceCode): + CopyPasteUtil(sourceCode), + m_paster(paster), + m_locations(locations) + {} + + bool operator()(QmlJS::AST::UiProgram *sourceAST) { + m_inStates = false; + m_stateName = QString(); + + Node::accept(sourceAST->imports, this); + + if (sourceAST->members && sourceAST->members->member) { + visitRootMember(sourceAST->members->member); + } + + return m_locations.isEmpty(); + } + +protected: + bool switchInStates(bool newInStates) { + bool oldInStates = m_inStates; + m_inStates = newInStates; + return oldInStates; + } + + QString switchStateName(const QString &newStateName) { + QString oldStateName = m_stateName; + m_stateName = newStateName; + return oldStateName; + } + + bool visit(UiImportList *ast) { + for (UiImportList *it = ast; it; it = it->next) { + if (it->import) { + m_paster->addImports(createImport(it->import)); + + m_locations.remove(toLocation(it->import->firstSourceLocation(), it->import->lastSourceLocation())); + } + } + + return false; + } + + void visitRootMember(UiObjectMember *rootMember) { + if (UiObjectDefinition* root = AST::cast<UiObjectDefinition*>(rootMember)) { + if (m_locations.contains(toLocation(root->firstSourceLocation(), root->lastSourceLocation()))) { + m_paster->addNode(textAt(root->firstSourceLocation(), root->lastSourceLocation())); + } else if (root->initializer) { + for (UiObjectMemberList *it = root->initializer->members; it; it = it->next) { + if (!it->member) + continue; + + if (UiArrayBinding *array = AST::cast<UiArrayBinding *>(it->member)) { + if (array->qualifiedId->name->asString() == "states") { + bool prevInStates = switchInStates(true); + Node::accept(array->members, this); + switchInStates(prevInStates); + continue; + } + } + + Node::accept(it->member, this); + } + } + } + } + + bool visit(UiObjectDefinition *ast) { + const SourceLocation start = ast->firstSourceLocation(); + const SourceLocation end = ast->lastSourceLocation(); + + if (m_inStates) { + if (ast->qualifiedTypeNameId && ast->qualifiedTypeNameId->name && ast->qualifiedTypeNameId->name->asString() == "State") { + QString prevStateName = switchStateName(QString()); + Node::accept(ast->initializer, this); + switchStateName(prevStateName); + return false; + } else if (ast->qualifiedTypeNameId && ast->qualifiedTypeNameId->name && ast->qualifiedTypeNameId->name->asString() == "PropertyChanges") { + if (m_locations.remove(toLocation(start, end))) { + m_paster->addNodeState(m_stateName, textAt(start, end)); + } + + return false; + } else { + return true; + } + } else { + if (m_locations.remove(toLocation(start, end))) { + m_paster->addNode(textAt(start, end)); + } + + return false; + } + + return true; + } + + bool visit(UiObjectBinding *ast) { + const SourceLocation start = ast->firstSourceLocation(); + const SourceLocation end = ast->lastSourceLocation(); + + if (m_locations.remove(toLocation(start, end))) { + m_paster->addNode(textAt(start, end)); + + return false; + } + + return true; + } + + bool visit(UiScriptBinding *ast) { + if (m_inStates && ast->qualifiedId && ast->qualifiedId->name && ast->qualifiedId->name->asString() == "name") { + if (ExpressionStatement *stmt = AST::cast<ExpressionStatement *>(ast->statement)) { + if (StringLiteral * str = AST::cast<StringLiteral *>(stmt->expression)) { + m_stateName = str->value->asString(); + } + } + } + + return true; + } +}; + +static inline void mergeNestedLocation(const TextLocation &newLocation, QList<TextLocation> &locations) +{ + for (int i = 0; i < locations.length(); ++i) { + const TextLocation l = locations[i]; + + if (l.contains(newLocation)) + return; + + if (newLocation.contains(l)) { + locations.replace(i, newLocation); + return; + } + } + + locations.append(newLocation); +} + +static QList<TextLocation> mergeNestedLocation(const QList<TextLocation> &locations) +{ + QList<TextLocation> originals(locations); + QList<TextLocation> result; + + if (originals.isEmpty()) + return result; + + result.append(originals.takeFirst()); + + foreach (const TextLocation &l, originals) { + mergeNestedLocation(l, result); + } + + return result; +} + +Paster::Paster(QMimeData *transferData, const InternalNode::Pointer &intoNode): + m_targetNode(intoNode) +{ + Q_ASSERT(!intoNode.isNull() && intoNode->isValid()); + + QList<TextLocation> locations = extractPasteData(transferData); + + m_pastedLocations = mergeNestedLocation(locations); +} + +bool Paster::canPaste() +{ + if (m_pastedLocations.isEmpty() || m_pastedSource.isEmpty()) + return false; + + ParsedQML pastedQml(m_pastedSource, "<clipboard>"); + if (!pastedQml.isValid()) + return false; + + // TODO: expand this by checking if the paste locations can be found (esp. for states) + return true; +} + +bool Paster::doPaste(ModelToTextMerger &modelToTextMerger) +{ + if (m_pastedLocations.isEmpty() || m_pastedSource.isEmpty()) + return false; + + ParsedQML pastedQml(m_pastedSource, "<clipboard>"); + if (!pastedQml.isValid()) + return false; + + m_modelToTextMerger = &modelToTextMerger; + + ASTExtractor extractor(this, QSet<TextLocation>::fromList(m_pastedLocations), m_pastedSource); + bool result = extractor(pastedQml.ast()); + + m_modelToTextMerger = 0; + return result; +} + +void Paster::addImports(const Import &imports) +{ + m_modelToTextMerger->addImport(imports); +} + +void Paster::addNode(const QString &nodeText) +{ + m_modelToTextMerger->pasteNode(m_targetNode->baseNodeState()->location(), nodeText); +} + +void Paster::addNodeState(const QString &stateName, const QString &propertyChanges) +{ + foreach (const InternalNodeState::Pointer &nodeState, m_targetNode->nodeStates()) { + const InternalModelState::Pointer modelState = nodeState->modelState(); + + if (modelState->name() == stateName) + m_modelToTextMerger->pastePropertyChanges(modelState, propertyChanges); + } +} + +QList<TextLocation> Paster::extractPasteData(QMimeData *transferData) +{ + QList<TextLocation> locations; + + if (!transferData) + return locations; + + QByteArray data = transferData->data("application/x-qt-bauhaus"); + if (data.isEmpty()) + return locations; + + QDataStream is(&data, QIODevice::ReadOnly); + + int locationCount = 0; + is >> locationCount; + for (int i = 0; i < locationCount; ++i) { + int position = 0, length = 0; + is >> position; + is >> length; + locations.append(TextLocation(position, length)); + } + + is >> m_pastedSource; + + return locations; +} + +} // namespace Internal +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/core/model/paster.h b/src/plugins/qmldesigner/core/model/paster.h new file mode 100644 index 00000000000..6672b1e7226 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/paster.h @@ -0,0 +1,76 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef PASTER_H +#define PASTER_H + +#include <QtCore/QList> +#include <QtCore/QMimeData> +#include <QtCore/QSet> +#include <QtCore/QString> + +#include "import.h" +#include "internalnode_p.h" +#include "modeltotextmerger.h" +#include "textlocation.h" + +namespace QmlDesigner { +namespace Internal { + +class ASTExtractor; + +class Paster +{ + friend class ASTExtractor; + +public: + Paster(QMimeData *transferData, const InternalNode::Pointer &intoNode); + + bool canPaste(); + bool doPaste(ModelToTextMerger &modelToTextMerger); + +protected: + void addImports(const Import &imports); + void addNode(const QString &nodeText); + void addNodeState(const QString &stateName, const QString &propertyChanges); + +private: + QList<TextLocation> extractPasteData(QMimeData *transferData); + +private: + InternalNode::Pointer m_targetNode; + QList<TextLocation> m_pastedLocations; + QString m_pastedSource; + ModelToTextMerger *m_modelToTextMerger; +}; + +} // namespace Internal +} // namespace QmlDesigner + +#endif // PASTER_H diff --git a/src/plugins/qmldesigner/core/model/plaintexteditmodifier.cpp b/src/plugins/qmldesigner/core/model/plaintexteditmodifier.cpp new file mode 100644 index 00000000000..d6ff674cdd9 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/plaintexteditmodifier.cpp @@ -0,0 +1,186 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include <QtCore/QDebug> + +#include <QtGui/QPlainTextEdit> +#include <QtGui/QUndoStack> + +#include <filemanager/changeset.h> + +#include "plaintexteditmodifier.h" + +using namespace Utils; +using namespace QmlDesigner; + +PlainTextEditModifier::PlainTextEditModifier(QPlainTextEdit *textEdit): + m_changeSet(0), + m_textEdit(textEdit), + m_changeSignalsEnabled(true), + m_pendingChangeSignal(false), + m_ongoingTextChange(false) +{ + connect(m_textEdit, SIGNAL(textChanged()), + this, SLOT(textEditChanged())); +} + +PlainTextEditModifier::~PlainTextEditModifier() +{ +} + +void PlainTextEditModifier::save(QIODevice *device) +{ + device->write(m_textEdit->toPlainText().toLatin1()); +} + +void PlainTextEditModifier::replace(int offset, int length, const QString &replacement) +{ +#if 0 + qDebug() << "Original:" << m_textEdit->toPlainText(); + qDebug() << "Replacement:" << replacement; + qDebug() << " offset:" << offset; + qDebug() << " length:" << length; +#endif + + Q_ASSERT(offset >= 0); + Q_ASSERT(length >= 0); + + const int replacementLength = replacement.length(); + + if (m_changeSet) { + m_changeSet->replace(offset, length, replacement); + emit replaced(offset, length, replacementLength); + } else { + ChangeSet changeSet; + changeSet.replace(offset, length, replacement); + emit replaced(offset, length, replacementLength); + runRewriting(&changeSet); + } +} + +void PlainTextEditModifier::move(const MoveInfo &moveInfo) +{ + Q_ASSERT(moveInfo.objectStart >= 0); + Q_ASSERT(moveInfo.objectEnd > moveInfo.objectStart); + Q_ASSERT(moveInfo.destination >= 0); + Q_ASSERT(moveInfo.leadingCharsToRemove >= 0); + Q_ASSERT(moveInfo.trailingCharsToRemove >= 0); + Q_ASSERT(moveInfo.objectStart - moveInfo.leadingCharsToRemove >= 0); + + if (m_changeSet) { + m_changeSet->insert(moveInfo.destination, moveInfo.prefixToInsert); + m_changeSet->move(moveInfo.objectStart, moveInfo.objectEnd - moveInfo.objectStart, moveInfo.destination); + m_changeSet->insert(moveInfo.destination, moveInfo.suffixToInsert); + m_changeSet->remove(moveInfo.objectStart - moveInfo.leadingCharsToRemove, moveInfo.leadingCharsToRemove); + m_changeSet->remove(moveInfo.objectEnd, moveInfo.trailingCharsToRemove); + emit moved(moveInfo); + } else { + ChangeSet changeSet; + changeSet.insert(moveInfo.destination, moveInfo.prefixToInsert); + changeSet.move(moveInfo.objectStart, moveInfo.objectEnd - moveInfo.objectStart, moveInfo.destination); + changeSet.insert(moveInfo.destination, moveInfo.suffixToInsert); + changeSet.remove(moveInfo.objectStart - moveInfo.leadingCharsToRemove, moveInfo.leadingCharsToRemove); + changeSet.remove(moveInfo.objectEnd, moveInfo.trailingCharsToRemove); + emit moved(moveInfo); + runRewriting(&changeSet); + } +} + +void PlainTextEditModifier::startGroup() +{ + if (!m_changeSet) + m_changeSet = new ChangeSet; + + m_textEdit->textCursor().beginEditBlock(); +} + +void PlainTextEditModifier::flushGroup() +{ + if (m_changeSet) + runRewriting(m_changeSet); +} + +void PlainTextEditModifier::commitGroup() +{ + if (m_changeSet) { + runRewriting(m_changeSet); + delete m_changeSet; + m_changeSet = 0; + } + + m_textEdit->textCursor().endEditBlock(); +} + +void PlainTextEditModifier::textEditChanged() +{ + if (!m_ongoingTextChange && m_changeSignalsEnabled) { + emit textChanged(); + } else { + m_pendingChangeSignal = true; + } +} + +void PlainTextEditModifier::runRewriting(ChangeSet *changeSet) +{ + m_ongoingTextChange = true; + QTextCursor cursor = m_textEdit->textCursor(); + changeSet->apply(&cursor); + m_ongoingTextChange = false; + textEditChanged(); +} + +QTextDocument *PlainTextEditModifier::textDocument() const +{ + return m_textEdit->document(); +} + +QString PlainTextEditModifier::text() const +{ + return m_textEdit->toPlainText(); +} + +QTextCursor PlainTextEditModifier::textCursor() const +{ + return m_textEdit->textCursor(); +} + +void PlainTextEditModifier::deactivateChangeSignals() +{ + m_changeSignalsEnabled = false; +} + +void PlainTextEditModifier::reactivateChangeSignals() +{ + m_changeSignalsEnabled = true; + + if (m_pendingChangeSignal) { + m_pendingChangeSignal = false; + emit textChanged(); + } +} diff --git a/src/plugins/qmldesigner/core/model/propertybinding.cpp b/src/plugins/qmldesigner/core/model/propertybinding.cpp new file mode 100644 index 00000000000..726c5456bcb --- /dev/null +++ b/src/plugins/qmldesigner/core/model/propertybinding.cpp @@ -0,0 +1,67 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "invalidpropertyexception.h" +#include "propertybinding.h" + +namespace QmlDesigner { + +PropertyBinding::PropertyBinding() +{ +} + +PropertyBinding::PropertyBinding(const QString &value): + m_value(value) +{ +} + +PropertyBinding::PropertyBinding(const PropertyBinding &other): + m_value(other.m_value) +{ +} + +PropertyBinding &PropertyBinding::operator=(const PropertyBinding &other) +{ + m_value = other.m_value; + + return *this; +} + +bool PropertyBinding::isValid() const +{ + return !m_value.isEmpty(); +} + +QString PropertyBinding::value() const +{ + return m_value; +} + + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/core/model/propertycontainer.cpp b/src/plugins/qmldesigner/core/model/propertycontainer.cpp new file mode 100644 index 00000000000..e55e1d27562 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/propertycontainer.cpp @@ -0,0 +1,143 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "propertycontainer.h" +#include "propertyparser.h" +#include <QVariant> +#include <QString> +#include <QRegExp> +#include <QSize> +#include <QSizeF> +#include <QPoint> +#include <QPointF> +#include <QtDebug> + +namespace QmlDesigner { + +using namespace QmlDesigner::Internal; + + +// Creates invalid PropertyContainer +PropertyContainer::PropertyContainer() +{ +} + +PropertyContainer::PropertyContainer(const QString &name, const QString &type, const QVariant &value) + : m_name(name), m_type(type), m_value(value) +{ + Q_ASSERT_X(!name.isEmpty(), Q_FUNC_INFO, "Name of property cannot be empty"); + Q_ASSERT_X(!type.isEmpty(), Q_FUNC_INFO, "Type of property cannot be empty"); +} + +void PropertyContainer::set(const QString &name, const QString &type, const QVariant &value) +{ + m_name = name; + m_type = type; + m_value = value; +} + +bool PropertyContainer::isValid() const +{ + return !m_name.isEmpty() && m_value.isValid(); +} + +QString PropertyContainer::name() const +{ + return m_name; +} + +QVariant PropertyContainer::value() const +{ + if (m_value.type() == QVariant::String) + m_value = PropertyParser::read(m_type, m_value.toString()); + return m_value; +} + + +void PropertyContainer::setValue(const QVariant &value) +{ + m_value = value; +} + +QString PropertyContainer::type() const +{ + return m_type; +} + +QDataStream &operator<<(QDataStream &stream, const PropertyContainer &propertyContainer) +{ + Q_ASSERT(!propertyContainer.name().isEmpty()); + Q_ASSERT(!propertyContainer.type().isEmpty()); + Q_ASSERT(propertyContainer.value().isValid()); + stream << propertyContainer.name(); + stream << propertyContainer.type(); + stream << propertyContainer.value(); + + + return stream; +} + +QDataStream &operator>>(QDataStream &stream, PropertyContainer &propertyContainer) +{ + + stream >> propertyContainer.m_name; + stream >> propertyContainer.m_type; + stream >> propertyContainer.m_value; + + Q_ASSERT(!propertyContainer.name().isEmpty()); + + return stream; +} + +QDataStream &operator<<(QDataStream &stream, const QList<PropertyContainer> &propertyContainerList) +{ + stream << propertyContainerList.count(); + foreach (const PropertyContainer &propertyContainer, propertyContainerList) + stream << propertyContainer; + + return stream; +} + +QDataStream &operator>>(QDataStream &stream, QList<PropertyContainer> &propertyContainerList) +{ + int count; + stream >> count; + Q_ASSERT(count >= 0); + for( int i = 0; i < count; i++) { + PropertyContainer propertyContainer; + stream >> propertyContainer; + propertyContainerList.append(propertyContainer); + } + + return stream; +} + + +} //namespace QmlDesigner + diff --git a/src/plugins/qmldesigner/core/model/propertynode.cpp b/src/plugins/qmldesigner/core/model/propertynode.cpp new file mode 100644 index 00000000000..063b93c5a6b --- /dev/null +++ b/src/plugins/qmldesigner/core/model/propertynode.cpp @@ -0,0 +1,38 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "propertynode.h" + +namespace QmlDesigner { + +PropertyNode::PropertyNode() +{ +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/core/model/propertyparser.cpp b/src/plugins/qmldesigner/core/model/propertyparser.cpp new file mode 100644 index 00000000000..8dfb88b49b8 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/propertyparser.cpp @@ -0,0 +1,176 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "propertyparser.h" +#include <QUrl> +#include <QtCore/QDebug> +#include <QtDeclarative/private/qmlstringconverters_p.h> +#include <modelnode.h> +#include <metainfo.h> + +namespace QmlDesigner { +namespace Internal { +namespace PropertyParser { + +static QVariant fromEnum(const QString &string, const QString &type, const MetaInfo &metaInfo) +{ + if (string.isEmpty()) + return QVariant(); + + // TODO Use model metainfo + EnumeratorMetaInfo enumerator = metaInfo.enumerator(type); + int value = enumerator.elementValue(string); + return QVariant(value); +} + +QVariant read(const QString &typeStr, const QString &str, const MetaInfo &metaInfo) +{ + if (metaInfo.hasEnumerator(typeStr)) { + return fromEnum(str, typeStr, metaInfo); + } + + return read(typeStr, str); +} + +QVariant read(const QString &typeStr, const QString &str) +{ + QMetaType::Type type = static_cast<QMetaType::Type>(QMetaType::type(typeStr.toAscii().constData())); + if (type == 0) + qWarning() << "Type " << typeStr + << " is unknown to QMetaType system. Cannot create properly typed QVariant for value " + << str; + + QVariant value; + + bool conversionOk = true; + switch (type) { + case QMetaType::QPoint: + value = QmlStringConverters::pointFFromString(str, &conversionOk).toPoint(); + break; + case QMetaType::QPointF: + value = QmlStringConverters::pointFFromString(str, &conversionOk); + break; + case QMetaType::QSize: + value = QmlStringConverters::sizeFFromString(str, &conversionOk).toSize(); + break; + case QMetaType::QSizeF: + value = QmlStringConverters::sizeFFromString(str, &conversionOk); + break; + case QMetaType::QRect: + value = QmlStringConverters::rectFFromString(str, &conversionOk).toRect(); + break; + case QMetaType::QRectF: + value = QmlStringConverters::rectFFromString(str, &conversionOk); + break; + case QMetaType::Bool: + value = QmlStringConverters::boolFromString(str, &conversionOk); + break; + case QMetaType::QUrl: + value = QVariant(QUrl(str)); + break; + case QMetaType::QColor: + value = QmlStringConverters::colorFromString(str); + break; + default: { + value = QVariant(str); + QVariant::Type varType = static_cast<QVariant::Type>(type); + value.convert(varType); + break; + } + } + + if (!conversionOk) { + value = QVariant(); + qWarning() << "Could not convert" << str << "to" << QMetaType::typeName(type); + } + + return value; +} + +QString write(const QVariant &variant) +{ + if (!variant.isValid()) { + qWarning() << "Trying to serialize invalid QVariant"; + return QString(); + } + QString value; + switch (variant.type()) { + case QMetaType::QPoint: + { + QPoint p = variant.toPoint(); + value = QString("%1,%2").arg(QString::number(p.x()), QString::number(p.y())); + break; + } + case QMetaType::QPointF: + { + QPointF p = variant.toPointF(); + value = QString("%1,%2").arg(QString::number(p.x(), 'f'), QString::number(p.y(), 'f')); + break; + } + case QMetaType::QSize: + { + QSize s = variant.toSize(); + value = QString("%1x%2").arg(QString::number(s.width()), QString::number(s.height())); + break; + } + case QMetaType::QSizeF: + { + QSizeF s = variant.toSizeF(); + value = QString("%1x%2").arg(QString::number(s.width(), 'f'), QString::number(s.height(), 'f')); + break; + } + case QMetaType::QRect: + { + QRect r = variant.toRect(); + value = QString("%1,%2,%3x%4").arg(QString::number(r.x()), QString::number(r.y()), + QString::number(r.width()), QString::number(r.height())); + break; + } + case QMetaType::QRectF: + { + QRectF r = variant.toRectF(); + value = QString("%1,%2,%3x%4").arg(QString::number(r.x(), 'f'), QString::number(r.y(), 'f'), + QString::number(r.width(), 'f'), QString::number(r.height(), 'f')); + break; + } + default: + QVariant strVariant = variant; + strVariant.convert(QVariant::String); + if (!strVariant.isValid()) + qWarning() << Q_FUNC_INFO << "cannot serialize type " << QMetaType::typeName(variant.type()); + value = strVariant.toString(); + } + + return value; +} + +} // namespace PropertyParser +} // namespace Internal +} // namespace Designer + diff --git a/src/plugins/qmldesigner/core/model/propertyparser.h b/src/plugins/qmldesigner/core/model/propertyparser.h new file mode 100644 index 00000000000..25f6a8ef362 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/propertyparser.h @@ -0,0 +1,49 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + + #ifndef PROPERTYPARSER_H +#define PROPERTYPARSER_H + +#include <QtCore/QVariant> + +namespace QmlDesigner { +class MetaInfo; + +namespace Internal { +namespace PropertyParser { + +QVariant read(const QString &typeStr, const QString &str, const MetaInfo &metaInfo); +QVariant read(const QString &typeStr, const QString &str); +QString write(const QVariant &variant, const MetaInfo &metaInfo); + +} // namespace PropertyParser +} // namespace Internal +} // namespace Designer + +#endif // PROPERTYPARSER_H diff --git a/src/plugins/qmldesigner/core/model/qmlanchors.cpp b/src/plugins/qmldesigner/core/model/qmlanchors.cpp new file mode 100644 index 00000000000..9cb00a5de06 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/qmlanchors.cpp @@ -0,0 +1,411 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "qmlanchors.h" +#include "variantproperty.h" +#include "bindingproperty.h" +#include "nodeabstractproperty.h" +#include "nodeinstance.h" +#include "rewritertransaction.h" +#include "qmlmodelview.h" +#include "mathutils.h" + +namespace QmlDesigner { + + +static QString lineTypeToString(AnchorLine::Type lineType) +{ + QString typeString; + + switch (lineType) { + case AnchorLine::Left: return QLatin1String("left"); + case AnchorLine::Top: return QLatin1String("top"); + case AnchorLine::Right: return QLatin1String("right"); + case AnchorLine::Bottom: return QLatin1String("bottom"); + case AnchorLine::HorizontalCenter: return QLatin1String("horizontalCenter"); + case AnchorLine::VerticalCenter: return QLatin1String("verticalCenter"); + case AnchorLine::Baseline: return QLatin1String("baseline"); + case AnchorLine::Fill: return QLatin1String("fill"); + case AnchorLine::Center: return QLatin1String("centerIn"); + default: return QString::null; + } +} + +bool AnchorLine::isHorizontalAnchorLine(Type anchorline) +{ + return anchorline & HorizontalMask; +} + +bool AnchorLine::isVerticalAnchorLine(Type anchorline) +{ + return anchorline & VerticalMask; +} + +static AnchorLine::Type propertyNameToLineType(const QString & string) +{ + if (string == QLatin1String("left")) { + return AnchorLine::Left; + } else if (string == QLatin1String("top")) { + return AnchorLine::Top; + } else if (string == QLatin1String("right")) { + return AnchorLine::Right; + } else if (string == QLatin1String("bottom")) { + return AnchorLine::Bottom; + } else if (string == QLatin1String("horizontalCenter")) { + return AnchorLine::HorizontalCenter; + } else if (string == QLatin1String("verticalCenter")) { + return AnchorLine::VerticalCenter; + } else if (string == QLatin1String("baseline")) { + return AnchorLine::VerticalCenter; + } else if (string == QLatin1String("centerIn")) { + return AnchorLine::Center; + } else if (string == QLatin1String("fill")) { + return AnchorLine::Fill; + } + + return AnchorLine::Invalid; +} + +static QString marginPropertyName(AnchorLine::Type lineType) +{ + switch (lineType) { + case AnchorLine::Left: return QLatin1String("anchors.leftMargin"); + case AnchorLine::Top: return QLatin1String("anchors.topMargin"); + case AnchorLine::Right: return QLatin1String("anchors.rightMargin"); + case AnchorLine::Bottom: return QLatin1String("anchors.bottomMargin"); + case AnchorLine::HorizontalCenter: return QLatin1String("anchors.horizontalCenterOffset"); + case AnchorLine::VerticalCenter: return QLatin1String("anchors.verticalCenterOffset"); + default: return QString::null; + } +} + +static QString anchorPropertyName(AnchorLine::Type lineType) +{ + const QString typeString = lineTypeToString(lineType); + + if (typeString.isEmpty()) + return QString(); + else + return QString("anchors.%1").arg(typeString); +} + + +QmlAnchors::QmlAnchors(const QmlItemNode &fxItemNode) : m_qmlItemNode(fxItemNode) +{ +} + +QmlItemNode QmlAnchors::qmlItemNode() const +{ + return m_qmlItemNode; +} + +bool QmlAnchors::isValid() const +{ + return m_qmlItemNode.isValid(); +} + +void QmlAnchors::setAnchor(AnchorLine::Type sourceAnchorLine, + const QmlItemNode &targetQmlItemNode, + AnchorLine::Type targetAnchorLine) +{ + RewriterTransaction transaction = qmlItemNode().qmlModelView()->beginRewriterTransaction(); + if (qmlItemNode().isInBaseState()) { + if ((qmlItemNode().nodeInstance().hasAnchor("anchors.fill") && (sourceAnchorLine & AnchorLine::Fill)) + || ((qmlItemNode().nodeInstance().hasAnchor("anchors.centerIn") && (sourceAnchorLine & AnchorLine::Center)))) { + removeAnchor(sourceAnchorLine); + } + + const QString propertyName = anchorPropertyName(sourceAnchorLine); + QString targetExpression = targetQmlItemNode.modelNode().validId(); + if (targetQmlItemNode.modelNode() == qmlItemNode().modelNode().parentProperty().parentModelNode()) + targetExpression = "parent"; + targetExpression = targetExpression + "." + lineTypeToString(targetAnchorLine); + qmlItemNode().modelNode().bindingProperty(propertyName).setExpression(targetExpression); + } +} + +bool detectHorizontalCycle(const ModelNode &node, QList<ModelNode> knownNodeList) +{ + if (knownNodeList.contains(node)) + return true; + + knownNodeList.append(node); + + static QStringList validAnchorLines(QStringList() << "right" << "left" << "horizontalCenter"); + static QStringList anchorNames(QStringList() << "anchors.right" << "anchors.left" << "anchors.horizontalCenter"); + + foreach (const QString &anchorName, anchorNames) { + if (node.hasBindingProperty(anchorName)) { + AbstractProperty targetProperty = node.bindingProperty(anchorName).resolveToProperty(); + if (targetProperty.isValid()) { + if (!validAnchorLines.contains(targetProperty.name())) + return true; + + if (detectHorizontalCycle(targetProperty.parentModelNode(), knownNodeList)) + return true; + } + } + + } + + static QStringList anchorShortcutNames(QStringList() << "anchors.fill" << "anchors.centerIn"); + foreach (const QString &anchorName, anchorShortcutNames) { + if (node.hasBindingProperty(anchorName)) { + ModelNode targetNode = node.bindingProperty(anchorName).resolveToModelNode(); + + if (targetNode.isValid() && detectHorizontalCycle(targetNode, knownNodeList)) + return true; + } + } + + return false; +} + +bool detectVerticalCycle(const ModelNode &node, QList<ModelNode> knownNodeList) +{ + if (!node.isValid()) + return false; + + if (knownNodeList.contains(node)) + return true; + + knownNodeList.append(node); + + static QStringList validAnchorLines(QStringList() << "top" << "bottom" << "verticalCenter" << "baseline"); + static QStringList anchorNames(QStringList() << "anchors.top" << "anchors.bottom" << "anchors.verticalCenter" << "anchors.baseline"); + + foreach (const QString &anchorName, anchorNames) { + if (node.hasBindingProperty(anchorName)) { + AbstractProperty targetProperty = node.bindingProperty(anchorName).resolveToProperty(); + if (targetProperty.isValid()) { + if (!validAnchorLines.contains(targetProperty.name())) + return true; + + if (detectVerticalCycle(targetProperty.parentModelNode(), knownNodeList)) + return true; + } + } + + } + + static QStringList anchorShortcutNames(QStringList() << "anchors.fill" << "anchors.centerIn"); + foreach (const QString &anchorName, anchorShortcutNames) { + if (node.hasBindingProperty(anchorName)) { + ModelNode targetNode = node.bindingProperty(anchorName).resolveToModelNode(); + + if (targetNode.isValid() && detectVerticalCycle(targetNode, knownNodeList)) + return true; + } + } + + return false; +} + +bool QmlAnchors::canAnchor(const QmlItemNode &targetModelNode) const +{ + if (!qmlItemNode().isInBaseState()) + return false; + + if (targetModelNode == qmlItemNode().instanceParent()) + return true; + + if (qmlItemNode().instanceParent() == targetModelNode.instanceParent()) + return true; + + return false; +} + +AnchorLine::Type QmlAnchors::possibleAnchorLines(AnchorLine::Type sourceAnchorLineType, + const QmlItemNode &targetQmlItemNode) const +{ + if (!canAnchor(targetQmlItemNode)) + return AnchorLine::Invalid; + + if (AnchorLine::isHorizontalAnchorLine(sourceAnchorLineType)) { + if (!detectHorizontalCycle(targetQmlItemNode, QList<ModelNode>() << qmlItemNode().modelNode())) + return AnchorLine::HorizontalMask; + } + + if (AnchorLine::isVerticalAnchorLine(sourceAnchorLineType)) { + if (!detectVerticalCycle(targetQmlItemNode, QList<ModelNode>() << qmlItemNode().modelNode())) + return AnchorLine::VerticalMask; + } + + return AnchorLine::Invalid; +} + +AnchorLine QmlAnchors::instanceAnchor(AnchorLine::Type sourceAnchorLine) const +{ + QPair<QString, NodeInstance> targetAnchorLinePair; + if (qmlItemNode().nodeInstance().hasAnchor("anchors.fill") && (sourceAnchorLine & AnchorLine::Fill)) { + targetAnchorLinePair = qmlItemNode().nodeInstance().anchor("anchors.fill"); + targetAnchorLinePair.first = lineTypeToString(sourceAnchorLine); + } else if (qmlItemNode().nodeInstance().hasAnchor("anchors.centerIn") && (sourceAnchorLine & AnchorLine::Center)) { + targetAnchorLinePair = qmlItemNode().nodeInstance().anchor("anchors.centerIn"); + targetAnchorLinePair.first = lineTypeToString(sourceAnchorLine); + } else { + targetAnchorLinePair = qmlItemNode().nodeInstance().anchor(anchorPropertyName(sourceAnchorLine)); + } + + AnchorLine::Type targetAnchorLine = propertyNameToLineType(targetAnchorLinePair.first); + + if (targetAnchorLine == AnchorLine::Invalid ) + return AnchorLine(); + + Q_ASSERT(targetAnchorLinePair.second.isValid()); + return AnchorLine(QmlItemNode(qmlItemNode().nodeForInstance(targetAnchorLinePair.second)), targetAnchorLine); +} + +void QmlAnchors::removeAnchor(AnchorLine::Type sourceAnchorLine) +{ + RewriterTransaction transaction = qmlItemNode().qmlModelView()->beginRewriterTransaction(); + if (qmlItemNode().isInBaseState()) { + const QString propertyName = anchorPropertyName(sourceAnchorLine); + if (qmlItemNode().nodeInstance().hasAnchor("anchors.fill") && (sourceAnchorLine & AnchorLine::Fill)) { + qmlItemNode().modelNode().removeProperty("anchors.fill"); + qmlItemNode().modelNode().bindingProperty("anchors.top").setExpression("parent.top"); + qmlItemNode().modelNode().bindingProperty("anchors.left").setExpression("parent.left"); + qmlItemNode().modelNode().bindingProperty("anchors.bottom").setExpression("parent.bottom"); + qmlItemNode().modelNode().bindingProperty("anchors.right").setExpression("parent.right"); + + } else if (qmlItemNode().nodeInstance().hasAnchor("anchors.centerIn") && (sourceAnchorLine & AnchorLine::Center)) { + qmlItemNode().modelNode().removeProperty("anchors.centerIn"); + qmlItemNode().modelNode().bindingProperty("anchors.horizontalCenter").setExpression("parent.horizontalCenter"); + qmlItemNode().modelNode().bindingProperty("anchors.verticalCenter").setExpression("parent.verticalCenter"); + } + + qmlItemNode().modelNode().removeProperty(propertyName); + } +} + +void QmlAnchors::removeAnchors() +{ + RewriterTransaction transaction = qmlItemNode().qmlModelView()->beginRewriterTransaction(); + if (qmlItemNode().nodeInstance().hasAnchor("anchors.fill")) + qmlItemNode().modelNode().removeProperty("anchors.fill"); + if (qmlItemNode().nodeInstance().hasAnchor("anchors.centerIn")) + qmlItemNode().modelNode().removeProperty("anchors.centerIn"); + if (qmlItemNode().nodeInstance().hasAnchor("anchors.top")) + qmlItemNode().modelNode().removeProperty("anchors.top"); + if (qmlItemNode().nodeInstance().hasAnchor("anchors.left")) + qmlItemNode().modelNode().removeProperty("anchors.left"); + if (qmlItemNode().nodeInstance().hasAnchor("anchors.right")) + qmlItemNode().modelNode().removeProperty("anchors.right"); + if (qmlItemNode().nodeInstance().hasAnchor("anchors.bottom")) + qmlItemNode().modelNode().removeProperty("anchors.bottom"); + if (qmlItemNode().nodeInstance().hasAnchor("anchors.horizontalCenter")) + qmlItemNode().modelNode().removeProperty("anchors.horizontalCenter"); + if (qmlItemNode().nodeInstance().hasAnchor("anchors.verticalCenter")) + qmlItemNode().modelNode().removeProperty("anchors.verticalCenter"); + if (qmlItemNode().nodeInstance().hasAnchor("anchors.baseline")) + qmlItemNode().modelNode().removeProperty("anchors.baseline"); +} + +bool QmlAnchors::instanceHasAnchor(AnchorLine::Type sourceAnchorLine) const +{ + const QString propertyName = anchorPropertyName(sourceAnchorLine); + + if (sourceAnchorLine & AnchorLine::Fill) + return qmlItemNode().nodeInstance().hasAnchor(propertyName) || qmlItemNode().nodeInstance().hasAnchor("anchors.fill"); + + if (sourceAnchorLine & AnchorLine::Center) + return qmlItemNode().nodeInstance().hasAnchor(propertyName) || qmlItemNode().nodeInstance().hasAnchor("anchors.centerIn"); + + + return qmlItemNode().nodeInstance().hasAnchor(propertyName); +} + +bool QmlAnchors::instanceHasAnchors() const +{ + return instanceHasAnchor(AnchorLine::Left) || + instanceHasAnchor(AnchorLine::Right) || + instanceHasAnchor(AnchorLine::Top) || + instanceHasAnchor(AnchorLine::Bottom) || + instanceHasAnchor(AnchorLine::HorizontalCenter) || + instanceHasAnchor(AnchorLine::VerticalCenter) || + instanceHasAnchor(AnchorLine::Baseline); +} + +void QmlAnchors::setMargin(AnchorLine::Type sourceAnchorLineType, double margin) const +{ + QString propertyName = marginPropertyName(sourceAnchorLineType); + qmlItemNode().setVariantProperty(propertyName, round(margin, 4)); +} + +bool QmlAnchors::instanceHasMargin(AnchorLine::Type sourceAnchorLineType) const +{ + return !qIsNull(instanceMargin(sourceAnchorLineType)); +} + +double QmlAnchors::instanceMargin(AnchorLine::Type sourceAnchorLineType) const +{ + return qmlItemNode().nodeInstance().property(marginPropertyName(sourceAnchorLineType)).toDouble(); +} + +void QmlAnchors::removeMargin(AnchorLine::Type sourceAnchorLineType) +{ + if (qmlItemNode().isInBaseState()) { + QString propertyName = marginPropertyName(sourceAnchorLineType); + qmlItemNode().modelNode().removeProperty(propertyName); + } +} + +void QmlAnchors::removeMargins() +{ + RewriterTransaction transaction = qmlItemNode().qmlModelView()->beginRewriterTransaction(); + removeMargin(AnchorLine::Left); + removeMargin(AnchorLine::Right); + removeMargin(AnchorLine::Top); + removeMargin(AnchorLine::Bottom); + removeMargin(AnchorLine::HorizontalCenter); + removeMargin(AnchorLine::VerticalCenter); +} + +QmlItemNode AnchorLine::qmlItemNode() const +{ + return m_qmlItemNode; +} + +void QmlAnchors::fill() +{ + if (instanceHasAnchors()) + removeAnchors(); + + qmlItemNode().modelNode().bindingProperty("anchors.fill").setExpression("parent"); +} + +void QmlAnchors::centerIn() +{ + if (instanceHasAnchors()) + removeAnchors(); + + qmlItemNode().modelNode().bindingProperty("anchors.centerIn").setExpression("parent"); +} + +} //QmlDesigner diff --git a/src/plugins/qmldesigner/core/model/qmlchangeset.cpp b/src/plugins/qmldesigner/core/model/qmlchangeset.cpp new file mode 100644 index 00000000000..6637ff63512 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/qmlchangeset.cpp @@ -0,0 +1,72 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "qmlchangeset.h" +#include "bindingproperty.h" +#include "variantproperty.h" +#include "qmlmodelview.h" +#include <metainfo.h> + +namespace QmlDesigner { + +ModelNode QmlModelStateOperation::target() const +{ + if (modelNode().property("target").isBindingProperty()) { + return modelNode().bindingProperty("target").resolveToModelNode(); + } else { + return ModelNode(); //exception? + } +} + +void QmlModelStateOperation::setTarget(const ModelNode &target) +{ + modelNode().bindingProperty("target").setExpression(target.id()); +} + +bool QmlPropertyChanges::isValid() const +{ + return QmlModelNodeFacade::isValid() && modelNode().metaInfo().isSubclassOf("Qt/PropertyChanges", 4, 6); +} + +bool QmlModelStateOperation::isValid() const +{ + return QmlModelNodeFacade::isValid() && modelNode().metaInfo().isSubclassOf("Qt/QmlPropertyChanges", 4, 6); +} + +void QmlPropertyChanges::removeProperty(const QString &name) +{ + RewriterTransaction transaction(qmlModelView()->beginRewriterTransaction()); + if (name == "name") + return; + modelNode().removeProperty(name); + if (modelNode().variantProperties().isEmpty() && modelNode().bindingProperties().count() < 2) + modelNode().destroy(); +} + +} //QmlDesigner diff --git a/src/plugins/qmldesigner/core/model/qmlitemnode.cpp b/src/plugins/qmldesigner/core/model/qmlitemnode.cpp new file mode 100644 index 00000000000..303c4ea1997 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/qmlitemnode.cpp @@ -0,0 +1,330 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "qmlitemnode.h" +#include <metainfo.h> +#include "qmlview.h" +#include "qmlchangeset.h" +#include "variantproperty.h" +#include "nodelistproperty.h" +#include "nodeinstance.h" +#include "qmlanchors.h" +#include "invalidmodelnodeexception.h" +#include "rewritertransaction.h" +#include "qmlmodelview.h" +#include "mathutils.h" + +namespace QmlDesigner { + + +bool QmlItemNode::isValid() const +{ + return QmlModelNodeFacade::isValid() && modelNode().metaInfo().isSubclassOf("Qt/Item", 4, 6); +} + +bool QmlItemNode::isRootNode() const +{ + return modelNode().isValid() && modelNode().isRootNode(); +} + +QStringList QmlModelStateGroup::names() const +{ + QStringList returnList; + + if (!modelNode().isValid()) + throw new InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + if (modelNode().property("states").isNodeListProperty()) { + foreach (const ModelNode &node, modelNode().nodeListProperty("states").toModelNodeList()) { + if (QmlModelState(node).isValid()) + returnList.append(QmlModelState(node).name()); + } + } + return returnList; +} + +/** + \brief Returns list of states (without 'base state'). + The list contains all states defined by this item. + */ +QmlModelStateGroup QmlItemNode::states() const +{ + if (isValid()) + return QmlModelStateGroup(modelNode()); + else + return QmlModelStateGroup(); +} + +QList<QmlItemNode> QmlItemNode::children() const +{ + QList<QmlItemNode> returnList; + + if (isValid()) { + + QList<ModelNode> modelNodeList; + + if (modelNode().hasProperty("children")) { + Q_ASSERT(modelNode().property("children").isNodeListProperty()); + modelNodeList.append(modelNode().nodeListProperty("children").toModelNodeList()); + } + + if (modelNode().hasProperty("data")) { + Q_ASSERT(modelNode().property("data").isNodeListProperty()); + modelNodeList.append(modelNode().nodeListProperty("data").toModelNodeList()); + } + + foreach (const ModelNode &modelNode, modelNodeList) { + if (QmlItemNode(modelNode).isValid()) //if ModelNode is FxItem + returnList.append(modelNode); + } + } + return returnList; +} + +QList<QmlObjectNode> QmlItemNode::resources() const +{ + QList<QmlObjectNode> returnList; + + if (isValid()) { + QList<ModelNode> modelNodeList; + if (modelNode().hasProperty("resources")) { + Q_ASSERT(modelNode().property("resources").isNodeListProperty()); + modelNodeList.append(modelNode().nodeListProperty("resources").toModelNodeList()); + } + + if (modelNode().hasProperty("data")) { + Q_ASSERT(modelNode().property("data").isNodeListProperty()); + modelNodeList.append(modelNode().nodeListProperty("data").toModelNodeList()); + } + + foreach (const ModelNode &node, modelNodeList) { + if (!QmlItemNode(node).isValid()) //if ModelNode is no FxItem + returnList.append(node); + } + } + return returnList; +} + +QmlAnchors QmlItemNode::anchors() const +{ + return QmlAnchors(*this); +} + +bool QmlItemNode::hasChildren() const +{ + return !children().isEmpty(); +} + +bool QmlItemNode::hasResources() const +{ + return !resources().isEmpty(); +} + +bool QmlItemNode::instanceHasAnchors() const +{ + return anchors().instanceHasAnchors(); +} + +bool QmlItemNode::hasShowContent() const +{ + return nodeInstance().hasContent(); +} + +bool QmlItemNode::canReparent() const +{ + return QmlObjectNode::canReparent() && !anchors().instanceHasAnchors() && !instanceIsAnchoredBy(); +} + +bool QmlItemNode::instanceIsAnchoredBy() const +{ + return nodeInstance().isAnchoredBy(); +} + +QRectF QmlItemNode::instanceBoundingRect() const +{ + return nodeInstance().boundingRect(); +} + +QTransform QmlItemNode::instanceTransform() const +{ + return nodeInstance().transform(); +} + +QTransform QmlItemNode::instanceSceneTransform() const +{ + return nodeInstance().sceneTransform(); +} + +QPointF QmlItemNode::instancePosition() const +{ + return nodeInstance().position(); +} + +QSizeF QmlItemNode::instanceSize() const +{ + return nodeInstance().size(); +} + +void QmlItemNode::paintInstance(QPainter *painter) const +{ + if (nodeInstance().isValid()) + nodeInstance().paint(painter); +} + +void QmlItemNode::setSize(const QSizeF &size) +{ + RewriterTransaction transaction = qmlModelView()->beginRewriterTransaction(); + setVariantProperty("width", round(size.width(), 4)); + setVariantProperty("height", round(size.height(), 4)); +} + +void QmlItemNode::setPosition(const QPointF &position) +{ + RewriterTransaction transaction = qmlModelView()->beginRewriterTransaction(); + setVariantProperty("x", round(position.x(), 4)); + setVariantProperty("y", round(position.y(), 4)); +} + +void QmlItemNode::selectNode() +{ + modelNode().selectNode(); +} + +void QmlItemNode::deselectNode() +{ + modelNode().deselectNode(); +} + +bool QmlItemNode::isSelected() const +{ + return modelNode().isSelected(); +} + +QList<QmlModelState> QmlModelStateGroup::allStates() const +{ + QList<QmlModelState> returnList; + + if (!modelNode().isValid()) + throw new InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + if (modelNode().property("states").isNodeListProperty()) { + foreach (const ModelNode &node, modelNode().nodeListProperty("states").toModelNodeList()) { + if (QmlModelState(node).isValid()) + returnList.append(QmlModelState(node)); + } + } + return returnList; +} + +QString QmlItemNode::simplfiedTypeName() const +{ + return modelNode().simplifiedTypeName(); +} + +uint qHash(const QmlItemNode &node) +{ + return qHash(node.modelNode()); +} + +QmlModelState QmlModelStateGroup::addState(const QString &name) +{ + if (!modelNode().isValid()) + throw new InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + + PropertyListType propertyList; + propertyList.append(qMakePair(QString("name"), QVariant(name))); + + ModelNode newState(modelNode().addChildNode("Qt/State", 4, 6, "states", propertyList)); + + return newState; +} + +void QmlModelStateGroup::removeState(const QString &name) +{ + if (!modelNode().isValid()) + throw new InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + if (state(name).isValid()) + state(name).modelNode().destroy(); +} + +QmlModelState QmlModelStateGroup::state(const QString &name) const +{ + if (!modelNode().isValid()) + throw new InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + if (modelNode().property("states").isNodeListProperty()) { + foreach (const ModelNode &node, modelNode().nodeListProperty("states").toModelNodeList()) { + if (QmlModelState(node).name() == name) + return node; + } + } + return QmlModelState(); +} + +QList<ModelNode> toModelNodeList(const QList<QmlItemNode> &qmlItemNodeList) +{ + QList<ModelNode> modelNodeList; + + foreach (const QmlItemNode &qmlItemNode, qmlItemNodeList) + modelNodeList.append(qmlItemNode.modelNode()); + + return modelNodeList; +} + +QList<QmlItemNode> toQmlItemNodeList(const QList<ModelNode> &modelNodeList) +{ + QList<QmlItemNode> qmlItemNodeList; + + foreach (const ModelNode &modelNode, modelNodeList) { + QmlItemNode itemNode(modelNode); + if (itemNode.isValid()) + qmlItemNodeList.append(itemNode); + } + + return qmlItemNodeList; +} + +const QList<QmlItemNode> QmlItemNode::allDirectSubModelNodes() const +{ + return toQmlItemNodeList(modelNode().allDirectSubModelNodes()); +} + +const QList<QmlItemNode> QmlItemNode::allSubModelNodes() const +{ + return toQmlItemNodeList(modelNode().allSubModelNodes()); +} + +bool QmlItemNode::hasAnySubModelNodes() const +{ + return modelNode().hasAnySubModelNodes(); +} + +} //QmlDesigner diff --git a/src/plugins/qmldesigner/core/model/qmlmodelnodefacade.cpp b/src/plugins/qmldesigner/core/model/qmlmodelnodefacade.cpp new file mode 100644 index 00000000000..63e774723d1 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/qmlmodelnodefacade.cpp @@ -0,0 +1,59 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "qmlmodelnodefacade.h" +#include "qmlmodelview.h" +#include <QtDebug> +namespace QmlDesigner { + +QmlModelNodeFacade::QmlModelNodeFacade() : m_modelNode(ModelNode()) +{} + + +QmlModelNodeFacade::QmlModelNodeFacade(const ModelNode &modelNode) : m_modelNode(modelNode) +{} + +QmlModelNodeFacade::~QmlModelNodeFacade() +{} + +bool QmlModelNodeFacade::isValid() const +{ + return modelNode().isValid() && qmlModelView() && qmlModelView()->hasInstanceForModelNode(modelNode()) && qmlModelView()->instanceForModelNode(modelNode()).isValid(); +} + +QmlModelView* QmlModelNodeFacade::qmlModelView() const +{ + return modelNode().view()->toQmlModelView(); +} + +bool QmlModelNodeFacade::isRootNode() const +{ + return modelNode().isRootNode(); +} +} //QmlDesigner diff --git a/src/plugins/qmldesigner/core/model/qmlmodelview.cpp b/src/plugins/qmldesigner/core/model/qmlmodelview.cpp new file mode 100644 index 00000000000..d8103a8a5ce --- /dev/null +++ b/src/plugins/qmldesigner/core/model/qmlmodelview.cpp @@ -0,0 +1,321 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "qmlmodelview.h" +#include "qmlobjectnode.h" +#include "qmlitemnode.h" +#include "itemlibraryinfo.h" +#include "modelutilities.h" +#include "mathutils.h" +#include <QDir> +#include <QFileInfo> +#include <QDebug> + +namespace QmlDesigner { + +QmlModelView::QmlModelView(QObject *parent) + : ForwardView<NodeInstanceView>(parent) +{ + appendView(new NodeInstanceView(this)); + connect(nodeInstanceView(), SIGNAL(transformPropertyChanged(NodeInstance)), SLOT(notifyTransformChanged(NodeInstance))); + connect(nodeInstanceView(), SIGNAL(parentPropertyChanged(NodeInstance)), SLOT(notifyParentChanged(NodeInstance))); + connect(nodeInstanceView(), SIGNAL(otherPropertyChanged(NodeInstance)), SLOT(notifyOtherPropertyChanged(NodeInstance))); + connect(nodeInstanceView(), SIGNAL(updateItem(NodeInstance)), SLOT(notifyUpdateItem(NodeInstance))); +} + +void QmlModelView::setCurrentState(const QmlModelState &state) +{ + if (m_state == state) + return; + QmlModelState oldState = m_state; + emitCustomNotification("__state changed__", QList<ModelNode>() << state.modelNode()); +} + +QmlModelState QmlModelView::currentState() const +{ + return m_state; +} + +QmlModelState QmlModelView::baseState() const +{ + return QmlModelState::createBaseState(this); +} + +QmlObjectNode QmlModelView::createQmlObjectNode(const QString &typeString, + int majorVersion, + int minorVersion, + const PropertyListType &propertyList) +{ + return QmlObjectNode(createModelNode(typeString, majorVersion, minorVersion, propertyList)); +} + +QmlItemNode QmlModelView::createQmlItemNode(const QString &typeString, + int majorVersion, + int minorVersion, + const PropertyListType &propertyList) +{ + return createQmlObjectNode(typeString, majorVersion, minorVersion, propertyList).toQmlItemNode(); +} + +QmlItemNode QmlModelView::createQmlItemNodeFromImage(const QString &imageName, const QPointF &position, QmlItemNode parentNode) +{ + if (!parentNode.isValid()) + parentNode = rootQmlItemNode(); + + if (!parentNode.isValid()) + return QmlItemNode(); + + QmlItemNode newNode; + RewriterTransaction transaction = beginRewriterTransaction(); + { + QList<QPair<QString, QVariant> > propertyPairList; + propertyPairList.append(qMakePair(QString("x"), QVariant( round(position.x(), 4)))); + propertyPairList.append(qMakePair(QString("y"), QVariant( round(position.y(), 4)))); + + QString relativeImageName = imageName; + + //use relative path + if (QFileInfo(model()->fileUrl().toLocalFile()).exists()) { + QDir fileDir(QFileInfo(model()->fileUrl().toLocalFile()).absolutePath()); + relativeImageName = fileDir.relativeFilePath(imageName); + } + + propertyPairList.append(qMakePair(QString("source"), QVariant(relativeImageName))); + newNode = createQmlItemNode("Qt/Image",4, 6, propertyPairList); + parentNode.nodeAbstractProperty("data").reparentHere(newNode); + + Q_ASSERT(newNode.isValid()); + + QString id; + int i = 1; + QString name("image"); + name.replace(" ", ""); + do { + id = name + QString::number(i); + i++; + } while (hasId(id)); //If the name already exists count upwards + + newNode.setId(id); + + Q_ASSERT(newNode.isValid()); + } + + Q_ASSERT(newNode.isValid()); + + return newNode; +} + +QmlItemNode QmlModelView::createQmlItemNode(const ItemLibraryInfo &itemLibraryRepresentation, const QPointF &position, QmlItemNode parentNode) +{ + if (!parentNode.isValid()) + parentNode = rootQmlItemNode(); + + Q_ASSERT(parentNode.isValid()); + + + QmlItemNode newNode; + RewriterTransaction transaction = beginRewriterTransaction(); + { + QList<QPair<QString, QVariant> > propertyPairList; + propertyPairList.append(qMakePair(QString("x"), QVariant(round(position.x(), 4)))); + propertyPairList.append(qMakePair(QString("y"), QVariant(round(position.y(), 4)))); + + foreach (const PropertyContainer &property, itemLibraryRepresentation.properties()) + propertyPairList.append(qMakePair(property.name(), property.value())); + + qDebug() << itemLibraryRepresentation.typeName(); + + newNode = createQmlItemNode(itemLibraryRepresentation.typeName(), itemLibraryRepresentation.majorVersion(), itemLibraryRepresentation.minorVersion(), propertyPairList); + parentNode.nodeAbstractProperty("data").reparentHere(newNode); + + Q_ASSERT(newNode.isValid()); + + QString id; + int i = 1; + QString name(itemLibraryRepresentation.name()); + name.replace(" ", ""); + do { + id = name + QString::number(i); + i++; + } while (hasId(id)); //If the name already exists count upwards + + newNode.setId(id); + + Q_ASSERT(newNode.isValid()); + } + + Q_ASSERT(newNode.isValid()); + + return newNode; +} + +QmlObjectNode QmlModelView::rootQmlObjectNode() const +{ + return QmlObjectNode(rootModelNode()); +} + +QmlItemNode QmlModelView::rootQmlItemNode() const +{ + return rootQmlObjectNode().toQmlItemNode(); +} + +void QmlModelView::setSelectedQmlObjectNodes(const QList<QmlObjectNode> &selectedNodeList) +{ + setSelectedModelNodes(QmlDesigner::toModelNodeList(selectedNodeList)); +} + +void QmlModelView::selectQmlObjectNode(const QmlObjectNode &node) +{ + selectModelNode(node.modelNode()); +} + +void QmlModelView::deselectQmlObjectNode(const QmlObjectNode &node) +{ + deselectModelNode(node.modelNode()); +} + +QList<QmlObjectNode> QmlModelView::selectedQmlObjectNodes() const +{ + return toQmlObjectNodeList(selectedModelNodes()); +} + +QList<QmlItemNode> QmlModelView::selectedQmlItemNodes() const +{ + return toQmlItemNodeList(selectedModelNodes()); +} + +void QmlModelView::setSelectedQmlItemNodes(const QList<QmlItemNode> &selectedNodeList) +{ + return setSelectedModelNodes(QmlDesigner::toModelNodeList(selectedNodeList)); +} + +QmlObjectNode QmlModelView::fxObjectNodeForId(const QString &id) +{ + return QmlObjectNode(modelNodeForId(id)); +} + + +void QmlModelView::notifyTransformChanged(const NodeInstance &nodeInstance) +{ + transformChanged(QmlObjectNode(ModelNode(nodeInstance.modelNode(), this))); +} + +void QmlModelView::notifyParentChanged(const NodeInstance &nodeInstance) +{ + parentChanged(QmlObjectNode(ModelNode(nodeInstance.modelNode(), this))); +} + +void QmlModelView::notifyOtherPropertyChanged(const NodeInstance &nodeInstance) +{ + otherPropertyChanged(QmlObjectNode(ModelNode(nodeInstance.modelNode(), this))); +} + +void QmlModelView::notifyUpdateItem(const NodeInstance &nodeInstance) +{ + updateItem(QmlObjectNode(ModelNode(nodeInstance.modelNode(), this))); +} + +void QmlModelView::customNotification(const AbstractView * /* view */, const QString &identifier, const QList<ModelNode> &nodeList, const QList<QVariant> & /* data */) +{ + if (identifier == "__state changed__") { + QmlModelState state(nodeList.first()); + if (state.isValid()) + stateChanged(state, currentState()); + else + stateChanged(baseState(), currentState()); + } +} + +NodeInstance QmlModelView::instanceForModelNode(const ModelNode &modelNode) +{ + return nodeInstanceView()->instanceForNode(modelNode); +} + +bool QmlModelView::hasInstanceForModelNode(const ModelNode &modelNode) +{ + return nodeInstanceView()->hasInstanceForNode(modelNode); +} + +NodeInstanceView *QmlModelView::nodeInstanceView() const +{ + return firstView(); +} + +void QmlModelView::modelAttached(Model *model) +{ + m_state = QmlModelState(); + ForwardView<NodeInstanceView>::modelAttached(model); + m_state = baseState(); + Q_ASSERT(m_state.isBaseState()); +} + +void QmlModelView::modelAboutToBeDetached(Model *model) +{ + ForwardView<NodeInstanceView>::modelAboutToBeDetached(model); + m_state = QmlModelState(); +} + +void QmlModelView::transformChanged(const QmlObjectNode &/*qmlObjectNode*/) +{ +} + +void QmlModelView::parentChanged(const QmlObjectNode &/*qmlObjectNode*/) +{ + +} + +void QmlModelView::otherPropertyChanged(const QmlObjectNode &/*qmlObjectNode*/) +{ + +} + +void QmlModelView::updateItem(const QmlObjectNode &/*qmlObjectNode*/) +{ + +} + +void QmlModelView::stateChanged(const QmlModelState &newQmlModelState, const QmlModelState &oldQmlModelState) +{ + m_state = newQmlModelState; + if (newQmlModelState.isValid()) { + NodeInstance newStateInstance = instanceForModelNode(newQmlModelState.modelNode()); + Q_ASSERT(newStateInstance.isValid()); + if (!newQmlModelState.isBaseState()) + newStateInstance.setPropertyVariant("__activateState", true); + } + + if (oldQmlModelState.isValid()) { + NodeInstance oldStateInstance = instanceForModelNode(oldQmlModelState.modelNode()); + Q_ASSERT(oldStateInstance.isValid()); + if (!oldQmlModelState.isBaseState()) + oldStateInstance.setPropertyVariant("__activateState", false); + } +} + +} //QmlDesigner diff --git a/src/plugins/qmldesigner/core/model/qmlobjectnode.cpp b/src/plugins/qmldesigner/core/model/qmlobjectnode.cpp new file mode 100644 index 00000000000..f2a25cca35c --- /dev/null +++ b/src/plugins/qmldesigner/core/model/qmlobjectnode.cpp @@ -0,0 +1,401 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "qmlobjectnode.h" +#include "qmlitemnode.h" +#include "qmlitemnode.h" +#include "qmlstate.h" +#include "variantproperty.h" +#include "nodeproperty.h" +#include <invalidmodelnodeexception.h> +#include "qmlmodelview.h" +#include "nodeinstanceview.h" +#include "nodeinstance.h" + +namespace QmlDesigner { + +void QmlObjectNode::setVariantProperty(const QString &name, const QVariant &value) +{ + if (!isValid()) + throw new InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + if (isInBaseState()) { + modelNode().variantProperty(name) = value; //basestate + } else { + modelNode().validId(); + + QmlPropertyChanges changeSet(currentState().propertyChanges(modelNode())); + Q_ASSERT(changeSet.isValid()); + changeSet.modelNode().variantProperty(name) = value; + } +} + +void QmlObjectNode::setBindingProperty(const QString &name, const QString &expression) +{ + if (!isValid()) + throw new InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + if (isInBaseState()) { + modelNode().bindingProperty(name) = expression; //basestate + } else { + QmlPropertyChanges changeSet(currentState().propertyChanges(modelNode())); + Q_ASSERT(changeSet.isValid()); + changeSet.modelNode().bindingProperty(name) = expression; + } +} + +QmlModelState QmlObjectNode::currentState() const +{ + if (isValid()) + return qmlModelView()->currentState(); + else + return QmlModelState(); +} + +bool QmlObjectNode::isRootModelNode() const +{ + return modelNode().isRootNode(); +} + + +/*! \brief returns the value of a property based on an actual instance +The return value is not the value in the model, but the value of a real +instanciated instance of this object. +\return the value of this property based on the instance + +*/ +QVariant QmlObjectNode::instanceValue(const QString &name) const +{ + Q_ASSERT(qmlModelView()->hasInstanceForModelNode(modelNode())); + return qmlModelView()->instanceForModelNode(modelNode()).property(name); +} + + +bool QmlObjectNode::hasProperty(const QString &name) const +{ + if (!isValid()) + throw new InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + return modelNode().hasProperty(name); +} + +NodeAbstractProperty QmlObjectNode::nodeAbstractProperty(const QString &name) const +{ + return modelNode().nodeAbstractProperty(name); +} + +NodeProperty QmlObjectNode::nodeProperty(const QString &name) const +{ + return modelNode().nodeProperty(name); +} + +NodeListProperty QmlObjectNode::nodeListProperty(const QString &name) const +{ + return modelNode().nodeListProperty(name); +} + +bool QmlObjectNode::propertyAffectedByCurrentState(const QString &name) const +{ + if (!isValid()) + throw new InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + if (currentState().isBaseState()) + return modelNode().hasProperty(name); + + if (!currentState().hasPropertyChanges(modelNode())) + return false; + + return currentState().propertyChanges(modelNode()).modelNode().hasProperty(name); +} + +QVariant QmlObjectNode::modelValue(const QString &name) const +{ + if (!isValid()) + throw new InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + if (currentState().isBaseState()) + return modelNode().variantProperty(name).value(); + + if (!currentState().hasPropertyChanges(modelNode())) + return modelNode().variantProperty(name).value(); + + QmlPropertyChanges propertyChanges(currentState().propertyChanges(modelNode())); + + if (!propertyChanges.modelNode().hasProperty(name)) + return modelNode().variantProperty(name).value(); + + return propertyChanges.modelNode().variantProperty(name).value(); +} + +/*! \brief returns if ObjectNode is the BaseState + +\return true if the ObjectNode is in the BaseState + +*/ +bool QmlObjectNode::isInBaseState() const +{ + return currentState().isBaseState(); +} + +bool QmlObjectNode::canReparent() const +{ + return isInBaseState(); +} + +QmlPropertyChanges QmlObjectNode::propertyChangeForCurrentState() const +{ + if (!isValid()) + throw new InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + if (currentState().isBaseState()) + return QmlPropertyChanges(); + + if (!currentState().hasPropertyChanges(modelNode())) + return QmlPropertyChanges(); + + return currentState().propertyChanges(modelNode()); +} + +static void removeStateOperationsForChildren(const QmlObjectNode &node) +{ + if (node.isValid()) { + foreach (QmlModelStateOperation stateOperation, node.allAffectingStatesOperations()) { + stateOperation.modelNode().destroy(); //remove of belonging StatesOperations + } + + foreach (const QmlObjectNode &childNode, node.modelNode().allDirectSubModelNodes()) { + removeStateOperationsForChildren(childNode); + } + } +} + + +/*! \brief Deletes this objects ModeNode and its dependencies from the model +Every thing that belongs to this Object, the ModelNode and ChangeOperations +are deleted from the model. + +*/ +void QmlObjectNode::destroy() +{ + if (!isValid()) + throw new InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + foreach (QmlModelStateOperation stateOperation, allAffectingStatesOperations()) { + stateOperation.modelNode().destroy(); //remove of belonging StatesOperations + } + removeStateOperationsForChildren(modelNode()); + modelNode().destroy(); +} + +/*! \brief Returns a list of all states that are affecting this object. + +\return list of states affecting this object +*/ + +QList<QmlModelState> QmlObjectNode::allAffectingStates() const +{ + if (!isValid()) + throw new InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + QList<QmlModelState> returnList; + + foreach (const QmlModelState &state, allDefinedStates()) { + if (state.affectsModelNode(modelNode())) + returnList.append(state); + } + return returnList; +} + +/*! \brief Returns a list of all state operations that are affecting this object. + +\return list of state operations affecting this object +*/ + +QList<QmlModelStateOperation> QmlObjectNode::allAffectingStatesOperations() const +{ + if (!isValid()) + throw new InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + QList<QmlModelStateOperation> returnList; + foreach (const QmlModelState &state, allDefinedStates()) { + if (state.affectsModelNode(modelNode())) + returnList.append(state.stateOperations(modelNode())); + } + + return returnList; +} + +static QList<QmlItemNode> allFxItemsRecursive(const QmlItemNode &fxNode) +{ + QList<QmlItemNode> returnList; + + if (fxNode.isValid()) { + returnList.append(fxNode); + QList<QmlItemNode> allChildNodes; + foreach (const ModelNode &node, fxNode.modelNode().allDirectSubModelNodes()) { + if (QmlItemNode(node).isValid()) + allChildNodes.append(node); + } + foreach (const QmlItemNode &node, allChildNodes) { + returnList.append(allFxItemsRecursive(node)); + } + } + return returnList; +} + +QList<QmlModelState> QmlObjectNode::allDefinedStates() const +{ + if (!isValid()) + throw new InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + QList<QmlModelState> returnList; + + QList<QmlItemNode> allFxItems; + + QmlItemNode rootNode(qmlModelView()->rootModelNode()); + + if (rootNode.isValid()) + allFxItems.append(allFxItemsRecursive(rootNode)); + + foreach (const QmlItemNode &item, allFxItems) { + returnList.append(item.states().allStates()); + } + + return returnList; +} + + +/*! \brief Removes a variant property of this object from the model + +*/ + +void QmlObjectNode::removeVariantProperty(const QString &name) +{ + if (!isValid()) + throw new InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + if (isInBaseState()) { + modelNode().removeProperty(name); //basestate + } else { + QmlPropertyChanges changeSet(currentState().propertyChanges(modelNode())); + Q_ASSERT(changeSet.isValid()); + changeSet.removeProperty(name); + } +} + +QList<ModelNode> toModelNodeList(const QList<QmlObjectNode> &fxObjectNodeList) +{ + QList<ModelNode> modelNodeList; + + foreach(const QmlObjectNode &fxObjectNode, fxObjectNodeList) + modelNodeList.append(fxObjectNode.modelNode()); + + return modelNodeList; +} + +QList<QmlObjectNode> toQmlObjectNodeList(const QList<ModelNode> &modelNodeList) +{ + QList<QmlObjectNode> qmlObjectNodeList; + + foreach(const ModelNode &modelNode, modelNodeList) { + QmlObjectNode objectNode(modelNode); + if (objectNode.isValid()) + qmlObjectNodeList.append(objectNode); + } + + return qmlObjectNodeList; +} + +bool QmlObjectNode::isAncestorOf(const QmlObjectNode &objectNode) const +{ + return modelNode().isAncestorOf(objectNode.modelNode()); +} + +NodeInstance QmlObjectNode::nodeInstance() const +{ + return qmlModelView()->nodeInstanceView()->instanceForNode(modelNode()); +} + +QmlObjectNode QmlObjectNode::nodeForInstance(const NodeInstance &instance) const +{ + return QmlObjectNode(ModelNode(instance.modelNode(), qmlModelView())); +} + +bool QmlObjectNode::hasNodeParent() const +{ + return modelNode().hasParentProperty(); +} + +bool QmlObjectNode::hasInstanceParent() const +{ + return nodeInstance().hasParent(); +} + + +void QmlObjectNode::setParentProperty(const NodeAbstractProperty &parentProeprty) +{ + return modelNode().setParentProperty(parentProeprty); +} + +QmlObjectNode QmlObjectNode::instanceParent() const +{ + return nodeForInstance(nodeInstance().parent()); +} + +void QmlObjectNode::setId(const QString &id) +{ + modelNode().setId(id); +} + +QString QmlObjectNode::id() const +{ + return modelNode().id(); +} + +QString QmlObjectNode::validId() +{ + return modelNode().validId(); +} + +void QmlObjectNode::setParent(QmlObjectNode newParent) +{ + newParent.modelNode().nodeListProperty("data").reparentHere(modelNode()); + //TODO use metasystem for default Property +} + +QmlItemNode QmlObjectNode::toQmlItemNode() const +{ + return QmlItemNode(modelNode()); +} + +uint qHash(const QmlObjectNode &node) +{ + return qHash(node.modelNode()); +} +} //QmlDesigner diff --git a/src/plugins/qmldesigner/core/model/qmlstate.cpp b/src/plugins/qmldesigner/core/model/qmlstate.cpp new file mode 100644 index 00000000000..774fc5ab700 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/qmlstate.cpp @@ -0,0 +1,288 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "qmlstate.h" +#include "qmlmodelview.h" +#include <nodelistproperty.h> +#include <variantproperty.h> +#include <metainfo.h> +#include <invalidmodelnodeexception.h> + + +namespace QmlDesigner { + +QmlModelState::QmlModelState() + : QmlModelNodeFacade(), + m_isBaseState(false) +{ +} + +QmlModelState::QmlModelState(const ModelNode &modelNode) + : QmlModelNodeFacade(modelNode), + m_isBaseState(false) +{ +} + +QmlPropertyChanges QmlModelState::propertyChanges(const ModelNode &node) +{ + //### exception if not valid + + if (isBaseState()) + return QmlPropertyChanges(); + + addChangeSetIfNotExists(node); + foreach (const ModelNode &childNode, modelNode().nodeListProperty("changes").toModelNodeList()) { + //### exception if not valid QmlModelStateOperation + if (QmlPropertyChanges(childNode).target().isValid() && QmlPropertyChanges(childNode).target() == node) + return QmlPropertyChanges(childNode); //### exception if not valid(childNode); + } + return QmlPropertyChanges(); //not found +} + +QList<QmlModelStateOperation> QmlModelState::stateOperations(const ModelNode &node) const +{ + QList<QmlModelStateOperation> returnList; + //### exception if not valid + + if (isBaseState()) + return returnList; + + if (!modelNode().hasProperty("changes")) + return returnList; + + Q_ASSERT(modelNode().property("changes").isNodeListProperty()); + + foreach (const ModelNode &childNode, modelNode().nodeListProperty("changes").toModelNodeList()) { + //### exception if not valid QmlModelStateOperation + if (QmlModelStateOperation(childNode).target() == node) + returnList.append(QmlModelStateOperation(childNode)); //### exception if not valid(childNode); + } + return returnList; //not found +} + +QList<QmlPropertyChanges> QmlModelState::propertyChanges() const +{ + //### exception if not valid + QList<QmlPropertyChanges> returnList; + + if (isBaseState()) + return returnList; + + if (!modelNode().hasProperty("changes")) + return returnList; + + Q_ASSERT(modelNode().property("changes").isNodeListProperty()); + + foreach (const ModelNode &childNode, modelNode().nodeListProperty("changes").toModelNodeList()) { + //### exception if not valid QmlModelStateOperation + if (QmlPropertyChanges(childNode).isValid()) + returnList.append(QmlPropertyChanges(childNode)); + } + return returnList; +} + + +bool QmlModelState::hasPropertyChanges(const ModelNode &node) const +{ + //### exception if not valid + + if (isBaseState()) + return false; + + foreach(const QmlPropertyChanges &changeSet, propertyChanges()) { + if (changeSet.target().isValid() && changeSet.target() == node) + return true; + } + return false; +} + +bool QmlModelState::hasStateOperation(const ModelNode &node) const +{ + //### exception if not valid + + if (isBaseState()) + return false; + + foreach(const QmlModelStateOperation &stateOperation, stateOperations()) { + if (stateOperation.target() == node) + return true; + } + return false; +} + +QList<QmlModelStateOperation> QmlModelState::stateOperations() const +{ + //### exception if not valid + QList<QmlModelStateOperation> returnList; + + if (isBaseState()) + return returnList; + + if (!modelNode().hasProperty("changes")) + return returnList; + + Q_ASSERT(modelNode().property("changes").isNodeListProperty()); + + foreach (const ModelNode &childNode, modelNode().nodeListProperty("changes").toModelNodeList()) { + //### exception if not valid QmlModelStateOperation + if (QmlModelStateOperation(childNode).isValid()) + returnList.append(QmlModelStateOperation(childNode)); + } + return returnList; +} + + +/*! \brief Add a ChangeSet for the specified ModelNode to this state + The new ChangeSet if only added if no ChangeSet for the ModelNode + does not exist, yet. +*/ + +void QmlModelState::addChangeSetIfNotExists(const ModelNode &node) +{ + //### exception if not valid + + if (!isValid()) + throw new InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + if (hasPropertyChanges(node)) { + return; //changeSet already there + } + + ModelNode newChangeSet(modelNode().addChildNode("Qt/PropertyChanges", 4, 6, "changes")); + QmlPropertyChanges(newChangeSet).setTarget(node); + Q_ASSERT(QmlPropertyChanges(newChangeSet).isValid()); +} + +void QmlModelState::removePropertyChanges(const ModelNode &node) +{ + //### exception if not valid + + if (isBaseState()) + return; + + QmlPropertyChanges theChangeSet(propertyChanges(node)); + if (theChangeSet.isValid()) + theChangeSet.modelNode().destroy(); +} + + + +/*! \brief Returns if this state affects the specified ModelNode + +\return true if this state affects the specifigied ModelNode +*/ +bool QmlModelState::affectsModelNode(const ModelNode &node) const +{ + if (isBaseState()) + return false; + + return !stateOperations(node).isEmpty(); +} + +QString QmlModelState::name() const +{ + if (isBaseState()) + return QString(""); + + return modelNode().variantProperty("name").value().toString(); +} + +void QmlModelState::setName(const QString &name) +{ + if ((!isBaseState()) && (modelNode().isValid())) + modelNode().variantProperty("name").setValue(name); +} + +bool QmlModelState::isValid() const +{ + return QmlModelNodeFacade::isValid() && + (modelNode().metaInfo().isSubclassOf("Qt/State", 4, 6) || isBaseState()); +} + +/** + Removes state node & all subnodes. + */ +void QmlModelState::destroy() +{ + Q_ASSERT(isValid()); + modelNode().destroy(); +} + +/*! \brief Returns if this state is the base state + +\return true if this state is the base state +*/ + +bool QmlModelState::isBaseState() const +{ + return m_isBaseState && modelNode().isRootNode(); +} + +QmlModelState QmlModelState::duplicate(const QString &name) const +{ + if (!isValid()) + throw new InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + QmlItemNode parentNode(modelNode().parentProperty().parentModelNode()); + if (!parentNode.isValid()) + throw new InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + +// QmlModelState newState(stateGroup().addState(name)); + PropertyListType propertyList; + propertyList.append(qMakePair(QString("name"), QVariant(name))); + QmlModelState newState ( qmlModelView()->createModelNode("Qt/State", 4, 6, propertyList) ); + + foreach (const ModelNode &childNode, modelNode().nodeListProperty("changes").toModelNodeList()) { + ModelNode newModelNode(qmlModelView()->createModelNode(childNode.type(), childNode.majorVersion(), childNode.minorVersion())); + foreach (const BindingProperty &bindingProperty, childNode.bindingProperties()) + newModelNode.bindingProperty(bindingProperty.name()).setExpression(bindingProperty.expression()); + foreach (const VariantProperty &variantProperty, childNode.variantProperties()) + newModelNode.variantProperty(variantProperty.name()) = variantProperty.value(); + newState.modelNode().nodeListProperty("changes").reparentHere(newModelNode); + } + + modelNode().parentProperty().reparentHere(newState); + + return newState; +} + +QmlModelStateGroup QmlModelState::stateGroup() const +{ + QmlItemNode parentNode(modelNode().parentProperty().parentModelNode()); + return parentNode.states(); +} + +QmlModelState QmlModelState::createBaseState(const QmlModelView *view) +{ + QmlModelState fxState(view->rootModelNode()); + fxState.m_isBaseState = true; + return fxState; +} + +} // QmlDesigner diff --git a/src/plugins/qmldesigner/core/model/qmltextgenerator.cpp b/src/plugins/qmldesigner/core/model/qmltextgenerator.cpp new file mode 100644 index 00000000000..32c6c00bf2a --- /dev/null +++ b/src/plugins/qmldesigner/core/model/qmltextgenerator.cpp @@ -0,0 +1,178 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include <QtCore/QVariant> + +#include "bindingproperty.h" +#include "nodeproperty.h" +#include "nodelistproperty.h" +#include "qmltextgenerator.h" +#include "variantproperty.h" + +using namespace QmlDesigner; +using namespace QmlDesigner::Internal; + +QmlTextGenerator::QmlTextGenerator(const QStringList &propertyOrder, int indentDepth): + m_propertyOrder(propertyOrder), + m_indentDepth(indentDepth) +{ +} + +QString QmlTextGenerator::toQml(const AbstractProperty &property, int indentDepth) const +{ + if (property.isBindingProperty()) { + return property.toBindingProperty().expression(); + } else if (property.isNodeProperty()) { + return toQml(property.toNodeProperty().modelNode(), indentDepth); + } else if (property.isNodeListProperty()) { + const QList<ModelNode> nodes = property.toNodeListProperty().toModelNodeList(); + if (property.isDefaultProperty()) { + QString result; + for (int i = 0; i < nodes.length(); ++i) { + if (i > 0) + result += QLatin1String("\n\n"); + result += QString(indentDepth, QLatin1Char(' ')); + result += toQml(nodes.at(i), indentDepth); + } + return result; + } else { + QString result = QLatin1String("["); + const int arrayContentDepth = indentDepth + 4; + const QString arrayContentIndentation(arrayContentDepth, QLatin1Char(' ')); + for (int i = 0; i < nodes.length(); ++i) { + if (i > 0) + result += QLatin1Char(','); + result += QLatin1Char('\n'); + result += arrayContentIndentation; + result += toQml(nodes.at(i), arrayContentDepth); + } + return result + QLatin1String("]"); + } + } else if (property.isVariantProperty()) { + const VariantProperty variantProperty = property.toVariantProperty(); + const QVariant value = variantProperty.value(); + const QString stringValue = value.toString(); + + if (property.name() == QLatin1String("id")) + return stringValue; + + switch (value.type()) { + case QVariant::Bool: + if (value.value<bool>()) + return QLatin1String("true"); + else + return QLatin1String("false"); + + case QVariant::Double: + case QVariant::Int: + case QVariant::LongLong: + case QVariant::UInt: + case QVariant::ULongLong: + return stringValue; + + default: + return QString(QLatin1String("\"%1\"")).arg(stringValue); + } + } else { + Q_ASSERT("Unknown property type"); + return QString::null; + } +} + +QString QmlTextGenerator::toQml(const ModelNode &node, int indentDepth) const +{ + QString type = node.type(); + int lastSlashIndex = type.lastIndexOf('/'); + if (lastSlashIndex != -1) + type = type.mid(lastSlashIndex + 1); + + QString result = type; + result += QLatin1String(" {\n"); + + const int propertyIndentDepth = indentDepth + 4; + + const QString properties = propertiesToQml(node, propertyIndentDepth); + + return result + properties + QString(indentDepth, QLatin1Char(' ')) + QLatin1Char('}'); +} + +QString QmlTextGenerator::propertiesToQml(const ModelNode &node, int indentDepth) const +{ + QString topPart; + QString bottomPart; + + QStringList nodePropertyNames = node.propertyNames(); + bool addToTop = true; + + foreach (const QString &propertyName, m_propertyOrder) { + if (QLatin1String("id") == propertyName) { + // the model handles the id property special, so: + if (!node.id().isEmpty()) { + QString idLine(indentDepth, QLatin1Char(' ')); + idLine += QLatin1String("id: "); + idLine += node.id(); + idLine += QLatin1Char('\n'); + + if (addToTop) + topPart.append(idLine); + else + bottomPart.append(idLine); + } + } else if (propertyName.isEmpty()) { + addToTop = false; + } else if (nodePropertyNames.removeAll(propertyName)) { + const QString newContent = propertyToQml(node.property(propertyName), indentDepth); + + if (addToTop) + topPart.append(newContent); + else + bottomPart.append(newContent); + } + } + + foreach (const QString &propertyName, nodePropertyNames) { + bottomPart.prepend(propertyToQml(node.property(propertyName), indentDepth)); + } + + return topPart + bottomPart; +} + +QString QmlTextGenerator::propertyToQml(const AbstractProperty &property, int indentDepth) const +{ + QString result; + + if (property.isDefaultProperty()) + result = toQml(property, indentDepth); + else + result = QString(indentDepth, QLatin1Char(' ')) + property.name() + QLatin1String(": ") + toQml(property, indentDepth); + + result += QLatin1Char('\n'); + + return result; +} diff --git a/src/plugins/qmldesigner/core/model/qmltextgenerator.h b/src/plugins/qmldesigner/core/model/qmltextgenerator.h new file mode 100644 index 00000000000..c8a87ad4863 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/qmltextgenerator.h @@ -0,0 +1,67 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef QMLTEXTGENERATOR_H +#define QMLTEXTGENERATOR_H + +#include <QtCore/QString> +#include <QtCore/QStringList> + +#include "abstractproperty.h" +#include "modelnode.h" + +namespace QmlDesigner { +namespace Internal { + +class QmlTextGenerator +{ +public: + QmlTextGenerator(const QStringList &propertyOrder, int indentDepth = 0); + + QString operator()(const AbstractProperty &property) const + { return toQml(property, m_indentDepth); } + + QString operator()(const ModelNode &modelNode) const + { return toQml(modelNode, m_indentDepth); } + +private: + QString toQml(const AbstractProperty &property, int indentDepth) const; + QString toQml(const ModelNode &modelNode, int indentDepth) const; + QString propertiesToQml(const ModelNode &node, int indentDepth) const; + QString propertyToQml(const AbstractProperty &property, int indentDepth) const; + +private: + QStringList m_propertyOrder; + int m_indentDepth; +}; + +} // namespace Internal +} // namespace QmlDesigner + +#endif // QMLTEXTGENERATOR_H diff --git a/src/plugins/qmldesigner/core/model/rewriteaction.cpp b/src/plugins/qmldesigner/core/model/rewriteaction.cpp new file mode 100644 index 00000000000..363995bbadd --- /dev/null +++ b/src/plugins/qmldesigner/core/model/rewriteaction.cpp @@ -0,0 +1,256 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include <QtCore/QDebug> + +#include "nodeabstractproperty.h" +#include "rewriteaction.h" + +using namespace QmlDesigner; +using namespace QmlDesigner::Internal; +using namespace QmlEditor; + +static inline QString toString(QmlRefactoring::PropertyType type) +{ + switch (type) { + case QmlRefactoring::ArrayBinding: return QLatin1String("array binding"); + case QmlRefactoring::ObjectBinding: return QLatin1String("object binding"); + case QmlRefactoring::ScriptBinding: return QLatin1String("script binding"); + default: return QLatin1String("UNKNOWN"); + } +} + +bool AddPropertyRewriteAction::execute(QmlRefactoring &refactoring, ModelNodePositionStorage &positionStore) +{ + const int nodeLocation = positionStore.nodeOffset(m_property.parentModelNode()); + bool result = false; + + if (m_property.isDefaultProperty()) + result = refactoring.addToObjectMemberList(nodeLocation, m_valueText); + else + result = refactoring.addProperty(nodeLocation, m_property.name(), m_valueText, m_propertyType); + + Q_ASSERT(result); + return result; +} + +void AddPropertyRewriteAction::dump(const QString &prefix) const +{ + qDebug() << qPrintable(prefix) + << "AddPropertyRewriteAction for property" + << m_property.name() + << "(" << qPrintable(toString(m_propertyType)) << ")" + ; +} + +bool ChangeIdRewriteAction::execute(QmlEditor::QmlRefactoring &refactoring, ModelNodePositionStorage &positionStore) +{ + const int nodeLocation = positionStore.nodeOffset(m_node); + static const QLatin1String idPropertyName("id"); + bool result = false; + + if (m_oldId.isEmpty()) + result = refactoring.addProperty(nodeLocation, idPropertyName, m_newId, QmlRefactoring::ScriptBinding); + else if (m_newId.isEmpty()) + result = refactoring.removeProperty(nodeLocation, idPropertyName); + else + result = refactoring.changeProperty(nodeLocation, idPropertyName, m_newId, QmlRefactoring::ScriptBinding); + + Q_ASSERT(result); + return result; +} + +void ChangeIdRewriteAction::dump(const QString &prefix) const +{ + qDebug() << qPrintable(prefix) + << "ChangeIdRewriteAction" + ; +} + +bool ChangePropertyRewriteAction::execute(QmlEditor::QmlRefactoring &refactoring, ModelNodePositionStorage &positionStore) +{ + const int nodeLocation = positionStore.nodeOffset(m_property.parentModelNode()); + bool result = false; + + if (m_property.isDefaultProperty()) + result = refactoring.addToObjectMemberList(nodeLocation, m_valueText); + else if (m_propertyType == QmlRefactoring::ArrayBinding) + result = refactoring.addToArrayMemberList(nodeLocation, m_property.name(), m_valueText); + else + result = refactoring.changeProperty(nodeLocation, m_property.name(), m_valueText, m_propertyType); + + Q_ASSERT(result); + return result; +} + +void ChangePropertyRewriteAction::dump(const QString &prefix) const +{ + qDebug() << qPrintable(prefix) + << "ChangePropertyRewriteAction for property" + << m_property.name() + << "(" << qPrintable(toString(m_propertyType)) << ")" + << "of node" + << (m_property.parentModelNode().isValid() ? m_property.parentModelNode().id() : "(invalid)") + << "with new value" + << QString(m_valueText).replace('\n', "\\n") + << "and contained object" + << (m_containedModelNode.isValid() ? m_containedModelNode.id() : "(none)") + ; +} + +bool ChangeTypeRewriteAction::execute(QmlEditor::QmlRefactoring &refactoring, ModelNodePositionStorage &positionStore) +{ + const int nodeLocation = positionStore.nodeOffset(m_node); + bool result = false; + + QString newNodeType = m_node.type(); + const int slashIdx = newNodeType.lastIndexOf('/'); + if (slashIdx != -1) + newNodeType = newNodeType.mid(slashIdx + 1); + + result = refactoring.changeObjectType(nodeLocation, newNodeType); + + Q_ASSERT(result); + return result; +} + +void ChangeTypeRewriteAction::dump(const QString &prefix) const +{ + qDebug() << qPrintable(prefix) + << "ChangeTypeRewriteAction" + ; +} + +bool RemoveNodeRewriteAction::execute(QmlEditor::QmlRefactoring &refactoring, ModelNodePositionStorage &positionStore) +{ + const int nodeLocation = positionStore.nodeOffset(m_node); + bool result = false; + + result = refactoring.removeObject(nodeLocation); + + Q_ASSERT(result); + return result; +} + +void RemoveNodeRewriteAction::dump(const QString &prefix) const +{ + qDebug() << qPrintable(prefix) + << "RemoveNodeRewriteAction" + ; +} + +bool RemovePropertyRewriteAction::execute(QmlEditor::QmlRefactoring &refactoring, ModelNodePositionStorage &positionStore) +{ + const int nodeLocation = positionStore.nodeOffset(m_property.parentModelNode()); + bool result = false; + + result = refactoring.removeProperty(nodeLocation, m_property.name()); + + Q_ASSERT(result); + return result; +} + +void RemovePropertyRewriteAction::dump(const QString &prefix) const +{ + qDebug() << qPrintable(prefix) + << "RemovePropertyRewriteAction for property" + << m_property.name() + ; +} + +bool ReparentNodeRewriteAction::execute(QmlEditor::QmlRefactoring &refactoring, ModelNodePositionStorage &positionStore) +{ + const int nodeLocation = positionStore.nodeOffset(m_node); + const int targetParentObjectLocation = positionStore.nodeOffset(m_targetProperty.parentModelNode()); + const bool isArrayBinding = m_targetProperty.isNodeListProperty(); + bool result = false; + + QString targetPropertyName; + if (!m_targetProperty.isDefaultProperty()) + targetPropertyName = m_targetProperty.name(); + + result = refactoring.moveObject(nodeLocation, targetPropertyName, isArrayBinding, targetParentObjectLocation); + + Q_ASSERT(result); + return result; +} + +void ReparentNodeRewriteAction::dump(const QString &prefix) const +{ + if (m_node.isValid()) { + qDebug() << qPrintable(prefix) + << "ReparentNodeRewriteAction for node" + << m_node.id() + << "into property" + << m_targetProperty.name() + << "of node" + << m_targetProperty.parentModelNode().id() + ; + } else { + qDebug() << qPrintable(prefix) + << "reparentNodeRewriteAction for an invalid node" + ; + } +} + +bool MoveNodeRewriteAction::execute(QmlRefactoring &refactoring, + ModelNodePositionStorage &positionStore) +{ + const int movingNodeLocation = positionStore.nodeOffset(m_movingNode); + const int newTrailingNodeLocation = m_newTrailingNode.isValid() ? positionStore.nodeOffset(m_newTrailingNode) : -1; + bool result = false; + + result = refactoring.moveObjectBeforeObject(movingNodeLocation, newTrailingNodeLocation); + + Q_ASSERT(result); + return result; +} + +void MoveNodeRewriteAction::dump(const QString &prefix) const +{ + if (m_movingNode.isValid()) { + if (m_newTrailingNode.isValid()) + qDebug() << qPrintable(prefix) + << "MoveNodeRewriteAction for node" + << m_movingNode.id() + << "before node" + << m_newTrailingNode.id() + ; + else + qDebug() << qPrintable(prefix) + << "MoveNodeRewriteAction for node" + << m_movingNode.id() + << "to the end of its containing property" + ; + } else { + qDebug() << qPrintable(prefix) + << "MoveNodeRewriteAction for an invalid node" + ; + } +} diff --git a/src/plugins/qmldesigner/core/model/rewriteaction.h b/src/plugins/qmldesigner/core/model/rewriteaction.h new file mode 100644 index 00000000000..2027d5258a6 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/rewriteaction.h @@ -0,0 +1,261 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef REWRITEACTION_H +#define REWRITEACTION_H + +#include "abstractproperty.h" +#include "modelnodepositionstorage.h" + +#include <filemanager/qmlrefactoring.h> + +namespace QmlDesigner { +namespace Internal { + +class AddPropertyRewriteAction; +class ChangeIdRewriteAction; +class ChangePropertyRewriteAction; +class ChangeTypeRewriteAction; +class RemoveNodeRewriteAction; +class RemovePropertyRewriteAction; +class ReparentNodeRewriteAction; +class MoveNodeRewriteAction; + +class RewriteAction +{ +public: + virtual bool execute(QmlEditor::QmlRefactoring &refactoring, ModelNodePositionStorage &positionStore) = 0; + virtual void dump(const QString &prefix) const = 0; + + virtual AddPropertyRewriteAction const *asAddPropertyRewriteAction() const { return 0; } + virtual ChangeIdRewriteAction const *asChangeIdRewriteAction() const { return 0; } + virtual ChangePropertyRewriteAction const *asChangePropertyRewriteAction() const { return 0; } + virtual ChangeTypeRewriteAction const *asChangeTypeRewriteAction() const { return 0; } + virtual RemoveNodeRewriteAction const *asRemoveNodeRewriteAction() const { return 0; } + virtual RemovePropertyRewriteAction const *asRemovePropertyRewriteAction() const { return 0; } + virtual ReparentNodeRewriteAction const *asReparentNodeRewriteAction() const { return 0; } + virtual MoveNodeRewriteAction const *asMoveNodeRewriteAction() const { return 0; } + +protected: + RewriteAction() + {} + +private: + RewriteAction(const RewriteAction &); + RewriteAction &operator=(const RewriteAction&); +}; + +class AddPropertyRewriteAction: public RewriteAction +{ +public: + AddPropertyRewriteAction(const AbstractProperty &property, const QString &valueText, QmlEditor::QmlRefactoring::PropertyType propertyType, const ModelNode &containedModelNode/* = ModelNode()*/): + m_property(property), m_valueText(valueText), m_propertyType(propertyType), m_containedModelNode(containedModelNode) + {} + + virtual bool execute(QmlEditor::QmlRefactoring &refactoring, ModelNodePositionStorage &positionStore); + virtual void dump(const QString &prefix) const; + + virtual AddPropertyRewriteAction const *asAddPropertyRewriteAction() const { return this; } + + AbstractProperty property() const + { return m_property; } + + QString valueText() const + { return m_valueText; } + + QmlEditor::QmlRefactoring::PropertyType propertyType() const + { return m_propertyType; } + + ModelNode containedModelNode() const + { return m_containedModelNode; } + +private: + AbstractProperty m_property; + QString m_valueText; + QmlEditor::QmlRefactoring::PropertyType m_propertyType; + ModelNode m_containedModelNode; +}; + +class ChangeIdRewriteAction: public RewriteAction +{ +public: + ChangeIdRewriteAction(const ModelNode &node, const QString &oldId, const QString &newId): + m_node(node), m_oldId(oldId), m_newId(newId) + {} + + virtual bool execute(QmlEditor::QmlRefactoring &refactoring, ModelNodePositionStorage &positionStore); + virtual void dump(const QString &prefix) const; + + virtual ChangeIdRewriteAction const *asChangeIdRewriteAction() const { return this; } + + ModelNode node() const + { return m_node; } + +private: + ModelNode m_node; + QString m_oldId; + QString m_newId; +}; + +class ChangePropertyRewriteAction: public RewriteAction +{ +public: + ChangePropertyRewriteAction(const AbstractProperty &property, const QString &valueText, QmlEditor::QmlRefactoring::PropertyType propertyType, const ModelNode &containedModelNode/* = ModelNode()*/): + m_property(property), m_valueText(valueText), m_propertyType(propertyType), m_containedModelNode(containedModelNode) + {} + + virtual bool execute(QmlEditor::QmlRefactoring &refactoring, ModelNodePositionStorage &positionStore); + virtual void dump(const QString &prefix) const; + + virtual ChangePropertyRewriteAction const *asChangePropertyRewriteAction() const { return this; } + + AbstractProperty property() const + { return m_property; } + + QString valueText() const + { return m_valueText; } + + QmlEditor::QmlRefactoring::PropertyType propertyType() const + { return m_propertyType; } + + ModelNode containedModelNode() const + { return m_containedModelNode; } + +private: + AbstractProperty m_property; + QString m_valueText; + QmlEditor::QmlRefactoring::PropertyType m_propertyType; + ModelNode m_containedModelNode; +}; + +class ChangeTypeRewriteAction:public RewriteAction +{ +public: + ChangeTypeRewriteAction(const ModelNode &node): + m_node(node) + {} + + virtual bool execute(QmlEditor::QmlRefactoring &refactoring, ModelNodePositionStorage &positionStore); + virtual void dump(const QString &prefix) const; + + virtual ChangeTypeRewriteAction const *asChangeTypeRewriteAction() const { return this; } + + ModelNode node() const + { return m_node; } + +private: + ModelNode m_node; +}; + +class RemoveNodeRewriteAction: public RewriteAction +{ +public: + RemoveNodeRewriteAction(const ModelNode &node): + m_node(node) + {} + + virtual bool execute(QmlEditor::QmlRefactoring &refactoring, ModelNodePositionStorage &positionStore); + virtual void dump(const QString &prefix) const; + + virtual RemoveNodeRewriteAction const *asRemoveNodeRewriteAction() const { return this; } + + ModelNode node() const + { return m_node; } + +private: + ModelNode m_node; +}; + +class RemovePropertyRewriteAction: public RewriteAction +{ +public: + RemovePropertyRewriteAction(const AbstractProperty &property): + m_property(property) + {} + + virtual bool execute(QmlEditor::QmlRefactoring &refactoring, ModelNodePositionStorage &positionStore); + virtual void dump(const QString &prefix) const; + + virtual RemovePropertyRewriteAction const *asRemovePropertyRewriteAction() const { return this; } + + AbstractProperty property() const + { return m_property; } + +private: + AbstractProperty m_property; +}; + +class ReparentNodeRewriteAction: public RewriteAction +{ +public: + ReparentNodeRewriteAction(const ModelNode &node, const AbstractProperty &targetProperty, QmlEditor::QmlRefactoring::PropertyType propertyType): + m_node(node), m_targetProperty(targetProperty), m_propertyType(propertyType) + {} + + virtual bool execute(QmlEditor::QmlRefactoring &refactoring, ModelNodePositionStorage &positionStore); + virtual void dump(const QString &prefix) const; + + virtual ReparentNodeRewriteAction const *asReparentNodeRewriteAction() const { return this; } + + ModelNode reparentedNode() const + { return m_node; } + + AbstractProperty targetProperty() const + { return m_targetProperty; } + + QmlEditor::QmlRefactoring::PropertyType propertyType() const + { return m_propertyType; } + +private: + ModelNode m_node; + AbstractProperty m_targetProperty; + QmlEditor::QmlRefactoring::PropertyType m_propertyType; +}; + +class MoveNodeRewriteAction: public RewriteAction +{ +public: + MoveNodeRewriteAction(const ModelNode &movingNode, const ModelNode &newTrailingNode): + m_movingNode(movingNode), m_newTrailingNode(newTrailingNode) + {} + + virtual bool execute(QmlEditor::QmlRefactoring &refactoring, ModelNodePositionStorage &positionStore); + virtual void dump(const QString &prefix) const; + + virtual MoveNodeRewriteAction const *asMoveNodeRewriteAction() const { return this; } + +private: + ModelNode m_movingNode; + ModelNode m_newTrailingNode; +}; + +} // namespace Internal +} // namespace QmlDesigner + +#endif // REWRITEACTION_H diff --git a/src/plugins/qmldesigner/core/model/rewriteactioncompressor.cpp b/src/plugins/qmldesigner/core/model/rewriteactioncompressor.cpp new file mode 100644 index 00000000000..6ccda0ff2cb --- /dev/null +++ b/src/plugins/qmldesigner/core/model/rewriteactioncompressor.cpp @@ -0,0 +1,305 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include <QtCore/QSet> + +#include "modelnode.h" +#include "nodelistproperty.h" +#include "nodeproperty.h" +#include "qmltextgenerator.h" +#include "rewriteactioncompressor.h" + +using namespace QmlDesigner; +using namespace QmlDesigner::Internal; + +static bool nodeOrParentInSet(const ModelNode &node, const QSet<ModelNode> &nodeSet) +{ + ModelNode n = node; + while (n.isValid()) { + if (nodeSet.contains(n)) + return true; + + if (!n.hasParentProperty()) + return false; + + n = n.parentProperty().parentModelNode(); + } + + return false; +} + +void RewriteActionCompressor::operator()(QList<RewriteAction *> &actions) const +{ + compressReparentActions(actions); + compressPropertyActions(actions); + compressAddEditRemoveNodeActions(actions); + compressAddEditActions(actions); + compressAddReparentActions(actions); +} + +void RewriteActionCompressor::compressReparentActions(QList<RewriteAction *> &actions) const +{ + QSet<ModelNode> reparentedNodes; + + QMutableListIterator<RewriteAction*> iter(actions); + iter.toBack(); + while (iter.hasPrevious()) { + RewriteAction *action = iter.previous(); + + if (ReparentNodeRewriteAction const *reparentAction = action->asReparentNodeRewriteAction()) { + const ModelNode reparentedNode = reparentAction->reparentedNode(); + + if (reparentedNodes.contains(reparentedNode)) { + remove(iter); + } else { + reparentedNodes.insert(reparentedNode); + } + } + } +} + +void RewriteActionCompressor::compressAddEditRemoveNodeActions(QList<RewriteAction *> &actions) const +{ + QHash<ModelNode, RewriteAction *> removedNodes; + QSet<RewriteAction *> removeActionsToRemove; + + QMutableListIterator<RewriteAction*> iter(actions); + iter.toBack(); + while (iter.hasPrevious()) { + RewriteAction *action = iter.previous(); + + if (RemoveNodeRewriteAction const *removeNodeAction = action->asRemoveNodeRewriteAction()) { + const ModelNode modelNode = removeNodeAction->node(); + + if (removedNodes.contains(modelNode)) + remove(iter); + else + removedNodes.insert(modelNode, action); + } else if (action->asAddPropertyRewriteAction() || action->asChangePropertyRewriteAction()) { + AbstractProperty property; + ModelNode containedModelNode; + if (action->asAddPropertyRewriteAction()) { + property = action->asAddPropertyRewriteAction()->property(); + containedModelNode = action->asAddPropertyRewriteAction()->containedModelNode(); + } else { + property = action->asChangePropertyRewriteAction()->property(); + containedModelNode = action->asChangePropertyRewriteAction()->containedModelNode(); + } + + if (removedNodes.contains(property.parentModelNode())) + remove(iter); + else if (removedNodes.contains(containedModelNode)) { + remove(iter); + removeActionsToRemove.insert(removedNodes[containedModelNode]); + } + } else if (RemovePropertyRewriteAction const *removePropertyAction = action->asRemovePropertyRewriteAction()) { + const AbstractProperty property = removePropertyAction->property(); + + if (removedNodes.contains(property.parentModelNode())) + remove(iter); + } else if (ChangeIdRewriteAction const *changeIdAction = action->asChangeIdRewriteAction()) { + if (removedNodes.contains(changeIdAction->node())) + remove(iter); + } else if (ChangeTypeRewriteAction const *changeTypeAction = action->asChangeTypeRewriteAction()) { + if (removedNodes.contains(changeTypeAction->node())) + remove(iter); + } else if (ReparentNodeRewriteAction const *reparentAction = action->asReparentNodeRewriteAction()) { + if (removedNodes.contains(reparentAction->reparentedNode())) + remove(iter); + } + } + + foreach (RewriteAction *action, removeActionsToRemove) { + actions.removeOne(action); + delete action; + } +} + +void RewriteActionCompressor::compressPropertyActions(QList<RewriteAction *> &actions) const +{ + QHash<AbstractProperty, RewriteAction *> removedProperties; + QHash<AbstractProperty, ChangePropertyRewriteAction const*> changedProperties; + QSet<AbstractProperty> addedProperties; + + QMutableListIterator<RewriteAction*> iter(actions); + iter.toBack(); + while (iter.hasPrevious()) { + RewriteAction *action = iter.previous(); + + if (RemovePropertyRewriteAction const *removeAction = action->asRemovePropertyRewriteAction()) { + removedProperties.insert(removeAction->property(), action); + } else if (ChangePropertyRewriteAction const *changeAction = action->asChangePropertyRewriteAction()) { + const AbstractProperty property = changeAction->property(); + + if (removedProperties.contains(property)) { + remove(iter); + } else if (changedProperties.contains(property)) { + if (!property.isValid() || !property.isDefaultProperty()) + remove(iter); + } else { + changedProperties.insert(property, changeAction); + } + } else if (AddPropertyRewriteAction const *addAction = action->asAddPropertyRewriteAction()) { + const AbstractProperty property = addAction->property(); + + if (RewriteAction *removeAction = removedProperties.value(property, 0)) { + actions.removeOne(removeAction); + removedProperties.remove(property); + delete removeAction; + remove(iter); + } else { + if (changedProperties.contains(property)) + changedProperties.remove(property); + + addedProperties.insert(property); + } + } + } +} + +void RewriteActionCompressor::compressAddEditActions(QList<RewriteAction *> &actions) const +{ + QSet<ModelNode> addedNodes; + QSet<RewriteAction *> dirtyActions; + + QMutableListIterator<RewriteAction*> iter(actions); + while (iter.hasNext()) { + RewriteAction *action = iter.next(); + + if (action->asAddPropertyRewriteAction() || action->asChangePropertyRewriteAction()) { + AbstractProperty property; + ModelNode containedNode; + + if (AddPropertyRewriteAction const *addAction = action->asAddPropertyRewriteAction()) { + property = addAction->property(); + containedNode = addAction->containedModelNode(); + } else if (ChangePropertyRewriteAction const *changeAction = action->asChangePropertyRewriteAction()) { + property = changeAction->property(); + containedNode = changeAction->containedModelNode(); + } + + if (property.isValid() && addedNodes.contains(property.parentModelNode())) { + remove(iter); + continue; + } + + if (!containedNode.isValid()) + continue; + + if (nodeOrParentInSet(containedNode, addedNodes)) { + remove(iter); + } else { + addedNodes.insert(containedNode); + dirtyActions.insert(action); + } + } else if (ChangeIdRewriteAction const *changeIdAction = action->asChangeIdRewriteAction()) { + if (nodeOrParentInSet(changeIdAction->node(), addedNodes)) { + remove(iter); + } + } else if (ChangeTypeRewriteAction const *changeTypeAction = action->asChangeTypeRewriteAction()) { + if (nodeOrParentInSet(changeTypeAction->node(), addedNodes)) { + remove(iter); + } + } + } + + QmlTextGenerator gen(m_propertyOrder); + foreach (RewriteAction *action, dirtyActions) { + RewriteAction *newAction = 0; + if (AddPropertyRewriteAction const *addAction = action->asAddPropertyRewriteAction()) { + newAction = new AddPropertyRewriteAction(addAction->property(), + gen(addAction->containedModelNode()), + addAction->propertyType(), + addAction->containedModelNode()); + } else if (ChangePropertyRewriteAction const *changeAction = action->asChangePropertyRewriteAction()) { + newAction = new ChangePropertyRewriteAction(changeAction->property(), + gen(changeAction->containedModelNode()), + changeAction->propertyType(), + changeAction->containedModelNode()); + } + + const int idx = actions.indexOf(action); + if (newAction && idx >= 0) { + actions[idx] = newAction; + } + } +} + +void RewriteActionCompressor::compressAddReparentActions(QList<RewriteAction *> &actions) const +{ + QMap<ModelNode, RewriteAction*> addedNodes; + + QMutableListIterator<RewriteAction*> iter(actions); + while (iter.hasNext()) { + RewriteAction *action = iter.next(); + + if (action->asAddPropertyRewriteAction() || action->asChangePropertyRewriteAction()) { + ModelNode containedNode; + + if (AddPropertyRewriteAction const *addAction = action->asAddPropertyRewriteAction()) { + containedNode = addAction->containedModelNode(); + } else if (ChangePropertyRewriteAction const *changeAction = action->asChangePropertyRewriteAction()) { + containedNode = changeAction->containedModelNode(); + } + + if (!containedNode.isValid()) + continue; + + addedNodes.insert(containedNode, action); + } else if (ReparentNodeRewriteAction const *reparentAction = action->asReparentNodeRewriteAction()) { + if (addedNodes.contains(reparentAction->reparentedNode())) { + RewriteAction *previousAction = addedNodes[reparentAction->reparentedNode()]; + actions.removeOne(previousAction); + + RewriteAction *replacementAction = 0; + if (AddPropertyRewriteAction const *addAction = previousAction->asAddPropertyRewriteAction()) { + replacementAction = new AddPropertyRewriteAction(reparentAction->targetProperty(), + addAction->valueText(), + reparentAction->propertyType(), + addAction->containedModelNode()); + } else if (ChangePropertyRewriteAction const *changeAction = previousAction->asChangePropertyRewriteAction()) { + replacementAction = new AddPropertyRewriteAction(reparentAction->targetProperty(), + changeAction->valueText(), + reparentAction->propertyType(), + changeAction->containedModelNode()); + } + + iter.setValue(replacementAction); + delete previousAction; + delete action; + } + } + } +} + +void RewriteActionCompressor::remove(QMutableListIterator<RewriteAction*> &iter) const +{ + delete iter.value(); + iter.remove(); +} diff --git a/src/plugins/qmldesigner/core/model/rewriteactioncompressor.h b/src/plugins/qmldesigner/core/model/rewriteactioncompressor.h new file mode 100644 index 00000000000..594d490a5b2 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/rewriteactioncompressor.h @@ -0,0 +1,63 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef REWRITEACTIONCOMPRESSOR_H +#define REWRITEACTIONCOMPRESSOR_H + +#include <QtCore/QStringList> + +#include "rewriteaction.h" + +namespace QmlDesigner { +namespace Internal { + +class RewriteActionCompressor +{ +public: + RewriteActionCompressor(const QStringList &propertyOrder): m_propertyOrder(propertyOrder) {} + + void operator()(QList<RewriteAction *> &actions) const; + +private: + void compressReparentActions(QList<RewriteAction *> &actions) const; + void compressAddEditRemoveNodeActions(QList<RewriteAction *> &actions) const; + void compressPropertyActions(QList<RewriteAction *> &actions) const; + void compressAddEditActions(QList<RewriteAction *> &actions) const; + void compressAddReparentActions(QList<RewriteAction *> &actions) const; + + void remove(QMutableListIterator<RewriteAction*> &iter) const; + +private: + QStringList m_propertyOrder; +}; + +} // namespace Internal +} // namespace QmlDesigner + +#endif // REWRITEACTIONCOMPRESSOR_H diff --git a/src/plugins/qmldesigner/core/model/rewriterview.cpp b/src/plugins/qmldesigner/core/model/rewriterview.cpp new file mode 100644 index 00000000000..ea754e2efa6 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/rewriterview.cpp @@ -0,0 +1,459 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include <filemanager/astobjecttextextractor.h> +#include <filemanager/objectlengthcalculator.h> +#include <filemanager/firstdefinitionfinder.h> +#include <customnotifications.h> + +#include "rewriterview.h" +#include "textmodifier.h" +#include "texttomodelmerger.h" +#include "modelnodepositionstorage.h" +#include "modeltotextmerger.h" +#include "nodelistproperty.h" +#include "nodeproperty.h" +#include "invalidmodelnodeexception.h" + +using namespace QmlDesigner::Internal; + +namespace QmlDesigner { + +RewriterView::Error::Error(): + m_type(NoError), + m_line(-1), + m_column(-1) +{ +} + +RewriterView::Error::Error(const Exception &exception): + m_type(InternalError), + m_line(exception.line()), + m_column(-1), + m_description(exception.description()), + m_url(exception.file()) +{ +} + +RewriterView::Error::Error(const QmlError &qmlError): + m_type(ParseError), + m_line(qmlError.line()), + m_column(qmlError.column()), + m_description(qmlError.description()), + m_url(qmlError.url()) +{ +} + +QString RewriterView::Error::toString() const +{ + QString str; + + if (url().isValid()) + str = url().toString() + QLatin1Char(':'); + if (line() != -1) + str += QString::number(line()) + QLatin1Char(':'); + if(column() != -1) + str += QString::number(column()) + QLatin1Char(':'); + + str += description(); + + return str; +} + +RewriterView::RewriterView(DifferenceHandling differenceHandling, QObject *parent): + AbstractView(parent), + m_differenceHandling(differenceHandling), + m_modificationGroupActive(false), + m_positionStorage(new ModelNodePositionStorage), + m_modelToTextMerger(new Internal::ModelToTextMerger(this)), + m_textToModelMerger(new Internal::TextToModelMerger(this)), + m_textModifier(0), + transactionLevel(0) +{ +} + +RewriterView::~RewriterView() +{ + delete m_positionStorage; +} + +Internal::ModelToTextMerger *RewriterView::modelToTextMerger() const +{ + return m_modelToTextMerger.data(); +} + +Internal::TextToModelMerger *RewriterView::textToModelMerger() const +{ + return m_textToModelMerger.data(); +} + +void RewriterView::modelAttached(Model *model) +{ + AbstractView::modelAttached(model); + + ModelAmender differenceHandler(m_textToModelMerger.data()); + m_textToModelMerger->load(m_textModifier->text().toUtf8(), differenceHandler); +} + +void RewriterView::modelAboutToBeDetached(Model * /*model*/) +{ + m_positionStorage->clear(); +} + +void RewriterView::nodeCreated(const ModelNode &createdNode) +{ + if (createdNode.type() == QLatin1String("Qt/Component")) + setupComponent(createdNode); + Q_ASSERT(textModifier()); + m_positionStorage->setNodeOffset(createdNode, ModelNodePositionStorage::INVALID_LOCATION); + if (textToModelMerger()->isActive()) + return; + + modelToTextMerger()->nodeCreated(createdNode); + + if (!isModificationGroupActive()) + modelToTextMerger()->applyChanges(); +} + +void RewriterView::nodeAboutToBeRemoved(const ModelNode &/*removedNode*/) +{ +} + +void RewriterView::nodeRemoved(const ModelNode &removedNode, const NodeAbstractProperty &parentProperty, PropertyChangeFlags propertyChange) +{ + Q_ASSERT(textModifier()); + if (textToModelMerger()->isActive()) + return; + + modelToTextMerger()->nodeRemoved(removedNode, parentProperty, propertyChange); + + if (!isModificationGroupActive()) + modelToTextMerger()->applyChanges(); +} + +void RewriterView::propertiesAdded(const ModelNode &/*node*/, const QList<AbstractProperty>& /*propertyList*/) +{ + Q_ASSERT(0); +} + +void RewriterView::propertiesAboutToBeRemoved(const QList<AbstractProperty> &propertyList) +{ + Q_ASSERT(textModifier()); + if (textToModelMerger()->isActive()) + return; + + + foreach (const AbstractProperty &property, propertyList) { + if (property.isDefaultProperty() && property.isNodeListProperty()) { + m_removeDefaultPropertyTransaction = beginRewriterTransaction(); + + foreach (const ModelNode &node, property.toNodeListProperty().toModelNodeList()) { + modelToTextMerger()->nodeRemoved(node, property.toNodeAbstractProperty(), AbstractView::NoAdditionalChanges); + } + } + } +} + +void RewriterView::propertiesRemoved(const QList<AbstractProperty>& propertyList) +{ + Q_ASSERT(textModifier()); + if (textToModelMerger()->isActive()) + return; + + modelToTextMerger()->propertiesRemoved(propertyList); + + if (m_removeDefaultPropertyTransaction.isValid()) + m_removeDefaultPropertyTransaction.commit(); + + if (!isModificationGroupActive()) + modelToTextMerger()->applyChanges(); +} + +void RewriterView::variantPropertiesChanged(const QList<VariantProperty>& propertyList, PropertyChangeFlags propertyChange) +{ + Q_ASSERT(textModifier()); + if (textToModelMerger()->isActive()) + return; + + QList<AbstractProperty> usefulPropertyList; + foreach (const VariantProperty &property, propertyList) + usefulPropertyList.append(property); + + modelToTextMerger()->propertiesChanged(usefulPropertyList, propertyChange); + + if (!isModificationGroupActive()) + modelToTextMerger()->applyChanges(); +} + +void RewriterView::bindingPropertiesChanged(const QList<BindingProperty>& propertyList, PropertyChangeFlags propertyChange) +{ + Q_ASSERT(textModifier()); + if (textToModelMerger()->isActive()) + return; + + QList<AbstractProperty> usefulPropertyList; + foreach (const BindingProperty &property, propertyList) + usefulPropertyList.append(property); + + modelToTextMerger()->propertiesChanged(usefulPropertyList, propertyChange); + + if (!isModificationGroupActive()) + modelToTextMerger()->applyChanges(); +} + +void RewriterView::nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent, const NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags propertyChange) +{ + if (node.type() == QLatin1String("Qt/Component")) + setupComponent(node); + + Q_ASSERT(textModifier()); + if (textToModelMerger()->isActive()) + return; + + modelToTextMerger()->nodeReparented(node, newPropertyParent, oldPropertyParent, propertyChange); + + if (!isModificationGroupActive()) + modelToTextMerger()->applyChanges(); +} + +void RewriterView::fileUrlChanged(const QUrl &/*oldUrl*/, const QUrl &/*newUrl*/) +{ +} + +void RewriterView::nodeIdChanged(const ModelNode& node, const QString& newId, const QString& oldId) +{ + Q_ASSERT(textModifier()); + if (textToModelMerger()->isActive()) + return; + + modelToTextMerger()->nodeIdChanged(node, newId, oldId); + + if (!isModificationGroupActive()) + modelToTextMerger()->applyChanges(); +} + +void RewriterView::nodeSlidedToIndex(const NodeListProperty &listProperty, int newIndex, int /*oldIndex*/) +{ // FIXME: "slided" ain't no English, probably "slid" or "sliding" is meant... + Q_ASSERT(textModifier()); + if (textToModelMerger()->isActive()) + return; + + const QList<ModelNode> nodes = listProperty.toModelNodeList(); + const ModelNode movingNode = nodes.at(newIndex); + ModelNode trailingNode; + if (newIndex + 1 < nodes.size()) + trailingNode = nodes.at(newIndex + 1); + modelToTextMerger()->nodeSlidAround(movingNode, trailingNode); + + if (!isModificationGroupActive()) + modelToTextMerger()->applyChanges(); +} + +void RewriterView::nodeTypeChanged(const ModelNode &node,const QString &type, int majorVersion, int minorVersion) +{ + Q_ASSERT(textModifier()); + if (textToModelMerger()->isActive()) + return; + + modelToTextMerger()->nodeTypeChanged(node, type, majorVersion, minorVersion); + + if (!isModificationGroupActive()) + modelToTextMerger()->applyChanges(); +} + +void RewriterView::customNotification(const AbstractView * /*view*/, const QString &identifier, const QList<ModelNode> & /* nodeList */, const QList<QVariant> & /*data */) +{ + if (identifier == ("__start rewriter transaction__")) { + transactionLevel++; + setModificationGroupActive(true); + } + else if (identifier == ("__end rewriter transaction__")) { + transactionLevel--; + Q_ASSERT(transactionLevel >= 0); + + } + if (transactionLevel == 0) + { + setModificationGroupActive(false); + applyModificationGroupChanges(); + } +} + +void RewriterView::selectedNodesChanged(const QList<ModelNode> & /* selectedNodeList, */, const QList<ModelNode> & /*lastSelectedNodeList */) +{ +} + +bool RewriterView::isModificationGroupActive() const +{ + return m_modificationGroupActive; +} + +void RewriterView::setModificationGroupActive(bool active) +{ + m_modificationGroupActive = active; +} + +TextModifier *RewriterView::textModifier() const +{ + return m_textModifier; +} + +void RewriterView::setTextModifier(TextModifier *textModifier) +{ + if (m_textModifier) + disconnect(m_textModifier, SIGNAL(textChanged()), this, SLOT(qmlTextChanged())); + + m_textModifier = textModifier; + + if (m_textModifier) + connect(m_textModifier, SIGNAL(textChanged()), this, SLOT(qmlTextChanged())); +} + +void RewriterView::applyModificationGroupChanges() +{ + Q_ASSERT(transactionLevel == 0); + modelToTextMerger()->applyChanges(); +} + +void RewriterView::setupComponent(const ModelNode &node) +{ + Q_ASSERT(node.type() == QLatin1String("Qt/Component")); + + QString componentText = extractText(QList<ModelNode>() << node).value(node); + + if (componentText.isEmpty()) + return; + + QString result = ""; + if (componentText.contains("Component")) { //explicit component + FirstDefinitionFinder firstDefinitionFinder(componentText); + int offset = firstDefinitionFinder(0); + ObjectLengthCalculator objectLengthCalculator(componentText); + int length = objectLengthCalculator(offset); + for (int i = offset;i<offset + length;i++) + result.append(componentText.at(i)); + } else { + result = componentText; //implicit component + } + + node.variantProperty("__component_data") = result; +} + +QList<RewriterView::Error> RewriterView::errors() const +{ + return m_errors; +} + +void RewriterView::clearErrors() +{ + m_errors.clear(); + emit errorsChanged(m_errors); +} + +void RewriterView::addErrors(const QList<RewriterView::Error> &errors) +{ + m_errors.append(errors); + emit errorsChanged(m_errors); +} + +void RewriterView::addError(const RewriterView::Error &error) +{ + m_errors.append(error); + emit errorsChanged(m_errors); +} + +QMap<ModelNode, QString> RewriterView::extractText(const QList<ModelNode> &nodes) const +{ + QmlEditor::ASTObjectTextExtractor extract(m_textModifier->text()); + QMap<ModelNode, QString> result; + + foreach (const ModelNode &node, nodes) { + const int nodeLocation = m_positionStorage->nodeOffset(node); + + if (nodeLocation == ModelNodePositionStorage::INVALID_LOCATION) + result.insert(node, QString::null); + else + result.insert(node, extract(nodeLocation)); + } + + return result; +} + +int RewriterView::nodeOffset(const ModelNode &node) const +{ + return m_positionStorage->nodeOffset(node); +} + +int RewriterView::nodeLength(const ModelNode &node) const +{ + ObjectLengthCalculator objectLengthCalculator(m_textModifier->text()); + return objectLengthCalculator(nodeOffset(node)); +} + +int RewriterView::firstDefinitionInsideOffset(const ModelNode &node) const +{ + FirstDefinitionFinder firstDefinitionFinder(m_textModifier->text()); + return firstDefinitionFinder(nodeOffset(node)); +} + +int RewriterView::firstDefinitionInsideLength(const ModelNode &node) const +{ + FirstDefinitionFinder firstDefinitionFinder(m_textModifier->text()); + int offset = firstDefinitionFinder(nodeOffset(node)); + ObjectLengthCalculator objectLengthCalculator(m_textModifier->text()); + return objectLengthCalculator(offset); +} + +void RewriterView::qmlTextChanged() +{ + if (m_textToModelMerger && m_textModifier) { + const QString newQmlText = m_textModifier->text(); + +// qDebug() << "qmlTextChanged:" << newQmlText; + + switch (m_differenceHandling) { + case Validate: { + ModelValidator differenceHandler(m_textToModelMerger.data()); + m_textToModelMerger->load(newQmlText.toUtf8(), differenceHandler); + break; + } + + case Amend: + default: { + emitCustomNotification(StartRewriterAmend); + ModelAmender differenceHandler(m_textToModelMerger.data()); + m_textToModelMerger->load(newQmlText.toUtf8(), differenceHandler); + emitCustomNotification(EndRewriterAmend); + break; + } + } + } +} + +} //QmlDesigner diff --git a/src/plugins/qmldesigner/core/model/textmodifier.cpp b/src/plugins/qmldesigner/core/model/textmodifier.cpp new file mode 100644 index 00000000000..34365f67639 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/textmodifier.cpp @@ -0,0 +1,36 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "textmodifier.h" + +using namespace QmlDesigner; + +TextModifier::~TextModifier() +{ +} diff --git a/src/plugins/qmldesigner/core/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/core/model/texttomodelmerger.cpp new file mode 100644 index 00000000000..107a49e8267 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/texttomodelmerger.cpp @@ -0,0 +1,509 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "abstractproperty.h" +#include "bindingproperty.h" +#include "nodemetainfo.h" +#include "nodeproperty.h" +#include "propertymetainfo.h" +#include "texttomodelmerger.h" +#include "rewriterview.h" +#include "variantproperty.h" +#include <QmlDomDocument> +#include <QmlEngine> +#include <QSet> + +using namespace QmlDesigner; +using namespace QmlDesigner::Internal; + +static inline bool equals(const QVariant &a, const QVariant &b) +{ + if (a.type() == QVariant::Double && b.type() == QVariant::Double) + return qFuzzyCompare(a.toDouble(), b.toDouble()); + else + return a == b; +} + +TextToModelMerger::TextToModelMerger(RewriterView *reWriterView): + m_rewriterView(reWriterView), + m_isActive(false) +{ +} + +void TextToModelMerger::setActive(bool active) +{ + m_isActive = active; +} + +bool TextToModelMerger::isActive() const +{ + return m_isActive; +} + +void TextToModelMerger::setupImports(QmlDomDocument &doc) +{ + foreach (const Import &import, m_rewriterView->model()->imports()) + m_rewriterView->model()->removeImport(import); + foreach (const QmlDomImport &qmlImport, doc.imports()) { + if (qmlImport.type() == QmlDomImport::Library) { + Import import(Import::createLibraryImport(QUrl(qmlImport.uri()), qmlImport.version(), qmlImport.qualifier())); + m_rewriterView->model()->addImport(import); + } + if (qmlImport.type() == QmlDomImport::File) { + Import import(Import:: createFileImport(qmlImport.uri(), qmlImport.version(), qmlImport.qualifier())); + m_rewriterView->model()->addImport(import); + } + } +} + +bool TextToModelMerger::load(const QByteArray &data, DifferenceHandler &differenceHandler) +{ + setActive(true); + + try { + QmlEngine engine; + QmlDomDocument doc; + const QUrl url = m_rewriterView->model()->fileUrl(); + const bool success = doc.load(&engine, data, url); + + if (success) { + setupImports(doc); + + const QmlDomObject rootDomObject = doc.rootObject(); + ModelNode modelRootNode = m_rewriterView->rootModelNode(); + syncNode(modelRootNode, rootDomObject, differenceHandler); + m_rewriterView->positionStorage()->cleanupInvalidOffsets(); + m_rewriterView->clearErrors(); + } else { + QList<RewriterView::Error> errors; + foreach (const QmlError &qmlError, doc.errors()) + errors.append(RewriterView::Error(qmlError)); + m_rewriterView->addErrors(errors); + } + + setActive(false); + return success; + } catch (Exception &e) { + m_rewriterView->addError(RewriterView::Error(e)); + + setActive(false); + + throw; + } +} + +void TextToModelMerger::syncNode(ModelNode &modelNode, const QmlDomObject &domObject, DifferenceHandler &differenceHandler) +{ + m_rewriterView->positionStorage()->setNodeOffset(modelNode, domObject.position()); + + if (modelNode.type() != domObject.objectType() + || modelNode.majorVersion() != domObject.objectTypeMajorVersion() + || modelNode.minorVersion() != domObject.objectTypeMinorVersion()) { + differenceHandler.typeDiffers(modelNode, domObject); + } + + { + const QString domObjectId = domObject.objectId(); + + if (!domObjectId.isEmpty()) { + if (modelNode.id() != domObjectId) + differenceHandler.idsDiffer(modelNode, domObjectId); + } + } + + QSet<QString> modelPropertyNames = QSet<QString>::fromList(modelNode.propertyNames()); + + foreach (const QmlDomProperty &domProperty, domObject.properties()) { + const QString domPropertyName = domProperty.propertyName(); + + if (isSignalPropertyName(domPropertyName.toUtf8())) + continue; + + if (domPropertyName == QLatin1String("id")) { + const QmlDomValue domValue = domProperty.value(); + if (domValue.isLiteral()) { + const QString domId = domValue.toLiteral().literal(); + + if (modelNode.id() != domId) + differenceHandler.idsDiffer(modelNode, domId); + } else { + qWarning() << "id property found which is not a literal"; + } + } else if (domPropertyName.isEmpty()) { + qWarning() << "QML DOM returned an empty property name"; + continue; + } else if (domPropertyName.at(0).isUpper() && domPropertyName.contains('.')) { + // An attached property, which we currently don't handle. + // So, skipping it. + modelPropertyNames.remove(domPropertyName); + continue; + } else { + const QmlDomDynamicProperty dynamicProperty = domObject.dynamicProperty(domProperty.propertyName()); + if (dynamicProperty.isValid() || modelNode.metaInfo().hasProperty(domPropertyName, true) || modelNode.type() == QLatin1String("Qt/PropertyChanges")) { + AbstractProperty modelProperty = modelNode.property(domPropertyName); + syncProperty(modelProperty, domProperty, dynamicProperty, differenceHandler); + modelPropertyNames.remove(domPropertyName); + } + } + } + + { // for new dynamic properties which have no property definitions: + foreach (const QmlDomDynamicProperty &dynamicDomProperty, domObject.dynamicProperties()) { + const QByteArray propertyName = dynamicDomProperty.propertyName(); + if (domObject.property(propertyName).isValid()) + continue; + + if (dynamicDomProperty.isAlias()) + continue; // we don't handle alias properties yet. + + AbstractProperty modelProperty = modelNode.property(propertyName); + const QString dynamicTypeName = QMetaType::typeName(dynamicDomProperty.propertyType()); + + // a dynamic property definition without a value + if (modelProperty.isValid() && modelProperty.isVariantProperty()) { + VariantProperty modelVariantProperty = modelProperty.toVariantProperty(); + if (modelVariantProperty.value() != QVariant()) + differenceHandler.variantValuesDiffer(modelVariantProperty, QVariant(), dynamicTypeName); + } else { + differenceHandler.shouldBeVariantProperty(modelProperty, QVariant(), dynamicTypeName); + } + } + } + + foreach (const QString &modelPropertyName, modelPropertyNames) { + AbstractProperty modelProperty = modelNode.property(modelPropertyName); + const QmlDomDynamicProperty dynamicDomProperty = domObject.dynamicProperty(modelPropertyName.toUtf8()); + + if (dynamicDomProperty.isValid()) { + const QString dynamicTypeName = QMetaType::typeName(dynamicDomProperty.propertyType()); + + // a dynamic property definition without a value + if (modelProperty.isValid() && modelProperty.isVariantProperty()) { + VariantProperty modelVariantProperty = modelProperty.toVariantProperty(); + if (modelVariantProperty.value() != QVariant()) + differenceHandler.variantValuesDiffer(modelVariantProperty, QVariant(), dynamicTypeName); + } else { + differenceHandler.shouldBeVariantProperty(modelProperty, QVariant(), dynamicTypeName); + } + } else { + // property deleted. + differenceHandler.propertyAbsentFromQml(modelProperty); + } + } +} + +void TextToModelMerger::syncProperty(AbstractProperty &modelProperty, + const QmlDomProperty &qmlProperty, + const QmlDomDynamicProperty &qmlDynamicProperty, + DifferenceHandler &differenceHandler) +{ + Q_ASSERT(modelProperty.name() == qmlProperty.propertyName()); + + const QmlDomValue qmlValue = qmlProperty.value(); + + if (qmlValue.isBinding()) { + const QString qmlBinding = qmlValue.toBinding().binding(); + if (modelProperty.isBindingProperty()) { + BindingProperty bindingProperty = modelProperty.toBindingProperty(); + if (bindingProperty.expression() != qmlBinding) { + differenceHandler.bindingExpressionsDiffer(bindingProperty, qmlBinding); + } + } else { + differenceHandler.shouldBeBindingProperty(modelProperty, qmlBinding); + } + } else if (qmlValue.isList()) { + if (modelProperty.isNodeListProperty()) { + NodeListProperty nodeListProperty = modelProperty.toNodeListProperty(); + syncNodeListProperty(nodeListProperty, qmlValue.toList(), differenceHandler); + } else { + differenceHandler.shouldBeNodeListProperty(modelProperty, qmlValue.toList()); + } + } else if (qmlValue.isLiteral()) { + const QVariant qmlVariantValue = convertToVariant(modelProperty.parentModelNode(), qmlProperty, qmlDynamicProperty); + QString dynamicTypeName; + if (qmlDynamicProperty.isValid()) + dynamicTypeName = QMetaType::typeName(qmlDynamicProperty.propertyType()); + + if (modelProperty.isVariantProperty()) { + VariantProperty modelVariantProperty = modelProperty.toVariantProperty(); + + if (!equals(modelVariantProperty.value(), qmlVariantValue) + || qmlDynamicProperty.isValid() != modelVariantProperty.isDynamic() + || dynamicTypeName != modelVariantProperty.dynamicTypeName()) { + differenceHandler.variantValuesDiffer(modelVariantProperty, qmlVariantValue, dynamicTypeName); + } + } else { + differenceHandler.shouldBeVariantProperty(modelProperty, qmlVariantValue, dynamicTypeName); + } + } else if (qmlValue.isObject()) { + if (modelProperty.isNodeProperty()) { + ModelNode nodePropertyNode = modelProperty.toNodeProperty().modelNode(); + syncNode(nodePropertyNode, qmlValue.toObject(), differenceHandler); + } else { + differenceHandler.shouldBeNodeProperty(modelProperty, qmlValue.toObject()); + } + } else if (qmlValue.isValueSource()) { + if (modelProperty.isNodeProperty()) { + ModelNode nodePropertyNode = modelProperty.toNodeProperty().modelNode(); + syncNode(nodePropertyNode, qmlValue.toValueSource().object(), differenceHandler); + } else { + differenceHandler.shouldBeNodeProperty(modelProperty, qmlValue.toValueSource().object()); + } + } else if (qmlValue.isInvalid()) { + // skip these nodes + } else { + qWarning() << "Found an unknown qml value!"; + } +} + +void TextToModelMerger::syncNodeListProperty(NodeListProperty &modelListProperty, const QmlDomList &domList, DifferenceHandler &differenceHandler) +{ + QList<ModelNode> modelNodes = modelListProperty.toModelNodeList(); + QList<QmlDomValue> domValues = domList.values(); + int i = 0; + for (; i < modelNodes.size() && i < domValues.size(); ++i) { + QmlDomValue value = domValues.at(i); + if (value.isObject()) { + ModelNode modelNode = modelNodes.at(i); + syncNode(modelNode, value.toObject(), differenceHandler); + } else { + qDebug() << "*** Oops, we got a non-object item in the list!"; + } + } + + for (int j = i; j < domValues.size(); ++j) { + // more elements in the dom-list, so add them to the model + QmlDomValue value = domValues.at(j); + if (value.isObject()) { + differenceHandler.listPropertyMissingModelNode(modelListProperty, value.toObject()); + } else { + qDebug() << "*** Oops, we got a non-object item in the list!"; + } + } + + for (int j = i; j < modelNodes.size(); ++j) { + // more elements in the model, so remove them. + ModelNode modelNode = modelNodes.at(j); + differenceHandler.modelNodeAbsentFromQml(modelNode); + } +} + +ModelNode TextToModelMerger::createModelNode(const QmlDomObject &domObject, DifferenceHandler &differenceHandler) +{ + ModelNode newNode = m_rewriterView->createModelNode(domObject.objectType(), domObject.objectTypeMajorVersion(), domObject.objectTypeMinorVersion()); + syncNode(newNode, domObject, differenceHandler); + return newNode; +} + +QVariant TextToModelMerger::convertToVariant(const ModelNode &node, const QmlDomProperty &qmlProperty, const QmlDomDynamicProperty &qmlDynamicProperty) +{ + QString stringValue = qmlProperty.value().toLiteral().literal(); + + if (qmlDynamicProperty.isValid()) { + const int type = qmlDynamicProperty.propertyType(); + QVariant value(stringValue); + value.convert(static_cast<QVariant::Type>(type)); + return value; + } + + const NodeMetaInfo nodeMetaInfo = node.metaInfo(); + + if (nodeMetaInfo.isValid()) { + const PropertyMetaInfo propertyMetaInfo = nodeMetaInfo.property(qmlProperty.propertyName(), true); + + if (propertyMetaInfo.isValid()) { + QVariant castedValue = propertyMetaInfo.castedValue(stringValue); + if (!castedValue.isValid()) + qWarning() << "Casting the value" << stringValue << "of property" << propertyMetaInfo.name() << "to the property type" << propertyMetaInfo.type() << "failed"; + return castedValue; + } else if (node.type() == QLatin1String("Qt/PropertyChanges")) { + // In the future, we should do the type resolving in a second pass, or delay setting properties until the full file has been parsed. + return QVariant(stringValue); + } else { + qWarning() << "Unknown property" << qmlProperty.propertyName() << "in node" << node.type() << "with value" << stringValue; + return QVariant(); + } + } else { + qWarning() << "Unknown property" << qmlProperty.propertyName() << "in node" << node.type() << "with value" << stringValue; + return QVariant::fromValue(stringValue); + } +} + +void ModelValidator::bindingExpressionsDiffer(BindingProperty &modelProperty, const QString &qmlBinding) +{ + Q_ASSERT(modelProperty.expression() == qmlBinding); + Q_ASSERT(0); +} + +void ModelValidator::shouldBeBindingProperty(AbstractProperty &modelProperty, const QString &/*qmlBinding*/) +{ + Q_ASSERT(modelProperty.isBindingProperty()); + Q_ASSERT(0); +} + +void ModelValidator::shouldBeNodeListProperty(AbstractProperty &modelProperty, const QmlDomList &/*domList*/) +{ + Q_ASSERT(modelProperty.isNodeListProperty()); + Q_ASSERT(0); +} + +void ModelValidator::variantValuesDiffer(VariantProperty &modelProperty, const QVariant &qmlVariantValue, const QString &dynamicTypeName) +{ + Q_ASSERT(modelProperty.isDynamic() == !dynamicTypeName.isEmpty()); + if (modelProperty.isDynamic()) { + Q_ASSERT(modelProperty.dynamicTypeName() == dynamicTypeName); + } + + Q_ASSERT(equals(modelProperty.value(), qmlVariantValue)); + Q_ASSERT(0); +} + +void ModelValidator::shouldBeVariantProperty(AbstractProperty &modelProperty, const QVariant &/*qmlVariantValue*/, const QString &/*dynamicTypeName*/) +{ + Q_ASSERT(modelProperty.isVariantProperty()); + Q_ASSERT(0); +} + +void ModelValidator::shouldBeNodeProperty(AbstractProperty &modelProperty, const QmlDomObject &/*qmlObject*/) +{ + Q_ASSERT(modelProperty.isNodeProperty()); + Q_ASSERT(0); +} + +void ModelValidator::modelNodeAbsentFromQml(ModelNode &modelNode) +{ + Q_ASSERT(!modelNode.isValid()); + Q_ASSERT(0); +} + +void ModelValidator::listPropertyMissingModelNode(NodeListProperty &/*modelProperty*/, const QmlDomObject &/*qmlObject*/) +{ + Q_ASSERT(0); +} + +void ModelValidator::typeDiffers(ModelNode &modelNode, const QmlDomObject &domObject) +{ + Q_ASSERT(modelNode.type() == domObject.objectType()); + Q_ASSERT(modelNode.majorVersion() == domObject.objectTypeMajorVersion()); + Q_ASSERT(modelNode.minorVersion() == domObject.objectTypeMinorVersion()); + Q_ASSERT(0); +} + +void ModelValidator::propertyAbsentFromQml(AbstractProperty &modelProperty) +{ + Q_ASSERT(!modelProperty.isValid()); + Q_ASSERT(0); +} + +void ModelValidator::idsDiffer(ModelNode &modelNode, const QString &qmlId) +{ + Q_ASSERT(modelNode.id() == qmlId); + Q_ASSERT(0); +} + +void ModelAmender::bindingExpressionsDiffer(BindingProperty &modelProperty, const QString &qmlBinding) +{ + modelProperty.toBindingProperty().setExpression(qmlBinding); +} + +void ModelAmender::shouldBeBindingProperty(AbstractProperty &modelProperty, const QString &qmlBinding) +{ + ModelNode theNode = modelProperty.parentModelNode(); + BindingProperty newModelProperty = theNode.bindingProperty(modelProperty.name()); + newModelProperty.setExpression(qmlBinding); +} + +void ModelAmender::shouldBeNodeListProperty(AbstractProperty &modelProperty, const QmlDomList &domList) +{ + ModelNode theNode = modelProperty.parentModelNode(); + NodeListProperty newNodeListProperty = theNode.nodeListProperty(modelProperty.name()); + m_merger->syncNodeListProperty(newNodeListProperty, domList, *this); +} + +void ModelAmender::variantValuesDiffer(VariantProperty &modelProperty, const QVariant &qmlVariantValue, const QString &dynamicType) +{ + if (dynamicType.isEmpty()) + modelProperty.setValue(qmlVariantValue); + else + modelProperty.setDynamicTypeNameAndValue(dynamicType, qmlVariantValue); +} + +void ModelAmender::shouldBeVariantProperty(AbstractProperty &modelProperty, const QVariant &qmlVariantValue, const QString &dynamicTypeName) +{ +// qDebug() << "property" << modelProperty.name() << "in node" << modelProperty.parentModelNode().id(); + ModelNode theNode = modelProperty.parentModelNode(); + VariantProperty newModelProperty = theNode.variantProperty(modelProperty.name()); + + if (dynamicTypeName.isEmpty()) + newModelProperty.setValue(qmlVariantValue); + else + newModelProperty.setDynamicTypeNameAndValue(dynamicTypeName, qmlVariantValue); +} + +void ModelAmender::shouldBeNodeProperty(AbstractProperty &modelProperty, const QmlDomObject &qmlObject) +{ + ModelNode theNode = modelProperty.parentModelNode(); + NodeProperty newNodeProperty = theNode.nodeProperty(modelProperty.name()); + newNodeProperty.setModelNode(m_merger->createModelNode(qmlObject, *this)); +} + +void ModelAmender::modelNodeAbsentFromQml(ModelNode &modelNode) +{ + modelNode.destroy(); +} + +void ModelAmender::listPropertyMissingModelNode(NodeListProperty &modelProperty, const QmlDomObject &qmlObject) +{ + modelProperty.reparentHere(m_merger->createModelNode(qmlObject, *this)); +} + +void ModelAmender::typeDiffers(ModelNode &modelNode, const QmlDomObject &domObject) +{ + foreach (const QString &propertyName, modelNode.propertyNames()) + modelNode.removeProperty(propertyName); + + modelNode.changeType(domObject.objectType(), domObject.objectTypeMajorVersion(), domObject.objectTypeMinorVersion()); +} + +void ModelAmender::propertyAbsentFromQml(AbstractProperty &modelProperty) +{ + modelProperty.parentModelNode().removeProperty(modelProperty.name()); +} + +void ModelAmender::idsDiffer(ModelNode &modelNode, const QString &qmlId) +{ + modelNode.setId(qmlId); +} + +bool TextToModelMerger::isSignalPropertyName(const QString &signalName) +{ + // see QmlCompiler::isSignalPropertyName + return signalName.length() >= 3 && signalName.startsWith(QLatin1String("on")) && + signalName.at(2).isLetter(); +} diff --git a/src/plugins/qmldesigner/core/model/texttomodelmerger.h b/src/plugins/qmldesigner/core/model/texttomodelmerger.h new file mode 100644 index 00000000000..b76bff48090 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/texttomodelmerger.h @@ -0,0 +1,159 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef TEXTTOMODELMERGER_H +#define TEXTTOMODELMERGER_H + +#include "corelib_global.h" +#include "nodelistproperty.h" +#include "modelnode.h" + +#include <QmlDomObject> + +namespace QmlDesigner { + +class CORESHARED_EXPORT RewriterView; + +namespace Internal { + +class DifferenceHandler; + +class TextToModelMerger +{ + TextToModelMerger(const TextToModelMerger&); + TextToModelMerger &operator=(const TextToModelMerger&); + +public: + TextToModelMerger(RewriterView *reWriterView); + bool isActive() const; + + void setupImports(QmlDomDocument &doc); + bool load(const QByteArray &data, DifferenceHandler &differenceHandler); + +protected: + RewriterView *view(); + void setActive(bool active); + +public: + void syncNode(ModelNode &modelNode, + const QmlDomObject &qmlObject, + DifferenceHandler &differenceHandler); + void syncProperty(AbstractProperty &modelProperty, + const QmlDomProperty &qmlProperty, + const QmlDomDynamicProperty &qmlDynamicProperty, + DifferenceHandler &differenceHandler); + void syncNodeListProperty(NodeListProperty &modelListProperty, + const QmlDomList &domList, + DifferenceHandler &differenceHandler); + ModelNode createModelNode(const QmlDomObject &domObject, + DifferenceHandler &differenceHandler); + static QVariant convertToVariant(const ModelNode &node, + const QmlDomProperty &qmlProperty, + const QmlDomDynamicProperty &qmlDynamicProperty); + +private: + static bool isSignalPropertyName(const QString &signalName); + + RewriterView *m_rewriterView; + bool m_isActive; +}; + +class DifferenceHandler +{ +public: + DifferenceHandler(TextToModelMerger *textToModelMerger): + m_merger(textToModelMerger) + {} + virtual ~DifferenceHandler() + {} + + virtual void bindingExpressionsDiffer(BindingProperty &modelProperty, const QString &qmlBinding) = 0; + virtual void shouldBeBindingProperty(AbstractProperty &modelProperty, const QString &qmlBinding) = 0; + virtual void shouldBeNodeListProperty(AbstractProperty &modelProperty, const QmlDomList &domList) = 0; + virtual void variantValuesDiffer(VariantProperty &modelProperty, const QVariant &qmlVariantValue, const QString &dynamicTypeName) = 0; + virtual void shouldBeVariantProperty(AbstractProperty &modelProperty, const QVariant &qmlVariantValue, const QString &dynamicTypeName) = 0; + virtual void shouldBeNodeProperty(AbstractProperty &modelProperty, const QmlDomObject &qmlObject) = 0; + virtual void modelNodeAbsentFromQml(ModelNode &modelNode) = 0; + virtual void listPropertyMissingModelNode(NodeListProperty &modelProperty, const QmlDomObject &qmlObject) = 0; + virtual void typeDiffers(ModelNode &modelNode, const QmlDomObject &domObject) = 0; + virtual void propertyAbsentFromQml(AbstractProperty &modelProperty) = 0; + virtual void idsDiffer(ModelNode &modelNode, const QString &qmlId) = 0; + +protected: + TextToModelMerger *m_merger; +}; + +class ModelValidator: public DifferenceHandler +{ +public: + ModelValidator(TextToModelMerger *textToModelMerger): + DifferenceHandler(textToModelMerger) + {} + ~ModelValidator() + {} + + virtual void bindingExpressionsDiffer(BindingProperty &modelProperty, const QString &qmlBinding); + virtual void shouldBeBindingProperty(AbstractProperty &modelProperty, const QString &qmlBinding); + virtual void shouldBeNodeListProperty(AbstractProperty &modelProperty, const QmlDomList &domList); + virtual void variantValuesDiffer(VariantProperty &modelProperty, const QVariant &qmlVariantValue, const QString &dynamicTypeName); + virtual void shouldBeVariantProperty(AbstractProperty &modelProperty, const QVariant &qmlVariantValue, const QString &dynamicTypeName); + virtual void shouldBeNodeProperty(AbstractProperty &modelProperty, const QmlDomObject &qmlObject); + virtual void modelNodeAbsentFromQml(ModelNode &modelNode); + virtual void listPropertyMissingModelNode(NodeListProperty &modelProperty, const QmlDomObject &qmlObject); + virtual void typeDiffers(ModelNode &modelNode, const QmlDomObject &domObject); + virtual void propertyAbsentFromQml(AbstractProperty &modelProperty); + virtual void idsDiffer(ModelNode &modelNode, const QString &qmlId); +}; + +class ModelAmender: public DifferenceHandler +{ +public: + ModelAmender(TextToModelMerger *textToModelMerger): + DifferenceHandler(textToModelMerger) + {} + ~ModelAmender() + {} + + virtual void bindingExpressionsDiffer(BindingProperty &modelProperty, const QString &qmlBinding); + virtual void shouldBeBindingProperty(AbstractProperty &modelProperty, const QString &qmlBinding); + virtual void shouldBeNodeListProperty(AbstractProperty &modelProperty, const QmlDomList &domList); + virtual void variantValuesDiffer(VariantProperty &modelProperty, const QVariant &qmlVariantValue, const QString &dynamicType); + virtual void shouldBeVariantProperty(AbstractProperty &modelProperty, const QVariant &qmlVariantValue, const QString &dynamicTypeName); + virtual void shouldBeNodeProperty(AbstractProperty &modelProperty, const QmlDomObject &qmlObject); + virtual void modelNodeAbsentFromQml(ModelNode &modelNode); + virtual void listPropertyMissingModelNode(NodeListProperty &modelProperty, const QmlDomObject &qmlObject); + virtual void typeDiffers(ModelNode &modelNode, const QmlDomObject &domObject); + virtual void propertyAbsentFromQml(AbstractProperty &modelProperty); + virtual void idsDiffer(ModelNode &modelNode, const QString &qmlId); +}; + +} //Internal +} //QmlDesigner + +#endif // TEXTTOMODELMERGER_H diff --git a/src/plugins/qmldesigner/core/model/variantparser.cpp b/src/plugins/qmldesigner/core/model/variantparser.cpp new file mode 100644 index 00000000000..d50a042e81a --- /dev/null +++ b/src/plugins/qmldesigner/core/model/variantparser.cpp @@ -0,0 +1,149 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "variantparser.h" +#include <private/qmlvaluetype_p.h> + +#include <QPoint> +#include <QPointF> +#include <QFont> +#include <QRect> +#include <QRectF> +#include <QSize> +#include <QSizeF> +#include <QVector3D> +#include <QMetaProperty> + +namespace QmlDesigner { +namespace Internal { + +VariantParser::VariantParser(const QVariant &value) : m_valueType(QmlValueTypeFactory::valueType(value.type())) +{ + if (m_valueType) { + m_valueType->setValue(value); + if (!m_valueType->value().isValid()) + qWarning("VariantParser::VariantParser: value not valid"); + } +} + +VariantParser::~VariantParser() +{ + if (m_valueType) + delete m_valueType; +} + +QVariant VariantParser::value() const +{ + return m_valueType->value(); +} + +QVariant VariantParser::property(QString name) const +{ + if (!m_valueType) + return QVariant(); + return m_valueType->property(name.toLatin1()); +} + +bool VariantParser::setProperty(const QString &name, const QVariant &value) +{ + if (!m_valueType) + return false; + if (name == "pixelSize" && value.toInt() < 1) + return false; //this check we have to harcode + return m_valueType->setProperty(name.toLatin1(), value); +} + +bool VariantParser::isValueType(const QString &type) +{ + return VariantParser::create(type).isValid(); +} + +QStringList VariantParser::properties() +{ + if (!m_valueType) + return QStringList(); + QStringList propertyList; + for (int i=1; i < m_valueType->metaObject()->propertyCount(); i++) { + QMetaProperty metaProperty = m_valueType->metaObject()->property(i); + propertyList.append(metaProperty.name()); + } + return propertyList; +} + +VariantParser VariantParser::create(const QString &type) +{ + if (type == "QFont") + return VariantParser(QVariant(QFont())); + if (type == "QPoint") + return VariantParser(QVariant(QPoint())); + if (type == "QPointF") + return VariantParser(QVariant(QPointF())); + if (type == "QSize") + return VariantParser(QVariant(QSize())); + if (type == "QSizeF") + return VariantParser(QVariant(QSizeF())); + if (type == "QRect") + return VariantParser(QVariant(QRect())); + if (type == "QRectF") + return VariantParser(QVariant(QRectF())); + if (type == "QVector3D") + return VariantParser(QVariant(QVector3D())); + + return VariantParser(QVariant()); +} + +void VariantParser::init(const QString &type) +{ + if (type == "QFont") + m_valueType = QmlValueTypeFactory::valueType(QVariant::Font); + if (type == "QPoint") + m_valueType = QmlValueTypeFactory::valueType(QVariant::Point); + if (type == "QPointF") + m_valueType = QmlValueTypeFactory::valueType(QVariant::PointF); + if (type == "QSize") + m_valueType = QmlValueTypeFactory::valueType(QVariant::Size); + if (type == "QSizeF") + m_valueType = QmlValueTypeFactory::valueType(QVariant::SizeF); + if (type == "QRect") + m_valueType = QmlValueTypeFactory::valueType(QVariant::Rect); + if (type == "QRectF") + m_valueType = QmlValueTypeFactory::valueType(QVariant::RectF); + if (type == "QVector3D") + m_valueType = QmlValueTypeFactory::valueType(QVariant::Vector3D); +} + +bool VariantParser::isValid() +{ + return m_valueType && m_valueType->value().isValid(); +} + +} // namespace Internal +} // namespace QmlDesigner + + diff --git a/src/plugins/qmldesigner/core/model/variantparser.h b/src/plugins/qmldesigner/core/model/variantparser.h new file mode 100644 index 00000000000..711e3b8f4ba --- /dev/null +++ b/src/plugins/qmldesigner/core/model/variantparser.h @@ -0,0 +1,65 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef QVARIANTPARSER_H +#define QVARIANTPARSER_H + +#include <QVariant> +#include <QString> +#include <QStringList> + +class QmlValueType; + +namespace QmlDesigner { +namespace Internal { + +class VariantParser +{ +public: + VariantParser(const QVariant &value); + ~VariantParser(); + QVariant value() const; + QVariant property(QString name) const; + bool setProperty(const QString &name, const QVariant &value); + bool isValid(); + QStringList properties(); + void init(const QString &type); + + static bool isValueType(const QString &type); + static VariantParser create(const QString &type); +private: + QmlValueType *m_valueType; +}; + + + +} // namespace Internal +} // namespace QmlDesigner + +#endif // QVARIANTPARSER_H diff --git a/src/plugins/qmldesigner/core/model/variantproperty.cpp b/src/plugins/qmldesigner/core/model/variantproperty.cpp new file mode 100644 index 00000000000..051b6fddf73 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/variantproperty.cpp @@ -0,0 +1,143 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "variantproperty.h" +#include "internalproperty.h" +#include "internalvariantproperty.h" +#include "invalidmodelnodeexception.h" +#include "invalidpropertyexception.h" +#include "invalidargumentexception.h" +#include "internalnode_p.h" +#include "model.h" +#include "model_p.h" + + + +namespace QmlDesigner { + +VariantProperty::VariantProperty() +{} + +VariantProperty::VariantProperty(const VariantProperty &property, AbstractView *view) + : AbstractProperty(property.name(), property.internalNode(), property.model(), view) +{ + +} + +VariantProperty::VariantProperty(const QString &propertyName, const Internal::InternalNodePointer &internalNode, Model* model, AbstractView *view) : + AbstractProperty(propertyName, internalNode, model, view) +{ +} + +void VariantProperty::setValue(const QVariant &value) +{ + if (!isValid()) + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + if (value.isNull()) + throw InvalidArgumentException(__LINE__, __FUNCTION__, __FILE__, name()); + + if (internalNode()->hasProperty(name())) { //check if oldValue != value + Internal::InternalProperty::Pointer internalProperty = internalNode()->property(name()); + if (internalProperty->isVariantProperty() + && internalProperty->toVariantProperty()->value() == value + && dynamicTypeName().isEmpty()) + + return; + } + + if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isVariantProperty()) + model()->m_d->removeProperty(internalNode()->property(name())); + + model()->m_d->setVariantProperty(internalNode(), name(), value); +} + +QVariant VariantProperty::value() const +{ + if (internalNode()->hasProperty(name()) + && internalNode()->property(name())->isVariantProperty()) + return internalNode()->variantProperty(name())->value(); + + return QVariant(); +} + +VariantProperty& VariantProperty::operator= (const QVariant &value) +{ + setValue(value); + + return *this; +} + +void VariantProperty::setDynamicTypeNameAndValue(const QString &type, const QVariant &value) +{ + if (!isValid()) + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + + if (type.isEmpty()) { + throw InvalidArgumentException(__LINE__, __FUNCTION__, __FILE__, name()); + } + + if (internalNode()->hasProperty(name())) { //check if oldValue != value + Internal::InternalProperty::Pointer internalProperty = internalNode()->property(name()); + if (internalProperty->isVariantProperty() + && internalProperty->toVariantProperty()->value() == value + && internalProperty->toVariantProperty()->dynamicTypeName() == type) + + return; + } + + if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isVariantProperty()) + model()->m_d->removeProperty(internalNode()->property(name())); + + model()->m_d->setDynamicVariantProperty(internalNode(), name(), type, value); +} + +VariantProperty& VariantProperty::operator= (const QPair<QString, QVariant> &typeValuePair) +{ + setDynamicTypeNameAndValue(typeValuePair.first, typeValuePair.second); + return *this; +} + + + + + +QDebug operator<<(QDebug debug, const VariantProperty &VariantProperty) +{ + return debug.nospace() << "VariantProperty(" << VariantProperty.name() << ")"; +} +QTextStream& operator<<(QTextStream &stream, const VariantProperty &property) +{ + stream << "VariantProperty(" << property.name() << ")"; + + return stream; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/core/model/viewlogger.cpp b/src/plugins/qmldesigner/core/model/viewlogger.cpp new file mode 100644 index 00000000000..2b1c5556648 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/viewlogger.cpp @@ -0,0 +1,200 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "viewlogger.h" +#include <QDebug> +#include <QTemporaryFile> +#include <QDir> +#include <variantproperty.h> +#include <bindingproperty.h> +#include <nodeabstractproperty.h> +#include <nodelistproperty.h> + +namespace QmlDesigner { +namespace Internal { + +static QString serialize(AbstractView::PropertyChangeFlags change) +{ + QStringList tokenList; + + if (change.testFlag(AbstractView::PropertiesAdded)) + tokenList.append(QLatin1String("PropertiesAdded")); + + if (change.testFlag(AbstractView::EmptyPropertiesRemoved)) + tokenList.append(QLatin1String("EmptyPropertiesRemoved")); + + return tokenList.join(" "); + + return QString(); +} + +static QString indent(const QString &name = QString()) { + return name.leftJustified(30, ' '); +} + +QString ViewLogger::time() const +{ + return QString::number(m_timer.elapsed()).leftJustified(7, ' '); +} + +ViewLogger::ViewLogger(QObject *parent) + : AbstractView(parent) +{ +#ifdef Q_OS_MAC + const QLatin1String logPath("Library/Logs/Bauhaus"); + QDir logDir(QDir::homePath()); + logDir.mkpath(logPath); + const QString tempPath = QDir::homePath() + QDir::separator() + logPath; +#else + const QString tempPath = QDir::tempPath(); +#endif + + QTemporaryFile *temporaryFile = new QTemporaryFile(tempPath + QString("/bauhaus-logger-%1-XXXXXX.txt").arg(QDateTime::currentDateTime().toString(Qt::ISODate)), this); + temporaryFile->setAutoRemove(false); + if (temporaryFile->open()) { + qDebug() << "TemporaryLoggerFile is:" << temporaryFile->fileName(); + + m_output.setDevice(temporaryFile); + } + + m_timer.start(); +} + +void ViewLogger::modelAttached(Model *model) +{ + m_output << time() << indent("modelAttached:") << model << endl; + AbstractView::modelAttached(model); +} + +void ViewLogger::modelAboutToBeDetached(Model *model) +{ + m_output << time() << indent("modelAboutToBeDetached:") << model << endl; + AbstractView::modelAboutToBeDetached(model); +} + +void ViewLogger::nodeCreated(const ModelNode &createdNode) +{ + m_output << time() << indent("nodeCreated:") << createdNode << endl; +} + +void ViewLogger::nodeAboutToBeRemoved(const ModelNode &removedNode) +{ + m_output << time() << indent("nodeAboutToBeRemoved:") << removedNode << endl; +} + +void ViewLogger::nodeRemoved(const ModelNode &removedNode, const NodeAbstractProperty &parentProperty, PropertyChangeFlags propertyChange) +{ + m_output << time() << indent("nodeRemoved:") << removedNode << parentProperty << serialize(propertyChange) << endl; +} + +void ViewLogger::nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent, const NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags propertyChange) +{ + m_output << time() << indent("nodeReparented:") << node << "\t" << newPropertyParent << "\t" << oldPropertyParent << "\t" << serialize(propertyChange) << endl; +} + +void ViewLogger::nodeIdChanged(const ModelNode& node, const QString& newId, const QString& oldId) +{ + m_output << time() << indent("nodeIdChanged:") << node << "\t" << newId << "\t" << oldId << endl; +} + +void ViewLogger::propertiesAboutToBeRemoved(const QList<AbstractProperty>& propertyList) +{ + m_output << time() << indent("propertiesAboutToBeRemoved:") << endl; + foreach (const AbstractProperty &property, propertyList) + m_output << time() << indent() << property << endl; +} + +void ViewLogger::propertiesRemoved(const QList<AbstractProperty> &propertyList) +{ + m_output << time() << indent("propertiesRemoved:") << endl; + foreach (const AbstractProperty &property, propertyList) + m_output << time() << indent() << property << endl; +} + +void ViewLogger::variantPropertiesChanged(const QList<VariantProperty>& propertyList, PropertyChangeFlags propertyChange) +{ + m_output << time() << indent("variantPropertiesChanged:") << serialize(propertyChange) << endl; + foreach(const VariantProperty &property, propertyList) + m_output << time() << indent() << property << endl; +} + +void ViewLogger::bindingPropertiesChanged(const QList<BindingProperty>& propertyList, PropertyChangeFlags propertyChange) +{ + m_output << time() << indent("bindingPropertiesChanged:") << serialize(propertyChange) << endl; + foreach(const BindingProperty &property, propertyList) + m_output << time() << indent() << property << endl; +} + +void ViewLogger::nodeTypeChanged(const ModelNode &node,const QString &type, int majorVersion, int minorVersion) +{ + m_output << time() << indent("nodeTypeChanged:") << node << type << majorVersion << minorVersion << endl; +} + +void ViewLogger::selectedNodesChanged(const QList<ModelNode> &selectedNodeList, + const QList<ModelNode> &lastSelectedNodeList) +{ + m_output << time() << indent("selectedNodesChanged:") << endl; + foreach(const ModelNode &node, selectedNodeList) + m_output << time() << indent("new: ") << node << endl; + foreach(const ModelNode &node, lastSelectedNodeList) + m_output << time() << indent("old: ") << node << endl; +} + +void ViewLogger::fileUrlChanged(const QUrl &oldUrl, const QUrl &newUrl) +{ + m_output << time() << indent("fileUrlChanged:") << oldUrl.toString() << "\t" << newUrl.toString() << endl; +} + +void ViewLogger::nodeSlidedToIndex(const NodeListProperty &listProperty, int newIndex, int oldIndex) +{ + m_output << time() << indent("nodeSlidedToIndex:") << listProperty << newIndex << oldIndex << endl; +} + +void ViewLogger::importsChanged() +{ + m_output << time() << indent("importsChanged:") << endl; +} + +void ViewLogger::auxiliaryDataChanged(const ModelNode &node, const QString &name, const QVariant &data) +{ + m_output << time() << indent("auxiliaryDataChanged:") << node << "\t" << name << "\t" << data.toString() << endl; +} + +void ViewLogger::customNotification(const AbstractView *view, const QString &identifier, const QList<ModelNode> &nodeList, const QList<QVariant> &data) +{ + m_output << time() << indent("customNotification:") << view << identifier << endl; + foreach(const ModelNode &node, nodeList) + m_output << time() << indent("node: ") << node << endl; + foreach(const QVariant &variant, data) + m_output << time() << indent("data: ") << variant.toString() << endl; +} + + +} // namespace Internal +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/core/model/viewlogger.h b/src/plugins/qmldesigner/core/model/viewlogger.h new file mode 100644 index 00000000000..388c3ab8cc7 --- /dev/null +++ b/src/plugins/qmldesigner/core/model/viewlogger.h @@ -0,0 +1,85 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef VIEWLOGGER_H +#define VIEWLOGGER_H + +#include "abstractview.h" + +#include <QTextStream> +#include <QTime> + +namespace QmlDesigner { +namespace Internal { + +class ViewLogger : public QmlDesigner::AbstractView +{ + Q_OBJECT +public: + ViewLogger(QObject *parent); + + void modelAttached(Model *model); + void modelAboutToBeDetached(Model *model); + + void nodeCreated(const ModelNode &createdNode); + void nodeAboutToBeRemoved(const ModelNode &removedNode); + void nodeRemoved(const ModelNode &removedNode, const NodeAbstractProperty &parentProperty, PropertyChangeFlags propertyChange); + void nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent, const NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags propertyChange); + void nodeIdChanged(const ModelNode& node, const QString& newId, const QString& oldId); + void propertiesAboutToBeRemoved(const QList<AbstractProperty>& propertyList); + void propertiesRemoved(const QList<AbstractProperty>& propertyList); + void variantPropertiesChanged(const QList<VariantProperty>& propertyList, PropertyChangeFlags propertyChange); + void bindingPropertiesChanged(const QList<BindingProperty>& propertyList, PropertyChangeFlags propertyChange); + void nodeTypeChanged(const ModelNode &node,const QString &type, int majorVersion, int minorVersion); + + void selectedNodesChanged(const QList<ModelNode> &selectedNodeList, + const QList<ModelNode> &lastSelectedNodeList); + + void fileUrlChanged(const QUrl &oldUrl, const QUrl &newUrl); + + void nodeSlidedToIndex(const NodeListProperty &listProperty, int newIndex, int oldIndex); + + void importsChanged(); + + void auxiliaryDataChanged(const ModelNode &node, const QString &name, const QVariant &data); + + void customNotification(const AbstractView *view, const QString &identifier, const QList<ModelNode> &nodeList, const QList<QVariant> &data); + +protected: + QString time() const; + +private: + QTextStream m_output; + QTime m_timer; +}; + +} // namespace Internal +} // namespace QmlDesigner + +#endif // VIEWLOGGER_H diff --git a/src/plugins/qmldesigner/core/pluginmanager/widgetpluginmanager.cpp b/src/plugins/qmldesigner/core/pluginmanager/widgetpluginmanager.cpp new file mode 100644 index 00000000000..72557af669b --- /dev/null +++ b/src/plugins/qmldesigner/core/pluginmanager/widgetpluginmanager.cpp @@ -0,0 +1,103 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "widgetpluginmanager.h" +#include "widgetpluginpath.h" +#include <iwidgetplugin.h> + +#include <QtCore/QCoreApplication> +#include <QtCore/QObject> +#include <QtCore/QSharedData> +#include <QtCore/QDir> +#include <QtCore/QStringList> +#include <QtCore/QDebug> +#include <QWeakPointer> +#include <QtCore/QPluginLoader> +#include <QtCore/QFileInfo> +#include <QtCore/QLibraryInfo> + +#include <QtGui/QStandardItemModel> +#include <QtGui/QStandardItem> + +enum { debug = 0 }; + +namespace QmlDesigner { + +namespace Internal { + +// ---- PluginManager[Private] +class WidgetPluginManagerPrivate { +public: + typedef QList<WidgetPluginPath> PluginPathList; + PluginPathList m_paths; +}; + +WidgetPluginManager::WidgetPluginManager() : + m_d(new WidgetPluginManagerPrivate) +{ +} + +WidgetPluginManager::~WidgetPluginManager() +{ + delete m_d; +} + +WidgetPluginManager::IWidgetPluginList WidgetPluginManager::instances() +{ + if (debug) + qDebug() << '>' << Q_FUNC_INFO << QLibraryInfo::buildKey(); + IWidgetPluginList rc; + const WidgetPluginManagerPrivate::PluginPathList::iterator end = m_d->m_paths.end(); + for (WidgetPluginManagerPrivate::PluginPathList::iterator it = m_d->m_paths.begin(); it != end; ++it) + it->getInstances(&rc); + if (debug) + qDebug() << '<' << Q_FUNC_INFO << rc.size(); + return rc; +} + +bool WidgetPluginManager::addPath(const QString &path) +{ + const QDir dir(path); + if (!dir.exists()) + return false; + m_d->m_paths.push_back(WidgetPluginPath(dir)); + return true; +} + +QAbstractItemModel *WidgetPluginManager::createModel(QObject *parent) +{ + QStandardItemModel *model = new QStandardItemModel(parent); + const WidgetPluginManagerPrivate::PluginPathList::iterator end = m_d->m_paths.end(); + for (WidgetPluginManagerPrivate::PluginPathList::iterator it = m_d->m_paths.begin(); it != end; ++it) + model->appendRow(it->createModelItem()); + return model; +} + +} // namespace Internal +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/core/pluginmanager/widgetpluginmanager.h b/src/plugins/qmldesigner/core/pluginmanager/widgetpluginmanager.h new file mode 100644 index 00000000000..d2b4a765690 --- /dev/null +++ b/src/plugins/qmldesigner/core/pluginmanager/widgetpluginmanager.h @@ -0,0 +1,76 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef WIDGETPLUGINMANAGER_H +#define WIDGETPLUGINMANAGER_H + +#include <QtCore/QObject> +#include <QtCore/QList> + +QT_BEGIN_NAMESPACE +class QString; +class QAbstractItemModel; +QT_END_NAMESPACE + +namespace QmlDesigner { + +class IWidgetPlugin; + +namespace Internal { + +class WidgetPluginManagerPrivate; + +// PluginManager: Loads the plugin libraries on demand "as lazy as +// possible", that is, directories are scanned and +// instances are created only when instances() is called. + +class WidgetPluginManager +{ + Q_DISABLE_COPY(WidgetPluginManager) +public: + typedef QList<IWidgetPlugin *> IWidgetPluginList; + + WidgetPluginManager(); + ~WidgetPluginManager(); + + bool addPath(const QString &path); + + IWidgetPluginList instances(); + + // Convenience to create a model for an "About Plugins" + // dialog. Forces plugin initialization. + QAbstractItemModel *createModel(QObject *parent = 0); + +private: + WidgetPluginManagerPrivate *m_d; +}; + +} // namespace Internal +} // namespace QmlDesigner +#endif // WIDGETPLUGINMANAGER_H diff --git a/src/plugins/qmldesigner/core/pluginmanager/widgetpluginpath.cpp b/src/plugins/qmldesigner/core/pluginmanager/widgetpluginpath.cpp new file mode 100644 index 00000000000..07108fa0945 --- /dev/null +++ b/src/plugins/qmldesigner/core/pluginmanager/widgetpluginpath.cpp @@ -0,0 +1,206 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "widgetpluginpath.h" +#include <iwidgetplugin.h> +#include <QtCore/QLibrary> +#include <QWeakPointer> +#include <QtCore/QPluginLoader> +#include <QtCore/QFileInfo> +#include <QtCore/QLibraryInfo> +#include <QtCore/QCoreApplication> +#include <QtCore/QObject> +#include <QtCore/QSharedData> +#include <QDebug> + +enum { debug = 1 }; + +namespace QmlDesigner { + +namespace Internal { + +// Initialize and create instance of a plugin from scratch, +// that is, make sure the library is loaded and has an instance +// of the IPlugin type. Once something fails, mark it as failed +// ignore it from then on. +static IWidgetPlugin *instance(WidgetPluginData &p) +{ + qWarning() << "START"; + qWarning() << p.path; + // Go stale once something fails + if (p.failed) + return 0; + // Pull up the plugin, retrieve IPlugin instance. + if (!p.instanceGuard) { + p.instance = 0; + QPluginLoader loader(p.path); + qWarning() << "guard"; + qWarning() << p.path; + if (!(loader.isLoaded() || loader.load())) { + p.failed = true; + qWarning() << p.errorMessage; + p.errorMessage = loader.errorString(); + return 0; + } + QObject *object = loader.instance(); + if (!object) { + p.failed = true; + qWarning() << "plugin loading failed"; + p.errorMessage = QCoreApplication::translate("WidgetPluginManager", "Failed to create instance."); + qWarning() << p.errorMessage; + return 0; + } + IWidgetPlugin *iplugin = qobject_cast<IWidgetPlugin *>(object); + if (!iplugin) { + p.failed = true; + p.errorMessage = QCoreApplication::translate("WidgetPluginManager", "Not a QmlDesigner plugin."); + qWarning() << "cast failed"; + qWarning() << p.errorMessage; + delete object; + return 0; + } + p.instanceGuard = object; + p.instance = iplugin; + } + // Ensure it is initialized + /*if (!p.instance->isInitialized()) { + if (!p.instance->initialize(&p.errorMessage)) { + p.failed = true; + delete p.instance; + p.instance = 0; + return 0; + } + }*/ + qWarning() << "instance created"; + return p.instance; +} + +WidgetPluginData::WidgetPluginData(const QString &p) : + path(p), + failed(false), + instance(0) +{ +} + + +WidgetPluginPath::WidgetPluginPath(const QDir &path) : + m_path(path), + m_loaded(false) +{ +} + +// Determine a unique list of library files in that directory +QStringList WidgetPluginPath::libraryFilePaths(const QDir &dir) +{ + const QFileInfoList infoList = dir.entryInfoList(QDir::Files|QDir::Readable|QDir::NoDotAndDotDot); + if (infoList.empty()) + return QStringList(); + // Load symbolic links but make sure all file names are unique as not + // to fall for something like 'libplugin.so.1 -> libplugin.so' + QStringList result; + const QFileInfoList::const_iterator icend = infoList.constEnd(); + for (QFileInfoList::const_iterator it = infoList.constBegin(); it != icend; ++it) { + QString fileName; + if (it->isSymLink()) { + const QFileInfo linkTarget = QFileInfo(it->symLinkTarget()); + if (linkTarget.exists() && linkTarget.isFile()) + fileName = linkTarget.absoluteFilePath(); + } else { + fileName = it->absoluteFilePath(); + } + if (!fileName.isEmpty() && QLibrary::isLibrary(fileName) && !result.contains(fileName)) + result += fileName; + } + + qDebug() << "Library file paths: " << result; + + return result; +} + +void WidgetPluginPath::clear() +{ + m_loaded = false; + m_plugins.clear(); +} + +void WidgetPluginPath::ensureLoaded() +{ + if (!m_loaded) { + const QStringList libraryFiles = libraryFilePaths(m_path); + if (debug) + qDebug() << "Checking " << libraryFiles.size() << " plugins " << m_path.absolutePath(); + foreach (const QString &libFile, libraryFiles) + m_plugins.push_back(WidgetPluginData(libFile)); + m_loaded = true; + } +} + +void WidgetPluginPath::getInstances(WidgetPluginManager::IWidgetPluginList *list) +{ + ensureLoaded(); + // Compile list of instances + if (m_plugins.empty()) + return; + const PluginDataList::iterator end = m_plugins.end(); + for (PluginDataList::iterator it = m_plugins.begin(); it != end; ++it) + if (IWidgetPlugin *i = instance(*it)) + list->push_back(i); +} + +QStandardItem *WidgetPluginPath::createModelItem() +{ + ensureLoaded(); + // Create a list of plugin lib files with classes. + // If there are failed ones, create a separate "Failed" + // category at the end + QStandardItem *pathItem = new QStandardItem(m_path.absolutePath()); + QStandardItem *failedCategory = 0; + const PluginDataList::iterator end = m_plugins.end(); + for (PluginDataList::iterator it = m_plugins.begin(); it != end; ++it) { + QStandardItem *pluginItem = new QStandardItem(QFileInfo(it->path).fileName()); + if (instance(*it)) { + pluginItem->appendRow(new QStandardItem(QString::fromLatin1(it->instanceGuard->metaObject()->className()))); + pathItem->appendRow(pluginItem); + } else { + pluginItem->setToolTip(it->errorMessage); + if (!failedCategory) { + const QString failed = QCoreApplication::translate("PluginManager", "Failed Plugins"); + failedCategory = new QStandardItem(failed); + } + failedCategory->appendRow(pluginItem); + } + } + if (failedCategory) + pathItem->appendRow(failedCategory); + return pathItem; +} + +} // namespace Internal +} // namespace QmlDesigner + diff --git a/src/plugins/qmldesigner/core/pluginmanager/widgetpluginpath.h b/src/plugins/qmldesigner/core/pluginmanager/widgetpluginpath.h new file mode 100644 index 00000000000..cba2d94d500 --- /dev/null +++ b/src/plugins/qmldesigner/core/pluginmanager/widgetpluginpath.h @@ -0,0 +1,102 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef WIDGETPLUGINPATH_H +#define WIDGETPLUGINPATH_H + +#include "widgetpluginmanager.h" + +#include <QtCore/QObject> +#include <QWeakPointer> +#include <QtCore/QList> +#include <QtCore/QDir> +#include <QtGui/QStandardItem> + +QT_BEGIN_NAMESPACE +class QString; +class QAbstractItemModel; +QT_END_NAMESPACE + +namespace QmlDesigner { +class IWidgetPlugin; +namespace Internal { + + + +// Dumb plugin data structure. Note that whereas QObjects can +// casted to an interface, QWeakPointer does not work with the +// interface class, so, we need a separate QWeakPointer as a guard +// to detect the deletion of a plugin instance which can happen +// in theory. +struct WidgetPluginData { + WidgetPluginData(const QString &p = QString()); + + QString path; + bool failed; + QString errorMessage; + QWeakPointer<QObject> instanceGuard; + IWidgetPlugin *instance; +}; + + +// PluginPath: Manages a plugin directory. It does nothing +// on construction following the "as lazy as possible" principle. +// In the "loaded" stage, it scans the directories and creates +// a list of PluginData for the libraries found. +// getInstances() will return the fully initialized list of +// IPlugins. + +class WidgetPluginPath { +public: + explicit WidgetPluginPath(const QDir &path); + + + void getInstances(WidgetPluginManager::IWidgetPluginList *list); + + QDir path() const { return m_path; } + + // Convenience to populate a "About Plugin" dialog with + // plugins from that path. Forces initialization. + QStandardItem *createModelItem(); + +private: + typedef QList<WidgetPluginData> PluginDataList; + + static QStringList libraryFilePaths(const QDir &dir); + void clear(); + void ensureLoaded(); + + QDir m_path; + bool m_loaded; + PluginDataList m_plugins; +}; + +} // namespace Internal +} // namespace QmlDesigner +#endif // WIDGETPLUGINPATH_H diff --git a/src/plugins/qmldesigner/core/preview/stylemanager.cpp b/src/plugins/qmldesigner/core/preview/stylemanager.cpp new file mode 100644 index 00000000000..de936063fb7 --- /dev/null +++ b/src/plugins/qmldesigner/core/preview/stylemanager.cpp @@ -0,0 +1,102 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "stylemanager.h" +#include <QStyleFactory> +#include <QApplication> +#include <QStyle> + +namespace QmlDesigner { + +namespace Internal { + +// TODO KAI: REMOVE THIS CLASS + +//### if we use this pattern often: make a template out of this! +class StyleManagerGuard { //This guard destroys the singleton in its destructor +public: //This should avoid that a memory leak is reported + ~StyleManagerGuard() { + if (StyleManager::m_instance != 0) + delete StyleManager::m_instance; + } +}; +} //namespace Internal + +StyleManager* StyleManager::m_instance = 0; + +void StyleManager::addView(NodeInstanceView* view) +{ + instance()->m_views.append(view); +} + +void StyleManager::removeView(NodeInstanceView* view) +{ + instance()->m_views.removeAll(view); +} + +QStringList StyleManager::styles() +{ + return QStyleFactory::keys(); +} + +void StyleManager::setStyle(const QString &styleName) +{ + QStyle *style = QStyleFactory::create(styleName); + if (style) { + foreach (NodeInstanceView* view, instance()->m_views) + view->setStyle(style); + } +} + +StyleManager* StyleManager::instance() +{ + static Internal::StyleManagerGuard guard; //The destructor destroys the singleton. See above + if (m_instance == 0) + m_instance = new StyleManager(); + return m_instance; +} + +QString StyleManager::applicationStyle() +{ + QStyle *applicationStyle = qApp->style(); + QStyle *style; + if (applicationStyle) + foreach(const QString &name, styles()) + if ((style = QStyleFactory::create(name)) && + (applicationStyle->metaObject()->className() == + style->metaObject()->className())) + return name; + return QString(); +} + + + + + +} //namespace QmlDesigner diff --git a/src/plugins/qmldesigner/core/rewritertransaction.cpp b/src/plugins/qmldesigner/core/rewritertransaction.cpp new file mode 100644 index 00000000000..6139c4c71f9 --- /dev/null +++ b/src/plugins/qmldesigner/core/rewritertransaction.cpp @@ -0,0 +1,91 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "rewritertransaction.h" +#include <abstractview.h> + +namespace QmlDesigner { + + +RewriterTransaction::RewriterTransaction() : m_valid(false) +{ +} + +RewriterTransaction::RewriterTransaction(AbstractView *_view) : m_view(_view), m_valid(true) +{ + Q_ASSERT(view()); + view()->emitCustomNotification("__start rewriter transaction__"); +} + +RewriterTransaction::~RewriterTransaction() +{ + commit(); +} + +bool RewriterTransaction::isValid() const +{ + return m_valid; +} + +void RewriterTransaction::commit() +{ + if (m_valid) { + m_valid = false; + view()->emitCustomNotification("__end rewriter transaction__"); + } +} + +AbstractView *RewriterTransaction::view() +{ + return m_view.data(); +} + +RewriterTransaction::RewriterTransaction(const RewriterTransaction &other) + : m_valid(false) +{ + if (&other != this) { + m_valid = other.m_valid; + m_view = other.m_view; + other.m_valid = false; + } +} + +RewriterTransaction& RewriterTransaction::operator=(const RewriterTransaction &other) +{ + if (!m_valid && (&other != this)) { + m_valid = other.m_valid; + m_view = other.m_view; + other.m_valid = false; + } + + return *this; +} + +} //QmlDesigner + diff --git a/src/plugins/qmldesigner/core/rewritertransaction.h b/src/plugins/qmldesigner/core/rewritertransaction.h new file mode 100644 index 00000000000..0e535db1a51 --- /dev/null +++ b/src/plugins/qmldesigner/core/rewritertransaction.h @@ -0,0 +1,60 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef REWRITERTRANSACTION_H +#define REWRITERTRANSACTION_H + +#include <QWeakPointer> + +namespace QmlDesigner { + +class AbstractView; + +class RewriterTransaction +{ +public: + RewriterTransaction(); + RewriterTransaction(AbstractView *view); + ~RewriterTransaction(); + void commit(); + RewriterTransaction(const RewriterTransaction &other); + RewriterTransaction& operator=(const RewriterTransaction &other); + + bool isValid() const; + +protected: + AbstractView *view(); +private: + QWeakPointer<AbstractView> m_view; + mutable bool m_valid; +}; + +} //QmlDesigner + +#endif // REWRITERTRANSACTION_H diff --git a/src/plugins/qmldesigner/designmode.cpp b/src/plugins/qmldesigner/designmode.cpp new file mode 100644 index 00000000000..fa1e65ce88c --- /dev/null +++ b/src/plugins/qmldesigner/designmode.cpp @@ -0,0 +1,287 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "designmode.h" +#include "qmldesignerconstants.h" +#include "designmodewidget.h" + +#include <coreplugin/icore.h> +#include <coreplugin/modemanager.h> +#include <coreplugin/uniqueidmanager.h> +#include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/editormanager/openeditorsmodel.h> +#include <coreplugin/actionmanager/actionmanager.h> +#include <coreplugin/actionmanager/command.h> +#include <coreplugin/coreconstants.h> + +#include <extensionsystem/pluginmanager.h> + +#include <QAction> +#include <QDebug> +#include <QPlainTextEdit> +#include <QFileInfo> + +namespace QmlDesigner { +namespace Internal { + +enum { + debug = false +}; + +DesignModeCoreListener::DesignModeCoreListener(DesignMode *mode) : + m_mode(mode) +{ +} + +bool DesignModeCoreListener::coreAboutToClose() +{ + // make sure settings are stored before actual program exit + m_mode->currentEditorChanged(0); + return true; +} + +DesignMode::DesignMode() : + IMode(), + m_mainWidget(new DesignModeWidget(this)), + m_coreListener(new DesignModeCoreListener(this)), + m_isActive(false), + m_revertToSavedAction(new QAction(this)), + m_saveAction(new QAction(this)), + m_saveAsAction(new QAction(this)), + m_closeCurrentEditorAction(new QAction(this)), + m_closeAllEditorsAction(new QAction(this)), + m_closeOtherEditorsAction(new QAction(this)) +{ + Core::ICore *creatorCore = Core::ICore::instance(); + Core::ModeManager *modeManager = creatorCore->modeManager(); + + connect(modeManager, SIGNAL(currentModeChanged(Core::IMode*)), + this, SLOT(modeChanged(Core::IMode*))); + + ExtensionSystem::PluginManager::instance()->addObject(m_coreListener); + + Core::ActionManager *actionManager = creatorCore->actionManager(); + Core::EditorManager *editorManager = creatorCore->editorManager(); + + // Undo / Redo + actionManager->registerAction(m_mainWidget->undoAction(), Core::Constants::UNDO, context()); + actionManager->registerAction(m_mainWidget->redoAction(), Core::Constants::REDO, context()); + + // Revert to saved + actionManager->registerAction(m_revertToSavedAction, + Core::Constants::REVERTTOSAVED, context()); + connect(m_revertToSavedAction, SIGNAL(triggered()), editorManager, SLOT(revertToSaved())); + + //Save + actionManager->registerAction(m_saveAction, Core::Constants::SAVE, context()); + connect(m_saveAction, SIGNAL(triggered()), editorManager, SLOT(saveFile())); + + //Save As + actionManager->registerAction(m_saveAsAction, Core::Constants::SAVEAS, context()); + connect(m_saveAsAction, SIGNAL(triggered()), editorManager, SLOT(saveFileAs())); + + //Close Editor + actionManager->registerAction(m_closeCurrentEditorAction, Core::Constants::CLOSE, context()); + connect(m_closeCurrentEditorAction, SIGNAL(triggered()), editorManager, SLOT(closeEditor())); + + //Close All + actionManager->registerAction(m_closeAllEditorsAction, Core::Constants::CLOSEALL, context()); + connect(m_closeAllEditorsAction, SIGNAL(triggered()), editorManager, SLOT(closeAllEditors())); + + //Close All Others Action + actionManager->registerAction(m_closeOtherEditorsAction, Core::Constants::CLOSEOTHERS, context()); + connect(m_closeOtherEditorsAction, SIGNAL(triggered()), editorManager, SLOT(closeOtherEditors())); + + connect(editorManager, SIGNAL(currentEditorChanged(Core::IEditor*)), + this, SLOT(currentEditorChanged(Core::IEditor*))); + connect(editorManager, SIGNAL(editorsClosed(QList<Core::IEditor*>)), + this, SLOT(textEditorsClosed(QList<Core::IEditor*>))); + + Core::ActionContainer *editMenu = actionManager->actionContainer(Core::Constants::M_EDIT); + + Core::Command *command; + command = actionManager->registerAction(m_mainWidget->deleteAction(), + QmlDesigner::Constants::DELETE, context()); + command->setDefaultKeySequence(QKeySequence::Delete); + command->setAttribute(Core::Command::CA_Hide); // don't show delete in other modes + editMenu->addAction(command, Core::Constants::G_EDIT_COPYPASTE); + + command = actionManager->registerAction(m_mainWidget->cutAction(), + Core::Constants::CUT, context()); + command->setDefaultKeySequence(QKeySequence::Cut); + editMenu->addAction(command, Core::Constants::G_EDIT_COPYPASTE); + + command = actionManager->registerAction(m_mainWidget->copyAction(), + Core::Constants::COPY, context()); + command->setDefaultKeySequence(QKeySequence::Copy); + editMenu->addAction(command, Core::Constants::G_EDIT_COPYPASTE); + + command = actionManager->registerAction(m_mainWidget->pasteAction(), + Core::Constants::PASTE, context()); + command->setDefaultKeySequence(QKeySequence::Paste); + editMenu->addAction(command, Core::Constants::G_EDIT_COPYPASTE); + + // add second shortcut to trigger delete + QAction *deleteAction = new QAction(m_mainWidget); + deleteAction->setShortcut(QKeySequence(QLatin1String("Backspace"))); + connect(deleteAction, SIGNAL(triggered()), m_mainWidget->deleteAction(), + SIGNAL(triggered())); + + m_mainWidget->addAction(deleteAction); + + updateActions(); +} + +DesignMode::~DesignMode() +{ + delete m_mainWidget; + ExtensionSystem::PluginManager::instance()->removeObject(m_coreListener); + delete m_coreListener; +} + +QList<int> DesignMode::context() const +{ + static QList<int> contexts = QList<int>() << + Core::UniqueIDManager::instance()->uniqueIdentifier(Constants::C_DESIGN_MODE); + return contexts; +} + +QWidget *DesignMode::widget() +{ + return m_mainWidget; +} + +QString DesignMode::name() const +{ + return tr(Constants::DESIGN_MODE_NAME); +} + +QIcon DesignMode::icon() const +{ + return QIcon(QLatin1String(":/qmldesigner/images/mode_Design.png")); +} + +int DesignMode::priority() const +{ + return Constants::DESIGN_MODE_PRIORITY; +} + +const char *DesignMode::uniqueModeName() const +{ + return Constants::DESIGN_MODE_NAME; +} + +void DesignMode::textEditorsClosed(QList<Core::IEditor*> editors) +{ + m_mainWidget->closeEditors(editors); +} + +void DesignMode::modeChanged(Core::IMode *mode) +{ + if (debug) + qDebug() << Q_FUNC_INFO << ((mode == this) ? "Design mode" : "other mode"); + if (mode == this) { + m_isActive = true; + m_mainWidget->showEditor(m_currentEditor.data()); + } else { + if (m_isActive) { + m_isActive = false; +// m_mainWidget->showEditor(0); + } + } +} + +void DesignMode::currentEditorChanged(Core::IEditor *editor) +{ + if (debug) + qDebug() << Q_FUNC_INFO << editor; + + if (m_currentEditor.data() == editor) + return; + + if (m_currentEditor) + disconnect(m_currentEditor.data(), SIGNAL(changed()), this, SLOT(updateActions())); + + m_currentEditor = QWeakPointer<Core::IEditor>(editor); + + if (m_currentEditor) + connect(m_currentEditor.data(), SIGNAL(changed()), this, SLOT(updateActions())); + + updateActions(); +} + +void DesignMode::makeCurrentEditorWritable() +{ + Core::ICore *creatorCore = Core::ICore::instance(); + if (m_currentEditor) + creatorCore->editorManager()->makeEditorWritable(m_currentEditor.data()); +} + +// copied from EditorManager::updateActions +void DesignMode::updateActions() +{ + Core::ICore *creatorCore = Core::ICore::instance(); + Core::EditorManager *editorManager = creatorCore->editorManager(); + + Core::IEditor *curEditor = m_currentEditor.data(); + int openedCount = editorManager->openedEditors().count() + + editorManager->openedEditorsModel()->restoredEditorCount(); + + QString fName; + if (curEditor) { + if (!curEditor->file()->fileName().isEmpty()) { + QFileInfo fi(curEditor->file()->fileName()); + fName = fi.fileName(); + } else { + fName = curEditor->displayName(); + } + } + + m_saveAction->setEnabled(curEditor != 0 && curEditor->file()->isModified()); + m_saveAsAction->setEnabled(curEditor != 0 && curEditor->file()->isSaveAsAllowed()); + m_revertToSavedAction->setEnabled(curEditor != 0 + && !curEditor->file()->fileName().isEmpty() + && curEditor->file()->isModified()); + + QString quotedName; + if (!fName.isEmpty()) + quotedName = '"' + fName + '"'; + m_saveAsAction->setText(tr("Save %1 As...").arg(quotedName)); + m_saveAction->setText(tr("&Save %1").arg(quotedName)); + m_revertToSavedAction->setText(tr("Revert %1 to Saved").arg(quotedName)); + + m_closeCurrentEditorAction->setEnabled(curEditor != 0); + m_closeCurrentEditorAction->setText(tr("Close %1").arg(quotedName)); + m_closeAllEditorsAction->setEnabled(openedCount > 0); + m_closeOtherEditorsAction->setEnabled(openedCount > 1); + m_closeOtherEditorsAction->setText((openedCount > 1 ? tr("Close All Except %1").arg(quotedName) : tr("Close Others"))); +} + +} // namespace Internal +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designmode.h b/src/plugins/qmldesigner/designmode.h new file mode 100644 index 00000000000..08fea33b2b5 --- /dev/null +++ b/src/plugins/qmldesigner/designmode.h @@ -0,0 +1,101 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef DESIGNMODE_H +#define DESIGNMODE_H + +#include <coreplugin/imode.h> +#include <coreplugin/icorelistener.h> +#include <coreplugin/editormanager/ieditor.h> + +#include <QWeakPointer> + +class QAction; + +namespace QmlDesigner { +namespace Internal { + +class DesignMode; +class DesignModeWidget; + +class DesignModeCoreListener : public Core::ICoreListener +{ + Q_OBJECT +public: + DesignModeCoreListener(DesignMode* mode); + bool coreAboutToClose(); +private: + DesignMode *m_mode; +}; + +class DesignMode : public Core::IMode +{ + Q_OBJECT + +public: + DesignMode(); + ~DesignMode(); + + // IContext + QList<int> context() const; + QWidget *widget(); + + // IMode + QString name() const; + QIcon icon() const; + int priority() const; + const char *uniqueModeName() const; + +private slots: + void textEditorsClosed(QList<Core::IEditor *> editors); + void modeChanged(Core::IMode *mode); + void currentEditorChanged(Core::IEditor *editor); + void makeCurrentEditorWritable(); + void updateActions(); + +private: + DesignModeWidget *m_mainWidget; + DesignModeCoreListener *m_coreListener; + QWeakPointer<Core::IEditor> m_currentEditor; + bool m_isActive; + + QAction *m_revertToSavedAction; + QAction *m_saveAction; + QAction *m_saveAsAction; + QAction *m_closeCurrentEditorAction; + QAction *m_closeAllEditorsAction; + QAction *m_closeOtherEditorsAction; + + friend class DesignModeCoreListener; +}; + +} // namespace Internal +} // namespace QmlDesigner + +#endif // DESIGNMODE_H diff --git a/src/plugins/qmldesigner/designmodewidget.cpp b/src/plugins/qmldesigner/designmodewidget.cpp new file mode 100644 index 00000000000..51c4c53981b --- /dev/null +++ b/src/plugins/qmldesigner/designmodewidget.cpp @@ -0,0 +1,675 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "designmodewidget.h" +#include "designmode.h" +#include "qmldesignerconstants.h" + +#include <model.h> +#include <rewriterview.h> + +#include <coreplugin/icore.h> +#include <coreplugin/minisplitter.h> +#include <coreplugin/sidebar.h> +#include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/editormanager/openeditorsmodel.h> +#include <coreplugin/editormanager/ieditor.h> +#include <coreplugin/modemanager.h> + +#include <utils/parameteraction.h> +#include <utils/qtcassert.h> + +#include <QtCore/QSettings> +#include <QtCore/QEvent> +#include <QtCore/QDir> +#include <QtGui/QApplication> +#include <QtGui/QPlainTextEdit> +#include <QtGui/QVBoxLayout> +#include <QtGui/QScrollArea> +#include <QtGui/QTabWidget> +#include <QtGui/QToolButton> +#include <QtGui/QMenu> +#include <QtGui/QClipboard> +#include <QtGui/QLabel> + +using Core::MiniSplitter; +using Core::IEditor; +using Core::EditorManager; + +using namespace QmlDesigner; + +Q_DECLARE_METATYPE(Core::IEditor*) + +enum { + debug = false +}; + +namespace QmlDesigner { +namespace Internal { + +/*! + Mimic the look of the text editor toolbar as defined in e.g. EditorView::EditorView + */ +DocumentToolBar::DocumentToolBar(DocumentWidget *documentWidget, DesignModeWidget *mainWidget, QWidget *parent) : + QWidget(parent), + m_mainWidget(mainWidget), + m_documentWidget(documentWidget), + m_editorList(new QComboBox), + m_closeButton(new QToolButton), + m_lockButton(new QToolButton) +{ + Core::ICore *core = Core::ICore::instance(); + + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + + m_editorsListModel = core->editorManager()->openedEditorsModel(); + + // copied from EditorView::EditorView + m_editorList->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + m_editorList->setMinimumContentsLength(20); + m_editorList->setModel(m_editorsListModel); + m_editorList->setMaxVisibleItems(40); + m_editorList->setContextMenuPolicy(Qt::CustomContextMenu); + m_editorList->setCurrentIndex(m_editorsListModel->indexOf(documentWidget->textEditor()).row()); + + QToolBar *editorListToolBar = new QToolBar; + editorListToolBar->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Ignored); + editorListToolBar->addWidget(m_editorList); + + QToolBar *designToolBar = new QToolBar; + designToolBar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Ignored); + + m_lockButton->setAutoRaise(true); + m_lockButton->setProperty("type", QLatin1String("dockbutton")); + + m_closeButton->setAutoRaise(true); + m_closeButton->setIcon(QIcon(":/core/images/closebutton.png")); + m_closeButton->setProperty("type", QLatin1String("dockbutton")); + + QToolBar *rightToolBar = new QToolBar; + rightToolBar->setLayoutDirection(Qt::RightToLeft); + rightToolBar->addWidget(m_closeButton); + rightToolBar->addWidget(m_lockButton); + + QHBoxLayout *toplayout = new QHBoxLayout(this); + toplayout->setSpacing(0); + toplayout->setMargin(0); + + toplayout->addWidget(editorListToolBar); + toplayout->addWidget(designToolBar); + toplayout->addWidget(rightToolBar); + + connect(m_editorList, SIGNAL(activated(int)), this, SLOT(listSelectionActivated(int))); + connect(m_editorList, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(listContextMenu(QPoint))); + connect(m_lockButton, SIGNAL(clicked()), this, SLOT(makeEditorWritable())); + connect(m_closeButton, SIGNAL(clicked()), this, SLOT(close())); + + connect(m_documentWidget->textEditor(), SIGNAL(changed()), this, SLOT(updateEditorStatus())); + + updateEditorStatus(); +} + +void DocumentToolBar::close() +{ + Core::ICore::instance()->editorManager()->closeEditors(QList<IEditor*>() << m_documentWidget->textEditor()); +} + +void DocumentToolBar::listSelectionActivated(int row) +{ + EditorManager *em = Core::ICore::instance()->editorManager(); + QAbstractItemModel *model = m_editorList->model(); + + const QModelIndex modelIndex = model->index(row, 0); + IEditor *editor = model->data(modelIndex, Qt::UserRole).value<IEditor*>(); + if (editor) { + em->activateEditor(editor, EditorManager::NoModeSwitch); + } else { + QString fileName = model->data(modelIndex, Qt::UserRole + 1).toString(); + QByteArray kind = model->data(modelIndex, Qt::UserRole + 2).toByteArray(); + editor = em->openEditor(fileName, kind, EditorManager::NoModeSwitch); + } + if (editor) { + m_mainWidget->showEditor(editor); + m_editorList->setCurrentIndex(m_editorsListModel->indexOf(m_documentWidget->textEditor()).row()); + } +} + +void DocumentToolBar::listContextMenu(QPoint pos) +{ + QModelIndex index = m_editorsListModel->index(m_editorList->currentIndex(), 0); + QString fileName = m_editorsListModel->data(index, Qt::UserRole + 1).toString(); + if (fileName.isEmpty()) + return; + QMenu menu; + menu.addAction(tr("Copy full path to clipboard")); + if (menu.exec(m_editorList->mapToGlobal(pos))) { + QApplication::clipboard()->setText(fileName); + } +} + +void DocumentToolBar::makeEditorWritable() +{ + Core::ICore::instance()->editorManager()->makeEditorWritable(m_documentWidget->textEditor()); +} + +void DocumentToolBar::updateEditorStatus() +{ + Core::IEditor *editor = m_documentWidget->textEditor(); + + static const QIcon lockedIcon(QLatin1String(":/core/images/locked.png")); + static const QIcon unlockedIcon(QLatin1String(":/core/images/unlocked.png")); + + if (editor->file()->isReadOnly()) { + m_lockButton->setIcon(lockedIcon); + m_lockButton->setEnabled(!editor->file()->fileName().isEmpty()); + m_lockButton->setToolTip(tr("Make writable")); + } else { + m_lockButton->setIcon(unlockedIcon); + m_lockButton->setEnabled(false); + m_lockButton->setToolTip(tr("File is writable")); + } + m_editorList->setToolTip( + editor->file()->fileName().isEmpty() + ? editor->displayName() + : QDir::toNativeSeparators(editor->file()->fileName()) + ); +} + +DocumentWarningWidget::DocumentWarningWidget(DocumentWidget *documentWidget, QWidget *parent) : + QFrame(parent), + m_errorMessage(new QLabel("Placeholder", this)), + m_goToError(new QLabel(this)), + m_documentWidget(documentWidget) +{ + setFrameStyle(QFrame::Panel | QFrame::Raised); + setLineWidth(1); + setForegroundRole(QPalette::ToolTipText); + setBackgroundRole(QPalette::ToolTipBase); + setAutoFillBackground(true); + + m_errorMessage->setForegroundRole(QPalette::ToolTipText); + m_goToError->setText(tr("<a href=\"goToError\">Go to error</a>")); + m_goToError->setForegroundRole(QPalette::Link); + connect(m_goToError, SIGNAL(linkActivated(QString)), this, SLOT(goToError())); + + QVBoxLayout *layout = new QVBoxLayout(this); + layout->setMargin(20); + layout->setSpacing(5); + layout->addWidget(m_errorMessage); + layout->addWidget(m_goToError, 1, Qt::AlignRight); +} + +void DocumentWarningWidget::setError(const RewriterView::Error &error) +{ + m_error = error; + QString str = tr("%3 (%1:%2)").arg(QString::number(error.line()), QString::number(error.column()), error.description()); + + m_errorMessage->setText(str); + resize(layout()->totalSizeHint()); +} + +void DocumentWarningWidget::goToError() +{ + m_documentWidget->textEditor()->gotoLine(m_error.line(), m_error.column()); + Core::EditorManager::instance()->ensureEditorManagerVisible(); +} + +DocumentWidget::DocumentWidget(TextEditor::ITextEditor *textEditor, QPlainTextEdit *textEdit, QmlDesigner::DesignDocumentController *document, DesignModeWidget *mainWidget) : + QWidget(), + m_textEditor(textEditor), + m_textBuffer(textEdit), + m_document(document), + m_mainWidget(mainWidget), + m_mainSplitter(0), + m_leftSideBar(0), + m_rightSideBar(0), + m_isDisabled(false), + m_warningWidget(0) +{ + setup(); +} + +DocumentWidget::~DocumentWidget() +{ + // Make sure component widgets are deleted first in SideBarItem::~SideBarItem + // before the DesignDocumentController runs (and deletes them again). + m_document->deleteLater(); +} + +QmlDesigner::DesignDocumentController *DocumentWidget::document() const +{ + return m_document; +} + +TextEditor::ITextEditor *DocumentWidget::textEditor() const +{ + return m_textEditor; +} + +void DocumentWidget::setAutoSynchronization(bool sync) +{ + if (debug) + qDebug() << Q_FUNC_INFO << sync; + + document()->blockModelSync(!sync); + + if (sync) { + // text editor -> visual editor + if (!m_document->model()) { + QList<RewriterView::Error> errors = m_document->loadMaster(m_textBuffer.data()); + if (!errors.isEmpty()) { + disable(errors); + } else { + connect(m_document, SIGNAL(qmlErrorsChanged(QList<RewriterView::Error>)), + this, SLOT(updateErrorStatus(QList<RewriterView::Error>))); + } + } + if (m_document->model() && m_document->qmlErrors().isEmpty()) { + // set selection to text cursor + RewriterView *rewriter = m_document->rewriterView(); + const int cursorPos = m_textBuffer->textCursor().position(); + ModelNode node = nodeForPosition(cursorPos); + if (node.isValid()) { + rewriter->setSelectedModelNodes(QList<ModelNode>() << node); + } + } + } else { + if (m_document->model() && m_document->qmlErrors().isEmpty()) { + RewriterView *rewriter = m_document->rewriterView(); + // visual editor -> text editor + ModelNode selectedNode; + if (!rewriter->selectedModelNodes().isEmpty()) + selectedNode = rewriter->selectedModelNodes().first(); + + if (selectedNode.isValid()) { + int nodeOffset = rewriter->nodeOffset(selectedNode); + QTextCursor editTextCursor = m_textBuffer->textCursor(); + if (nodeOffset > 0 + && nodeForPosition(editTextCursor.position()) != selectedNode) { + if (debug) + qDebug() << "Moving text cursor to " << nodeOffset; + editTextCursor.setPosition(nodeOffset); + m_textBuffer->setTextCursor(editTextCursor); + } + } + } + } +} + +void DocumentWidget::enable() +{ + if (debug) + qDebug() << Q_FUNC_INFO; + m_warningWidget->setVisible(false); + m_document->documentWidget()->setEnabled(true); + m_leftSideBar->setEnabled(true); + m_rightSideBar->setEnabled(true); + m_isDisabled = false; +} + +void DocumentWidget::disable(const QList<RewriterView::Error> &errors) +{ + if (debug) + qDebug() << Q_FUNC_INFO; + Q_ASSERT(!errors.isEmpty()); + m_warningWidget->setError(errors.first()); + m_warningWidget->setVisible(true); + m_document->documentWidget()->setEnabled(false); + m_leftSideBar->setEnabled(false); + m_rightSideBar->setEnabled(false); + m_isDisabled = true; +} + +void DocumentWidget::updateErrorStatus(const QList<RewriterView::Error> &errors) +{ + if (m_isDisabled && errors.isEmpty()) { + enable(); + } else if (!m_isDisabled && !errors.isEmpty()) { + disable(errors); + } +} + +void DocumentWidget::readSettings() +{ + QSettings *settings = Core::ICore::instance()->settings(); + + settings->beginGroup("Bauhaus"); + m_leftSideBar->readSettings(settings, QLatin1String("LeftSideBar")); + m_rightSideBar->readSettings(settings, QLatin1String("RightSideBar")); + if (settings->contains("MainSplitter")) { + const QByteArray splitterState = settings->value("MainSplitter").toByteArray(); + m_mainSplitter->restoreState(splitterState); + } + settings->endGroup(); +} + +void DocumentWidget::saveSettings() +{ + QSettings *settings = Core::ICore::instance()->settings(); + + settings->beginGroup("Bauhaus"); + m_leftSideBar->saveSettings(settings, QLatin1String("LeftSideBar")); + m_rightSideBar->saveSettings(settings, QLatin1String("RightSideBar")); + settings->setValue("MainSplitter", m_mainSplitter->saveState()); + settings->endGroup(); +} + + +void DocumentWidget::resizeEvent(QResizeEvent *event) +{ + m_warningWidget->move(QPoint(event->size().width() / 2, + event->size().height() / 2)); + QWidget::resizeEvent(event); +} + +void DocumentWidget::setup() +{ + m_mainSplitter = new MiniSplitter(this); + m_mainSplitter->setObjectName("mainSplitter"); + + // warning frame should be not in layout, but still child of the widget + m_warningWidget = new DocumentWarningWidget(this, this); + m_warningWidget->setVisible(false); + + // Left area: + Core::SideBarItem *navigatorItem = new Core::SideBarItem(m_document->navigator()); + Core::SideBarItem *libraryItem = new Core::SideBarItem(m_document->itemLibrary()); + Core::SideBarItem *propertiesItem = new Core::SideBarItem(m_document->allPropertiesBox()); + + QList<Core::SideBarItem*> leftSideBarItems, rightSideBarItems; + leftSideBarItems << navigatorItem << libraryItem; + rightSideBarItems << propertiesItem; + + m_leftSideBar = new Core::SideBar(leftSideBarItems, QList<Core::SideBarItem*>() << navigatorItem << libraryItem); + m_rightSideBar = new Core::SideBar(rightSideBarItems, QList<Core::SideBarItem*>() << propertiesItem); + + // right area: + QWidget *centerWidget = new QWidget; + { + QVBoxLayout *rightLayout = new QVBoxLayout(centerWidget); + rightLayout->setMargin(0); + rightLayout->setSpacing(0); + rightLayout->addWidget(new DocumentToolBar(this, m_mainWidget)); + rightLayout->addWidget(m_document->statesEditorWidget()); + rightLayout->addWidget(m_document->documentWidget()); + } + + // m_mainSplitter area: + m_mainSplitter->addWidget(m_leftSideBar); + m_mainSplitter->addWidget(centerWidget); + m_mainSplitter->addWidget(m_rightSideBar); + + // Finishing touches: + m_mainSplitter->setOpaqueResize(false); + m_mainSplitter->setStretchFactor(1, 1); + m_mainSplitter->setSizes(QList<int>() << 150 << 300 << 150); + + QLayout *mainLayout = new QBoxLayout(QBoxLayout::RightToLeft, this); + mainLayout->setMargin(0); + mainLayout->setSpacing(0); + mainLayout->addWidget(m_mainSplitter); +} + +bool DocumentWidget::isInNodeDefinition(int nodeOffset, int nodeLength, int cursorPos) const { + return (nodeOffset <= cursorPos) && (nodeOffset + nodeLength > cursorPos); +} + +ModelNode DocumentWidget::nodeForPosition(int cursorPos) const +{ + RewriterView *rewriter = m_document->rewriterView(); + QList<ModelNode> nodes = rewriter->allModelNodes(); + + ModelNode bestNode; + int bestNodeOffset = -1; + + foreach (const ModelNode &node, nodes) { + const int nodeOffset = rewriter->nodeOffset(node); + const int nodeLength = rewriter->nodeLength(node); + if (isInNodeDefinition(nodeOffset, nodeLength, cursorPos) + && (nodeOffset > bestNodeOffset)) { + bestNode = node; + bestNodeOffset = nodeOffset; + } + } + + return bestNode; +} + +// ---------- DesignModeWidget +DesignModeWidget::DesignModeWidget(DesignMode *designMode, QWidget *parent) : + QWidget(parent), + m_designMode(designMode), + m_documentWidgetStack(new QStackedWidget), + m_currentDocumentWidget(0), + m_currentTextEdit(0), + m_syncWithTextEdit(false) +{ + QVBoxLayout *layout = new QVBoxLayout(this); + layout->setMargin(0); + layout->addWidget(m_documentWidgetStack); + + m_undoAction = new QAction(tr("&Undo"), this); + connect(m_undoAction, SIGNAL(triggered()), this, SLOT(undo())); + m_redoAction = new QAction(tr("&Redo"), this); + connect(m_redoAction, SIGNAL(triggered()), this, SLOT(redo())); + m_deleteAction = new Utils::ParameterAction(tr("Delete"), tr("Delete \"%1\""), Utils::ParameterAction::EnabledWithParameter, this); + connect(m_deleteAction, SIGNAL(triggered()), this, SLOT(deleteSelected())); + m_cutAction = new Utils::ParameterAction(tr("Cu&t"), tr("Cut \"%1\""), Utils::ParameterAction::EnabledWithParameter, this); + connect(m_cutAction, SIGNAL(triggered()), this, SLOT(cutSelected())); + m_copyAction = new Utils::ParameterAction(tr("&Copy"), tr("Copy \"%1\""), Utils::ParameterAction::EnabledWithParameter, this); + connect(m_copyAction, SIGNAL(triggered()), this, SLOT(copySelected())); + m_pasteAction = new Utils::ParameterAction(tr("&Paste"), tr("Paste \"%1\""), Utils::ParameterAction::EnabledWithParameter, this); + connect(m_pasteAction, SIGNAL(triggered()), this, SLOT(paste())); + + QLabel *defaultBackground = new QLabel(tr("Open/Create a qml file first.")); + defaultBackground->setAlignment(Qt::AlignCenter); + + m_documentWidgetStack->addWidget(defaultBackground); +} + +DesignModeWidget::~DesignModeWidget() +{ +}; + +void DesignModeWidget::showEditor(Core::IEditor *editor) +{ + QString fileName; + QPlainTextEdit *textEdit = 0; + TextEditor::ITextEditor *textEditor = 0; + + if (editor) { + fileName = editor->file()->fileName(); + textEdit = qobject_cast<QPlainTextEdit*>(editor->widget()); + textEditor = qobject_cast<TextEditor::ITextEditor*>(editor); + } + + if (debug) + qDebug() << Q_FUNC_INFO << fileName; + + m_currentTextEdit = textEdit; + DocumentWidget *documentWidget = 0; + + if (textEdit && textEditor && fileName.endsWith(".qml")) { + if (m_documentHash.contains(textEdit)) { + documentWidget = m_documentHash.value(textEdit); + documentWidget->setAutoSynchronization(true); + } else { + DesignDocumentController *newDocument = new DesignDocumentController(0); + newDocument->setFileName(fileName); + + documentWidget = new DocumentWidget(textEditor, textEdit, newDocument, this); + connect(documentWidget->document(), SIGNAL(undoAvailable(bool)), + this, SLOT +(undoAvailable(bool))); + connect(documentWidget->document(), SIGNAL(redoAvailable(bool)), + this, SLOT(redoAvailable(bool))); +// connect(documentWidget->document(), SIGNAL(deleteAvailable(bool)), +// this, SLOT(deleteAvailable(bool))); + + m_documentHash.insert(textEdit, documentWidget); + m_documentWidgetStack->addWidget(documentWidget); + } + } + + setCurrentDocumentWidget(documentWidget); +} + +void DesignModeWidget::closeEditors(QList<Core::IEditor*> editors) +{ + foreach (Core::IEditor* editor, editors) { + if (QPlainTextEdit *textEdit = qobject_cast<QPlainTextEdit*>(editor->widget())) { + if (m_currentTextEdit == textEdit) { + setCurrentDocumentWidget(0); + } + if (m_documentHash.contains(textEdit)) { + if (debug) + qDebug() << Q_FUNC_INFO << editor->file()->fileName(); + DocumentWidget *document = m_documentHash.take(textEdit); + m_documentWidgetStack->removeWidget(document); + delete document; + } + } + } +} + +QAction *DesignModeWidget::undoAction() const +{ + return m_undoAction; +} + +QAction *DesignModeWidget::redoAction() const +{ + return m_redoAction; +} + +QAction *DesignModeWidget::deleteAction() const +{ + return m_deleteAction; +} + +QAction *DesignModeWidget::cutAction() const +{ + return m_cutAction; +} + +QAction *DesignModeWidget::copyAction() const +{ + return m_copyAction; +} + +QAction *DesignModeWidget::pasteAction() const +{ + return m_pasteAction; +} + +DesignMode *DesignModeWidget::designMode() const +{ + return m_designMode; +} + +void DesignModeWidget::undo() +{ + if (m_currentDocumentWidget) + m_currentDocumentWidget->document()->undo(); +} + +void DesignModeWidget::redo() +{ + if (m_currentDocumentWidget) + m_currentDocumentWidget->document()->redo(); +} + +void DesignModeWidget::deleteSelected() +{ + if (m_currentDocumentWidget) + m_currentDocumentWidget->document()->deleteSelected(); +} + +void DesignModeWidget::cutSelected() +{ + if (m_currentDocumentWidget) + m_currentDocumentWidget->document()->cutSelected(); +} + +void DesignModeWidget::copySelected() +{ + if (m_currentDocumentWidget) + m_currentDocumentWidget->document()->copySelected(); +} + +void DesignModeWidget::paste() +{ + if (m_currentDocumentWidget) + m_currentDocumentWidget->document()->paste(); +} + +void DesignModeWidget::undoAvailable(bool isAvailable) +{ + DesignDocumentController *documentController = qobject_cast<DesignDocumentController*>(sender()); + if (m_currentDocumentWidget && + m_currentDocumentWidget->document() == documentController) { + m_undoAction->setEnabled(isAvailable); + } +} + +void DesignModeWidget::redoAvailable(bool isAvailable) +{ + DesignDocumentController *documentController = qobject_cast<DesignDocumentController*>(sender()); + if (m_currentDocumentWidget && + m_currentDocumentWidget->document() == documentController) { + m_redoAction->setEnabled(isAvailable); + } +} + +void DesignModeWidget::setCurrentDocumentWidget(DocumentWidget *newDocumentWidget) +{ + if (debug) + qDebug() << Q_FUNC_INFO << newDocumentWidget; + if (m_currentDocumentWidget) { + m_currentDocumentWidget->setAutoSynchronization(false); + m_currentDocumentWidget->saveSettings(); + } + + m_currentDocumentWidget = newDocumentWidget; + + if (m_currentDocumentWidget) { + m_currentDocumentWidget->setAutoSynchronization(true); + m_documentWidgetStack->setCurrentWidget(m_currentDocumentWidget); + m_currentDocumentWidget->readSettings(); + m_undoAction->setEnabled(m_currentDocumentWidget->document()->isUndoAvailable()); + m_redoAction->setEnabled(m_currentDocumentWidget->document()->isRedoAvailable()); + } else { + m_documentWidgetStack->setCurrentIndex(0); + m_undoAction->setEnabled(false); + m_redoAction->setEnabled(false); + } +} + +} // namespace Internal +} // namespace Designer diff --git a/src/plugins/qmldesigner/designmodewidget.h b/src/plugins/qmldesigner/designmodewidget.h new file mode 100644 index 00000000000..e80fb8c1ffe --- /dev/null +++ b/src/plugins/qmldesigner/designmodewidget.h @@ -0,0 +1,212 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef DESIGNMODEWIDGET_H +#define DESIGNMODEWIDGET_H + +#include <coreplugin/minisplitter.h> +#include <coreplugin/editormanager/ieditor.h> +#include <texteditor/itexteditor.h> + +#include <integrationcore.h> + +#include <allpropertiesbox.h> +#include <designdocumentcontroller.h> +#include <itemlibrary.h> +#include <navigatorwidget.h> +#include <stateseditorwidget.h> +#include <modelnode.h> + + +#include <QWeakPointer> +#include <QmlError> +#include <QtCore/QHash> +#include <QtGui/QWidget> +#include <QtGui/QToolBar> +#include <QtGui/QComboBox> +#include <QtGui/QLabel> + +class QStackedWidget; +class QTabWidget; +class QVBoxLayout; +class QToolButton; + +namespace Core { + class SideBar; + class OpenEditorsModel; +} + +namespace QmlDesigner { + +namespace Internal { + +class DesignMode; +class DocumentWidget; +class DesignModeWidget; + +class DocumentToolBar : public QWidget +{ + Q_OBJECT + Q_DISABLE_COPY(DocumentToolBar) +public: + explicit DocumentToolBar(DocumentWidget *documentWidget, + DesignModeWidget *mainWidget, QWidget *parent = 0); + +private slots: + void close(); + void listSelectionActivated(int row); + void listContextMenu(QPoint); + void makeEditorWritable(); + void updateEditorStatus(); + +private: + DesignModeWidget *m_mainWidget; + DocumentWidget *m_documentWidget; + Core::OpenEditorsModel *m_editorsListModel; + QComboBox *m_editorList; + QToolButton *m_closeButton; + QToolButton *m_lockButton; +}; + +class DocumentWarningWidget : public QFrame +{ + Q_OBJECT + Q_DISABLE_COPY(DocumentWarningWidget) +public: + DocumentWarningWidget(DocumentWidget *documentWidget, QWidget *parent = 0); + + void setError(const RewriterView::Error &error); + +private slots: + void goToError(); + +private: + QLabel *m_errorMessage; + QLabel *m_goToError; + DocumentWidget *m_documentWidget; + RewriterView::Error m_error; +}; + +class DocumentWidget : public QWidget +{ + Q_OBJECT + Q_DISABLE_COPY(DocumentWidget) +public: + explicit DocumentWidget(TextEditor::ITextEditor *textEditor, QPlainTextEdit *textEdit, QmlDesigner::DesignDocumentController *document, DesignModeWidget *mainWidget); + ~DocumentWidget(); + + QmlDesigner::DesignDocumentController *document() const; + TextEditor::ITextEditor *textEditor() const; + + void setAutoSynchronization(bool sync); + + void readSettings(); + void saveSettings(); + +protected: + void resizeEvent(QResizeEvent *event); + +private slots: + void enable(); + void disable(const QList<RewriterView::Error> &errors); + void updateErrorStatus(const QList<RewriterView::Error> &errors); + +private: + void setup(); + bool isInNodeDefinition(int nodeOffset, int nodeLength, int cursorPos) const; + QmlDesigner::ModelNode nodeForPosition(int cursorPos) const; + + TextEditor::ITextEditor *m_textEditor; + QWeakPointer<QPlainTextEdit> m_textBuffer; + QmlDesigner::DesignDocumentController *m_document; + DesignModeWidget *m_mainWidget; + QSplitter *m_mainSplitter; + Core::SideBar *m_leftSideBar; + Core::SideBar *m_rightSideBar; + + bool m_isDisabled; + DocumentWarningWidget *m_warningWidget; +}; + +class DesignModeWidget : public QWidget +{ + Q_OBJECT + Q_DISABLE_COPY(DesignModeWidget) +public: + explicit DesignModeWidget(DesignMode *designMode, QWidget *parent = 0); + ~DesignModeWidget(); + +// void syncWithTextEdit(bool sync); + void showEditor(Core::IEditor *editor); + void closeEditors(const QList<Core::IEditor*> editors); + + QAction *undoAction() const; + QAction *redoAction() const; + QAction *deleteAction() const; + QAction *cutAction() const; + QAction *copyAction() const; + QAction *pasteAction() const; + + DesignMode *designMode() const; + +private slots: + void undo(); + void redo(); + void deleteSelected(); + void cutSelected(); + void copySelected(); + void paste(); + + void undoAvailable(bool isAvailable); + void redoAvailable(bool isAvailable); + +private: + void setCurrentDocumentWidget(DocumentWidget *newDocumentWidget); + + DesignMode *m_designMode; + + QStackedWidget *m_documentWidgetStack; + QHash<QPlainTextEdit*,DocumentWidget*> m_documentHash; + DocumentWidget *m_currentDocumentWidget; + QPlainTextEdit *m_currentTextEdit; + + QAction *m_undoAction; + QAction *m_redoAction; + QAction *m_deleteAction; + QAction *m_cutAction; + QAction *m_copyAction; + QAction *m_pasteAction; + + bool m_syncWithTextEdit; +}; + +} // namespace Internal +} // namespace Designer + +#endif // DESIGNMODEWIDGET_H diff --git a/src/plugins/qmldesigner/fxplugin/fx.metainfo b/src/plugins/qmldesigner/fxplugin/fx.metainfo new file mode 100644 index 00000000000..fbc60236e26 --- /dev/null +++ b/src/plugins/qmldesigner/fxplugin/fx.metainfo @@ -0,0 +1,143 @@ +<metainfo> + <node name="Qt/Item" showInItemLibrary="true" category="QtFx" isContainer="true"> + <propertyDefaultValue name="width" type="int" defaultValue="600"/> + <propertyDefaultValue name="height" type="int" defaultValue="400"/> + <itemlibraryrepresentation name="Item" icon=":/fxplugin/images/item-icon.png"> + <property name="width" type="int" value="200"/> + <property name="height" type="int" value="200"/> + </itemlibraryrepresentation> + </node> + <node name="Qt/Rectangle" showInItemLibrary="true" category="QtFx" isContainer="true"> + <itemlibraryrepresentation name="Rectangle" icon=":/fxplugin/images/rect-icon.png"> + <property name="width" type="int" value="100"/> + <property name="height" type="int" value="100"/> + <property name="color" type="QColor" value="#ffffff"/> + </itemlibraryrepresentation> + </node> + <node name="Qt/Text" showInItemLibrary="true" category="QtFx"> + <propertyDefaultValue name="width" type="int" defaultValue="80"/> + <propertyDefaultValue name="height" type="int" defaultValue="20"/> + <itemlibraryrepresentation name="Text" icon=":/fxplugin/images/text-icon.png"> + <property name="width" type="int" value="80"/> + <property name="height" type="int" value="20"/> + <property name="text" type="QString" value="text"/> + </itemlibraryrepresentation> + </node> + <node name="Qt/TextEdit" showInItemLibrary="true" category="QtFx" isContainer="false"> + <propertyDefaultValue name="width" type="int" defaultValue="80"/> + <propertyDefaultValue name="height" type="int" defaultValue="20"/> + <itemlibraryrepresentation name="Text Edit" icon=":/fxplugin/images/text-edit-icon.png"> + <property name="width" type="int" value="80"/> + <property name="height" type="int" value="20"/> + <property name="text" type="QString" value="textEdit"/> + </itemlibraryrepresentation> + </node> + <node name="Qt/TextInput" showInItemLibrary="true" category="QtFx" isContainer="false"> + <propertyDefaultValue name="width" type="int" defaultValue="80"/> + <propertyDefaultValue name="height" type="int" defaultValue="20"/> + <itemlibraryrepresentation name="Text Input" icon=":/fxplugin/images/text-edit-icon.png"> + <property name="width" type="int" value="80"/> + <property name="height" type="int" value="20"/> + <property name="text" type="QString" value="textInput"/> + </itemlibraryrepresentation> + </node> + <node name="Qt/MouseRegion" showInItemLibrary="true" category="QtFx"> + <inherits name="Item" /> + <itemlibraryrepresentation name="Mouse Region" icon=":/fxplugin/images/mouse-region-icon.png"> + <property name="width" type="int" value="100"/> + <property name="height" type="int" value="100"/> + </itemlibraryrepresentation> + </node> + <node name="Qt/Image" showInItemLibrary="true" category="QtFx"> + <propertyDefaultValue name="width" type="int" defaultValue="200"/> + <propertyDefaultValue name="height" type="int" defaultValue="200"/> + <itemlibraryrepresentation name="Image" icon=":/fxplugin/images/image-icon.png"> + <property name="width" type="int" value="100"/> + <property name="height" type="int" value="100"/> + <property name="source" type="QUrl" value="qrc:/fxplugin/images/template_image.png"/> + </itemlibraryrepresentation> + </node> + <node name="Qt/BorderImage" showInItemLibrary="true" category="QtFx"> + <propertyDefaultValue name="width" type="int" defaultValue="200"/> + <propertyDefaultValue name="height" type="int" defaultValue="200"/> + <itemlibraryrepresentation name="Border Image" icon=":/fxplugin/images/image-icon.png"> + <property name="width" type="int" value="100"/> + <property name="height" type="int" value="100"/> + <property name="source" type="QUrl" value="qrc:/fxplugin/images/template_image.png"/> + </itemlibraryrepresentation> + </node> + <node name="Qt/Flickable" showInItemLibrary="true" category="QtFx"> + <propertyDefaultValue name="width" type="int" defaultValue="300"/> + <propertyDefaultValue name="height" type="int" defaultValue="300"/> + <itemlibraryrepresentation name="Flickable"> + <property name="width" type="int" value="300"/> + <property name="height" type="int" value="300"/> + </itemlibraryrepresentation> + </node> + <node name="Qt/Flipable" showInItemLibrary="true" category="QtFx"> + <propertyDefaultValue name="width" type="int" defaultValue="300"/> + <propertyDefaultValue name="height" type="int" defaultValue="300"/> + <itemlibraryrepresentation name="Flipable"> + <property name="width" type="int" value="300"/> + <property name="height" type="int" value="300"/> + </itemlibraryrepresentation> + </node> + <node name="Qt/GridView" showInItemLibrary="true" category="QtFx"> + <itemlibraryrepresentation name="Grid View" icon=":/fxplugin/images/grid-icon.png"> + <property name="width" type="int" value="300"/> + <property name="height" type="int" value="300"/> + </itemlibraryrepresentation> + </node> + <node name="Qt/ListView" showInItemLibrary="true" category="QtFx"> + <itemlibraryrepresentation name="List View" icon=":/fxplugin/images/list-icon.png"> + <property name="width" type="int" value="300"/> + <property name="height" type="int" value="300"/> + </itemlibraryrepresentation> + </node> + + <node name="Qt/FocusScope" showInItemLibrary="true" category="QtFx"> + <propertyDefaultValue name="width" type="int" defaultValue="100"/> + <propertyDefaultValue name="height" type="int" defaultValue="100"/> + <itemlibraryrepresentation name="Focus Scope"> + <property name="width" type="int" value="100"/> + <property name="height" type="int" value="100"/> + </itemlibraryrepresentation> + </node> + + <node name="Qt/Row" showInItemLibrary="true" category="QtFx" isContainer="true"> + <propertyDefaultValue name="width" type="int" defaultValue="100"/> + <propertyDefaultValue name="height" type="int" defaultValue="100"/> + <itemlibraryrepresentation name="Row"> + <property name="width" type="int" value="100"/> + <property name="height" type="int" value="100"/> + </itemlibraryrepresentation> + </node> + + <node name="Qt/Column" showInItemLibrary="true" category="QtFx" isContainer="true"> + <propertyDefaultValue name="width" type="int" defaultValue="100"/> + <propertyDefaultValue name="height" type="int" defaultValue="100"/> + <itemlibraryrepresentation name="Column"> + <property name="width" type="int" value="100"/> + <property name="height" type="int" value="100"/> + </itemlibraryrepresentation> + </node> + + <node name="Qt/Grid" showInItemLibrary="true" category="QtFx" isContainer="true"> + <propertyDefaultValue name="width" type="int" defaultValue="100"/> + <propertyDefaultValue name="height" type="int" defaultValue="100"/> + <itemlibraryrepresentation name="Grid"> + <property name="width" type="int" value="100"/> + <property name="height" type="int" value="100"/> + </itemlibraryrepresentation> + </node> + + <node name="Qt/WebView" showInItemLibrary="true" category="QtFx"> + <propertyDefaultValue name="width" type="int" defaultValue="300"/> + <propertyDefaultValue name="height" type="int" defaultValue="300"/> + <itemlibraryrepresentation name="Web View"> + <property name="width" type="int" value="300"/> + <property name="height" type="int" value="300"/> + <property name="url" type="QString" value="http://www.trolltech.com"/> + </itemlibraryrepresentation> + </node> +</metainfo> diff --git a/src/plugins/qmldesigner/fxplugin/fxplugin.cpp b/src/plugins/qmldesigner/fxplugin/fxplugin.cpp new file mode 100644 index 00000000000..38858c22d1b --- /dev/null +++ b/src/plugins/qmldesigner/fxplugin/fxplugin.cpp @@ -0,0 +1,49 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "fxplugin.h" +#include <widgetplugin_helper.h> +#include <QtCore/QtPlugin> + +namespace QmlDesigner { + +QString FxPlugin::pluginName() +{ + return ("FxPlugin"); +} + +QString FxPlugin::metaInfo() +{ + return QString(":/fxplugin/fx.metainfo"); +} + +} + +Q_EXPORT_PLUGIN(QmlDesigner::FxPlugin) + diff --git a/src/plugins/qmldesigner/fxplugin/fxplugin.h b/src/plugins/qmldesigner/fxplugin/fxplugin.h new file mode 100644 index 00000000000..fe73ccc9991 --- /dev/null +++ b/src/plugins/qmldesigner/fxplugin/fxplugin.h @@ -0,0 +1,58 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef FXPLUGIN_H +#define FXPLUGIN_H + +#include <iwidgetplugin.h> +#include <QtDeclarative/qml.h> + +QT_BEGIN_NAMESPACE +QT_END_NAMESPACE + +namespace QmlDesigner { + +class FxPlugin : public QObject, QmlDesigner::IWidgetPlugin +{ + Q_OBJECT + Q_DISABLE_COPY(FxPlugin) + Q_INTERFACES(QmlDesigner::IWidgetPlugin) +public: + FxPlugin() {} + ~FxPlugin() {} + + QString metaInfo(); + QString pluginName(); + +}; + +} // namespace QmlDesigner + + +#endif // FXPLUGIN_H diff --git a/src/plugins/qmldesigner/fxplugin/fxplugin.pri b/src/plugins/qmldesigner/fxplugin/fxplugin.pri new file mode 100644 index 00000000000..9760d0e7227 --- /dev/null +++ b/src/plugins/qmldesigner/fxplugin/fxplugin.pri @@ -0,0 +1,17 @@ +TARGET = fxplugin +TEMPLATE = lib +CONFIG += plugin + +QT += script \ + declarative + +include (../core/iwidgetplugin.pri) + +DEFINES += FX_LIBRARY +SOURCES += $$PWD/fxplugin.cpp + +HEADERS += $$PWD/fxplugin.h $$PWD/../core/include/iwidgetplugin.h + +RESOURCES += $$PWD/fxplugin.qrc + +OTHER_FILES += $$PWD/fx.metainfo diff --git a/src/plugins/qmldesigner/fxplugin/fxplugin.pro b/src/plugins/qmldesigner/fxplugin/fxplugin.pro new file mode 100644 index 00000000000..82e1c6503e2 --- /dev/null +++ b/src/plugins/qmldesigner/fxplugin/fxplugin.pro @@ -0,0 +1,4 @@ +include(../../../../qtcreator.pri) + +include(fxplugin.pri) +include(plugindestdir.pri) diff --git a/src/plugins/qmldesigner/fxplugin/fxplugin.qrc b/src/plugins/qmldesigner/fxplugin/fxplugin.qrc new file mode 100644 index 00000000000..886248877b6 --- /dev/null +++ b/src/plugins/qmldesigner/fxplugin/fxplugin.qrc @@ -0,0 +1,15 @@ +<RCC> + <qresource prefix="/fxplugin" > + <file>images/blended-image-icon.png</file> + <file>images/image-icon.png</file> + <file>images/item-icon.png</file> + <file>images/mouse-region-icon.png</file> + <file>images/rect-icon.png</file> + <file>images/text-edit-icon.png</file> + <file>images/text-icon.png</file> + <file>images/list-icon.png</file> + <file>images/grid-icon.png</file> + <file>images/template_image.png</file> + <file>fx.metainfo</file> + </qresource> +</RCC> diff --git a/src/plugins/qmldesigner/fxplugin/images/blended-image-icon.png b/src/plugins/qmldesigner/fxplugin/images/blended-image-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..7c0911d63df28d558f10438cf19f1d21d3638df2 GIT binary patch literal 340 zcmV-a0jvIrP)<h;3K|Lk000e1NJLTq000&M000&U0ssI2OV;~r0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUy_DMuRRCwC7R7(nkFbtiAcku**xbYnB zJX>++)(Z$Cf+z4Qe&@+6H58^(>r56kt$EMnnI@{L>h;W`ec$6a4#rQ@6m8oEmT??C zF-elhE#N1`aU57ZDG+Q^tm}#gR_xO>b%2LqAVwYncv7GcQozC5_q~It>pJ6SS$0t@ z%VH?#`xw8i3n7e2($DkU0dCut@h1fU@aB0o6w?1>CI;{yQJ^FqQ48t2DJY`WA~=gb zXi?X-Y`YLGtbgvSh0MZge%~x)Ye`c|eeF&yCmXydid$g2!L71+<!;}RFbZ7=M~-|^ mmZksr(Nn8wnkVD;Ex-U*=!rfoJ#hyB0000<MNUMnLSTY0S&k_H literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/fxplugin/images/default-icon.png b/src/plugins/qmldesigner/fxplugin/images/default-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a90779f02a090f925796852ade5478ed33f22cda GIT binary patch literal 341 zcmV-b0jmCqP)<h;3K|Lk000e1NJLTq000&M000&U0ssI2OV;~r0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUy_en%SRCwB?lsgiHAPk0gXS|EOm1AQ~ z<q2#(lf7O*8%xH<(%zf+LXFD|8$nqXQaI7@e<mUDF3a-eX5O}KzVBP>hhgv-WBa~a zBS3y4AQ30Tx~>Az_r0Mq2m<ba{DJ`A5P>BZ#Ih`w2&jj@&-0AeMNuUDuIr>0d=c<x zft;oZucMT5CywJV4BNI<vjDjcAqj9)Mu@D&aa8q$5LnPO&21qk0=H8|BuN6%s;YGD z)Mvp%Pv4CSc{+rT2(_JQn!>XviXRBgj;MQavm>~j(rAI@dH&yYnh*G8I&l_Rmbo4? n3p)|)W3IPWUDqzx?;*ed!{Rpa=`bfP00000NkvXXu0mjfBNC9Q literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/fxplugin/images/grid-icon.png b/src/plugins/qmldesigner/fxplugin/images/grid-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..113e14c00a05aa7e0e13e65ce2b01226dacb6406 GIT binary patch literal 369 zcmV-%0gnEOP)<h;3K|Lk000e1NJLTq000&M000&U0ssI2OV;~r00009a7bBm000XU z000XU0RWnu7ytkPBS}O-R5%f}R67cTKom@Zcku>_ja|UbF4zg)O0cvLynv0h=ncGz z-^^x~hk>x!B;*y3o#@P)H!eAsWqJCooNe1&*Zo>#97kte*L~lIVfaGD^E}X%1dO2j zzW<R*-}fXs%d+6a-gTX*1VLbiLcGX{Lhyo;AI*>y?P8v1V0IX>hZv?L5Ck~vZQF7e z8;Ez51P4yj1Q%f#N)~S+DWE?|G)*HCQ52b>NL;uAFkGQuA7u%-td5hC>@Z>vbrzh@ zUe~q62_ZAoE{==DaV%Nju^D2(3yG?#L?THNGZYCGC%&I7e|wZA<Zb-J?JSBy;)Fpn zL>Bsj!w*jAX_`tFZy_m95_z5r-}1oO>MsYCWtk=I_!_zA<HtWfwm;tiNpHy#4g1N~ P00000NkvXXu0mjfX?T|? literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/fxplugin/images/image-icon.png b/src/plugins/qmldesigner/fxplugin/images/image-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..4dfcae7d472d50acfc50098e28788edec8adee0d GIT binary patch literal 292 zcmV+<0o(qGP)<h;3K|Lk000e1NJLTq000&M000&U0ssI2OV;~r0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUy#z{m$RCwB~l<f(_AP|OI$Sy7*IFF+V zj^Gl43pk43Jvm8jX{aCZ$Au&JKJnh@J*AY7y>;ZA2_egRYweLz0?IN1<P}j$fy5Xy zC8m@%M2rz0n9P13BFHNO8wnvmWQ_e4Ypo%o>IY)Pm%g$ga7*}Nq@BL9A|S`aIX8&t zD?$E7c<%=hePu&nSI8dmqOYuo(~Gr6?)G8}vFpXx9}z-K9q<o3@xa`0N5<FX9a%)+ qmN@5&jhrKAYicQ_cE;~ZfB^u@$4`MW06mES0000<MNUMnLSTZgpLJjW literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/fxplugin/images/item-icon.png b/src/plugins/qmldesigner/fxplugin/images/item-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..82587ae0ecc4d29e2fa6213874d4b32698ccb63f GIT binary patch literal 293 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H1SGw${mubWk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xj(fT|hE&|T6LOpLh=RZ#?r%w~O=mg6o=sO>!f}x6QrE{q zW$8Wf<{!1WXN8uv%{=<%diDQGu|?OkmhS1b?{l6KbfC$(XCiBuS0a1O;jFc-0$j!n zi~^k}EFU+^PKkQ$$$4Nh3xhRlj{vLXtj@sy%Pr1Nb~SvZ&-G%Vwu(;IX%4;<L0&pt zf&4#|xFQy<{u$Tv%g=G|j<t_M_eLy8I(Rwi^MT{}&*mie@-XG49Fdqjlkr4qiyNQD p+O0aAEw7Z>v+sXbfBaqj0K?*wmyWp|GX{By!PC{xWt~$(698>GbOZnZ literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/fxplugin/images/list-icon.png b/src/plugins/qmldesigner/fxplugin/images/list-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..9c96743f0e4f3fd22a160864382f2dbf05c6de0d GIT binary patch literal 424 zcmV;Z0ayNsP)<h;3K|Lk000e1NJLTq000&M000&U0ssI2OV;~r00009a7bBm000XU z000XU0RWnu7ytkPT1iAfR5%f(k~^w{U=W3Gz`bM#O0ZS169ns^yRr&`UBKGHRwVAg zt(d4ea>6kA)tmbWDDyhs9L5}qqWJpX&zh#`cs%|Y()YbJj^jMfM08#ELzZpZoP>Nq zz=-6I<9wvtZZ|El9*>9QjN7&a<#amju?)!u0uvCE+cb>^xvq=!JH-8dhm!k=FubtH zF`|K6T;Lqc(UVv&H)1`uAT)9q2JETp8XC{@45g$TV+#2O0uyM{b5&KML=Xf4-Wv!p zj~-&*_c6yJW1+CfF%}mW`n(!1vUQ-xUbiq@lw}E76h&(>E2ay5UiR?DD7n|`RfFO< zM*11S{X`gEwr{cisCw;Y3>W-f=$hvwF3)rH&gb){o-#o!=forD<X!N_9E+FBMT3$g zAwncCghnFfeijWcEJCIwZ~qJQvMkeU_u@AMEQzZ$P4_fLT_gw&VHg_i-=<G@KFMKo S#%xLe0000<MNUMnLSTZ5i?|p7 literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/fxplugin/images/mouse-region-icon.png b/src/plugins/qmldesigner/fxplugin/images/mouse-region-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..8c23b6c0fce201d1621cf30a0c259021ca4f5812 GIT binary patch literal 511 zcmV<b0RaAqP)<h;3K|Lk000e1NJLTq000&M000&U0ssI2OV;~r0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzp-DtRRCwB?luOHkP!NW{PmQRgD1!(J zC8#J`w1_|w`m_FomhKGd7YMp>BhdwlF3{b2<G`W3be)BcI`cU5&N=5_Rn@=WjB{PL zTCIMaZ<;1|KA%DP8iDj1;&Qp*VLTqk<MCuN`7z>jI^l+g-EQYNP9~En6bdZMzKKBk z4M7?K2>x_BozLgRViBy55pa&jBOF{U%SxqEk|af=(YFw!5o8_?2l{O`n~`$z`TPq6 zGAUxe-}`4a8jZ^3aw3rkL?G)00*05Q9Q4I7OfZ6Ifi5tVCrb$Ps@LltnQsWH#d8Fi zx~}V@DAsB<j^hHf2t%aPX-tRs>-BmvnXFc;Mx(*=yjP2R1g3zX!C<i2Y!-_}yWRFr zmnz5*Enq}Wu)E!Er_;foTdfwj%jL4)@4F%PP(>^jlVusL(m$YuQmJGZhHcwW5p@L* zmC8xBJn^YDn@!}Skl}C$N-mf4FD-^F@GwJ=q9}+#N1CQ>w_8uZ!yXAUK@iX&oNP85 zi9|fX4}0W^RVtPHDR>d|$oc8i>h*fR9KTlq1^_E;urd8Sacuwq002ovPDHLkV1h8m B<2L{R literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/fxplugin/images/rect-icon.png b/src/plugins/qmldesigner/fxplugin/images/rect-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..4555d886fbc35cf63ca9408cd0aa25a08a16dc20 GIT binary patch literal 237 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H1SGw${mubWk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5X`aNA7Ln?0FnR1$!L6L*CT)288)9Vea-J7MgH;C^4%@g+F z!4|m-r+pd}^3VOeD7*dIr+b%c)?PhpnW(u`poW9-N8p>if}E`lc|DEX69YIZL^N80 zIVwc=mQQ8+S<n^Dk+3H!dk)i%iA<`6T$zvFvd%razAAA->%okhJUb4WU#=^QwA56s jdb-EZ?dh+-ED{Xcx@D98J(G(9I*q~8)z4*}Q$iB}?8jRa literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/fxplugin/images/template_image.png b/src/plugins/qmldesigner/fxplugin/images/template_image.png new file mode 100644 index 0000000000000000000000000000000000000000..a62b3057bc9a01157e984c9ebc3be70324e57f5b GIT binary patch literal 4652 zcmc&&XH=8h(ngL7(gOkl(mSDg6aokcMi3CB3DT7!s7Mi{Lp*{Chbj<3FmMP`LvKNv z5_(VQ9fQ()2?Rp9+h5<WZ{2_Q?6uyNl|6Y^X3xws&oevH$WVuoj+>5(ii%P9F5HBQ z>YViNhn5=b?DKO8fsb?ECOTSF#eG+o!Iw*(cP+iCsOZ^#Kj)}YKVJcxG(NfpcWB1X z^U=xhJ%@yOfh{hdI}d$sd%Sqz?CwK#`=#?^A7>{4zo$N~0y??|MizfDaZ*u1KIy_W z&HN|Ur$dY`lYV~R4!f<anSNfGk-llM=W^0tMjeX^y?I<kZ9~@*74)HIU7c-hd{^~( z(j;HGiS3TJvFMqK88;~yzqy=bblKPl&IGGz{=*O!ikM<(-t;Bz@5d&}HQFPFV<}%7 z8iHnJ>w<!0Wn^UNsN!Ow7%Bm+*R<zY1aE~?MX(5HQNd|h1gV71N3c*qsNuB0By=8_ zAk_cL&;DHpZduHa40Cj}w%&o2WoN%lrgOXW{|4&2yL%kbyD^Zol7To4I6Xdm^XARY z&dUb75;LigkPsTCt5+Zp1@DEPr-M0PYxPe2rDS9b3=R9by1Kf%p>lGg%RV3_>gwu( zf`UKWVuFZoIiK{T5Em9+lWc5lQE9(d5HJi33?wA5#%MdrUtzd-+tf5sFMDTahYl=8 zMn-!2sF;|TzyCIoLZT{}c(}PuOic#|270<pt*t3r#O9fXAPR*NcSS+y=FQ-dLQ4n) zg0`SmwJ|X0T^`KsPge_???TSa%>k!qnE5IzEByll3x-!T!!E|~D7nwIM3$A6p|DzO z0|TvN74haS$0rA~Sy@>{#fjn>!7ms4P-2PsKi_wEbfnx2C?7tsG%~`+@~RfbiWSt< z_yaRNrz`^#Q#A=mIyg96%8L6tDvFmv{oug^Q8BTj!$V8k*O|B9pY9FbH!v_zY|vIz z4%l(Bv9SS;Wf!tkS)DBBW<0x|DtnNQxF=Naw>c9f*XTN!=0EU77l}lonayl%PgXD` z6W1q_Buxu27~XP4w}A?m{+5=OckeE~uUDZQY$hkzTZ3btog8E|3)lNB50JNiIv28d zcx>{Ak~L>sgdkmU@^W&U`MK!{+iw`TujdsO_RJZB`2LDfeEF-Rpg>Ao$5k<KZ>i32 zb7Hslx{}hjvs20r-*kKX;;+uc*uU{k*FsK6qrMx&Z+ip!RaI5+r7yH4wsjmVECQ5a zZdKpHL+0p_bjtoZG^jG)G*>-=mYuHk`(J;&<(C+kJ)q~1E?p!9kyc)v9tTi_uXD$E zFZR9apo@%%$ji?c#2Lnw=H!Tph<sU3QV%(jwyD_~wMX>!_m8?>2f254cK-C~)9OeO zJtHG~$X-hee3#75&dz~IJ3Ks;d06`6M^jj`oPBX#o;-KV!Nzo*ClR-pflznF3lIpQ zhx44{&zna_{r1<!pN|xdV?FFaiT0)`L7`Aka<;b9C{@4miVCd9Y$JFPloR%#I*7cL z8O`b3n|g!yc%F-&A8}0Hem+{fObSF9x~OlE`cyafKy|HuV_;yYN1QdPK@@M?Xl-q+ z!is=e?@pGxud6F5A%S<juANm@SST$n?y|ek3n~p=(bCq|Wi1GIZLSA9&rdx3{0`zY zPHVzY@$cTf>sP1TgWZkeQ!BQudvfiQ*!}b=72|9ZOWqgj6IJL<t7ZQ03zq5rbCF1> zMY?*3)tst!?qJBlv>#eBbi0+ax>2Y-mX}S9{A*<;H9r24{FRBc?WQmq{@^Xm$)7y{ zsb%6gcBpyffkiU_hIA~|C1zsuRb>;Hhn5yK#MeFi#q1G?waLZt`hW`0{cQbY_u0k< z65<bPYFV2a0q07O#zSq`#7L2~sCrgMN5=`}fG0*fK*wAshTC}?-w<?Ay}b1Z#Ps#K zKf>>8W}Bd3URqtW`T6<0{0&JpBD%V|=!$PUUC@O=EtB+a5W~0pq6;TZLTgjBprcIk z{Zmn}&e7k~+}zyWp8JY(Z)F%WppypkLVIHI%Fv4X`rxe}O&oEJp(laM!iCmY!y0v@ zp0l3b-+P*&lyxjqU!5M8CT6%`_!+T2ibJM+8Rp{X=y>m5bgO`Zf&zSO8Nvti{GQ|7 z(bu=}Bawp7ysa4X5s4JN`s;|a+Gm-Odq1Pve+iE_irifx1rpd;0fI7%qPAy(HpkgA zm-w@Pw#Rw6xmCW>b)T*sJ_?wS5*7}lRaL;%)WEo+6x_y2_kBa1q7|JwSfaf4H>OuB zA4pBnB-YsDeVOYY%r=HNUo|T%F5W*lNIsZ*R%Lbn{(a)eSkB3ZYDKrPM+y_ihr9g5 zphk+lYVEUOser`~S7E7JDKo86oT%!HcA<yPQIfdctUM5Rdhz0iWm+|&WnKOK9)~;g zJ0B67GY!mqH%n8<Bi0^O2bxbG1P9l@f3Gts+1c5to%Nx6M8#R3yjIcQWgQJNMW@5_ z=acAq0>FiE{p+@;n7u9}6z7+jv>vy_|MJeAJ3AadK~Q$=raC(A5^a=~mG=wTh@EYw zxrSw;YnM%P?tM`DIe1?~=Cj1jN5UsoTv3wq^Yez*mrjmIY<4*JSH*jKK8c+oH@^(c zjGJLD0@vK9VAhKRuGeuI1z}-fAj#{N;<)BNLB++b*iQ5cC;0_r9y-_);gXz)C^`m) z$`li8>(xr+Bd-CQOrs<u(h>y~7uSJpe_c$kaM4Ah(QYVFH8tCsxIZ1+7*t0iJy%oH z(kOW|+0)0Ed&1h$@k&`spWF$(S(okX>^Mw)R8aMqD=RD4{`|9|yu8@1!OG0c=j`;P zpr8OW)7`J|<up#rnRX2u#z;atj_*Z(x1i%b;_PJfFS+M5{N(`Ppzp=JR)b6&U3kN! zJ-A9zjKL2LB_$;6?DlJ9sJtpDD<kv`o7mfXkY=YEL#$!jpIp+*7QNnbGCxFSWXOG% zpv2#U5-)rQjb0RID5%OqaAYk{<cJ+{=x!Yc=*S4(=>)uVwaV(t)k}a5(ml~?$++&c zhA&^>Fa=rJVv?T9ZmOGYhT_Tl+xrF)csf5nKblyv?d~k5Aw0g+f7^jzdI4pyXRy4! z{-QqxH~lgo;{wV{MYC(>97NrBWp&l1C&g=_=abIP5?ZyvS6jkyzqVFo@#gHp!V{tJ zY*1-fi+`+&Vq;<e_3~VE>Htt4dRB$SVsmqI0bws;ZBgDOoB^tb*VO52(>6lNXUg>F zdI$-LiFYhoKP!9prK>$3e9(=~*fw7*ugPiY>Eo$1Gcy~j6;T5WvoBLBx`qy)y<?Vd zrs}&Ezs-1%n#vte7ke?R$pLLwf?XVojt=AJ<Kx457g!aIjE-U_>qn;iDxoTV8=trE z$DmnCZ1S;L6`8l$02pu@78Vv(E(Ua_*zQH_6<K3z2fGczZ3!QQb)<9drN|T4v9rDt z*7li35i>n2JM-Q9H|4l0qGnb<&=QtGPkW>=AEPuAua>%W!PPu6JsG<@A<I?4%&Rg) z%(wujT@7EH|1ExkeBGUUZuqV}B)04>_N(qUw-!W1&=OmQhK4rwUe;B@_8J439txC& zEwgf(z)IBmHz2VKMB-zwjhfn8tC?X9u|!E2jNFG(tybqOGUoxjc`VclSV+eGk+`w3 z@rNxt9`occrKLvxA5u;7hg`3h%e{Bd2mfyo9+55|PhQL}y(M{kd<_2<CPdjmg;4f8 zQ=Tv3GjZKjASr(96T=~P9(`=SO@#1d&g73DyMm?w4|myRPgcKR>&r98I8W70)Y~Gl zBK9F9q-XKz31t+-)`uQG#;(H_>LR7AY}ppZrye|(&TNk|6c1D}Y-><at--z>4!FS` zqnFLvN1ji%XBC9_j=19La6M^C_uJ+radK>vM>g=ycWVm^zP~yk%lZik2?I?Y3A$X5 zNh`xRF3_E=thgbpk8`7|U#*&Vf19?om9wjN9h~lkd-OW2dJ|Sjnwpxpai9)Fw{xV$ z4L>iD>!eLC<`)&QH~4>_ot^DV>jkpSrrzIcqVg&HZunV0NOSn83Jnd7{;_j*d3m{l zV;gMc;dero&K(^iDI~_CWP_|ozI5qQY%SwyB%3%q?lZ=$z_!k}GsyV#l(IQeA_fj( zm8yYgTp~n(mhLB?OmLk7oY$)KZsJtnef?rWO3IjB&$)Bw^pj<Qa+8skF2rB};NQM| z+aAXs7Z-=EA>^HC&{lZ;Y}@B>1afV_&mbx)O6kRflh+Mu2y13G8ZAV~f-LNgT35Rr z0zuOAP0j4V1JDM}+W8UEmgR8&)ui}%76N_E3oC!nD6PtUq>x?5)&jV}+}I<Sk;LWZ z_ssUyZsR~e0zJGj(_n3Hf3`)ujWV<_GfPTOZ+tOXEvo-9!tTxOaJa+<kXyc?%<}T` zrKP2K!T#+0{HMzU**hG~larIjs|q;NvyF|7YT3>lgEXgLxiV-5Mi?inHi4ZDh1NCW z@%ZE8;CA)o={mp3K+5{o))v&FIK_oCGy_nvPMgD*+}v(TnUj!+mU0%aTA*1QV~!o- zgal-DpT8`Ssu=U4gq>H7Ky7$TRu`>%NYQyfVV*y31w<KJSYQEL?uRlxs0}?i5)~1# zsr8AZjVu)gd=VKLY2uEbTUk*F+IJ&*Feo2~%|4PB5fyEqY}645glAO~)6=&3W=CXl z_`7#~puYem1Z4MfclYWPeSbDWUY<k{M^;YGlkSf))iW`Zt*y>IDGI>%<JFRHDyL74 z7nfdgdS<DqpnlJXYHDgwXm@bGeOS{EB9XYh&dbVrEttHOl9B@S9KA>2?5nkz21>8e ztefh5)u^4S|JFYz!n0q!#lf4Zs&)VT^J9N~5~xoSiKLxH7#tkz{=J~`@}6U*k3qip z6&&DBPEJ7@5Yk;1!Ot$^<<1<VS;h9>?Do)~+v#S}uXlm@;k`@r^o~`fqTz|)L~V1J zPQC2%f&#s^65evr^S{WTMrCrM`8Cq1BUubzlD8fc4+_@4dNI{yy=J7Nrxz9$mMKnO z9n1wXGjgkaX`GdM&cVR}%q3!uZ2&7ECPWej#Nf-V>hRL}n7KJLAha0nDpvyftM?+) z75Q6T7d^q`Rp-0TI`bX@Tn`8c=sQC~g<8&n!;8*%!8pOo%ge{7{%lABfk0?zXdv~n zho50E7=3+xFuSCE_y9<Ri^x7n_ZHXO{B!E7x9{na9AvI67S9LD3{Y9clU@hpj*=kX zxhgAEYO3B}@Ccm>L6im})ZKlfC6djO2TrRDW)L8JSlHQzn?eTRhh%aK7&#UeHpVM- z$gPMiB|*0*@>hU52Xqb2t*`fcRazA_J<2O7QTlWb-~|L<LPA1BL_}IzT16!g7k&w? y!N(wUevJ3R|IPUS=Dh#kFB$%a_Y&mUErb~4rixw^Bof@@Q0d+=gcoZ)dh;KzNWj4W literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/fxplugin/images/text-edit-icon.png b/src/plugins/qmldesigner/fxplugin/images/text-edit-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..6a064ab07c1fb301161797f48b611106f371fc9c GIT binary patch literal 368 zcmV-$0gwKPP)<h;3K|Lk000e1NJLTq000&M000&U0ssI2OV;~r0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz5=lfsRCwB?lsyteKoEs@%j~*_Od?fs zmk@~sBm^W95`mIPgaVG>GS1>}+m59QI~pB~!1Sm4_0!L?EFbrR^nEXi;^q9hu1VK* zK)H;7^bs)(gX1_H#}Pta-+2Fm=lS`hK`+2fMNuY1+qUqC<M_h&;e_-N0gZT`x2?jm ztV7U(h@xow8l%yKfRcRQ-&SE51`5$M4IqLb_}48vAbmoFVThcFEXz9x=ouP84q_$H zh@e`4oa`!8RXxzcbzO`LSXv}Wa>|{8Agr~pZTplpXGNAO(llLFp=lb1wa96jCbD^5 z*NdXqF?g0`hgwLIw4LjMz<8Nv=6Sw@z!;hJkI<EKcWYHub!Yv41Q-Bn0{hY{$1xKC O0000<MNUMnLSTXi!Jl3L literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/fxplugin/images/text-icon.png b/src/plugins/qmldesigner/fxplugin/images/text-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..9ce62037ba3c7700e9f06dfdb48fcd61eda8f7f8 GIT binary patch literal 582 zcmV-M0=fN(P)<h;3K|Lk000e1NJLTq000&M000&U0ssI2OV;~r0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz=t)FDRCwB~lufItQ546o$MOD_w<HMz zDHA5XfHLp}d_n^sz`#HmiHVdlz(7$%DX)3w{@itXIOo)L<L>fUd#}Cz>%abM@2$z_ z^S|C3AD7D|7K^<c-|2Kd&gU~IFGCQ1gScL=Z$X?+C;ncBAp8dLw2*GMdpI1(q}6K8 zX0zApwb^Vcgdz}sAIs&EV*P%<+wI=%c1XBbEX4K6;%OmJFdB_crxTm}tyXJ19z*A| zkmK<v5Wo)xgUx1xVX;_3p^(Gj5QzPLFNOS^60QgYW4&HOk5s{6kWGz7V?LjYYrEaD zg2A`f>oIk?T;d}9#zN#IXEK?TJSLN=UayZvqi8hBbo*y86>mHqXR}1zScn#nW3^gI z%yc?cS!gsG;KJdsUav=s$z&p~FIr$Ab-&-oP-_+!5U3ak1hDy*N~LDAnNFuA>JCw> z)rP}iDwXp2d{(QqP$;0Q3ZdGHqEIFsQ4`;_;QY|`NF*ZN5edchXwwTsft0Gnodv7- zU`Vx8^w>G<*=*Ksw^^Z5#DeUdg?h9ynM}D{u2!pdyFHOepdW1(7v~CyN~K~j7(5;i zJ5fYjU+#!br^{xu>WP#`L|nOC?%R1)uhqX^v*$PU-OK(To*aI~V)32l_qPB80NSV? U4A;*g`~Uy|07*qoM6N<$f~({b82|tP literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/fxplugin/images/widget.png b/src/plugins/qmldesigner/fxplugin/images/widget.png new file mode 100644 index 0000000000000000000000000000000000000000..1cf960e61befdb4657cbf3f0b6646f09d9bcd926 GIT binary patch literal 716 zcmV;-0yF)IP)<h;3K|Lk000e1NJLTq000&M000&U1^@s6#I$TX00004b3#c}2nYxW zd<bNS00009a7bBm000fw000fw0YWI7cmMzZ8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H10!c|kK~y-6)s@Xo+dveBzj1I-5+oA5Nn#0`IE$io$Er&z`atc2 z^qDFa?F)2)s2v;#q>WG<D%+`S;pxI<lEI|vqAWU+C7*kIkFW0E2q`7cag1kC1;opD zpZ*wfAth2u);|(}5CTJpjec7IUp~D4Cs96}SMNWU!T_hIAIY*D0Mj%%JbZy^ngHZ^ z&e_@7MqWzE`T1AkIOgQ!EwGm32PeZY@VtR?DvE+22sZKnmzP(>ajZqH6;Dcu>pHYr zEoB>z$0SLr@MC^9n{j)4TS<o|Y}+=SPDkOJn<?{oq;Rj-)A+l)A6#8c6mGZMRpGt8 z4!vGi5m6K|olaTj`~9B6%jHi-qmjamMg!M%s=|)5GA2nv7>*TgwVE1#e}9kf`xHe1 zl*JqlJ!-XDRoF1f*DT8jf{}8n*G)XnQ@oTC-}lL~3@H2A^9I!Gb=HrT<1&(fJYRLd za5&UDAPmREaiXx}>}$n*QaB99ESF1#2ZI9|jfTS0={M%{xx(FU7t69Vz9KxCOjs-y zim>fIySuvzFBS`~udfyEblTW<U*RZ<G+qgR|2|Vj%d+TpR~?e3DHj(Z;`eT7Hk-Jv zyOFP?0$^un2gli0IL~u}Akd0A9IlG_z&~EUn(+4aHl8<7e=f&)q{dkYp%t?z3VdH5 z_lj_>R>Si=wzkw>A`C;4r0f7GB?kwtR?ILANz+vE>jR=xj*s6w&I(6IR>k(W{KiFJ yNLfCGL?S6uC;@>GAP^Wvx$K`>_~O62^L_ya)C>E9Py`780000<MNUMnLSTZ$U^(vq literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/fxplugin/plugindestdir.pri b/src/plugins/qmldesigner/fxplugin/plugindestdir.pri new file mode 100644 index 00000000000..9dff900cbda --- /dev/null +++ b/src/plugins/qmldesigner/fxplugin/plugindestdir.pri @@ -0,0 +1,5 @@ +macx { + DESTDIR = $$IDE_LIBRARY_PATH/QmlDesigner +} else { + DESTDIR = $$PWD/../../../../lib/qmldesigner +} diff --git a/src/plugins/qmldesigner/images/mode_Design.png b/src/plugins/qmldesigner/images/mode_Design.png new file mode 100644 index 0000000000000000000000000000000000000000..b00ca63d833e292639513a4c76f771fe86325634 GIT binary patch literal 394 zcmV;50d@X~P)<h;3K|Lk000e1NJLTq001xm001Be1^@s6RMh(%0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzEJ;K`RCwC#SW6DVAP|Ly6ZAH2b?-IU zcVRq&anm*4MYp|;Q8zA87MR);q2)11(n%UZ_`UaLh5}q`&8C`^a{8bEQ%dovW5nFW zt}Dg%8^#XjIM8;Ex|6fUi<pJ`Wh{wP$7l%{)u0}jIle;0?{58F`ud?vqyQg6;&{Oj zfOx=y87u8K*8VpHLKHT2boeAA;8GZ=d~y#^Byds>48bB3muwdrBY`F(kc#(9iY*ix zLZP70VN|7ve@cTApgu;xav;|+0!ko90>eYd2d<buaj{99!<H*$?knZufnq&i-f@g- zb)@>SbIc{MEqxbyHcb=M(LP;r&VRPIZA(hv;R56WKJ4!!mYTr*y|C_q;g0$Tov)pm oz-_3UPsv!NlZQ|)0xtmu0J{B&Y-R=@Jpcdz07*qoM6N<$g8x&db^rhX literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/qmldesigner.pro b/src/plugins/qmldesigner/qmldesigner.pro new file mode 100644 index 00000000000..28abb28999a --- /dev/null +++ b/src/plugins/qmldesigner/qmldesigner.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs + +SUBDIRS = qmldesignerplugin.pro fxplugin diff --git a/src/plugins/qmldesigner/qmldesigner_dependencies.pri b/src/plugins/qmldesigner/qmldesigner_dependencies.pri new file mode 100644 index 00000000000..c1fb3fc38b5 --- /dev/null +++ b/src/plugins/qmldesigner/qmldesigner_dependencies.pri @@ -0,0 +1,3 @@ +include(../../libs/utils/utils.pri) +include(../coreplugin/coreplugin.pri) +include(../texteditor/texteditor.pri) diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h new file mode 100644 index 00000000000..323dfb2192a --- /dev/null +++ b/src/plugins/qmldesigner/qmldesignerconstants.h @@ -0,0 +1,58 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef QMLDESIGNERPLUGIN_CONSTANTS_H +#define QMLDESIGNERPLUGIN_CONSTANTS_H + +#include <coreplugin/coreconstants.h> + +namespace QmlDesigner { +namespace Constants { + +const char * const DELETE = "QmlDesigner.Delete"; + +// context +const char * const C_DESIGN_MODE = "QmlDesigner::DesignMode"; +const char * const C_FORMEDITOR = "QmlDesigner::FormEditor"; + +// mode +const char * const DESIGN_MODE_NAME = "Design"; +const int DESIGN_MODE_PRIORITY = Core::Constants::P_MODE_EDIT - 1; + +// Wizard type +const char * const FORM_MIMETYPE = "application/x-qmldesigner"; + +namespace Internal { + enum { debug = 0 }; +} + +} // Constants +} // QmlDesigner + +#endif //QMLDESIGNERPLUGIN_CONSTANTS_H diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp new file mode 100644 index 00000000000..5d3c397966a --- /dev/null +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -0,0 +1,100 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "qmldesignerplugin.h" +#include "designmode.h" +#include "qmldesignerconstants.h" +#include "pluginmanager.h" + +#include <coreplugin/icore.h> +#include <coreplugin/dialogs/iwizard.h> +#include <coreplugin/editormanager/ieditorfactory.h> +#include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/mimedatabase.h> +#include <coreplugin/coreconstants.h> +#include <coreplugin/uniqueidmanager.h> +#include <utils/qtcassert.h> + +#include <integrationcore.h> + +#include <QtCore/QDebug> +#include <QtCore/QCoreApplication> +#include <QtCore/qplugin.h> + +namespace QmlDesigner { +namespace Internal { + +BauhausPlugin::BauhausPlugin() : + m_designerCore(0) +{ +} + +BauhausPlugin::~BauhausPlugin() +{ + delete m_designerCore; +} + +//////////////////////////////////////////////////// +// +// INHERITED FROM ExtensionSystem::Plugin +// +//////////////////////////////////////////////////// +bool BauhausPlugin::initialize(const QStringList & /*arguments*/, QString *error_message/* = 0*/) // =0; +{ + Core::ICore *core = Core::ICore::instance(); + + const int uid = core->uniqueIDManager()->uniqueIdentifier(QLatin1String(QmlDesigner::Constants::C_FORMEDITOR)); + const QList<int> context = QList<int>() << uid; + + m_designerCore = new QmlDesigner::IntegrationCore; + +#ifdef Q_OS_MAC + const QString pluginPath = QCoreApplication::applicationDirPath() + "/../PlugIns/QmlDesigner"; +#else + const QString pluginPath = QCoreApplication::applicationDirPath() + "/../" + + QLatin1String(IDE_LIBRARY_BASENAME) + "/qmldesigner"; +#endif + + m_designerCore->pluginManager()->setPluginPaths(QStringList() << pluginPath); + + addAutoReleasedObject(new DesignMode); + + error_message->clear(); + + return true; +} + +void BauhausPlugin::extensionsInitialized() +{ +} + +} +} + +Q_EXPORT_PLUGIN(QmlDesigner::Internal::BauhausPlugin) diff --git a/src/plugins/qmldesigner/qmldesignerplugin.h b/src/plugins/qmldesigner/qmldesignerplugin.h new file mode 100644 index 00000000000..d95386789e4 --- /dev/null +++ b/src/plugins/qmldesigner/qmldesignerplugin.h @@ -0,0 +1,68 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef QMLDESIGNERPLUGIN_H +#define QMLDESIGNERPLUGIN_H + +#include <extensionsystem/iplugin.h> + +namespace Core { + class IWizard; + class ICore; + class IEditorFactory; + class IEditor; +} + +namespace QmlDesigner { + class IntegrationCore; +} + +namespace QmlDesigner { +namespace Internal { + +class BauhausPlugin : public ExtensionSystem::IPlugin +{ + Q_OBJECT + +public: + BauhausPlugin(); + virtual ~BauhausPlugin(); + + //Plugin + virtual bool initialize(const QStringList &arguments, QString *error_message = 0); + virtual void extensionsInitialized(); + +private: + QmlDesigner::IntegrationCore *m_designerCore; +}; + +} // namespace Internal +} // namespace QmlDesigner + +#endif // QMLDESIGNERPLUGIN_H diff --git a/src/plugins/qmldesigner/qmldesignerplugin.pro b/src/plugins/qmldesigner/qmldesignerplugin.pro new file mode 100644 index 00000000000..ad820316be9 --- /dev/null +++ b/src/plugins/qmldesigner/qmldesignerplugin.pro @@ -0,0 +1,29 @@ +TEMPLATE = lib +TARGET = QmlDesigner + +include(../../qtcreatorplugin.pri) +include(qmldesigner_dependencies.pri) + +include(core/core.pri) +include(components/integration/integration.pri) +include(components/propertyeditor/propertyeditor.pri) +include(components/formeditor/formeditor.pri) +include(components/itemlibrary/itemlibrary.pri) +include(components/navigator/navigator.pri) +include(components/pluginmanager/pluginmanager.pri) +include(components/themeloader/qts60stylethemeio.pri) +include(components/stateseditor/stateseditor.pri) +include(components/resources/resources.pri) + +HEADERS += qmldesignerconstants.h \ + qmldesignerplugin.h \ + designmode.h \ + designmodewidget.h \ + application.h +SOURCES += qmldesignerplugin.cpp \ + designmode.cpp \ + designmodewidget.cpp \ + application.cpp + +OTHER_FILES += QmlDesigner.pluginspec +RESOURCES += qmldesignerplugin.qrc diff --git a/src/plugins/qmldesigner/qmldesignerplugin.qrc b/src/plugins/qmldesigner/qmldesignerplugin.qrc new file mode 100644 index 00000000000..23b97900409 --- /dev/null +++ b/src/plugins/qmldesigner/qmldesignerplugin.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/qmldesigner"> + <file>images/mode_Design.png</file> + </qresource> +</RCC> diff --git a/src/plugins/qmleditor/qmleditor.cpp b/src/plugins/qmleditor/qmleditor.cpp index 4466ec111f8..7bd674f4981 100644 --- a/src/plugins/qmleditor/qmleditor.cpp +++ b/src/plugins/qmleditor/qmleditor.cpp @@ -655,7 +655,6 @@ TextEditor::BaseTextEditor::Link QmlTextEditor::findLinkAt(const QTextCursor &cu QTextCursor expressionCursor(cursor); { // correct the position by moving to the end of an identifier (if we're hovering over one): - const QString txt = cursor.block().text(); int pos = cursor.position(); forever { const QChar ch = characterAt(pos); diff --git a/src/plugins/qmleditor/qmlhoverhandler.cpp b/src/plugins/qmleditor/qmlhoverhandler.cpp index 7d9ee7d7c6c..799a669b709 100644 --- a/src/plugins/qmleditor/qmlhoverhandler.cpp +++ b/src/plugins/qmleditor/qmlhoverhandler.cpp @@ -171,6 +171,7 @@ void QmlHoverHandler::updateHelpIdAndTooltip(TextEditor::ITextEditor *editor, in } } + QString symbolName = QLatin1String("<unknown>"); if (m_helpId.isEmpty()) { // Move to the end of a qualified name bool stop = false; @@ -194,7 +195,12 @@ void QmlHoverHandler::updateHelpIdAndTooltip(TextEditor::ITextEditor *editor, in QmlSymbol *resolvedSymbol = resolver.typeOf(expressionUnderCursor.expressionNode()); if (resolvedSymbol) { - m_helpId = buildHelpId(resolvedSymbol); + symbolName = resolvedSymbol->name(); + + if (resolvedSymbol->isBuildInSymbol()) + m_helpId = buildHelpId(resolvedSymbol); + else if (QmlSymbolFromFile *symbolFromFile = resolvedSymbol->asSymbolFromFile()) + m_toolTip = symbolFromFile->fileName(); } } @@ -216,6 +222,6 @@ void QmlHoverHandler::updateHelpIdAndTooltip(TextEditor::ITextEditor *editor, in } else if (!m_toolTip.isEmpty()) { m_toolTip = QString(QLatin1String("<nobr>%1")).arg(m_toolTip); } else if (!m_helpId.isEmpty()) { - m_toolTip = QString(QLatin1String("<nobr>No help available for \"%1\"")).arg(m_helpId); + m_toolTip = QString(QLatin1String("<nobr>No help available for \"%1\"")).arg(symbolName); } } diff --git a/src/plugins/qmleditor/qmllookupcontext.cpp b/src/plugins/qmleditor/qmllookupcontext.cpp index d1e88850e91..ae37b10ace7 100644 --- a/src/plugins/qmleditor/qmllookupcontext.cpp +++ b/src/plugins/qmleditor/qmllookupcontext.cpp @@ -159,7 +159,7 @@ QmlSymbol *QmlLookupContext::resolveBuildinType(const QString &name) QmlSymbol *QmlLookupContext::resolveProperty(const QString &name, QmlSymbol *scope, const QString &fileName) { foreach (QmlSymbol *symbol, scope->members()) - if (symbol->name() == name) + if (symbol->isProperty() && symbol->name() == name) return symbol; UiQualifiedId *typeName = 0; diff --git a/src/plugins/qmlprojectmanager/qmlprojectmanager.cpp b/src/plugins/qmlprojectmanager/qmlprojectmanager.cpp index 892144522c4..4baf42e83bb 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectmanager.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectmanager.cpp @@ -33,7 +33,10 @@ #include <coreplugin/icore.h> #include <coreplugin/uniqueidmanager.h> +#include <coreplugin/messagemanager.h> #include <projectexplorer/projectexplorerconstants.h> +#include <projectexplorer/projectexplorer.h> +#include <projectexplorer/session.h> #include <QtDebug> @@ -60,10 +63,23 @@ QString Manager::mimeType() const ProjectExplorer::Project *Manager::openProject(const QString &fileName) { + Core::MessageManager *messageManager = Core::ICore::instance()->messageManager(); + messageManager->displayStatusBarMessage(tr("Loading project %1 ...").arg(fileName), 50000); + QFileInfo fileInfo(fileName); + ProjectExplorer::ProjectExplorerPlugin *projectExplorer = ProjectExplorer::ProjectExplorerPlugin::instance(); + + foreach (ProjectExplorer::Project *pi, projectExplorer->session()->projects()) { + if (fileName == pi->file()->fileName()) { + messageManager->printToOutputPane(tr("Failed opening project '%1': Project already open").arg(QDir::toNativeSeparators(fileName))); + messageManager->displayStatusBarMessage(tr("Failed opening project"), 5000); + return 0; + } + } if (fileInfo.isFile()) { QmlProject *project = new QmlProject(this, fileName); + messageManager->displayStatusBarMessage(tr("Done opening project"), 5000); return project; } diff --git a/src/plugins/qmlprojectmanager/qmltaskmanager.cpp b/src/plugins/qmlprojectmanager/qmltaskmanager.cpp index ac92b7ce11c..041a009d7dc 100644 --- a/src/plugins/qmlprojectmanager/qmltaskmanager.cpp +++ b/src/plugins/qmlprojectmanager/qmltaskmanager.cpp @@ -50,6 +50,7 @@ void QmlTaskManager::setTaskWindow(ProjectExplorer::TaskWindow *taskWindow) void QmlTaskManager::documentUpdated(QmlEditor::QmlDocument::Ptr doc) { +#if 0 // This will give way too many flickering errors in the build-results pane *when you're typing* m_taskWindow->clearTasks(Constants::TASK_CATEGORY_QML); foreach (const QmlJS::DiagnosticMessage &msg, doc->diagnosticMessages()) { @@ -61,6 +62,7 @@ void QmlTaskManager::documentUpdated(QmlEditor::QmlDocument::Ptr doc) Constants::TASK_CATEGORY_QML); m_taskWindow->addTask(task); } +#endif } } // Internal diff --git a/src/shared/qml/metatype/QtDeclarativeMetaTypeBackend.cpp b/src/shared/qml/metatype/QtDeclarativeMetaTypeBackend.cpp index 0a20bb5d03f..6ae8739712d 100644 --- a/src/shared/qml/metatype/QtDeclarativeMetaTypeBackend.cpp +++ b/src/shared/qml/metatype/QtDeclarativeMetaTypeBackend.cpp @@ -98,6 +98,9 @@ public: return result; } + virtual bool isProperty() const + { return false; } + public: static QString key(const NodeMetaInfo &metaInfo) { @@ -173,6 +176,9 @@ public: return members(); } + virtual bool isProperty() const + { return true; } + private: PropertyMetaInfo m_metaInfo; }; diff --git a/src/shared/qml/metatype/exception.cpp b/src/shared/qml/metatype/exception.cpp index f6d966c8703..807cbc24bb4 100644 --- a/src/shared/qml/metatype/exception.cpp +++ b/src/shared/qml/metatype/exception.cpp @@ -41,7 +41,7 @@ \defgroup CoreExceptions */ /*! -\class QKineticDesigner::Exception +\class QmlDesigner::Exception \ingroup CoreExceptions \brief This is the abstract base class for all excetions. Exceptions should be used in cases there is no other way to say something goes wrong. For example diff --git a/src/shared/qml/metatype/invalidmetainfoexception.cpp b/src/shared/qml/metatype/invalidmetainfoexception.cpp index fc991695b6e..9d8aa37b0c1 100644 --- a/src/shared/qml/metatype/invalidmetainfoexception.cpp +++ b/src/shared/qml/metatype/invalidmetainfoexception.cpp @@ -30,7 +30,7 @@ #include "invalidmetainfoexception.h" /*! -\class QKineticDesigner::InvalidMetaInfoException +\class QmlDesigner::InvalidMetaInfoException \ingroup CoreExceptions \brief Exception for a invalid meta info diff --git a/src/shared/qml/metatype/metainfo.cpp b/src/shared/qml/metatype/metainfo.cpp index f979a064dba..f6ace0f95a2 100644 --- a/src/shared/qml/metatype/metainfo.cpp +++ b/src/shared/qml/metatype/metainfo.cpp @@ -254,7 +254,7 @@ QStringList MetaInfo::s_pluginDirs; /*! -\class QKineticDesigner::MetaInfo +\class QmlDesigner::MetaInfo \ingroup CoreModel \brief The MetaInfo class provides meta information about qml types and properties. @@ -268,7 +268,7 @@ The MetaInfo object should always be accessed via the model (see Model::metaInfo Otherwise types specific to a model (like sub components) might be missed. -\see Model::metaInfo(), QKineticDesigner::NodeMetaInfo, QKineticDesigner::PropertyMetaInfo, QKineticDesigner::EnumeratorMetaInfo +\see Model::metaInfo(), QmlDesigner::NodeMetaInfo, QmlDesigner::PropertyMetaInfo, QmlDesigner::EnumeratorMetaInfo */ /*! @@ -486,4 +486,4 @@ bool operator!=(const MetaInfo &first, const MetaInfo &second) { return !(first == second); } -} //namespace QKineticDesigner +} //namespace QmlDesigner diff --git a/src/shared/qml/metatype/nodemetainfo.cpp b/src/shared/qml/metatype/nodemetainfo.cpp index 907083b7523..86132aa4e0c 100644 --- a/src/shared/qml/metatype/nodemetainfo.cpp +++ b/src/shared/qml/metatype/nodemetainfo.cpp @@ -80,7 +80,7 @@ public: } // namespace Internal /*! -\class QKineticDesigner::NodeMetaInfo +\class QmlDesigner::NodeMetaInfo \ingroup CoreModel \brief The NodeMetaInfo class provides meta information about a qml type. @@ -93,7 +93,7 @@ if the enclosing type is deregistered from the meta type system (e.g. a sub component qml file is deleted). Trying to call any accessor methods on an invalid NodeMetaInfo object will result in an InvalidMetaInfoException being thrown. -\see QKineticDesigner::MetaInfo, QKineticDesigner::PropertyMetaInfo, QKineticDesigner::EnumeratorMetaInfo +\see QmlDesigner::MetaInfo, QmlDesigner::PropertyMetaInfo, QmlDesigner::EnumeratorMetaInfo */ NodeMetaInfo::NodeMetaInfo() diff --git a/src/shared/qml/metatype/propertymetainfo.cpp b/src/shared/qml/metatype/propertymetainfo.cpp index 1e2b7276da9..6596a9cfa49 100644 --- a/src/shared/qml/metatype/propertymetainfo.cpp +++ b/src/shared/qml/metatype/propertymetainfo.cpp @@ -72,7 +72,7 @@ public: } // namespace Internal /*! -\class QKineticDesigner::PropertyMetaInfo +\class QmlDesigner::PropertyMetaInfo \ingroup CoreModel \brief The PropertyMetaInfo class provides meta information about a qml type property. @@ -86,7 +86,7 @@ a sub component qml file is deleted). Trying to call any accessor methods on an PropertyMetaInfo object will result in an InvalidMetaInfoException being thrown. -\see QKineticDesigner::MetaInfo, QKineticDesigner::NodeMetaInfo, QKineticDesigner::EnumeratorMetaInfo +\see QmlDesigner::MetaInfo, QmlDesigner::NodeMetaInfo, QmlDesigner::EnumeratorMetaInfo */ PropertyMetaInfo::PropertyMetaInfo() diff --git a/src/shared/qml/qml.pri b/src/shared/qml/qml.pri index fb6165a5bb6..9e9bbc0a77a 100644 --- a/src/shared/qml/qml.pri +++ b/src/shared/qml/qml.pri @@ -1,6 +1,6 @@ include(parser/parser.pri) -DEPENDPATH += $$PWD +DEPENDPATH += $$PWD $$PWD/metatype INCLUDEPATH *= $$PWD/.. HEADERS += \ diff --git a/src/shared/qml/qmlsymbol.cpp b/src/shared/qml/qmlsymbol.cpp index 677a5ba3371..4a8da962e45 100644 --- a/src/shared/qml/qmlsymbol.cpp +++ b/src/shared/qml/qmlsymbol.cpp @@ -142,16 +142,16 @@ const QString QmlSymbolFromFile::name() const const QmlSymbol::List QmlSymbolFromFile::members() { if (!todo.isEmpty()) { - foreach (Node *node, todo) { - if (UiObjectBinding *objectBinding = cast<UiObjectBinding*>(node)) + foreach (Node *todoNode, todo) { + if (UiObjectBinding *objectBinding = cast<UiObjectBinding*>(todoNode)) _members.append(new QmlSymbolFromFile(fileName(), objectBinding)); - else if (UiObjectDefinition *objectDefinition = cast<UiObjectDefinition*>(node)) + else if (UiObjectDefinition *objectDefinition = cast<UiObjectDefinition*>(todoNode)) _members.append(new QmlSymbolFromFile(fileName(), objectDefinition)); - else if (UiArrayBinding *arrayBinding = cast<UiArrayBinding*>(node)) + else if (UiArrayBinding *arrayBinding = cast<UiArrayBinding*>(todoNode)) _members.append(new QmlSymbolFromFile(fileName(), arrayBinding)); - else if (UiPublicMember *publicMember = cast<UiPublicMember*>(node)) + else if (UiPublicMember *publicMember = cast<UiPublicMember*>(todoNode)) _members.append(new QmlPropertyDefinitionSymbol(fileName(), publicMember)); - else if (UiScriptBinding *scriptBinding = cast<UiScriptBinding*>(node)) { + else if (UiScriptBinding *scriptBinding = cast<UiScriptBinding*>(todoNode)) { if (scriptBinding->qualifiedId && scriptBinding->qualifiedId->name && scriptBinding->qualifiedId->name->asString() == QLatin1String("id") && !scriptBinding->qualifiedId->next) _members.append(new QmlIdSymbol(fileName(), scriptBinding, this)); else @@ -165,6 +165,9 @@ const QmlSymbol::List QmlSymbolFromFile::members() return _members; } +bool QmlSymbolFromFile::isProperty() const +{ return cast<UiObjectDefinition*>(_node) == 0; } + QmlSymbolFromFile *QmlSymbolFromFile::findMember(QmlJS::AST::Node *memberNode) { List symbols = members(); diff --git a/src/shared/qml/qmlsymbol.h b/src/shared/qml/qmlsymbol.h index b5bf6f424e8..306cd58a898 100644 --- a/src/shared/qml/qmlsymbol.h +++ b/src/shared/qml/qmlsymbol.h @@ -48,6 +48,7 @@ public: virtual const QString name() const = 0; virtual const List members() = 0; + virtual bool isProperty() const = 0; bool isBuildInSymbol(); bool isSymbolFromFile(); @@ -101,6 +102,7 @@ public: virtual const QString name() const; virtual const List members(); + virtual bool isProperty() const; virtual QmlSymbolFromFile *findMember(QmlJS::AST::Node *memberNode); private: diff --git a/src/tools/qmldesigner/qmldom/main.cpp b/src/tools/qmldesigner/qmldom/main.cpp new file mode 100644 index 00000000000..d0faf4ac26f --- /dev/null +++ b/src/tools/qmldesigner/qmldom/main.cpp @@ -0,0 +1,173 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include <QtCore/QDebug> +#include <QtCore/QtCore> +#include <QtCore/QCoreApplication> + +#include <QtDeclarative/QmlDomDocument> +#include <QtDeclarative/QmlEngine> + +#define SPACING (QString(" ")) + +const char* getDefault(const QmlDomProperty& prop) +{ + if (prop.isDefaultProperty()) + return " (default property)"; + else + return ""; +} + +void dumpList(const QmlDomList& list, const QString& indent); +void dumpTree(const QmlDomObject& domObject, const QString& indent); + +void dumpProperty(const QmlDomProperty& prop, const QString& indent) +{ + const QmlDomValue val(prop.value()); + + switch (val.type()) { + case QmlDomValue::Invalid: + qDebug() << indent.toAscii().data() << prop.propertyName() << "(invalid)" << getDefault(prop); + break; + + case QmlDomValue::List: + qDebug() << indent.toAscii().data() << prop.propertyName() << "(list)" << getDefault(prop); + dumpList(val.toList(), indent + SPACING); + break; + + case QmlDomValue::Literal: + qDebug() << indent.toAscii().data() << prop.propertyName() << "->" << val.toLiteral().literal() << "(literal)" << getDefault(prop); + break; + + case QmlDomValue::Object: + qDebug() << indent.toAscii().data() << prop.propertyName() << "->" << val.toObject().objectId() << "(object)" << getDefault(prop); + dumpTree(val.toObject(), indent + SPACING); + break; + + case QmlDomValue::PropertyBinding: + qDebug() << indent.toAscii().data() << prop.propertyName() << "->" << val.toBinding().binding() << "(property binding)" << getDefault(prop); + break; + + case QmlDomValue::ValueSource: + qDebug() << indent.toAscii().data() << prop.propertyName() << "->" << val.toValueSource().object().objectId() << "(value source)" << getDefault(prop); + break; + + default: + qDebug() << indent.toAscii().data() << prop.propertyName() << "(unknown)" << getDefault(prop); + break; + } +} + +void dumpList(const QmlDomList& list, const QString& indent) +{ + foreach (const QmlDomValue& val, list.values()) { + switch (val.type()) { + case QmlDomValue::Invalid: + qDebug() << indent.toAscii().data() << "(invalid)"; + break; + + case QmlDomValue::List: + qDebug() << indent.toAscii().data() << "(list)"; + dumpList(val.toList(), indent + SPACING); + break; + + case QmlDomValue::Literal: + qDebug() << indent.toAscii().data() << val.toLiteral().literal() << "(literal)"; + break; + + case QmlDomValue::Object: + qDebug() << indent.toAscii().data() << val.toObject().objectId() << "(object)"; + dumpTree(val.toObject(), indent + SPACING); + break; + + case QmlDomValue::PropertyBinding: + qDebug() << indent.toAscii().data() << val.toBinding().binding() << "(property binding)"; + break; + + case QmlDomValue::ValueSource: + qDebug() << indent.toAscii().data() << val.toValueSource().object().objectId() << "(value source)"; + break; + + default: + qDebug() << indent.toAscii().data() << "(unknown)"; + break; + } + } +} + +void dumpTree(const QmlDomObject& domObject, const QString& indent) +{ + qDebug() << (indent + "Object ID:").toAscii().data() << domObject.objectId() <<"Type: "<<domObject.objectType()<< "Class: " << domObject.objectClassName() << "Url: " << domObject.url().toString(); + + if (domObject.isComponent()) { + QString indent2 = indent + SPACING; + qDebug() << (indent2 + "isComponent").toAscii().data(); + QmlDomComponent component = domObject.toComponent(); + dumpTree(component.componentRoot(), indent2); + } else { + foreach (const QmlDomProperty& prop, domObject.properties()) { + QString indent2 = indent + SPACING; + dumpProperty(prop, indent2); + } + } +} + +int main(int argc, char *argv[]) +{ + QCoreApplication a(argc, argv); + + QByteArray qml; + + QStringList args = a.arguments(); + args.removeFirst(); + if (args.size() != 1) { + qDebug() << "add file name"; + return -2; + } + + QFile f(args.at(0)); + if (!f.open(QFile::ReadOnly)) { qDebug() << "cannot open file" << args.at(0); return -3;} + qml = f.readAll(); + + QmlEngine engine; + QmlDomDocument doc; + if (!doc.load(&engine, qml, QUrl::fromLocalFile(QFileInfo(f.fileName()).absoluteFilePath()))) { + foreach (const QmlError &error, doc.errors()) + qDebug() << QString("Error in %1, %2:%3: %4").arg(error.url().toString()).arg(error.line()).arg(error.column()).arg(error.description()); + return -1; + } + + qDebug() << "---------------------------"; + + foreach (const QmlDomImport &import, doc.imports()) + qDebug() << "Import type " << (import.type() == QmlDomImport::Library ? "Library" : "File") + << "uri" << import.uri() << "qualifier" << import.qualifier() << "version" << import.version(); + dumpTree(doc.rootObject(), ""); + return 0; +} diff --git a/src/tools/qmldesigner/qmldom/qmldom.pro b/src/tools/qmldesigner/qmldom/qmldom.pro new file mode 100644 index 00000000000..47b5aa44f26 --- /dev/null +++ b/src/tools/qmldesigner/qmldom/qmldom.pro @@ -0,0 +1,21 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2009-04-09T15:54:21 +# +#------------------------------------------------- + +!contains(QT_CONFIG, declarative) { + error("Qt is not configured with the declarative model."); +} + +QT -= gui +QT += declarative + +TARGET = qmldom +CONFIG += console +CONFIG -= app_bundle + +TEMPLATE = app + + +SOURCES += main.cpp diff --git a/src/tools/qmldesigner/standalone/128xBauhaus_Logo.png b/src/tools/qmldesigner/standalone/128xBauhaus_Logo.png new file mode 100644 index 0000000000000000000000000000000000000000..dc680dae3e0d875ee9e240455e689cb87d6519c8 GIT binary patch literal 15545 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 Hu0mjf0J&tR literal 0 HcmV?d00001 diff --git a/src/tools/qmldesigner/standalone/16xBauhaus_Logo.png b/src/tools/qmldesigner/standalone/16xBauhaus_Logo.png new file mode 100644 index 0000000000000000000000000000000000000000..851973b30ace531ad5f31a41d05ceb1b9d61c929 GIT binary patch literal 537 zcmV+!0_OdRP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzyGcYrRCwByQ(a42K@dGNYpyrO#vpwV z@q>c3P>QJdl84g2;cI_Oe@y>`_#{#gD)_F@3Q47;rIN(N>)q+u`%x1UTRX7qxih=x z&YYPQGefYd$}h)_&;)P^&>DDK!#`}M1Zs%hI8<V?r~;pc_pv7t1gmhx{C$zslT1q> zya*4Zmhb;fP?8ugCn+%y1z%02LwznQBBw8%i~dRwZh2-Pn!zl#T#px<Rjf4Uu-!I) zwdvgYqXc&$bsN{9@w|>I*%m17+h=IB;m?@K+*E>4|6M^)k|e_cPKK<91U0UAb=1F@ zKbb~g<%iG9$+nAD_i}e0-J@O}tFcv)U~z(gwGYAsl};vb8}N0SuC@B|!E%Wp1S;9U zjT6v67<DM_53!s$D@*bK@$JMQG2d?4iD2wkV{1k~dcn^HXYa4lF`c>-1dRV6ZA!(R zBgqbZr>X6S+4x5GW}$mOflmzI|8(9)dR7%_?<tmI&EG?~tl?$3q7>r89=`Uytxs*^ zs_N^{9-SMXX_B>43iXa};17DZScbkB8@`ai=-N<?*r_v?(w-Mw*94te!TF}@tA}U9 b`Ypf!$@a058Z=?c00000NkvXXu0mjfx~KcR literal 0 HcmV?d00001 diff --git a/src/tools/qmldesigner/standalone/256xBauhaus_Logo.png b/src/tools/qmldesigner/standalone/256xBauhaus_Logo.png new file mode 100644 index 0000000000000000000000000000000000000000..9ead12ddf2b08b4406b080bda6751b0799531ceb GIT binary patch literal 54353 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 literal 0 HcmV?d00001 diff --git a/src/tools/qmldesigner/standalone/32xBauhaus_Logo.png b/src/tools/qmldesigner/standalone/32xBauhaus_Logo.png new file mode 100644 index 0000000000000000000000000000000000000000..81aabba9d04036da2b2b1781486c4471c5fb4f4e GIT binary patch literal 36752 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 literal 0 HcmV?d00001 diff --git a/src/tools/qmldesigner/standalone/512xBauhaus_Logo.png b/src/tools/qmldesigner/standalone/512xBauhaus_Logo.png new file mode 100644 index 0000000000000000000000000000000000000000..d0229f9c89067fc42454c50e515f44018cd068b9 GIT binary patch literal 154832 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 B>w^FQ literal 0 HcmV?d00001 diff --git a/src/tools/qmldesigner/standalone/64xBauhaus_Logo.png b/src/tools/qmldesigner/standalone/64xBauhaus_Logo.png new file mode 100644 index 0000000000000000000000000000000000000000..098b823c945e10ecbbfb68cca4fc9af8e302f4ab GIT binary patch literal 4512 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 literal 0 HcmV?d00001 diff --git a/src/tools/qmldesigner/standalone/Info.plist b/src/tools/qmldesigner/standalone/Info.plist new file mode 100644 index 00000000000..ef1502c73c3 --- /dev/null +++ b/src/tools/qmldesigner/standalone/Info.plist @@ -0,0 +1,189 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDocumentTypes</key> + <array> + <dict> + <key>CFBundleTypeRole</key> + <string>Editor</string> + <key>CFBundleTypeIconFile</key> + <string>qtcreator.icns</string> + <key>CFBundleTypeExtensions</key> + <array> + <string>pro</string> + </array> + <key>CFBundleTypeName</key> + <string>Qt Project File</string> + <key>LSHandlerRank</key> + <string>Default</string> + </dict> + <dict> + <key>CFBundleTypeRole</key> + <string>Editor</string> + <key>CFBundleTypeExtensions</key> + <array> + <string>pri</string> + </array> + <key>CFBundleTypeName</key> + <string>Qt Project Include File</string> + <key>LSHandlerRank</key> + <string>Default</string> + </dict> + <dict> + <key>CFBundleTypeRole</key> + <string>Editor</string> + <key>CFBundleTypeExtensions</key> + <array> + <string>qrc</string> + </array> + <key>CFBundleTypeName</key> + <string>Qt Resource File</string> + <key>LSHandlerRank</key> + <string>Default</string> + </dict> + <dict> + <key>CFBundleTypeRole</key> + <string>Editor</string> + <key>CFBundleTypeExtensions</key> + <array> + <string>pri</string> + </array> + <key>CFBundleTypeName</key> + <string>Qt UI File</string> + </dict> + <dict> + <key>CFBundleTypeExtensions</key> + <array> + <string>h</string> + <string>hpp</string> + </array> + <key>CFBundleTypeName</key> + <string>Header File</string> + <key>CFBundleTypeOSTypes</key> + <array> + <string>TEXT</string> + <string>utxt</string> + </array> + <key>CFBundleTypeRole</key> + <string>Editor</string> + </dict> + <dict> + <key>CFBundleTypeExtensions</key> + <array> + <string>cc</string> + <string>CC</string> + <string>cp</string> + <string>CP</string> + <string>cpp</string> + <string>CPP</string> + <string>cxx</string> + <string>CXX</string> + <string>c++</string> + <string>C++</string> + </array> + <key>CFBundleTypeName</key> + <string>C++ Source File</string> + <key>CFBundleTypeOSTypes</key> + <array> + <string>TEXT</string> + <string>utxt</string> + </array> + <key>CFBundleTypeRole</key> + <string>Editor</string> + </dict> + <dict> + <key>CFBundleTypeExtensions</key> + <array> + <string>mm</string> + <string>MM</string> + </array> + <key>CFBundleTypeName</key> + <string>Objective-C++ Source File</string> + <key>CFBundleTypeOSTypes</key> + <array> + <string>TEXT</string> + <string>utxt</string> + </array> + <key>CFBundleTypeRole</key> + <string>Editor</string> + </dict> + <dict> + <key>CFBundleTypeExtensions</key> + <array> + <string>m</string> + </array> + <key>CFBundleTypeName</key> + <string>Objective-C Source File</string> + <key>CFBundleTypeOSTypes</key> + <array> + <string>TEXT</string> + <string>utxt</string> + </array> + <key>CFBundleTypeRole</key> + <string>Editor</string> + </dict> + <dict> + <key>CFBundleTypeExtensions</key> + <array> + <string>c</string> + <string>C</string> + </array> + <key>CFBundleTypeName</key> + <string>C Source File</string> + <key>CFBundleTypeOSTypes</key> + <array> + <string>TEXT</string> + <string>utxt</string> + </array> + <key>CFBundleTypeRole</key> + <string>Editor</string> + </dict> + <dict> + <key>CFBundleTypeExtensions</key> + <array> + <string>txt</string> + <string>text</string> + </array> + <key>CFBundleTypeName</key> + <string>Text File</string> + <key>CFBundleTypeOSTypes</key> + <array> + <string>TEXT</string> + </array> + <key>CFBundleTypeRole</key> + <string>Editor</string> + </dict> + <dict> + <key>CFBundleTypeExtensions</key> + <array> + <string>*</string> + </array> + <key>CFBundleTypeName</key> + <string>NSStringPboardType</string> + <key>CFBundleTypeOSTypes</key> + <array> + <string>****</string> + </array> + <key>CFBundleTypeRole</key> + <string>Editor</string> + </dict> + </array> + <key>CFBundleGetInfoString</key> + <string>Bauhaus; Copyright Nokia Corporation</string> + <key>CFBundleIconFile</key> + <string>@ICON@</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleSignature</key> + <string>@TYPEINFO@</string> + <key>CFBundleExecutable</key> + <string>@EXECUTABLE@</string> + <key>CFBundleIdentifier</key> + <string>com.nokia.bauhaus</string> + <key>CFBundleVersion</key> + <string>1.0.0</string> + <key>CFBundleShortVersionString</key> + <string>1.0.0</string> +</dict> +</plist> diff --git a/src/tools/qmldesigner/standalone/aboutdialog.cpp b/src/tools/qmldesigner/standalone/aboutdialog.cpp new file mode 100644 index 00000000000..db2b685381d --- /dev/null +++ b/src/tools/qmldesigner/standalone/aboutdialog.cpp @@ -0,0 +1,95 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "aboutdialog.h" +#include "integrationcore.h" +#include "pluginmanager.h" + +#include <QtCore/QDebug> +#include <QtCore/QUrl> + +#include <QtGui/QApplication> +#include <QtGui/QGridLayout> +#include <QtGui/QTextEdit> +#include <QtGui/QPushButton> + + +static QString aboutText = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">" + "<html><head><style type=\"text/css\">\np, li { white-space: pre-wrap; }\n</style></head><body style=\"font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;\">" + "<p align=\"center\"><img src=\"logo\"/></p>" + "<h2 align=\"center\">Bauhaus</h2>" + "</body></html>"; + +AboutDialog::AboutDialog(QWidget* parent): + QDialog(parent) +{ + setWindowFlags(windowFlags() | Qt::CustomizeWindowHint | Qt::WindowCloseButtonHint); + setStyleSheet(QString("background-color: #FFFFFF;")); + + QGridLayout* dialogLayout = new QGridLayout; + setLayout(dialogLayout); + + QTextEdit* textArea = new QTextEdit(this); + textArea->setReadOnly(true); + textArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + textArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + textArea->setFrameShape(QFrame::NoFrame); + textArea->setLineWidth(0); + QImage logoImage = QImage(QString(":/128xBauhaus_Logo.png")); + textArea->document()->addResource(QTextDocument::ImageResource, QUrl("logo"), logoImage); + textArea->setHtml(aboutText); + dialogLayout->addWidget(textArea, 0, 0, 1, 3); + + QPushButton* aboutPluginsButton = new QPushButton("About Plug-ins...", this); + dialogLayout->addWidget(aboutPluginsButton, 1, 0, 1, 1); + connect(aboutPluginsButton, SIGNAL(clicked()), this, SLOT(doAboutPlugins())); + + QPushButton* aboutQtButton = new QPushButton("About Qt...", this); + dialogLayout->addWidget(aboutQtButton, 1, 1, 1, 1); + connect(aboutQtButton, SIGNAL(clicked()), qApp, SLOT(aboutQt())); + + QPushButton* closeButton = new QPushButton("Close", this); + dialogLayout->addWidget(closeButton, 1, 2, 1, 1); + connect(closeButton, SIGNAL(clicked()), this, SLOT(close())); +} + +void AboutDialog::go(QWidget* parent) +{ + AboutDialog dialog(parent); + dialog.setWindowTitle(tr("About Bauhaus", "AboutDialog")); + dialog.exec(); +} + +void AboutDialog::doAboutPlugins() +{ + QmlDesigner::IntegrationCore *core = QmlDesigner::IntegrationCore::instance(); + QDialog* dialog = core->pluginManager()->createAboutPluginDialog(this); + dialog->setWindowFlags(Qt::Sheet); + dialog->exec(); +} diff --git a/src/tools/qmldesigner/standalone/aboutdialog.h b/src/tools/qmldesigner/standalone/aboutdialog.h new file mode 100644 index 00000000000..5feb08902e7 --- /dev/null +++ b/src/tools/qmldesigner/standalone/aboutdialog.h @@ -0,0 +1,53 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef AboutDialog_h +#define AboutDialog_h + +#include <QtGui/QDialog> + + +/*! + This dialog could do with some animations and general beautification. + */ +class AboutDialog: public QDialog +{ + Q_OBJECT + +public: + static void go(QWidget* parent = 0); + +public slots: + void doAboutPlugins(); + +private: + AboutDialog(QWidget* parent = 0); +}; + +#endif // AboutDialog_h diff --git a/src/tools/qmldesigner/standalone/application.cpp b/src/tools/qmldesigner/standalone/application.cpp new file mode 100644 index 00000000000..7c253ef17f0 --- /dev/null +++ b/src/tools/qmldesigner/standalone/application.cpp @@ -0,0 +1,43 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "application.h" +#include <QtCore/QCoreApplication> +#include <QtCore/QFileInfo> + +QString Application::sharedDirPath() +{ + QString path = QCoreApplication::applicationDirPath(); + +#ifdef Q_OS_MACX + path += "/../../.."; +#endif // Q_OS_MACX + + return QFileInfo(path + "/../../../../share/qtcreator/qmldesigner/").absoluteFilePath(); +} diff --git a/src/tools/qmldesigner/standalone/application.h b/src/tools/qmldesigner/standalone/application.h new file mode 100644 index 00000000000..adf7f81001d --- /dev/null +++ b/src/tools/qmldesigner/standalone/application.h @@ -0,0 +1,40 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef APPLICATION_H +#define APPLICATION_H + +#include <QtCore/QString> + +class Application { +public: + static QString sharedDirPath(); +}; + +#endif // APPLICATION_H diff --git a/src/tools/qmldesigner/standalone/bauhaus-logo.icns b/src/tools/qmldesigner/standalone/bauhaus-logo.icns new file mode 100644 index 0000000000000000000000000000000000000000..ab9758015a4386e9c0fc88000dfc645eca0d74fc GIT binary patch literal 267319 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 WHKFzbPyg9zV{TRe000a^@BjdW6NU8v literal 0 HcmV?d00001 diff --git a/src/tools/qmldesigner/standalone/bauhaus-macos.css b/src/tools/qmldesigner/standalone/bauhaus-macos.css new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/tools/qmldesigner/standalone/bauhaus-windows.css b/src/tools/qmldesigner/standalone/bauhaus-windows.css new file mode 100644 index 00000000000..6c5bf7d2c64 --- /dev/null +++ b/src/tools/qmldesigner/standalone/bauhaus-windows.css @@ -0,0 +1,3 @@ +QStatusBar { + border-top: 1px solid #b8b9b8; +} diff --git a/src/tools/qmldesigner/standalone/bauhaus-x11.css b/src/tools/qmldesigner/standalone/bauhaus-x11.css new file mode 100644 index 00000000000..6c5bf7d2c64 --- /dev/null +++ b/src/tools/qmldesigner/standalone/bauhaus-x11.css @@ -0,0 +1,3 @@ +QStatusBar { + border-top: 1px solid #b8b9b8; +} diff --git a/src/tools/qmldesigner/standalone/bauhaus.css b/src/tools/qmldesigner/standalone/bauhaus.css new file mode 100644 index 00000000000..32b97e36516 --- /dev/null +++ b/src/tools/qmldesigner/standalone/bauhaus.css @@ -0,0 +1,28 @@ +QTreeView#widgetTreeView { + background: #bababa; + border: 0px; + font-size: 11px; + selection-color: #fff; + selection-background-color: #3875d7; + font-variant: normal; +} + +//QTreeView::item { +// padding: 3px; +//} +// +//QTreeView::item:selected { +// border: 3px solid #ff0000; +// padding: 3px; +// background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #3c77ad, stop: 1 #003882); +//} + +//QTreeView#widgetTreeView::item { +// inactive, in favour of the 2 items above (for testing, as the QTreeView seems to contain a bug which prevents border/padding to work correctly +// border: 1px solid #ff0000; +//} + +QStackedWidget#widgetTreeStack { + background: #bababa; + border: 0px; +} diff --git a/src/tools/qmldesigner/standalone/bauhaus.ico b/src/tools/qmldesigner/standalone/bauhaus.ico new file mode 100644 index 0000000000000000000000000000000000000000..d6e91cc255ca47fe916fabce607f017a288f983c GIT binary patch literal 22382 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 literal 0 HcmV?d00001 diff --git a/src/tools/qmldesigner/standalone/bauhaus.qrc b/src/tools/qmldesigner/standalone/bauhaus.qrc new file mode 100644 index 00000000000..223d7115a1f --- /dev/null +++ b/src/tools/qmldesigner/standalone/bauhaus.qrc @@ -0,0 +1,19 @@ +<RCC> + <qresource prefix="/"> + <file>toolbar-cssedit.png</file> + <file>toolbar-edit.png</file> + <file>bauhaus-windows.css</file> + <file>bauhaus-macos.css</file> + <file>bauhaus-x11.css</file> + <file>128xBauhaus_Logo.png</file> + <file>bauhaus.css</file> + <file alias="qmldesigner/templates/General/Empty QWidget">templates/qwidget.qml</file> + <file alias="qmldesigner/templates/General/Empty QGraphicsView">templates/qgraphicsview.qml</file> + <file>64xBauhaus_Logo.png</file> + <file>preview.png</file> + <file alias="qmldesigner/templates/General/Empty Fx">templates/fx.qml</file> + <file>16xBauhaus_Logo.png</file> + <file>256xBauhaus_Logo.png</file> + <file>32xBauhaus_Logo.png</file> + </qresource> +</RCC> diff --git a/src/tools/qmldesigner/standalone/css.png b/src/tools/qmldesigner/standalone/css.png new file mode 100644 index 0000000000000000000000000000000000000000..3a2c2555c418bf8fe2534bcb482f9b01a05a36dd GIT binary patch literal 4170 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 Uzvm54^Z)<=07*qoM6N<$f^nVp>;M1& literal 0 HcmV?d00001 diff --git a/src/tools/qmldesigner/standalone/edit.png b/src/tools/qmldesigner/standalone/edit.png new file mode 100644 index 0000000000000000000000000000000000000000..2a55a4576d07188592620c5752660b6bc748fa09 GIT binary patch literal 3573 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 literal 0 HcmV?d00001 diff --git a/src/tools/qmldesigner/standalone/main.cpp b/src/tools/qmldesigner/standalone/main.cpp new file mode 100644 index 00000000000..21eb4f8e815 --- /dev/null +++ b/src/tools/qmldesigner/standalone/main.cpp @@ -0,0 +1,162 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include <QtDebug> +#include <QtCore/QFile> +#include <QtCore/QTextStream> + +#include <QtGui/QApplication> + +#include <integrationcore.h> +#include <pluginmanager.h> +#include <model.h> + +#include <exception.h> +#include "mainwindow.h" + +enum { + debug = false +}; + +static void doStyling(QApplication& app) +{ + QString ws; +#ifdef Q_WS_MAC + ws = "macos"; +#endif // Q_WS_MAC + +#ifdef Q_WS_WIN + ws = "windows"; +#endif // Q_WS_WIN + +#ifdef Q_WS_X11 + ws = "x11"; +#endif // Q_WS_X11 + + QFile platformCssFile(QString(":/bauhaus-%1.css").arg(ws)); + platformCssFile.open(QIODevice::ReadOnly | QIODevice::Text); + QTextStream platformCssStream(&platformCssFile); + QString styleSheet(platformCssStream.readAll()); + platformCssFile.close(); + + QFile genericCssFile(QLatin1String(":/bauhaus.css")); + genericCssFile.open(QIODevice::ReadOnly | QIODevice::Text); + QTextStream genericCssStream(&genericCssFile); + styleSheet.append(genericCssStream.readAll()); + genericCssFile.close(); + + app.setStyleSheet(styleSheet); +} + +static void parseArguments(const QStringList& argumentList, MainWindow& mainWindow) +{ + QStringList passArgumentList; + passArgumentList.append("--no-resync"); + passArgumentList.append("-h"); + passArgumentList.append("-graphicssystem"); + + for (int i = 1; i < argumentList.size(); ++i) { + if (argumentList[i].at(0) == QLatin1Char('-')) { + if (argumentList[i] == "--file" || argumentList[i] == "-file" || argumentList[i] == "-f") { + ++i; + if (i < argumentList.size()) { + mainWindow.openFile(argumentList[i]); + } else { + mainWindow.doOpen(); + } + } else if (argumentList[i] == "--help" || argumentList[i] == "-h") { + qWarning() << "Usage: bauhaus [OPTION...]\n"; + qWarning() << " -f, --file open this file"; + qWarning() << " --no-resync disable rewriter"; + exit(0); + } else if (passArgumentList.contains(argumentList[i].split("=").first())) { + } else { + qWarning() << "bauhaus: unrecognized option "<< argumentList[i]; + qWarning() << "Try `bauhaus --help'"; + exit(1); + } + } else { + mainWindow.openFile(argumentList[i]); + } + } +} + +static QStringList pluginPaths() +{ + QStringList result; +#ifdef Q_OS_MAC + result += QCoreApplication::applicationDirPath() + "/../PlugIns/Bauhaus/ItemLibs"; +#else // Q_OS_MAC + result += QCoreApplication::applicationDirPath() + "/../lib/itemlibs"; +#endif // Q_OS_MAC + + return result; +} + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + Q_INIT_RESOURCE(bauhaus); + + doStyling(app); + +#ifdef Q_WS_X11 + QIcon applicationIcon; + applicationIcon.addFile(":/16xBauhaus_Log"); + applicationIcon.addFile(":/32xBauhaus_Logo.png"); + applicationIcon.addFile(":/64xBauhaus_Logo.png"); + applicationIcon.addFile(":/128xBauhaus_Logo.png"); + applicationIcon.addFile(":/256xBauhaus_Logo.png"); + Q_ASSERT(!applicationIcon.isNull()); + app.setWindowIcon(applicationIcon); +#endif + + QCoreApplication::setOrganizationName("Nokia"); + QCoreApplication::setOrganizationDomain("nokia.com"); + QCoreApplication::setApplicationName("Bauhaus"); + + try { + QmlDesigner::IntegrationCore core; + core.pluginManager()->setPluginPaths(pluginPaths()); + + MainWindow mainWindow; + mainWindow.show(); + + parseArguments(app.arguments(), mainWindow); + +// if (mainWindow.documentCount() == 0) +// mainWindow.showWelcomeScreen(); +// + return app.exec(); + + } catch (const QmlDesigner::Exception &exception) { + qWarning() << exception; + return -1; + } +} diff --git a/src/tools/qmldesigner/standalone/mainwindow.cpp b/src/tools/qmldesigner/standalone/mainwindow.cpp new file mode 100644 index 00000000000..77fe96551dd --- /dev/null +++ b/src/tools/qmldesigner/standalone/mainwindow.cpp @@ -0,0 +1,565 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include <QtCore/QtDebug> +#include <QtCore/QBuffer> +#include <QtCore/QFileInfo> +#include <QtCore/QSettings> + +#include <QtGui/QAction> +#include <QtGui/QApplication> +#include <QtGui/QBoxLayout> +#include <QtGui/QCloseEvent> +#include <QtGui/QErrorMessage> +#include <QtGui/QFrame> +#include <QtGui/QMessageBox> +#include <QtGui/QSplitter> +#include <QtGui/QStackedWidget> +#include <QtGui/QStatusBar> +#include <QFile> + +#include "aboutdialog.h" +#include "allpropertiesviewcontroller.h" +#include "designdocumentcontroller.h" +#include "multipledocumentscontroller.h" +#include "mainwindow.h" +#include "stylemanager.h" +#include "widgetboxcontroller.h" +#include "navigatorcontroller.h" +#include "stateseditorcontroller.h" +#include "xuifiledialog.h" +#include "welcomescreen.h" + +using namespace QmlDesigner; + +class StyleAction: public QAction +{ +public: + StyleAction(const QString& text, const QString& styleName, QObject* parent): QAction(text, parent), m_styleName(styleName) {} + StyleAction(const QString& text, QObject* parent): QAction(text, parent), m_styleName(QString::null) {} + + QString styleName() const { return m_styleName; } + +private: + QString m_styleName; +}; + +MainWindow::MainWindow(QWidget* parent): + QMainWindow(parent), + m_shutdown(false), + m_lastPath(QString::null), + m_documentCount(0), + m_menuBar(new QMenuBar(this)), + m_itemLibraryController(new ItemLibraryController(this)), + m_navigatorController(new NavigatorController(this)), + m_statesEditorController(new StatesEditorController(this)), + m_propertiesViewController(new AllPropertiesViewController(this)), + m_multipleDocumentsController(new MultipleDocumentsController(this)), + m_recentFilesMenu(0), + m_mainArea(new QStackedWidget(this)), + m_welcomeScreen(new WelcomeScreen(this)) +{ + // setWindowFlags(Qt::MacWindowToolBarButtonHint); +// setUnifiedTitleAndToolBarOnMac(true); + setObjectName(QLatin1String("mainWindow")); + setWindowTitle(tr("Bauhaus", "MainWindowClass")); + resize(1046, 700); + + QFile file(":/qmldesigner/stylesheet.css"); + file.open(QFile::ReadOnly); + QString styleSheet = QLatin1String(file.readAll()); + setStyleSheet(styleSheet); + + setMenuBar(m_menuBar); + + createMenus(); + createMainArea(); + createStatusBar(); + + updateActions(); + updateMainArea(); + + connect(m_multipleDocumentsController, SIGNAL(documentCountChanged(unsigned)), this, SLOT(documentCountChanged(unsigned))); + connect(m_welcomeScreen, SIGNAL(newFile(QString)), this, SLOT(newFile(QString))); + connect(m_welcomeScreen, SIGNAL(openFile(QString)), this, SLOT(openFile(QString))); +} + +MainWindow::~MainWindow() +{ + m_documentActions.clear(); +} + +int MainWindow::documentCount() const +{ + return m_documentCount; +} + +void MainWindow::closeEvent(QCloseEvent *event) +{ + m_shutdown = true; + m_multipleDocumentsController->closeAll(true); + event->setAccepted(false); +} + +void MainWindow::doQuit() +{ + m_shutdown = true; + m_multipleDocumentsController->closeAll(true); +} + +void MainWindow::createMenus() +{ + // File menu: + QMenu* fileMenu = new QMenu(tr("&File"), m_menuBar); + m_menuBar->addMenu(fileMenu); + + QAction* newAction = new QAction(tr("&New..."), fileMenu); + newAction->setShortcut(QKeySequence(tr("Ctrl+N"))); + connect(newAction, SIGNAL(triggered()), this, SLOT(doNew())); + fileMenu->addAction(newAction); + + QAction* openAction = new QAction(tr("&Open..."), fileMenu); + openAction->setShortcut(QKeySequence(tr("Ctrl+O"))); + connect(openAction, SIGNAL(triggered()), this, SLOT(doOpen())); + fileMenu->addAction(openAction); + + m_recentFilesMenu = new QMenu(tr("Recent Files"), fileMenu); + connect(m_recentFilesMenu, SIGNAL(aboutToShow()), this, SLOT(showRecentFilesMenu())); + fileMenu->addMenu(m_recentFilesMenu); + + fileMenu->addSeparator(); + + QAction* saveAction = new QAction(tr("&Save"), fileMenu); + saveAction->setShortcut(QKeySequence(tr("Ctrl+S"))); + connect(saveAction, SIGNAL(triggered()), m_multipleDocumentsController, SLOT(doSave())); + fileMenu->addAction(saveAction); + m_documentActions.append(saveAction); + + QAction* saveAsAction = new QAction(tr("Save &As..."), fileMenu); + connect(saveAsAction, SIGNAL(triggered()), m_multipleDocumentsController, SLOT(doSaveAs())); + fileMenu->addAction(saveAsAction); + m_documentActions.append(saveAsAction); + + fileMenu->addSeparator(); + + m_previewAction = new QAction(tr("&Preview"), fileMenu); + m_previewAction->setShortcut(QKeySequence(tr("Ctrl+R"))); +// m_previewAction->setIcon(QIcon(":/preview.png")); + m_previewAction->setCheckable(true); + connect(m_previewAction, SIGNAL(toggled(bool)), m_multipleDocumentsController, SLOT(doPreview(bool))); + connect(m_multipleDocumentsController, SIGNAL(previewVisibilityChanged(bool)), m_previewAction, SLOT(setChecked(bool))); + fileMenu->addAction(m_previewAction); + m_documentActions.append(m_previewAction); + + m_previewWithDebugAction = new QAction(tr("&Preview with Debug"), fileMenu); + m_previewWithDebugAction->setShortcut(QKeySequence(tr("Ctrl+D"))); + m_previewWithDebugAction->setCheckable(true); + connect(m_previewWithDebugAction, SIGNAL(toggled(bool)), m_multipleDocumentsController, SLOT(doPreviewWithDebug(bool))); + connect(m_multipleDocumentsController, SIGNAL(previewWithDebugVisibilityChanged(bool)), m_previewWithDebugAction, SLOT(setChecked(bool))); + fileMenu->addAction(m_previewWithDebugAction); + m_documentActions.append(m_previewWithDebugAction); + +#ifndef Q_WS_MAC + fileMenu->addSeparator(); +#endif // !Q_WS_MAC + + QAction* quitAction = new QAction(tr("&Quit"), fileMenu); + quitAction->setShortcut(QKeySequence(tr("Ctrl+Q"))); + fileMenu->addAction(quitAction); + connect(quitAction, SIGNAL(triggered()), this, SLOT(doQuit())); + + // Edit menu: + QMenu* editMenu = new QMenu(tr("&Edit"), m_menuBar); + m_menuBar->addMenu(editMenu); + + QAction* undoAction = m_multipleDocumentsController->undoAction(); + undoAction->setParent(editMenu); + undoAction->setShortcut(tr("Ctrl+Z")); + editMenu->addAction(undoAction); + + QAction* redoAction = m_multipleDocumentsController->redoAction(); + redoAction->setParent(editMenu); +#ifdef Q_WS_WIN + redoAction->setShortcut(tr("Ctrl+Y")); +#else // !Q_WS_WIN + redoAction->setShortcut(tr("Ctrl+Shift+Z")); +#endif // Q_WS_WIN + editMenu->addAction(redoAction); + + editMenu->addSeparator(); + + QAction *copyAction = new QAction(tr("&Copy"), editMenu); + connect(copyAction, SIGNAL(triggered()), m_multipleDocumentsController, SLOT(doCopy())); + copyAction->setShortcuts(QList<QKeySequence>() << QKeySequence(QKeySequence::Copy)); + editMenu->addAction(copyAction); + m_documentActions.append(copyAction); + + QAction *cutAction = new QAction(tr("&Cut"), editMenu); + connect(cutAction, SIGNAL(triggered()), m_multipleDocumentsController, SLOT(doCut())); + cutAction->setShortcuts(QList<QKeySequence>() << QKeySequence(QKeySequence::Cut)); + editMenu->addAction(cutAction); + m_documentActions.append(cutAction); + + QAction *pasteAction = new QAction(tr("&Paste"), editMenu); + connect(pasteAction, SIGNAL(triggered()), m_multipleDocumentsController, SLOT(doPaste())); + pasteAction->setShortcuts(QList<QKeySequence>() << QKeySequence(QKeySequence::Paste)); + editMenu->addAction(pasteAction); + m_documentActions.append(pasteAction); + + QAction *deleteAction = new QAction(tr("&Delete"), editMenu); + connect(deleteAction, SIGNAL(triggered()), m_multipleDocumentsController, SLOT(doDelete())); + deleteAction->setShortcuts(QList<QKeySequence>() << QKeySequence(tr("Del")) << QKeySequence(tr("Backspace"))); + editMenu->addAction(deleteAction); + m_documentActions.append(deleteAction); + + // View menu: + QMenu* viewMenu = new QMenu(tr("&View"), m_menuBar); + m_menuBar->addMenu(viewMenu); +// +// m_showNewLookPropertiesAction = viewMenu->addAction(tr("&New Look Properties")); +// m_showNewLookPropertiesAction->setCheckable(true); +// m_showNewLookPropertiesAction->setEnabled(false); +// connect(m_showNewLookPropertiesAction, SIGNAL(triggered()), m_propertiesViewController, SLOT(showNewLookProperties())); +// m_documentActions.append(m_showNewLookPropertiesAction); +// +// m_showTraditionalPropertiesAction = viewMenu->addAction(tr("&Traditional Properties")); +// m_showTraditionalPropertiesAction->setCheckable(true); +// m_showTraditionalPropertiesAction->setEnabled(false); +// connect(m_showTraditionalPropertiesAction, SIGNAL(triggered()), m_propertiesViewController, SLOT(showTraditionalProperties())); +// m_documentActions.append(m_showTraditionalPropertiesAction); +// +// QActionGroup* propertiesLookGroup = new QActionGroup(this); +// propertiesLookGroup->addAction(m_showNewLookPropertiesAction); +// propertiesLookGroup->addAction(m_showTraditionalPropertiesAction); +// m_showNewLookPropertiesAction->setChecked(true); + +// viewMenu->addSeparator(); + +#ifdef ENABLE_TEXT_VIEW + QAction* showFormAction = m_multipleDocumentsController->showFormAction(viewMenu); + viewMenu->addAction(showFormAction); + QAction* showTextAction = m_multipleDocumentsController->showTextAction(viewMenu); + viewMenu->addAction(showTextAction); + + QActionGroup* formTextGroup = new QActionGroup(this); + formTextGroup->addAction(showFormAction); + formTextGroup->addAction(showTextAction); + showFormAction->setChecked(true); + + viewMenu->addSeparator(); +#endif // ENABLE_TEXT_VIEW + + // Style selection: +// QMenu* styleMenu = viewMenu->addMenu("&Style"); +// QActionGroup* styleGroup = new QActionGroup(this); +// +// StyleAction* defaultStyleAction = new StyleAction("&Default", viewMenu); +// styleMenu->addAction(defaultStyleAction); +// defaultStyleAction->setCheckable(true); +// styleGroup->addAction(defaultStyleAction); +// connect(defaultStyleAction, SIGNAL(triggered()), this, SLOT(styleActionTriggered())); +// +// styleMenu->addSeparator(); +// +// foreach (const QString styleName, StyleManager::styles()) { +// StyleAction* styleAction = new StyleAction(styleName, styleName, viewMenu); +// styleMenu->addAction(styleAction); +// styleAction->setCheckable(true); +// styleGroup->addAction(styleAction); +// connect(styleAction, SIGNAL(triggered()), this, SLOT(styleActionTriggered())); +// } +// +// defaultStyleAction->setChecked(true); + + // Help menu: + QMenu* helpMenu = new QMenu(tr("&Help"), this); + helpMenu->setObjectName(QString::fromAscii("helpMenu")); + m_menuBar->addMenu(helpMenu); + + QAction* aboutAppAction = new QAction(tr("&About..."), this); + aboutAppAction->setObjectName(QString::fromAscii("aboutAppAction")); + connect(aboutAppAction, SIGNAL(triggered()), this, SLOT(doAbout())); + helpMenu->addAction(aboutAppAction); +} + +void MainWindow::showRecentFilesMenu() +{ + m_recentFilesMenu->clear(); + foreach (const QString &path, recentFiles()) { + QAction *action = m_recentFilesMenu->addAction(path); + action->setData(path); + connect(action, SIGNAL(triggered()), this, SLOT(doOpen())); + } +} + +void MainWindow::styleActionTriggered() +{ + //### remove setStyle() +// StyleAction* source = dynamic_cast<StyleAction*>(sender()); +// +// if (source) { +// QString styleName = source->styleName(); +// +// if (styleName.isNull()) { +// } else { +// StyleManager::setStyle(styleName); +// } +// } +} + +void MainWindow::createMainArea() +{ + QSplitter* mainSplitter = new QSplitter(this); + mainSplitter->setObjectName("mainSplitter"); + + // Left area: + QSplitter* leftSplitter = new QSplitter(mainSplitter); + leftSplitter->setObjectName("leftSplitter"); + leftSplitter->setOrientation(Qt::Vertical); + { + QSizePolicy policy = leftSplitter->sizePolicy(); + policy.setHorizontalStretch(0); + policy.setHorizontalPolicy(QSizePolicy::Preferred); + leftSplitter->setSizePolicy(policy); + } + + leftSplitter->addWidget(m_navigatorController->widget()); + + QTabWidget *leftBottomTabWidget = new QTabWidget(this); + leftBottomTabWidget->addTab(m_itemLibraryController->widget(), "Library"); + leftBottomTabWidget->addTab(m_propertiesViewController->widget(), tr("Properties")); + leftSplitter->addWidget(leftBottomTabWidget); + + // right area: + QSplitter *rightSplitter = new QSplitter(mainSplitter); + rightSplitter->setObjectName("rightSplitter"); + rightSplitter->setOrientation(Qt::Vertical); + + rightSplitter->addWidget(m_statesEditorController->widget()); + rightSplitter->addWidget(m_multipleDocumentsController->tabWidget()); + { + QSizePolicy policy = m_multipleDocumentsController->tabWidget()->sizePolicy(); + policy.setHorizontalStretch(1); + policy.setHorizontalPolicy(QSizePolicy::Expanding); + m_multipleDocumentsController->tabWidget()->setSizePolicy(policy); + } + + // Finishing touches: + mainSplitter->setSizes(QList<int>() << 240 << 530); + rightSplitter->setSizes(QList<int>() << 100 << 400); + leftSplitter->setSizes(QList<int>() << 300 << 300); + + // Wire everything together: + connect(m_multipleDocumentsController, SIGNAL(activeDocumentChanged(DesignDocumentController*)), + m_itemLibraryController, SLOT(show(DesignDocumentController*))); + connect(m_multipleDocumentsController, SIGNAL(documentClosed(DesignDocumentController*)), + m_itemLibraryController, SLOT(close(DesignDocumentController*))); + + connect(m_multipleDocumentsController, SIGNAL(activeDocumentChanged(DesignDocumentController*)), + m_navigatorController, SLOT(show(DesignDocumentController*))); + connect(m_multipleDocumentsController, SIGNAL(documentClosed(DesignDocumentController*)), + m_navigatorController, SLOT(close(DesignDocumentController*))); + + connect(m_multipleDocumentsController, SIGNAL(activeDocumentChanged(DesignDocumentController*)), + m_statesEditorController, SLOT(show(DesignDocumentController*))); + connect(m_multipleDocumentsController, SIGNAL(documentClosed(DesignDocumentController*)), + m_statesEditorController, SLOT(close(DesignDocumentController*))); + + connect(m_multipleDocumentsController, SIGNAL(activeDocumentChanged(DesignDocumentController*)), + m_propertiesViewController, SLOT(show(DesignDocumentController*))); + connect(m_multipleDocumentsController, SIGNAL(documentClosed(DesignDocumentController*)), + m_propertiesViewController, SLOT(close(DesignDocumentController*))); + + m_mainArea->addWidget(m_welcomeScreen); + m_mainArea->addWidget(mainSplitter); + setCentralWidget(m_mainArea); +} + +void MainWindow::createStatusBar() +{ +// statusBar(); +} + +void MainWindow::doNew() +{ +// QWizard wizard; +// Internal::FormTemplateWizardPage page; +// wizard.addPage(&page); +// wizard.setWindowTitle("New Component Wizard"); +// if (wizard.exec() != QDialog::Accepted) +// return; + + QFile file(":/qmldesigner/templates/General/Empty Fx"); + file.open(QFile::ReadOnly); + newFile(file.readAll()); +// newFile(page.templateContents().toLatin1()); +} + +void MainWindow::doOpen() +{ + if (QAction *senderAction = qobject_cast<QAction*>(sender())) { + if (senderAction->data().isValid()) { + // from recent files menu + QString path = senderAction->data().toString(); + openFile(path); + return; + } + } + XUIFileDialog::runOpenFileDialog(m_lastPath, this, this, SLOT(openFile(QString))); +} + +void MainWindow::openFile(const QString &fileName) +{ +// qDebug() << "openFile("+fileName+")"; +// + if (fileName.isNull()) + return; + + m_lastPath = QFileInfo(fileName).path(); + + QString errorMessage; + + QFile file(fileName); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + errorMessage = tr("Could not open file <%1>").arg(fileName); + } else { + DesignDocumentController* controller = new DesignDocumentController(m_multipleDocumentsController); + controller->setFileName(fileName); + QList<QmlDesigner::RewriterView::Error> errors = controller->loadMaster(file.readAll()); + if (errors.isEmpty()) { + connect(controller, SIGNAL(fileToOpen(QString)), this, SLOT(openFile(QString))); + m_multipleDocumentsController->open(controller); + addRecentFile(QFileInfo(file).absoluteFilePath()); + } else { + errorMessage = serializeErrors(errors); + delete controller; + } + } + + if (!errorMessage.isEmpty()) { + QErrorMessage msgBox(this); + msgBox.showMessage(errorMessage); + msgBox.exec(); + } +} + +void MainWindow::newFile(const QString &templateName) +{ + if (templateName.isNull()) + return; + + QFile file(templateName); + if (!file.open(QFile::ReadOnly)) + return; + + newFile(file.readAll()); +} + +void MainWindow::doAbout() +{ + AboutDialog::go(this); +} + +void MainWindow::documentCountChanged(unsigned newCount) +{ + if (m_documentCount == newCount) + return; + m_documentCount = newCount; + + if (!m_shutdown) { + updateActions(); + updateMainArea(); + } +} + +void MainWindow::updateActions() +{ + bool enable = m_documentCount != 0; + foreach (QAction *documentAction, m_documentActions) + documentAction->setEnabled(enable); +} + +void MainWindow::updateMainArea() +{ + if (m_multipleDocumentsController->activeDocumentCount() == 0) { + m_welcomeScreen->setRecentFiles(recentFiles()); + m_mainArea->setCurrentIndex(0); // welcome screen + } else { + m_mainArea->setCurrentIndex(1); + } +} + +void MainWindow::newFile(const QByteArray &templateContents) +{ + DesignDocumentController *controller = new DesignDocumentController(m_multipleDocumentsController); + QList<QmlDesigner::RewriterView::Error> errors = controller->loadMaster(templateContents); + + if (errors.isEmpty()) { + connect(controller, SIGNAL(fileToOpen(QString)), this, SLOT(openFile(QString))); + m_multipleDocumentsController->open(controller); + } else { + delete controller; + QErrorMessage msgBox(this); + msgBox.showMessage(serializeErrors(errors)); + msgBox.exec(); + } +} + +QStringList MainWindow::recentFiles() const +{ + const QSettings settings; + return settings.value("recentFiles", QStringList()).toStringList(); +} + +void MainWindow::addRecentFile(const QString &path) +{ + QSettings settings; + QStringList files = settings.value("recentFiles", QStringList()).toStringList(); + + files.removeAll(path); + if (files.size() > 10) + files.removeLast(); + files.prepend(path); + + settings.setValue("recentFiles", files); +} + +QString MainWindow::serializeErrors(const QList<QmlDesigner::RewriterView::Error> &errors) +{ + if (errors.isEmpty()) + return QString(); + QString errorMsg = tr("Qml Errors:"); + foreach (const QmlDesigner::RewriterView::Error &error, errors) { + if (!error.url().isEmpty()) + errorMsg.append(tr("\n%1 %2:%3 - %4").arg(error.url().toString(), QString(error.line()), QString(error.column()), error.description())); + else + errorMsg.append(tr("\n%1:%2 - %3").arg(QString(error.line()), QString(error.column()), error.description())); + } + return errorMsg; +} diff --git a/src/tools/qmldesigner/standalone/mainwindow.h b/src/tools/qmldesigner/standalone/mainwindow.h new file mode 100644 index 00000000000..d78a439b308 --- /dev/null +++ b/src/tools/qmldesigner/standalone/mainwindow.h @@ -0,0 +1,120 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include <QtCore/QList> +#include <QtCore/QString> + +#include <QtGui/QMainWindow> +#include <QtGui/QMenuBar> +#include <QtGui/QMenu> +#include <QtGui/QStackedWidget> + +#include "rewriterview.h" + +class QmlError; +class WelcomeScreen; + +namespace QmlDesigner { + class ItemLibraryController; + class NavigatorController; + class StatesEditorController; + class AllPropertiesViewController; + class MultipleDocumentsController; +} + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget* parent = 0); + ~MainWindow(); + + int documentCount() const; + +public slots: + void documentCountChanged(unsigned newCount); + void openFile(const QString &fileName); + void newFile(const QString &fileTemplate); + + void doNew(); + void doOpen(); + void doQuit(); + void doAbout(); + +protected: + void closeEvent(QCloseEvent *event); + +private slots: + void showRecentFilesMenu(); + void styleActionTriggered(); + +private: + void createMenus(); + void createMainArea(); + void createMainEditArea(); + void createMainStyleArea(); + void createStatusBar(); + void updateActions(); + void updateMainArea(); + + void newFile(const QByteArray &templateContents); + + QStringList recentFiles() const; + void addRecentFile(const QString &path); + + QString serializeErrors(const QList<QmlDesigner::RewriterView::Error> &errors); + +private: + bool m_shutdown; + QString m_lastPath; + QList<QAction*> m_documentActions; + unsigned int m_documentCount; + + QMenuBar* m_menuBar; + + QmlDesigner::ItemLibraryController* m_itemLibraryController; + QmlDesigner::NavigatorController* m_navigatorController; + QmlDesigner::StatesEditorController* m_statesEditorController; + QmlDesigner::AllPropertiesViewController* m_propertiesViewController; + QmlDesigner::MultipleDocumentsController* m_multipleDocumentsController; + QAction* m_previewAction; + QAction* m_previewWithDebugAction; + QAction* m_showNewLookPropertiesAction; + QAction* m_showTraditionalPropertiesAction; + QMenu* m_recentFilesMenu; + + QStackedWidget *m_mainArea; + WelcomeScreen *m_welcomeScreen; +}; + +#endif // MAINWINDOW_H diff --git a/src/tools/qmldesigner/standalone/preview.png b/src/tools/qmldesigner/standalone/preview.png new file mode 100644 index 0000000000000000000000000000000000000000..496498ff9a0b781a7d75543a0709b496fdc74f27 GIT binary patch literal 1681 zcmV;C25$L@P)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F80000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU&Gf6~2RCwCNS6yrrRTMsV{)+jiS7 z<wppWD&RjgU>Z;(n9z_Iqlp@QFe-#TV0=)F{}1ScAx%tU0tS&LJ{S@oFd;Sa6C#8J z42p_ifzT*L+O@RXc6Vp?XJ)S7y%bWQ-Q9|Y+1$OeJ9E$Z&N<&X_guy~r(1a`bc-)- zIxm0r<8%AY4jp_nTO3vb9^7aRQ^?liL34chfpts1T3LxqR!Imx|LOVNza+cc3ckZa zT8!%L`BWR4Lou_FY@>#<g%pkE`)Sziqrq%1x!x!lYM9@fSkwK=17EJ07JyHVw9(6p zc9Q&;5p^7EbqBI%EK7^f?C9MzvuZIE&zMr60CF*XiY|<upq!VX8Fq8-^_IgSp@uO| z+fP1AuPy5?9fG0ZArc6-_CDsN+zV<vJdfta?x*_jeEA%nm=b{SP*crA6g3m{=jd@7 z$ey-7`r$rrebaueVh$veN$Pgix<a{7I<I;OTwP0fKXb)#<1?RdKk+x00OnUe2$m}} z==7>P{&>P|3vCNk1mHJ)%WmQJnAJ#)RSQW~4C&74USgeCUkF?p$egxL#<uLHJgq4U z;OFEwZMtGoJaikFtwQ-2wYPjNpKU$%3@M7pURB!4#w`vcm&6?4+aLGF<;;kKunvfN zd|+3b2v11>Rc75j!FHh>OosCGHP6?#?3DAyy>me-MFQaB1H}P(<ON0Y1CLDArdiR& z@?8V>QL}-D-197I&e(^)R!*4(Z|t4pJH-(tVK0yXlFm6QcrNTUsk1G4gSR#vY#|{d zfYm`D02HVg<T2+k@}w&vYOtFmag*7z6xOSQ&8zy<DLs%W3@MgY1)CV12a6SELOHk- zn3Kl&y%oo<-hX`fVe<Su0JtEmLq*?3{2eJL5xGMeB;=PY#R2yvdHU18lct(vzyg(r zhzAC3*tMu6>E3Mpp59q;1Xk8!#K?ppA{3fxL0*rPE993YVJ1H)7a2+zi)`Wy3w;JR zdKIyMW9tDqe|TuA%t}GfK=q~EiqcSF8kC!)YhkjKDq;YmLr8|_rj>^8E<Y;gxArdp z2v2gv=f&~>R8=QUvEaTCDqDnf79mR&B_%s80G}RxlCpjZa>B@r0{IGtUj~54dkrqv zga;6RQ?mi0hL~ZHj(Apbbsl~})&i~|Nx>b3SqLS_%v>qI3;+!;3J*Y`1Ywb-+cFc@ zwrnq{`0ejn;U!J3QB)+Zs!@Pip+KIHsVEqMR;CacagJ)nRJ<%$D?A}GL02MEDt>?e zQn(m(03}7lSddQVCW$hosPF=;zHFEV`E|S`fadrkJ*NiuF1)-*1qcnuHF|2%rfc5& z07^AgJYxux4gdzoszap)&>^Wz$8WCWs0dIVS7o(aSih>LB!IDevI8Y{8xz~NEVK$u zY<@ZpJx!V7ARaP+gPU~`TPP$!+A7>5V^BXtVdf}EIixT5bFyrUh9H%@*ikkM7B@V8 z;>@K(_e6|3DX0JeJC46fX(xqlCnHO*iR=jJ5fq##w(2-pT9iU+lo-6l7}nrD#~UH9 z=z@rh^2IdW)v)?R3SCn<0DQ!~*cr7NwqPUDMFwy`3jrR==qRk>P#mSGRVy|wj5s!$ zdRQNke1U4pV*?9B&G3a6e7T!Otwa&ZJFjk&CA#8%@nxx1?bzc2)NXQ%4!H$KS}t0% zT5GmwwP<GaPD<FfQ%z_VMa)`w#gOuSp2r@5*u!(Kml7HS0BoNqzZ8IPuiS*S>UF5Y z#dP6sxRz7cu&E}Tpt#*g)u9G-z%is~7-VIzxPW+PFvg%X4KHM9#QjTbxW%v*TQ`~f zQs;uJfdYj#BqH-p<@^-aP(#8-SQ1aLKLEQ$o8}Z#2+t+4)1gBiCK2ak_6)DLn|_7Y z+9#7+?p&}3<3-t3sfs_EyU=N9Oslc$He?F@%^sy$-cQSLh#gaG!J=i$0zlr+a&*l- zLEv|w4Vx&_KbJbPr>SOH#RrovjF&_js;c6lKzo;*-p%5HgSZa6eN!$Jp17XaP~P=# zPDWo`@x)D>zC;7ib-m@&IU9CO7P|_szVY=peKy=PKCi}TyzVe?gS*F5?c4wCZ1{g( b{|GPueq)}pg6_?<00000NkvXXu0mjf?5zgm literal 0 HcmV?d00001 diff --git a/src/tools/qmldesigner/standalone/standalone.pro b/src/tools/qmldesigner/standalone/standalone.pro new file mode 100644 index 00000000000..f0e8fb093fc --- /dev/null +++ b/src/tools/qmldesigner/standalone/standalone.pro @@ -0,0 +1,44 @@ +!contains(QT_CONFIG, declarative) { + error("Qt is not configured with the declarative model."); +} + +TEMPLATE = app +CONFIG(debug, debug|release):CONFIG += console +TARGET = bauhaus +macx:TARGET = Bauhaus +CONFIG += qt +CONFIG += webkit +QT += gui opengl + +HEADERS += \ + aboutdialog.h \ + mainwindow.h \ + application.h \ + welcomescreen.h + +SOURCES += \ + aboutdialog.cpp \ + main.cpp \ + mainwindow.cpp \ + application.cpp \ + welcomescreen.cpp + +include(../../../plugins/qmldesigner/config.pri) +include(../../../plugins/qmldesigner/components/integration/integration.pri) +include(../../../plugins/qmldesigner/components/propertyeditor/propertyeditor.pri) +include(../../../plugins/qmldesigner/components/formeditor/formeditor.pri) +include(../../../plugins/qmldesigner/components/navigator/navigator.pri) +include(../../../plugins/qmldesigner/components/stateseditor/stateseditor.pri) +include(../../../plugins/qmldesigner/components/itemlibrary/itemlibrary.pri) +include(../../../plugins/qmldesigner/components/resources/resources.pri) +include(../../../plugins/qmldesigner/components/pluginmanager/pluginmanager.pri) +include(../../../plugins/qmldesigner/components/themeloader/qts60stylethemeio.pri) +include (../../../plugins/qmldesigner/core/core.pri) +RESOURCES += bauhaus.qrc +win32:RC_FILE = bauhaus.rc +macx { + ICON = bauhaus-logo.icns + QMAKE_INFO_PLIST = Info.plist +} +QMAKE_CXXFLAGS_HIDESYMS="" + diff --git a/src/tools/qmldesigner/standalone/templates/fx.qml b/src/tools/qmldesigner/standalone/templates/fx.qml new file mode 100644 index 00000000000..0732fa811b3 --- /dev/null +++ b/src/tools/qmldesigner/standalone/templates/fx.qml @@ -0,0 +1,7 @@ +import Qt 4.6 + +Item { + id: RootItem + width: 640 + height: 480 +} diff --git a/src/tools/qmldesigner/standalone/templates/qgraphicsview.qml b/src/tools/qmldesigner/standalone/templates/qgraphicsview.qml new file mode 100644 index 00000000000..f85b2cf2384 --- /dev/null +++ b/src/tools/qmldesigner/standalone/templates/qgraphicsview.qml @@ -0,0 +1,10 @@ +import Qt 4.6 + +QGraphicsView { + objectName: "view" + width: 300 + height: 300 + scene: QGraphicsScene { + objectName: "scene" + } +} diff --git a/src/tools/qmldesigner/standalone/templates/qwidget.qml b/src/tools/qmldesigner/standalone/templates/qwidget.qml new file mode 100644 index 00000000000..568eedce103 --- /dev/null +++ b/src/tools/qmldesigner/standalone/templates/qwidget.qml @@ -0,0 +1,9 @@ +import Qt 4.6 + +QWidget { + objectName: "Form" + x: 300 + y: 200 + width: 455 + height: 376 +} diff --git a/src/tools/qmldesigner/standalone/toolbar-cssedit.png b/src/tools/qmldesigner/standalone/toolbar-cssedit.png new file mode 100644 index 0000000000000000000000000000000000000000..423d1b04b3023f870a7dfc444db94c200b23a744 GIT binary patch literal 1808 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 literal 0 HcmV?d00001 diff --git a/src/tools/qmldesigner/standalone/toolbar-edit.png b/src/tools/qmldesigner/standalone/toolbar-edit.png new file mode 100644 index 0000000000000000000000000000000000000000..8c2f4c1209a7fd4bada73c1178e09c192c0fa582 GIT binary patch literal 1142 zcmV-+1d02JP)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F800009a7bBm000XU z000XU0RWnu7ytkSC`m*?R9Fe+R$XjUWf*?ip8l*q>&jNPV%N1>k%5Syblr{KcmW|% z6QaP4bHXw)Gg&e(FeXML8ZLxLz>sZVkU*llFj3-eh$bv(V1HqZg+XiDHH@y^x^`VV zSK8AazpwF-lhLlfCMJH#oAaKh?|Gl+ywBHj4)A|pqJLum<6T{hI2QX}Yc#%^6p0*B zpRa9KZ?7X07n2Dr6}xY>9^Ym(zFEt%h|ka4R|A2Qr046I`ie7wrC?v4I`f9iZ23rO zvw_{S2T{2ksd!wPt!G&U{M@~(X{$1LVdsvWsHv=mXWowqjb@nxj%5>2HVbZdK%NZU zzGM_VtkGi;VXg;`X(uM8rn+A1@1M*jpltYXJ5D=~S8?2!jpIhQaQDu&a((Ce&gUX1 ziv=Txai~Hk>o&h)1>`#TCn5;mxHW86EOzG-Q1ah!v;)l$iEcLUZLU%`8W5S8!-K*5 zL5|~ETfT)emw=KcFxClWip7=s7dKU@EmkB#F}Sb)2}0hP4>=WA(k4vC@pVn3?xou8 zO~7ghGec8|g%c<3-{Hqxch3J_^aP0S?Mj`z)6}vRkZCYC?n213JS36fW4?Jl$D#xf z-(L_jqO+Ek&0utTEIVCzbjRzLNb%0@ekAfC=4%QTKz!GvlDJ}ezFw^|nGyHT!F|ig zk%!HZ-RI%q8|Qm0cn>5K_-3P}db{3g02f|>`{pzrudrX(FJdrXQej;|0!EJDeU(<( zW_qq6-NSp=gC$?&8Y9JL!ls4fd0Rkyw@IXuQ<fbTh!t`yc^2Vy_+}Xf?-Jirg^&ts z^AbRO*QHXp+|XQKQK8e|aWIOR$r<vhM?3M|D{NS3o|gdfTJNhf>9<rrQwwgDg?rMC zc#QqTb^*T?O1QQ@=N=%w_o%h1w~S3D&_T~S9wHowUTwXI6Kk6lbH45bi0>~mQppL+ zv&46)48cbW@Ok_$vgtaCA<J}4Rso|Q8tOQ){8B?>gG`}PAr^|j<M70Z?`>q$Eo5p| z43^yj>%Pwo8*3~Zqye!03Ai4(z$TK%$V=vrVu-R`lTpCP*`u2@+RB4<^%nA+--cOl z81cyX03kn}tx>UfMhmLc^0y6j`bvh8AiA^wmQjv*Q@W$YlB{>#Qxh;7v<i_*(NVcU zheR}t%U6d6ej2*_-nH>ic)dpD_$~c)wAa4*05@`mTe<#K>Vy6EPf25n(~$iW^b?y3 z7lwqK^FgO74E}loKfU+~(4ur6_zQ=B`F(n&ci`8a!C~hQ1UHc2ts*ZyBuUUmFU@Jt zoDY(wF=0(MjN<7#<B#+XRV4P2SR`?eL<I@joGzY>CAm-lB@+_zA}L05x@bMUPKZ(b zi2$0@5mFJfh>lr7LP$hTpNQZaQ*3RG5TiL=w4Mg7{co+m0XKVauMEA5_y7O^07*qo IM6N<$g1A^5#sB~S literal 0 HcmV?d00001 diff --git a/src/tools/qmldesigner/standalone/welcomescreen.cpp b/src/tools/qmldesigner/standalone/welcomescreen.cpp new file mode 100644 index 00000000000..778f7e1ed6e --- /dev/null +++ b/src/tools/qmldesigner/standalone/welcomescreen.cpp @@ -0,0 +1,150 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "welcomescreen.h" +#include "application.h" + +#include <QtGui/QBoxLayout> +#include <QtDeclarative/QmlView> +#include <QtDeclarative/QmlGraphicsItem> +#include <QtDeclarative/QmlContext> +#include <QtCore/QCoreApplication> + +RecentFileModel::RecentFileModel(QObject *parent) : + QListModelInterface(parent) +{ +} + +void RecentFileModel::setRecentFiles(const QStringList &filePaths) +{ + QStringList oldPaths = m_paths; + m_paths.clear(); + if (oldPaths.size() > 0) { + emit itemsRemoved(0, oldPaths.size()); + } + + m_paths = filePaths; + + if (m_paths.size() > 0) { + emit itemsInserted(0, m_paths.size()); + } +} + +int RecentFileModel::count() const +{ + return m_paths.size(); +} + +QHash<int, QVariant> RecentFileModel::data(int index, const QList<int> &/*roles*/) const +{ + QHash<int,QVariant> dataHash; + + dataHash.insert(NameRole, displayPath(m_paths.at(index))); + dataHash.insert(FileNameRole, m_paths.at(index)); + return dataHash; +} + +QVariant RecentFileModel::data(int index, int role) const +{ + if (role == NameRole) + return displayPath(m_paths.at(index)); + if (role == FileNameRole) + return m_paths.at(index); + return QVariant(); +} + +QList<int> RecentFileModel::roles() const +{ + return QList<int>() << NameRole << FileNameRole; +} + +QString RecentFileModel::toString(int role) const +{ + switch (role) { + case NameRole: return "name"; break; + case FileNameRole: return "fileName"; break; + default: Q_ASSERT_X(0, Q_FUNC_INFO, "Unknown role"); + } + return QString(); +} + +QString RecentFileModel::displayPath(const QString &filePath) const +{ + QString displayPath = filePath; + while (displayPath.length() > 25 && displayPath.contains('/')) { + displayPath.remove(0, displayPath.indexOf('/') + 1); + } + + if (displayPath != filePath) + displayPath.prepend("../"); + + return displayPath; +} + +WelcomeScreen::WelcomeScreen(QWidget *parent) : + QWidget(parent), + m_view(new QmlView(this)), + m_recentFileModel(new RecentFileModel(this)) +{ + m_view->setUrl(QUrl::fromLocalFile(Application::sharedDirPath() + "welcomescreen.qml")); + m_view->setContentResizable(true); + + QmlContext *context = m_view->rootContext(); + context->setContextProperty(QLatin1String("recentFiles"), m_recentFileModel); + + m_view->execute(); + + QObject *rootItem = m_view->root(); + connect(rootItem, SIGNAL(openFile()), this, SLOT(openFile())); + + QBoxLayout *layout = new QBoxLayout(QBoxLayout::LeftToRight, this); + layout->setMargin(0); + layout->addWidget(m_view); + + QPalette palette; + palette.setColor(QPalette::Window, QColor(0x2e, 0x2e, 0x2e)); + setPalette(palette); + setBackgroundRole(QPalette::Window); + setAutoFillBackground(true); +} + +void WelcomeScreen::setRecentFiles(const QStringList &files) +{ + m_recentFileModel->setRecentFiles(files); +} + +void WelcomeScreen::openFile() +{ + QString filePath = m_view->root()->property("selectedFile").toString(); + if (filePath.startsWith(':')) { + emit newFile(filePath); + } else { + emit openFile(filePath); + } +} diff --git a/src/tools/qmldesigner/standalone/welcomescreen.h b/src/tools/qmldesigner/standalone/welcomescreen.h new file mode 100644 index 00000000000..07debdfbd35 --- /dev/null +++ b/src/tools/qmldesigner/standalone/welcomescreen.h @@ -0,0 +1,77 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include <QtCore/QMetaType> +#include <QtGui/QDialog> +#include <private/qlistmodelinterface_p.h> + +class QmlView; + +class RecentFileModel : public QListModelInterface { + Q_OBJECT +public: + enum Roles { + NameRole, + FileNameRole + }; + + RecentFileModel(QObject *parent = 0); + void setRecentFiles(const QStringList &filePaths); + + int count() const; + QHash<int, QVariant> data(int index, const QList<int> &roles = QList<int>()) const; + QVariant data(int index, int role) const; + QList<int> roles() const; + QString toString(int role) const; +private: + QString displayPath(const QString &filePath) const; + + QStringList m_paths; +}; + +class WelcomeScreen : public QWidget { + Q_OBJECT + +public: + WelcomeScreen(QWidget *parent); + void setRecentFiles(const QStringList &recentFiles); + +signals: + void appExit(); + void openFile(const QString &filePath); + void newFile(const QString &templatePath); + +private slots: + void openFile(); + +private: + QmlView *m_view; + RecentFileModel *m_recentFileModel; +}; + diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro index 28111c1bd38..502e14c7416 100644 --- a/tests/auto/auto.pro +++ b/tests/auto/auto.pro @@ -6,4 +6,5 @@ SUBDIRS += \ fakevim \ # profilereader \ aggregation \ - changeset + changeset \ + qml diff --git a/tests/auto/qml/qml.pro b/tests/auto/qml/qml.pro new file mode 100644 index 00000000000..cc8224d5e7a --- /dev/null +++ b/tests/auto/qml/qml.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs + +SUBDIRS += qmldesigner diff --git a/tests/auto/qml/qmldesigner/bauhaustests/bauhaustests.pro b/tests/auto/qml/qmldesigner/bauhaustests/bauhaustests.pro new file mode 100644 index 00000000000..19398348f93 --- /dev/null +++ b/tests/auto/qml/qmldesigner/bauhaustests/bauhaustests.pro @@ -0,0 +1,18 @@ +include(../../../../../src/plugins/qmldesigner/config.pri) +QT += testlib + +DESTDIR = $$DESIGNER_BINARY_DIRECTORY +include(../../../../../src/plugins/qmldesigner/core/core.pri) + +##DEFINES += DONT_MESS_WITH_QDEBUG + +DEPENDPATH += .. +DEPENDPATH += ../../../../../src/plugins/qmldesigner/core/include + +TARGET = tst_bauhaus +CONFIG += console +CONFIG -= app_bundle +TEMPLATE = app +SOURCES += testbauhaus.cpp +HEADERS += testbauhaus.h +DEFINES += WORKDIR=\\\"$$DESTDIR\\\" diff --git a/tests/auto/qml/qmldesigner/bauhaustests/testbauhaus.cpp b/tests/auto/qml/qmldesigner/bauhaustests/testbauhaus.cpp new file mode 100644 index 00000000000..f400025552e --- /dev/null +++ b/tests/auto/qml/qmldesigner/bauhaustests/testbauhaus.cpp @@ -0,0 +1,122 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "testbauhaus.h" + +#include <QProcess> +#include <QFileInfo> +#include <QDir> +#include <QDebug> + +QStringList failures; + + +TestBauhaus::TestBauhaus() + : QObject() +{ +} + +void TestBauhaus::initTestCase() +{ +} + +void loadFile(const QString &fileName) +{ + QProcess process; + qDebug() << "starting: " << fileName; + QVERIFY(QFileInfo(fileName).exists()); +#ifdef Q_OS_WIN + const QString bauhausExecutable = "bauhaus.exe"; +#else + const QString bauhausExecutable = QDir::current().absoluteFilePath("bauhaus"); +#endif + QVERIFY(QFileInfo(bauhausExecutable).isExecutable()); + process.start(bauhausExecutable, QStringList() << fileName); + if (!process.waitForStarted()) + QFAIL(fileName.toLatin1()); + if (!QProcess::Running == process.state()) { + QFAIL(fileName.toLatin1()); + failures << fileName; + } + QTest::qWait(5000); + if (!QProcess::Running == process.state()) { + QFAIL(fileName.toLatin1()); + failures << fileName; + } +} + +void loadAllFiles(const QString &path) +{ + QDir::setCurrent(WORKDIR); + QVERIFY(QFileInfo(path).exists()); + QDir dir(path); + foreach (const QString &file, dir.entryList(QStringList() << "*.qml", QDir::Files)) + loadFile(path + "/" + file); + foreach (const QString &directory, dir.entryList(QStringList(), QDir::AllDirs | QDir::NoDotAndDotDot)) + loadAllFiles(path + "/" + directory); +} + +void TestBauhaus::cleanupTestCase() +{ +} + +void TestBauhaus::loadExamples() +{ + failures.clear(); + QString qtdir; + foreach (const QString &string, QProcess::systemEnvironment()) + if (string.contains("qtdir", Qt::CaseInsensitive)) + qtdir = string.split("=").last(); + if (qtdir.isEmpty()) + qWarning() << "QTDIR has to be set"; + QVERIFY(!qtdir.isEmpty()); + QVERIFY(QFileInfo(qtdir + "/examples/declarative").exists()); + loadAllFiles(qtdir + "/examples/declarative"); + qDebug() << failures; + QVERIFY(failures.isEmpty()); +} + +void TestBauhaus::loadDemos() +{ + failures.clear(); + QString qtdir; + foreach (const QString &string, QProcess::systemEnvironment()) + if (string.contains("qtdir", Qt::CaseInsensitive)) + qtdir = string.split("=").last(); + if (qtdir.isEmpty()) + qWarning() << "QTDIR has to be set"; + QVERIFY(!qtdir.isEmpty()); + QVERIFY(QFileInfo(qtdir + "/demos/declarative").exists()); + loadAllFiles(qtdir + "/demos/declarative"); + qDebug() << failures; + QVERIFY(failures.isEmpty()); +} + + +QTEST_MAIN(TestBauhaus); diff --git a/tests/auto/qml/qmldesigner/bauhaustests/testbauhaus.h b/tests/auto/qml/qmldesigner/bauhaustests/testbauhaus.h new file mode 100644 index 00000000000..f501917b38b --- /dev/null +++ b/tests/auto/qml/qmldesigner/bauhaustests/testbauhaus.h @@ -0,0 +1,51 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef TESTBAUHAUS_H +#define TESTBAUHAUS_H + +#include <QObject> + +#include <QtTest> + + +class TestBauhaus : public QObject +{ + Q_OBJECT +public: + TestBauhaus(); + +private slots: + void initTestCase(); + void cleanupTestCase(); + void loadExamples(); + void loadDemos(); +}; + +#endif // TESTBAUHAUS_H diff --git a/tests/auto/qml/qmldesigner/common/statichelpers.cpp b/tests/auto/qml/qmldesigner/common/statichelpers.cpp new file mode 100644 index 00000000000..02a36031d7a --- /dev/null +++ b/tests/auto/qml/qmldesigner/common/statichelpers.cpp @@ -0,0 +1,174 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +static QStringList pluginPaths() +{ + QStringList result; +#ifdef Q_OS_MAC + result += QCoreApplication::applicationDirPath() + "/Bauhaus.app/Contents/PlugIns/Bauhaus/ItemLibs"; +#else // Q_OS_MAC + result += QCoreApplication::applicationDirPath() + "/../lib/itemlibs"; +#endif // Q_OS_MAC + + return result; +} + +static void testMessageOutput(QtMsgType type, const char *msg) + { + switch (type) { + case QtDebugMsg: +#ifdef QDEBUG_IN_TESTS + std::fprintf(stderr, "Debug: %s\n", msg); +#endif // QDEBUG_IN_TESTS + break; + case QtWarningMsg: +#ifdef WARNINGS_IN_TESTS + std::fprintf(stderr, "Warning: %s\n", msg); +#endif // WARNINGS_IN_TESTS + break; + case QtCriticalMsg: + std::fprintf(stderr, "Critical: %s\n", msg); + break; + case QtFatalMsg: + std::fprintf(stderr, "Fatal: %s\n", msg); + break; + } + } + +static QString bareTemplate("import Qt 4.6\n" + "Item { id: parentItem;" + " %1" + "}"); +static QString contentsTemplate(bareTemplate.arg("Text { id: textChild; x:10; y: 10; text: \"%1\"; %2 }")); + + +void printErrors(const QList<QmlError> &errors, const QString &fileName) +{ + if (errors.isEmpty()) + return; + + qDebug() << "Error loading file \"" << fileName << "\":"; + + foreach (const QmlError &error, errors) { + qDebug() << error.line() << ":" << error.column() << ": " << error.description(); + } +} + +// TODO: this need to e updated for states +static bool compareProperty(const AbstractProperty &property1, const AbstractProperty &property2) +{ + return (property1.name() == property2.name()); +// && (property1.value().type() == property2.value().type()); +// && (property1.value() == property2.value())); +} + +// TODO: this need to e updated for states +static bool compareTree(const ModelNode &node1, const ModelNode &node2) +{ + if (!node1.isValid() || !node2.isValid()) { + return false; + } + + if (node1.type() != node2.type()) { + return false; + } + + // Compare properties + { + const QList<AbstractProperty> propList1 = node1.properties(); + const QList<AbstractProperty> propList2 = node2.properties(); + + QList<AbstractProperty>::const_iterator iter1 = propList1.constBegin(); + QList<AbstractProperty>::const_iterator iter2 = propList2.constBegin(); + for (; + iter1 != propList1.constEnd() && iter2 != propList2.constEnd(); + iter1++, iter2++) { + if (!compareProperty(*iter1, *iter2)) + return false; + } + + if (iter1 != propList1.constEnd() || iter2 != propList2.constEnd()) + return false; + } + + // Compare list of childs + { + const QList<ModelNode> childList1 = node1.allDirectSubModelNodes(); + const QList<ModelNode> childList2 = node2.allDirectSubModelNodes(); + + QList<ModelNode>::const_iterator iter1; + QList<ModelNode>::const_iterator iter2; + for (iter1 = childList1.constBegin(), iter2 = childList2.constBegin(); + iter1 != childList1.constEnd() && iter2 != childList2.constEnd(); + iter1++, iter2++) { + if (!compareTree((*iter1), (*iter2))) + return false; + } + + if (iter1 != childList1.constEnd() || iter2 != childList2.constEnd()) + return false; + } + return true; +} + +//void load(const QString &data, Model *&model, ByteArrayModifier *&modifier) +//{ +// model = 0; +// QByteArray bytes = data.toLatin1(); +// QBuffer file(&bytes, 0); +// QVERIFY(file.open(QIODevice::ReadOnly)); +// QList<QmlError> errors; +// QString fileText(file.readAll()); +// modifier = ByteArrayModifier::create(QString(fileText)); +// model = Model::create(modifier, QUrl(), &errors); +// +// if (!errors.isEmpty()) { +// printErrors(errors, "<inline>"); +// } +// +// file.close(); +//} + +//void reload(const QString &data, ByteArrayModifier *modifier) +//{ +// modifier->setText(data); +//} + +//static Model* create(const QString& document) +//{ +// ByteArrayModifier* modifier = 0; +// Model *model = 0; +// +// load(document, model, modifier); +// +// if (modifier && model) +// modifier->setParent(model); +// +// return model; +//} diff --git a/tests/auto/qml/qmldesigner/coretests/coretests.pro b/tests/auto/qml/qmldesigner/coretests/coretests.pro new file mode 100644 index 00000000000..9ed993bb048 --- /dev/null +++ b/tests/auto/qml/qmldesigner/coretests/coretests.pro @@ -0,0 +1,22 @@ +include(../../../../../src/plugins/qmldesigner/config.pri) +QT += testlib \ + script \ + declarative +DESTDIR = $$DESIGNER_BINARY_DIRECTORY +include(../../../../../src/plugins/qmldesigner/core/core.pri) +DEFINES+=QTCREATOR_UTILS_STATIC_LIB + +DEPENDPATH += .. +DEPENDPATH += ../../../../../src/plugins/qmldesigner/core/include + +TARGET = tst_qmldesigner_core +CONFIG += console +CONFIG -= app_bundle +TEMPLATE = app +SOURCES += testcore.cpp \ + ../testview.cpp \ + testrewriterview.cpp +HEADERS += testcore.h \ + ../testview.h \ + testrewriterview.h +RESOURCES += ../data/testfiles.qrc diff --git a/tests/auto/qml/qmldesigner/coretests/testcore.cpp b/tests/auto/qml/qmldesigner/coretests/testcore.cpp new file mode 100644 index 00000000000..54974e8328e --- /dev/null +++ b/tests/auto/qml/qmldesigner/coretests/testcore.cpp @@ -0,0 +1,6485 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "testcore.h" + +#include <QScopedPointer> +#include <QLatin1String> + +#include <metainfo.h> +#include <model.h> +#include <modelmerger.h> +#include <modelnode.h> +#include <qmlanchors.h> +#include <invalididexception.h> +#include <invalidmodelnodeexception.h> +#include <nodeinstanceview.h> +#include <nodeinstance.h> +#include <QDebug> + +#include "../testview.h" +#include <variantproperty.h> +#include <abstractproperty.h> +#include <bindingproperty.h> +#include <nodeproperty.h> + +#include <nodelistproperty.h> +#include <nodeabstractproperty.h> +#include <plaintexteditmodifier.h> +#include <componenttextmodifier.h> + +#include <bytearraymodifier.h> +#include "testrewriterview.h" + +#include <QPlainTextEdit> +#include <private/qmlstate_p.h> + + +using namespace QmlDesigner; +#include <cstdio> +#include "../common/statichelpers.cpp" + +TestCore::TestCore() + : QObject() +{ +} + +void TestCore::initTestCase() +{ +#ifndef QDEBUG_IN_TESTS + qInstallMsgHandler(testMessageOutput); +#endif + MetaInfo::setPluginPaths(pluginPaths()); + Exception::setShouldAssert(false); +} + +void TestCore::cleanupTestCase() +{ + MetaInfo::clearGlobal(); +} + +void TestCore::testModelCreateCoreModel() +{ + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + + QScopedPointer<TestView> testView(new TestView); + QVERIFY(testView.data()); + model->attachView(testView.data()); + + QVERIFY(testView->rootModelNode().isValid()); + NodeInstanceView *nodeInstanceView = new NodeInstanceView(model.data()); + model->attachView(nodeInstanceView); + model->detachView(nodeInstanceView); +} + +// TODO: this need to e updated for states +void TestCore::loadEmptyCoreModel() +{ + QList<QmlError> errors; + QFile file(":/fx/empty.qml"); + QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text)); + + QPlainTextEdit textEdit1; + textEdit1.setPlainText(file.readAll()); + PlainTextEditModifier modifier1(&textEdit1); + + QScopedPointer<Model> model1(Model::create("Qt/Item")); + + QScopedPointer<TestRewriterView> testRewriterView1(new TestRewriterView()); + testRewriterView1->setTextModifier(&modifier1); + model1->attachView(testRewriterView1.data()); + + QPlainTextEdit textEdit2; + textEdit2.setPlainText("import Qt 4.6; Item{}"); + PlainTextEditModifier modifier2(&textEdit2); + + QScopedPointer<Model> model2(Model::create("Qt/item")); + + QScopedPointer<TestRewriterView> testRewriterView2(new TestRewriterView()); + testRewriterView2->setTextModifier(&modifier2); + model2->attachView(testRewriterView2.data()); + + QVERIFY(compareTree(testRewriterView1->rootModelNode(), testRewriterView2->rootModelNode())); +} + +void TestCore::testRewriterView() +{ + try { + QPlainTextEdit textEdit; + textEdit.setPlainText("import Qt 4.6;\n\nItem {\n}\n"); + PlainTextEditModifier textModifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + ModelNode rootModelNode(view->rootModelNode()); + QVERIFY(rootModelNode.isValid()); + QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + model->attachView(testRewriterView.data()); + + ModelNode childNode(rootModelNode.addChildNode("Qt/Item", 4, 6, "data")); + QVERIFY(childNode.isValid()); + + childNode.changeType("Qt/Rectangle", 4, 6); + childNode.setId("childNode"); + + ModelNode childNode2(childNode.addChildNode("Qt/Rectangle", 4, 6, "data")); + childNode2.setId("childNode2"); + ModelNode childNode3(childNode2.addChildNode("Qt/Rectangle", 4, 6, "data")); + childNode3.setId("childNode3"); + ModelNode childNode4(childNode3.addChildNode("Qt/Rectangle", 4, 6, "data")); + childNode4.setId("childNode4"); + + QVERIFY(childNode.isValid()); + QVERIFY(childNode2.isValid()); + QVERIFY(childNode3.isValid()); + QVERIFY(childNode4.isValid()); + + testRewriterView->setModificationGroupActive(true); + + childNode.destroy(); + + QVERIFY(!childNode.isValid()); + QVERIFY(!childNode2.isValid()); + QVERIFY(!childNode3.isValid()); + QVERIFY(!childNode4.isValid()); + + QVERIFY(testRewriterView->modelToTextMerger()->isNodeScheduledForRemoval(childNode)); + QVERIFY(!testRewriterView->modelToTextMerger()->isNodeScheduledForRemoval(childNode2)); + QVERIFY(!testRewriterView->modelToTextMerger()->isNodeScheduledForRemoval(childNode3)); + QVERIFY(!testRewriterView->modelToTextMerger()->isNodeScheduledForRemoval(childNode4)); + + QVERIFY(!rootModelNode.hasProperty(QLatin1String("data"))); + + testRewriterView->modelToTextMerger()->applyChanges(); + + childNode = rootModelNode.addChildNode("Qt/Rectangle", 4, 6, "data"); + QVERIFY(testRewriterView->modelToTextMerger()->isNodeScheduledForAddition(childNode)); + + testRewriterView->modelToTextMerger()->applyChanges(); + + childNode.variantProperty("x") = 70; + childNode.variantProperty("y") = 90; + + QCOMPARE(testRewriterView->modelToTextMerger()->findAddedVariantProperty(childNode.variantProperty("x")).value(), QVariant(70)); + QCOMPARE(testRewriterView->modelToTextMerger()->findAddedVariantProperty(childNode.variantProperty("y")).value(), QVariant(90)); + + model->detachView(testRewriterView.data()); + } catch (Exception &e) { + QFAIL(qPrintable(e.description())); + } +} + +void TestCore::testRewriterErrors() +{ + QPlainTextEdit textEdit; + textEdit.setPlainText("import Qt 4.6;\n\nItem {\n}\n"); + PlainTextEditModifier textModifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + ModelNode rootModelNode(view->rootModelNode()); + QVERIFY(rootModelNode.isValid()); + QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + model->attachView(testRewriterView.data()); + + QVERIFY(testRewriterView->errors().isEmpty()); + textEdit.setPlainText("import Qt 4.6;\n\nError {\n}\n"); + QVERIFY(!testRewriterView->errors().isEmpty()); + + textEdit.setPlainText("import Qt 4.6;\n\nItem {\n}\n"); + QVERIFY(testRewriterView->errors().isEmpty()); +} + +void TestCore::saveEmptyCoreModel() +{ + QList<QmlError> errors; + QFile file(":/fx/empty.qml"); + QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text)); + + QPlainTextEdit textEdit1; + textEdit1.setPlainText(file.readAll()); + PlainTextEditModifier modifier1(&textEdit1); + + QScopedPointer<Model> model1(Model::create("Qt/Item")); + + QScopedPointer<TestRewriterView> testRewriterView1(new TestRewriterView()); + testRewriterView1->setTextModifier(&modifier1); + model1->attachView(testRewriterView1.data()); + + + QBuffer buffer; + buffer.open(QIODevice::ReadWrite | QIODevice::Text); + modifier1.save(&buffer); + + QPlainTextEdit textEdit2; + textEdit2.setPlainText("import Qt 4.6; Item{}"); + PlainTextEditModifier modifier2(&textEdit2); + + QScopedPointer<Model> model2(Model::create("Qt/item")); + + QScopedPointer<TestRewriterView> testRewriterView2(new TestRewriterView()); + testRewriterView2->setTextModifier(&modifier2); + model2->attachView(testRewriterView2.data()); + + QVERIFY(compareTree(testRewriterView1->rootModelNode(), testRewriterView2->rootModelNode())); + +} + +void TestCore::loadAttributesInCoreModel() +{ + QList<QmlError> errors; + QFile file(":/fx/attributes.qml"); + QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text)); + + QPlainTextEdit textEdit1; + textEdit1.setPlainText(file.readAll()); + PlainTextEditModifier modifier1(&textEdit1); + + QScopedPointer<Model> model1(Model::create("Qt/Item")); + + QScopedPointer<TestRewriterView> testRewriterView1(new TestRewriterView()); + testRewriterView1->setTextModifier(&modifier1); + model1->attachView(testRewriterView1.data()); + + QPlainTextEdit textEdit2; + textEdit2.setPlainText("import Qt 4.6; Item{}"); + PlainTextEditModifier modifier2(&textEdit2); + + QScopedPointer<Model> model2(Model::create("Qt/item")); + + QScopedPointer<TestRewriterView> testRewriterView2(new TestRewriterView()); + testRewriterView2->setTextModifier(&modifier2); + model2->attachView(testRewriterView2.data()); + + ModelNode rootModelNode = testRewriterView2->rootModelNode(); + + rootModelNode.setId("theItem"); + rootModelNode.variantProperty("x").setValue(QVariant(300)); + rootModelNode.variantProperty("visible").setValue(true); + rootModelNode.variantProperty("scale").setValue(0.5); + + QVERIFY(compareTree(testRewriterView1->rootModelNode(), testRewriterView2->rootModelNode())); +} + +void TestCore::saveAttributesInCoreModel() +{ + QFile file(":/fx/attributes.qml"); + QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text)); + + QPlainTextEdit textEdit1; + textEdit1.setPlainText(file.readAll()); + PlainTextEditModifier modifier1(&textEdit1); + + QScopedPointer<Model> model1(Model::create("Qt/Item")); + + QScopedPointer<TestRewriterView> testRewriterView1(new TestRewriterView()); + testRewriterView1->setTextModifier(&modifier1); + model1->attachView(testRewriterView1.data()); + + QVERIFY(testRewriterView1->errors().isEmpty()); + + QBuffer buffer; + buffer.open(QIODevice::ReadWrite | QIODevice::Text); + modifier1.save(&buffer); + + QPlainTextEdit textEdit2; + textEdit2.setPlainText(buffer.data()); + PlainTextEditModifier modifier2(&textEdit2); + + QScopedPointer<Model> model2(Model::create("Qt/Item")); + + QScopedPointer<TestRewriterView> testRewriterView2(new TestRewriterView()); + testRewriterView2->setTextModifier(&modifier2); + model2->attachView(testRewriterView2.data()); + + QVERIFY(testRewriterView2->errors().isEmpty()); + + QVERIFY(compareTree(testRewriterView1->rootModelNode(), testRewriterView2->rootModelNode())); +} + +void TestCore::testModelCreateRect() +{ + try { + + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + QVERIFY(view->rootModelNode().isValid()); + ModelNode childNode = view->rootModelNode().addChildNode("Qt/Rectangle", 4, 6, "data"); + QVERIFY(childNode.isValid()); + QVERIFY(view->rootModelNode().allDirectSubModelNodes().contains(childNode)); + QVERIFY(childNode.parentProperty().parentModelNode() == view->rootModelNode()); + QCOMPARE(childNode.simplifiedTypeName(), QString("Rectangle")); + + QVERIFY(childNode.id().isEmpty()); + + childNode.setId("Rect01"); + QCOMPARE(childNode.id(), QString("Rect01")); + + childNode.variantProperty("x") = 100; + childNode.variantProperty("y") = 100; + childNode.variantProperty("width") = 100; + childNode.variantProperty("height") = 100; + + QCOMPARE(childNode.propertyNames().count(), 4); + QCOMPARE(childNode.variantProperty("scale").value(), QVariant()); + + } catch (Exception &exception) { + QFAIL("Exception thrown"); + } + +} + +void TestCore::loadComponentPropertiesInCoreModel() +{ + QFile file(":/fx/properties.qml"); + QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text)); + + QPlainTextEdit textEdit1; + textEdit1.setPlainText(file.readAll()); + PlainTextEditModifier modifier1(&textEdit1); + + QScopedPointer<Model> model1(Model::create("Qt/Item")); + + QScopedPointer<TestRewriterView> testRewriterView1(new TestRewriterView()); + testRewriterView1->setTextModifier(&modifier1); + model1->attachView(testRewriterView1.data()); + + QVERIFY(testRewriterView1->rootModelNode().variantProperty("pushed").isDynamic()); + QVERIFY(!testRewriterView1->rootModelNode().variantProperty("pushed").value().isValid()); + + QPlainTextEdit textEdit2; + textEdit2.setPlainText("import Qt 4.6; Item{}"); + PlainTextEditModifier modifier2(&textEdit2); + + QScopedPointer<Model> model2(Model::create("Qt/Item")); + + QScopedPointer<TestRewriterView> testRewriterView2(new TestRewriterView()); + testRewriterView2->setTextModifier(&modifier2); + model2->attachView(testRewriterView2.data()); + + testRewriterView2->rootModelNode().variantProperty("pushed").setDynamicTypeNameAndValue("bool", QVariant()); + + QVERIFY(compareTree(testRewriterView1->rootModelNode(), testRewriterView2->rootModelNode())); +} + +void TestCore::loadSubItems() +{ + QFile file(QCoreApplication::applicationDirPath() + "/../tests/data/fx/topitem.qml"); + QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text)); + + QPlainTextEdit textEdit1; + textEdit1.setPlainText(file.readAll()); + PlainTextEditModifier modifier1(&textEdit1); + + QScopedPointer<Model> model1(Model::create("Qt/Item")); + + QScopedPointer<TestRewriterView> testRewriterView1(new TestRewriterView()); + testRewriterView1->setTextModifier(&modifier1); + model1->attachView(testRewriterView1.data()); +} + + +void TestCore::createInvalidCoreModel() +{ + QScopedPointer<Model> invalidModel(Model::create("ItemSUX")); + //QVERIFY(!invalidModel.data()); //#no direct ype checking in model atm + + QScopedPointer<Model> invalidModel2(Model::create("InvalidNode")); + //QVERIFY(!invalidModel2.data()); +} + +void TestCore::testModelCreateSubNode() +{ + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + QList<TestView::MethodCall> expectedCalls; + expectedCalls << TestView::MethodCall("modelAttached", QStringList() << QString::number(reinterpret_cast<long>(model.data()))); + QCOMPARE(view->methodCalls(), expectedCalls); + + QVERIFY(view->rootModelNode().isValid()); + ModelNode childNode = view->rootModelNode().addChildNode("Qt/Rectangle", 4, 6, "data"); + QVERIFY(childNode.isValid()); + QVERIFY(view->rootModelNode().allDirectSubModelNodes().contains(childNode)); + QVERIFY(childNode.parentProperty().parentModelNode() == view->rootModelNode()); + QCOMPARE(childNode.simplifiedTypeName(), QString("Rectangle")); + + expectedCalls << TestView::MethodCall("nodeCreated", QStringList() << ""); + expectedCalls << TestView::MethodCall("nodeReparented", QStringList() << "" << "data" << "" << "PropertiesAdded"); + QCOMPARE(view->methodCalls(), expectedCalls); + + QVERIFY(childNode.id().isEmpty()); + childNode.setId("Blah"); + QCOMPARE(childNode.id(), QString("Blah")); + + expectedCalls << TestView::MethodCall("nodeIdChanged", QStringList() << "Blah" << "Blah" << ""); + QCOMPARE(view->methodCalls(), expectedCalls); + + try { + childNode.setId(""); + QFAIL("Setting an invalid id does not throw an exception"); + } catch (Exception &exception) { + QCOMPARE(exception.type(), QString("InvalidIdException")); + } + try { + childNode.setId(QString()); + QFAIL("Setting an invalid id does not throw an exception"); + } catch (Exception &exception) { + QCOMPARE(exception.type(), QString("InvalidIdException")); + } + try { + childNode.setId("invalid id"); + QFAIL("Setting an invalid id does not throw an excxeption"); + } catch (Exception &exception) { + QCOMPARE(exception.type(), QString("InvalidIdException")); + } + + QCOMPARE(childNode.id(), QString("Blah")); + QCOMPARE(view->methodCalls(), expectedCalls); +} + + +void TestCore::testTypicalRewriterOperations() +{ + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + ModelNode rootModelNode = view->rootModelNode(); + QCOMPARE(rootModelNode.allDirectSubModelNodes().count(), 0); + + QVERIFY(rootModelNode.property("test").isValid()); + QVERIFY(!rootModelNode.property("test").isVariantProperty()); + QVERIFY(!rootModelNode.property("test").isBindingProperty()); + + QVERIFY(rootModelNode.variantProperty("test").isValid()); + QVERIFY(!rootModelNode.hasProperty("test")); + + rootModelNode.variantProperty("test") = 70; + + QVERIFY(rootModelNode.hasProperty("test")); + QVERIFY(rootModelNode.property("test").isVariantProperty()); + QCOMPARE(rootModelNode.variantProperty("test").value(), QVariant(70)); + + rootModelNode.bindingProperty("test") = "parent.x"; + QVERIFY(!rootModelNode.property("test").isVariantProperty()); + QVERIFY(rootModelNode.property("test").isBindingProperty()); + + QCOMPARE(rootModelNode.bindingProperty("test").expression(), QString("parent.x")); + + ModelNode childNode(rootModelNode.addChildNode("Qt/Rectangle", 4 ,6, "data")); + rootModelNode.nodeListProperty("test").reparentHere(childNode); + QCOMPARE(childNode.parentProperty(), rootModelNode.nodeAbstractProperty("test")); + QVERIFY(rootModelNode.property("test").isNodeAbstractProperty()); + QVERIFY(rootModelNode.property("test").isNodeListProperty()); + QVERIFY(!rootModelNode.property("test").isBindingProperty()); + QVERIFY(childNode.parentProperty().isNodeListProperty()); + QCOMPARE(childNode, childNode.parentProperty().toNodeListProperty().toModelNodeList().first()); + QCOMPARE(rootModelNode, childNode.parentProperty().parentModelNode()); + QCOMPARE(childNode.parentProperty().name(), QString("test")); + + QVERIFY(!rootModelNode.property("test").isVariantProperty()); + rootModelNode.variantProperty("test") = 90; + QVERIFY(rootModelNode.property("test").isVariantProperty()); + QCOMPARE(rootModelNode.variantProperty("test").value(), QVariant(90)); + +} + +void TestCore::testBasicStates() +{ + char qmlString[] = "import Qt 4.6\n" + "Rectangle {\n" + "id: root;\n" + "Rectangle {\n" + "id: rect1;\n" + "}\n" + "Rectangle {\n" + "id: rect2;\n" + "}\n" + "states: [\n" + "State {\n" + "name: \"state1\"\n" + "PropertyChanges {\n" + "target: rect1\n" + "}\n" + "PropertyChanges {\n" + "target: rect2\n" + "}\n" + "}\n" + "," + "State {\n" + "name: \"state2\"\n" + "PropertyChanges {\n" + "target: rect1\n" + "}\n" + "PropertyChanges {\n" + "target: rect2;\n" + "x: 10;\n" + "}\n" + "}\n" + "]\n" + "}\n"; + + Exception::setShouldAssert(true); + + QPlainTextEdit textEdit; + textEdit.setPlainText(qmlString); + PlainTextEditModifier textModifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + ModelNode rootModelNode(view->rootModelNode()); + QVERIFY(rootModelNode.isValid()); + QCOMPARE(rootModelNode.type(), QString("Qt/Item")); + QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + + model->attachView(testRewriterView.data()); + QVERIFY(rootModelNode.isValid()); + QCOMPARE(rootModelNode.type(), QString("Qt/Rectangle")); + + QVERIFY(rootModelNode.hasProperty("data")); + + QVERIFY(rootModelNode.property("data").isNodeListProperty()); + + QCOMPARE(rootModelNode.nodeListProperty("data").toModelNodeList().count(), 2); + + ModelNode rect1 = rootModelNode.nodeListProperty("data").toModelNodeList().first(); + ModelNode rect2 = rootModelNode.nodeListProperty("data").toModelNodeList().last(); + + QVERIFY(QmlItemNode(rect1).isValid()); + QVERIFY(QmlItemNode(rect2).isValid()); + + QVERIFY(QmlItemNode(rootModelNode).isValid()); + + QCOMPARE(QmlItemNode(rootModelNode).states().allStates().count(), 2); + QCOMPARE(QmlItemNode(rootModelNode).states().names().count(), 2); + QCOMPARE(QmlItemNode(rootModelNode).states().names().first(), QString("state1")); + QCOMPARE(QmlItemNode(rootModelNode).states().names().last(), QString("state2")); + + // + // QmlModelState API tests + // + QmlModelState state1 = QmlItemNode(rootModelNode).states().state("state1"); + QmlModelState state2 = QmlItemNode(rootModelNode).states().state("state2"); + + QVERIFY(state1.isValid()); + QVERIFY(state2.isValid()); + + QCOMPARE(state1.propertyChanges().count(), 2); + QCOMPARE(state2.propertyChanges().count(), 2); + + QVERIFY(!state1.hasPropertyChanges(rootModelNode)); + + QVERIFY(state1.propertyChanges(rect1).isValid()); + QVERIFY(state1.propertyChanges(rect2).isValid()); + + state1.propertyChanges(rect2).modelNode().hasProperty("x"); + + QCOMPARE(QmlItemNode(rect1).allAffectingStates().count(), 2); + QCOMPARE(QmlItemNode(rect2).allAffectingStates().count(), 2); + QCOMPARE(QmlItemNode(rootModelNode).allAffectingStates().count(), 0); + QCOMPARE(QmlItemNode(rect1).allAffectingStatesOperations().count(), 2); + QCOMPARE(QmlItemNode(rect2).allAffectingStatesOperations().count(), 2); + + // + // check real state2 object + // + + NodeInstance state2Instance = view->instanceForModelNode(state2.modelNode()); + QVERIFY(state2Instance.isValid()); + QmlState *stateObject = qobject_cast<QmlState*>(const_cast<QObject*>(state2Instance.testHandle())); + QCOMPARE(stateObject->changes()->count(), 2); + QCOMPARE(stateObject->changes()->at(0)->actions().size(), 0); + QCOMPARE(stateObject->changes()->at(1)->actions().size(), 1); + + + // + // actual state switching + // + + // base state + QCOMPARE(view->currentState(), view->baseState()); + NodeInstance rect2Instance = view->instanceForModelNode(rect2); + QVERIFY(rect2Instance.isValid()); + QCOMPARE(rect2Instance.property("x").toInt(), 0); + + int expectedViewMethodCount = view->methodCalls().count(); + + // base state-> state2 + view->setCurrentState(state2); + QCOMPARE(view->currentState(), state2); + QCOMPARE(view->methodCalls().size(), ++expectedViewMethodCount); + QCOMPARE(view->methodCalls().last(), TestView::MethodCall("stateChanged", QStringList() << "state2" << QString())); + QCOMPARE(rect2Instance.property("x").toInt(), 10); + + // state2 -> state1 + view->setCurrentState(state1); + QCOMPARE(view->currentState(), state1); + QCOMPARE(view->methodCalls().size(), ++expectedViewMethodCount); + QCOMPARE(view->methodCalls().last(), TestView::MethodCall("stateChanged", QStringList() << "state1" << "state2")); + QCOMPARE(rect2Instance.property("x").toInt(), 0); + + // state1 -> baseState + view->setCurrentState(view->baseState()); + QCOMPARE(view->currentState(), view->baseState()); + QCOMPARE(view->methodCalls().size(), ++expectedViewMethodCount); + QCOMPARE(view->methodCalls().last(), TestView::MethodCall("stateChanged", QStringList() << QString() << "state1")); + QCOMPARE(rect2Instance.property("x").toInt(), 0); +} + +void TestCore::testModelBasicOperations() +{ + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + ModelNode rootModelNode = view->rootModelNode(); + QCOMPARE(rootModelNode.allDirectSubModelNodes().count(), 0); + + rootModelNode.variantProperty("width").setValue(10); + rootModelNode.variantProperty("height").setValue(10); + + QCOMPARE(rootModelNode.variantProperty("width").value().toInt(), 10); + QCOMPARE(rootModelNode.variantProperty("height").value().toInt(), 10); + + QVERIFY(rootModelNode.hasProperty("width")); + rootModelNode.removeProperty("width"); + QVERIFY(!rootModelNode.hasProperty("width")); + + QVERIFY(!rootModelNode.hasProperty("children")); + ModelNode childNode1(rootModelNode.addChildNode("Qt/Rectangle", 4, 6, "children")); + ModelNode childNode2(rootModelNode.addChildNode("Qt/Rectangle", 4, 6, "data")); + + QVERIFY(childNode1.isValid()); + QVERIFY(childNode2.isValid()); + + QVERIFY(childNode1.parentProperty().parentModelNode().isValid()); + QVERIFY(childNode2.parentProperty().parentModelNode().isValid()); + + QVERIFY(rootModelNode.hasProperty("children")); + childNode2.setParentProperty(rootModelNode, "children"); + QVERIFY(!rootModelNode.hasProperty("data")); + QCOMPARE(childNode2.parentProperty().parentModelNode(), rootModelNode); + QVERIFY(rootModelNode.hasProperty("children")); + childNode2.setParentProperty(rootModelNode, "data"); + childNode1.setParentProperty(rootModelNode, "data"); + QCOMPARE(childNode2.parentProperty().parentModelNode(), rootModelNode); + QVERIFY(!rootModelNode.hasProperty("children")); + QVERIFY(rootModelNode.hasProperty("data")); + + QVERIFY(childNode2.isValid()); + QVERIFY(childNode2.parentProperty().parentModelNode().isValid()); + QCOMPARE(childNode2.parentProperty().parentModelNode(), rootModelNode); + + childNode1.setParentProperty(rootModelNode, "children"); + rootModelNode.removeProperty("data"); + QVERIFY(!childNode2.isValid()); + QVERIFY(!rootModelNode.hasProperty("data")); + + QVERIFY(childNode1.isValid()); + rootModelNode.nodeProperty("front").setModelNode(childNode1); + QVERIFY(rootModelNode.hasProperty("front")); + QCOMPARE(childNode1.parentProperty().parentModelNode(), rootModelNode); + rootModelNode.removeProperty("front"); + QVERIFY(!childNode1.isValid()); +} + +void TestCore::testModelResolveIds() +{ + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + ModelNode rootNode = view->rootModelNode(); + rootNode.setId("rootNode"); + + ModelNode childNode1(rootNode.addChildNode("Qt/Rectangle", 4, 6, "children")); + + ModelNode childNode2(childNode1.addChildNode("Qt/Rectangle", 4, 6, "children")); + childNode2.setId("childNode2"); + childNode2.bindingProperty("test").setExpression("parent.parent"); + + QCOMPARE(childNode2.bindingProperty("test").resolveToModelNode(), rootNode); + childNode1.setId("childNode1"); + childNode2.bindingProperty("test").setExpression("childNode1.parent"); + QCOMPARE(childNode2.bindingProperty("test").resolveToModelNode(), rootNode); + childNode2.bindingProperty("test").setExpression("rootNode"); + QCOMPARE(childNode2.bindingProperty("test").resolveToModelNode(), rootNode); + + ModelNode childNode3(childNode2.addChildNode("Qt/Rectangle", 4, 6, "children")); + childNode3.setId("childNode3"); + childNode2.nodeProperty("front").setModelNode(childNode3); + childNode2.bindingProperty("test").setExpression("childNode3.parent"); + QCOMPARE(childNode2.bindingProperty("test").resolveToModelNode(), childNode2); + childNode2.bindingProperty("test").setExpression("childNode3.parent.parent"); + QCOMPARE(childNode2.bindingProperty("test").resolveToModelNode(), childNode1); + childNode2.bindingProperty("test").setExpression("childNode3.parent.parent.parent"); + QCOMPARE(childNode2.bindingProperty("test").resolveToModelNode(), rootNode); + childNode2.bindingProperty("test").setExpression("childNode3"); + QCOMPARE(childNode2.bindingProperty("test").resolveToModelNode(), childNode3); + childNode2.bindingProperty("test").setExpression("front"); + QCOMPARE(childNode2.bindingProperty("test").resolveToModelNode(), childNode3); + childNode2.bindingProperty("test").setExpression("back"); + QVERIFY(!childNode2.bindingProperty("test").resolveToModelNode().isValid()); + childNode2.bindingProperty("test").setExpression("childNode3.parent.front"); + QCOMPARE(childNode2.bindingProperty("test").resolveToModelNode(), childNode3); + + childNode2.variantProperty("x") = 10; + QCOMPARE(childNode2.variantProperty("x").value().toInt(), 10); + + childNode2.bindingProperty("test").setExpression("childNode3.parent.x"); + QVERIFY(childNode2.bindingProperty("test").resolveToProperty().isVariantProperty()); + QCOMPARE(childNode2.bindingProperty("test").resolveToProperty().toVariantProperty().value().toInt(), 10); + + childNode2.bindingProperty("test").setExpression("childNode3.parent.test"); + QVERIFY(childNode2.bindingProperty("test").resolveToProperty().isBindingProperty()); + QCOMPARE(childNode2.bindingProperty("test").resolveToProperty().toBindingProperty().expression(), QString("childNode3.parent.test")); +} + +void TestCore::testModelNodeListProperty() +{ + QSKIP("Skip this for the time being", SkipAll); + + // + // Test NodeListProperty API + // + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + ModelNode rootNode = view->rootModelNode(); + + // + // Item {} + // + NodeListProperty rootChildren = rootNode.nodeListProperty("children"); + QVERIFY(!rootNode.hasProperty("children")); + QVERIFY(rootChildren.isValid()); + QVERIFY(!rootChildren.isNodeListProperty()); + QVERIFY(rootChildren.isEmpty()); + + ModelNode rectNode = view->createModelNode("Qt/Rectangle", 4, 6); + rootChildren.reparentHere(rectNode); + + // + // Item { children: [ Rectangle {} ] } + // + QVERIFY(rootNode.hasProperty("children")); + QVERIFY(rootChildren.isValid()); + QVERIFY(rootChildren.isNodeListProperty()); + QVERIFY(!rootChildren.isEmpty()); + + ModelNode mouseRegionNode = view->createModelNode("Qt/Item", 4, 6); + NodeListProperty rectChildren = rectNode.nodeListProperty("children"); + rectChildren.reparentHere(mouseRegionNode); + + // + // Item { children: [ Rectangle { children : [ MouseRegion {} ] } ] } + // + QVERIFY(rectNode.hasProperty("children")); + QVERIFY(rectChildren.isValid()); + QVERIFY(rectChildren.isNodeListProperty()); + QVERIFY(!rectChildren.isEmpty()); + + rectNode.destroy(); + + // + // Item { } + // + QVERIFY(!rectNode.isValid()); + QVERIFY(!mouseRegionNode.isValid()); + QVERIFY(!rootNode.hasProperty("children")); + QVERIFY(rootChildren.isValid()); + QVERIFY(!rootChildren.isNodeListProperty()); + QVERIFY(rootChildren.isEmpty()); + QVERIFY(!rectChildren.isValid()); +} + +void TestCore::testBasicOperationsWithView() +{ + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + NodeInstanceView *nodeInstanceView = new NodeInstanceView(model.data()); + model->attachView(nodeInstanceView); + + ModelNode rootModelNode = view->rootModelNode(); + QCOMPARE(rootModelNode.allDirectSubModelNodes().count(), 0); + NodeInstance rootInstance = nodeInstanceView->instanceForNode(rootModelNode); + + QCOMPARE(rootInstance.size().width(), 0.0); + QCOMPARE(rootInstance.size().height(), 0.0); + + QVERIFY(rootInstance.isValid()); + QVERIFY(rootInstance.isQmlGraphicsItem()); + + QVERIFY(rootModelNode.isValid()); + + rootModelNode.variantProperty("width").setValue(10); + rootModelNode.variantProperty("height").setValue(10); + + QCOMPARE(rootModelNode.variantProperty("width").value().toInt(), 10); + QCOMPARE(rootModelNode.variantProperty("height").value().toInt(), 10); + + QCOMPARE(rootInstance.size().width(), 10.0); + QCOMPARE(rootInstance.size().height(), 10.0); + + ModelNode childNode(rootModelNode.addChildNode("Qt/Rectangle", 4, 6, "data")); + ModelNode childNode2(childNode.addChildNode("Qt/Rectangle", 4, 6, "data")); + QVERIFY(childNode2.parentProperty().parentModelNode() == childNode); + + QVERIFY(childNode.isValid()); + + { + NodeInstance childInstance2 = nodeInstanceView->instanceForNode(childNode2); + NodeInstance childInstance = nodeInstanceView->instanceForNode(childNode); + + QVERIFY(childInstance.isValid()); + QVERIFY(childInstance.isQmlGraphicsItem()); + QVERIFY(childInstance2.testHandle()->parent() == childInstance.testHandle()); + QVERIFY(childInstance.testHandle()->parent() == rootInstance.testHandle()); + QCOMPARE(childInstance.size().width(), 0.0); + QCOMPARE(childInstance.size().height(), 0.0); + + + childNode.variantProperty("width").setValue(100); + childNode.variantProperty("height").setValue(100); + + QCOMPARE(childNode.variantProperty("width").value().toInt(), 100); + QCOMPARE(childNode.variantProperty("height").value().toInt(), 100); + + QCOMPARE(childInstance.size().width(), 100.0); + QCOMPARE(childInstance.size().height(), 100.0); + + childNode.destroy(); + QVERIFY(!childNode.isValid()); + QVERIFY(!childNode2.isValid()); + QVERIFY(childInstance.testHandle() == 0); + QVERIFY(childInstance2.testHandle() == 0); + QVERIFY(!childInstance.isValid()); + QVERIFY(!childInstance2.isValid()); + } + + childNode = rootModelNode.addChildNode("Qt/Image", 4, 6, "data"); + QVERIFY(childNode.isValid()); + QCOMPARE(childNode.type(), QString("Qt/Image")); + childNode2 = childNode.addChildNode("Qt/Rectangle", 4, 6, "data"); + QVERIFY(childNode2.isValid()); + childNode2.setParentProperty(rootModelNode, "data"); + QVERIFY(childNode2.isValid()); + + { + NodeInstance childInstance2 = nodeInstanceView->instanceForNode(childNode2); + NodeInstance childInstance = nodeInstanceView->instanceForNode(childNode); + + QVERIFY(childInstance.isValid()); + QVERIFY(childInstance.isQmlGraphicsItem()); + QVERIFY(childInstance2.testHandle()->parent() == rootInstance.testHandle()); + QVERIFY(childInstance.testHandle()->parent() == rootInstance.testHandle()); + QCOMPARE(childInstance.size().width(), 0.0); + QCOMPARE(childInstance.size().height(), 0.0); + + QCOMPARE(rootModelNode, childNode2.parentProperty().parentModelNode()); + + childNode.variantProperty("width").setValue(20); + QCOMPARE(childInstance.property("width").toInt(), 20); + + QCOMPARE(childNode.variantProperty("width").value().toInt(), 20); + childNode.removeProperty("width"); + QVERIFY(!childNode.hasProperty("width")); + + rootModelNode.removeProperty("data"); + QVERIFY(!childNode.isValid()); + QVERIFY(!childNode2.isValid()); + QVERIFY(childInstance.testHandle() == 0); + QVERIFY(childInstance2.testHandle() == 0); + QVERIFY(!childInstance.isValid()); + QVERIFY(!childInstance2.isValid()); + } + + model->detachView(nodeInstanceView); +} + +void TestCore::testQmlModelView() +{ + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + + QmlModelView *view = new TestView(model.data()); + QVERIFY(view); + model->attachView(view); + QVERIFY(view->model()); + QVERIFY(view->rootQmlObjectNode().isValid()); + QVERIFY(!view->rootQmlObjectNode().hasNodeParent()); + QVERIFY(!view->rootQmlObjectNode().hasInstanceParent()); + QVERIFY(view->rootQmlObjectNode().isRootModelNode()); + + PropertyListType propertyList; + propertyList.append(qMakePair(QString("x"), QVariant(20))); + propertyList.append(qMakePair(QString("y"), QVariant(20))); + propertyList.append(qMakePair(QString("width"), QVariant(20))); + propertyList.append(qMakePair(QString("height"), QVariant(20))); + + QmlObjectNode node1 = view->createQmlObjectNode("Qt/Rectangle", 4, 6, propertyList); + + QVERIFY(node1.isValid()); + QVERIFY(!node1.hasNodeParent()); + QVERIFY(!node1.hasInstanceParent()); + + QCOMPARE(node1.instanceValue("x").toInt(), 20); + QCOMPARE(node1.instanceValue("y").toInt(), 20); + + node1.setParentProperty(view->rootQmlObjectNode().nodeAbstractProperty("children")); + + QVERIFY(node1.hasNodeParent()); + QVERIFY(node1.hasInstanceParent()); + QVERIFY(node1.instanceParent() == view->rootQmlObjectNode()); + + + QmlObjectNode node2 = view->createQmlObjectNode("Qt/Rectangle", 4, 6, propertyList); + + QVERIFY(node2.isValid()); + QVERIFY(!node2.hasNodeParent()); + QVERIFY(!node2.hasInstanceParent()); + + node2.setParentProperty(view->rootQmlObjectNode().nodeAbstractProperty("children")); + + QVERIFY(node2.hasNodeParent()); + QVERIFY(node2.hasInstanceParent()); + QVERIFY(node2.instanceParent() == view->rootQmlObjectNode()); + + + node2.setParentProperty(node1.nodeAbstractProperty("children")); + + QVERIFY(node2.hasNodeParent()); + QVERIFY(node2.hasInstanceParent()); + QVERIFY(node2.instanceParent() == node1); + + node2.setParentProperty(view->rootQmlObjectNode().nodeAbstractProperty("children")); + + QCOMPARE(node1.instanceValue("x").toInt(), 20); + + node1.setVariantProperty("x", 2); + + QCOMPARE(node1.instanceValue("x").toInt(), 2); + + + QmlObjectNode node3 = view->createQmlObjectNode("Qt/Rectangle", 4, 6, propertyList); + QmlObjectNode node4 = view->createQmlObjectNode("Qt/Rectangle", 4, 6, propertyList); + QmlObjectNode node5 = view->createQmlObjectNode("Qt/Rectangle", 4, 6, propertyList); + QmlObjectNode node6 = view->createQmlObjectNode("Qt/Rectangle", 4, 6, propertyList); + QmlObjectNode node7 = view->createQmlObjectNode("Qt/Rectangle", 4, 6, propertyList); + QmlObjectNode node8 = view->createQmlObjectNode("Qt/Rectangle", 4, 6, propertyList); + + node3.setParentProperty(node2.nodeAbstractProperty("children")); + node4.setParentProperty(node3.nodeAbstractProperty("children")); + node5.setParentProperty(node4.nodeAbstractProperty("children")); + node6.setParentProperty(node5.nodeAbstractProperty("children")); + node7.setParentProperty(node6.nodeAbstractProperty("children")); + node8.setParentProperty(node7.nodeAbstractProperty("children")); + + QCOMPARE(node2.nodeAbstractProperty("children").allSubNodes().count(), 6); + QVERIFY(node8.isValid()); + node3.destroy(); + QVERIFY(!node8.isValid()); + QCOMPARE(node2.nodeAbstractProperty("children").allSubNodes().count(), 0); + + node1.setId("node1"); + + node2.setVariantProperty("x", 20); + node2.setBindingProperty("x", "node1.x"); + QCOMPARE(node2.instanceValue("x").toInt(), 2); + node1.setVariantProperty("x", 10); + QCOMPARE(node2.instanceValue("x").toInt(), 10); + + node1.destroy(); + QVERIFY(!node1.isValid()); + + QCOMPARE(node2.instanceValue("x").toInt(), 10); // is this right? or should it be a invalid qvariant? + + node1 = view->createQmlObjectNode("Qt/Rectangle", 4, 6, propertyList); + node1.setId("node1"); + + QCOMPARE(node2.instanceValue("x").toInt(), 20); + + node3 = view->createQmlObjectNode("Qt/Rectangle", 4, 6, propertyList); + node3.setParentProperty(node2.nodeAbstractProperty("children")); + QCOMPARE(node3.instanceValue("width").toInt(), 20); + node3.setVariantProperty("width", 0); + QCOMPARE(node3.instanceValue("width").toInt(), 0); + + QCOMPARE(node3.instanceValue("x").toInt(), 20); + QVERIFY(!QmlMetaType::toQObject(node3.instanceValue("anchors.fill"))); + node3.setBindingProperty("anchors.fill", "parent"); + QVERIFY(QmlMetaType::toQObject(node3.instanceValue("anchors.fill"))); + QCOMPARE(node3.instanceValue("x").toInt(), 0); + QCOMPARE(node3.instanceValue("width").toInt(), 20); + + node3.setParentProperty(node1.nodeAbstractProperty("children")); + node1.setVariantProperty("width", 50); + node2.setVariantProperty("width", 100); + QCOMPARE(node3.instanceValue("width").toInt(), 50); + +} + +void TestCore::testModelCreateInvalidSubNode() +{ + QSKIP("type checking not in the model", SkipAll); + ModelNode node; + QVERIFY(!node.isValid()); + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + try { + ModelNode invalidChildNode = view->rootModelNode().addChildNode("InvalidNode", 0, 0, "data"); + QFAIL("Adding an invalid typed node should result in an exception"); + } catch (Exception& exception) { + QCOMPARE(exception.type(), QString("InvalidModelNodeException")); + } +} + +void TestCore::testModelRemoveNode() +{ + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + NodeInstanceView *nodeInstanceView = new NodeInstanceView(model.data()); + model->attachView(nodeInstanceView); + + QCOMPARE(view->rootModelNode().allDirectSubModelNodes().count(), 0); + + + ModelNode childNode = view->rootModelNode().addChildNode("Qt/Rectangle", 4, 6, "data"); + QVERIFY(childNode.isValid()); + QCOMPARE(view->rootModelNode().allDirectSubModelNodes().count(), 1); + QVERIFY(view->rootModelNode().allDirectSubModelNodes().contains(childNode)); + QVERIFY(childNode.parentProperty().parentModelNode() == view->rootModelNode()); + + { + NodeInstance childInstance = nodeInstanceView->instanceForNode(childNode); + QVERIFY(childInstance.isValid()); + QVERIFY(childInstance.parent() == nodeInstanceView->instanceForNode(view->rootModelNode())); + } + + ModelNode subChildNode = childNode.addChildNode("Qt/Rectangle", 4, 6, "data"); + QVERIFY(subChildNode.isValid()); + QCOMPARE(childNode.allDirectSubModelNodes().count(), 1); + QVERIFY(childNode.allDirectSubModelNodes().contains(subChildNode)); + QVERIFY(subChildNode.parentProperty().parentModelNode() == childNode); + + { + NodeInstance subChildInstance = nodeInstanceView->instanceForNode(subChildNode); + QVERIFY(subChildInstance.isValid()); + QVERIFY(subChildInstance.parent() == nodeInstanceView->instanceForNode(childNode)); + } + + childNode.destroy(); + + QVERIFY(!nodeInstanceView->hasInstanceForNode(childNode)); + + QCOMPARE(view->rootModelNode().allDirectSubModelNodes().count(), 0); + QVERIFY(!view->rootModelNode().allDirectSubModelNodes().contains(childNode)); + QVERIFY(!childNode.isValid()); + QVERIFY(!subChildNode.isValid()); + + try { + view->rootModelNode().destroy(); + QFAIL("remove the rootModelNode should be throw a exception"); + } catch (Exception &exception) { + QCOMPARE(exception.type(), QString("InvalidArgumentException")); + } + + QVERIFY(view->rootModelNode().isValid()); + + // delete node not in hierarchy + childNode = view->createModelNode("Qt/Item", 4, 6); + childNode.destroy(); + + model->detachView(nodeInstanceView); +} + +void TestCore::reparentingNode() +{ + QScopedPointer<Model> model(Model::create("Qt/Item")); + + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + ModelNode rootModelNode = view->rootModelNode(); + QVERIFY(rootModelNode.isValid()); + rootModelNode.setId("rootModelNode"); + QCOMPARE(rootModelNode.id(), QString("rootModelNode")); + + + NodeInstanceView *nodeInstanceView = new NodeInstanceView(model.data()); + model->attachView(nodeInstanceView); + + ModelNode childNode = rootModelNode.addChildNode("Qt/Rectangle", 4, 6, "data"); + QCOMPARE(childNode.parentProperty().parentModelNode(), rootModelNode); + QVERIFY(rootModelNode.allDirectSubModelNodes().contains(childNode)); + + { + NodeInstance childInstance = nodeInstanceView->instanceForNode(childNode); + QVERIFY(childInstance.isValid()); + QVERIFY(childInstance.parent() == nodeInstanceView->instanceForNode(view->rootModelNode())); + } + + ModelNode childNode2 = rootModelNode.addChildNode("Qt/Item", 4, 6, "data"); + QCOMPARE(childNode2.parentProperty().parentModelNode(), rootModelNode); + QVERIFY(rootModelNode.allDirectSubModelNodes().contains(childNode2)); + + { + NodeInstance childIstance2 = nodeInstanceView->instanceForNode(childNode2); + QVERIFY(childIstance2.isValid()); + QVERIFY(childIstance2.parent() == nodeInstanceView->instanceForNode(view->rootModelNode())); + } + + childNode.setParentProperty(childNode2, "data"); + + QCOMPARE(childNode.parentProperty().parentModelNode(), childNode2); + QVERIFY(childNode2.allDirectSubModelNodes().contains(childNode)); + QVERIFY(!rootModelNode.allDirectSubModelNodes().contains(childNode)); + QVERIFY(rootModelNode.allDirectSubModelNodes().contains(childNode2)); + + { + NodeInstance childIstance = nodeInstanceView->instanceForNode(childNode); + QVERIFY(childIstance.isValid()); + QVERIFY(childIstance.parent() == nodeInstanceView->instanceForNode(childNode2)); + } + + childNode2.setParentProperty(rootModelNode, "data"); + QCOMPARE(childNode2.parentProperty().parentModelNode(), rootModelNode); + QVERIFY(rootModelNode.allDirectSubModelNodes().contains(childNode2)); + + { + NodeInstance childIstance2 = nodeInstanceView->instanceForNode(childNode2); + QVERIFY(childIstance2.isValid()); + QVERIFY(childIstance2.parent() == nodeInstanceView->instanceForNode(rootModelNode)); + } + + QCOMPARE(childNode.parentProperty().parentModelNode(), childNode2); + + model->detachView(nodeInstanceView); +} + +void TestCore::reparentingNodeLikeDragAndDrop() +{ + QPlainTextEdit textEdit; + textEdit.setPlainText("import Qt 4.6;\n\nItem {\n}\n"); + PlainTextEditModifier textModifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + + QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + model->attachView(testRewriterView.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + NodeInstanceView *nodeInstanceView = new NodeInstanceView(model.data()); + model->attachView(nodeInstanceView); + view->rootModelNode().setId("rootModelNode"); + QCOMPARE(view->rootModelNode().id(), QString("rootModelNode")); + + ModelNode rectNode = view->rootModelNode().addChildNode("Qt/Rectangle", 4, 6, "data"); + rectNode.setId("Rect_1"); + rectNode.variantProperty("x").setValue(20); + rectNode.variantProperty("y").setValue(30); + rectNode.variantProperty("width").setValue(40); + rectNode.variantProperty("height").setValue(50); + + RewriterTransaction transaction(view->beginRewriterTransaction()); + + ModelNode textNode = view->rootModelNode().addChildNode("Qt/Text", 4, 6, "data"); + QCOMPARE(textNode.parentProperty().parentModelNode(), view->rootModelNode()); + QVERIFY(view->rootModelNode().allDirectSubModelNodes().contains(textNode)); + + textNode.setId("Text_1"); + textNode.variantProperty("x").setValue(10); + textNode.variantProperty("y").setValue(10); + textNode.variantProperty("width").setValue(50); + textNode.variantProperty("height").setValue(20); + + textNode.variantProperty("x").setValue(30); + textNode.variantProperty("y").setValue(30); + + { + NodeInstance textInstance = nodeInstanceView->instanceForNode(textNode); + QVERIFY(textInstance.isValid()); + QVERIFY(textInstance.parent() == nodeInstanceView->instanceForNode(view->rootModelNode())); + QCOMPARE(textInstance.position().x(), 30.0); + QCOMPARE(textInstance.position().y(), 30.0); + QCOMPARE(textInstance.size().width(), 50.0); + QCOMPARE(textInstance.size().height(), 20.0); + } + + textNode.setParentProperty(rectNode, "data"); + QCOMPARE(textNode.parentProperty().parentModelNode(), rectNode); + QVERIFY(rectNode.allDirectSubModelNodes().contains(textNode)); + + { + NodeInstance textInstance = nodeInstanceView->instanceForNode(textNode); + QVERIFY(textInstance.isValid()); + QVERIFY(textInstance.parent() == nodeInstanceView->instanceForNode(rectNode)); + QCOMPARE(textInstance.position().x(), 30.0); + QCOMPARE(textInstance.position().y(), 30.0); + QCOMPARE(textInstance.size().width(), 50.0); + QCOMPARE(textInstance.size().height(), 20.0); + } + + textNode.setParentProperty(view->rootModelNode(), "data"); + QCOMPARE(textNode.parentProperty().parentModelNode(), view->rootModelNode()); + QVERIFY(view->rootModelNode().allDirectSubModelNodes().contains(textNode)); + + { + NodeInstance textInstance = nodeInstanceView->instanceForNode(textNode); + QVERIFY(textInstance.isValid()); + QVERIFY(textInstance.parent() == nodeInstanceView->instanceForNode(view->rootModelNode())); + QCOMPARE(textInstance.position().x(), 30.0); + QCOMPARE(textInstance.position().y(), 30.0); + QCOMPARE(textInstance.size().width(), 50.0); + QCOMPARE(textInstance.size().height(), 20.0); + } + + textNode.setParentProperty(rectNode, "data"); + QCOMPARE(textNode.parentProperty().parentModelNode(), rectNode); + QVERIFY(rectNode.allDirectSubModelNodes().contains(textNode)); + + { + NodeInstance textInstance = nodeInstanceView->instanceForNode(textNode); + QVERIFY(textInstance.isValid()); + QVERIFY(textInstance.parent() == nodeInstanceView->instanceForNode(rectNode)); + QCOMPARE(textInstance.position().x(), 30.0); + QCOMPARE(textInstance.position().y(), 30.0); + QCOMPARE(textInstance.size().width(), 50.0); + QCOMPARE(textInstance.size().height(), 20.0); + } + + { + NodeInstance textInstance = nodeInstanceView->instanceForNode(textNode); + QVERIFY(textInstance.isValid()); + QVERIFY(textInstance.parent() == nodeInstanceView->instanceForNode(rectNode)); + QCOMPARE(textInstance.position().x(), 30.0); + QCOMPARE(textInstance.position().y(), 30.0); + QCOMPARE(textInstance.size().width(), 50.0); + QCOMPARE(textInstance.size().height(), 20.0); + } + + transaction.commit(); + + if(!textNode.isValid()) + QFAIL("textnode node is not valid anymore"); + else + QCOMPARE(textNode.parentProperty().parentModelNode(), rectNode); + QVERIFY(rectNode.allDirectSubModelNodes().contains(textNode)); + + model->detachView(nodeInstanceView); +} + +void TestCore::testModelReorderSiblings() +{ + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + NodeInstanceView *nodeInstanceView = new NodeInstanceView(model.data()); + model->attachView(nodeInstanceView); + + ModelNode rootModelNode = view->rootModelNode(); + QVERIFY(rootModelNode.isValid()); + + ModelNode a = rootModelNode.addChildNode("Qt/Rectangle", 4, 6, "data"); + QVERIFY(a.isValid()); + ModelNode b = rootModelNode.addChildNode("Qt/Rectangle", 4, 6, "data"); + QVERIFY(b.isValid()); + ModelNode c = rootModelNode.addChildNode("Qt/Rectangle", 4, 6, "data"); + QVERIFY(c.isValid()); + + { + QVERIFY(nodeInstanceView->instanceForNode(a).parent() == nodeInstanceView->instanceForNode(rootModelNode)); + QVERIFY(nodeInstanceView->instanceForNode(b).parent() == nodeInstanceView->instanceForNode(rootModelNode)); + QVERIFY(nodeInstanceView->instanceForNode(c).parent() == nodeInstanceView->instanceForNode(rootModelNode)); + } + + NodeListProperty listProperty(rootModelNode.nodeListProperty("data")); + + listProperty.slide(listProperty.toModelNodeList().indexOf(a), 3); //a.slideToIndex(3); + + + QVERIFY(a.isValid()); QCOMPARE(listProperty.toModelNodeList().indexOf(a), 2); + QVERIFY(b.isValid()); QCOMPARE(listProperty.toModelNodeList().indexOf(b), 0); + QVERIFY(c.isValid()); QCOMPARE(listProperty.toModelNodeList().indexOf(c), 1); + + + listProperty.slide(listProperty.toModelNodeList().indexOf(c), 0); //c.slideToIndex(0); + + QVERIFY(a.isValid()); QCOMPARE(listProperty.toModelNodeList().indexOf(a), 2); + QVERIFY(b.isValid()); QCOMPARE(listProperty.toModelNodeList().indexOf(b), 1); + QVERIFY(c.isValid()); QCOMPARE(listProperty.toModelNodeList().indexOf(c), 0); + + { + QVERIFY(nodeInstanceView->instanceForNode(a).parent() == nodeInstanceView->instanceForNode(rootModelNode)); + QVERIFY(nodeInstanceView->instanceForNode(b).parent() == nodeInstanceView->instanceForNode(rootModelNode)); + QVERIFY(nodeInstanceView->instanceForNode(c).parent() == nodeInstanceView->instanceForNode(rootModelNode)); + } + + model->detachView(nodeInstanceView); +} + +void TestCore::testModelRootNode() +{ + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + try { + ModelNode rootModelNode = view->rootModelNode(); + QVERIFY(rootModelNode.isValid()); + QVERIFY(rootModelNode.isRootNode()); + ModelNode topChildNode = rootModelNode.addChildNode("Qt/Rectangle", 4, 6, "data"); + QVERIFY(topChildNode.isValid()); + QVERIFY(rootModelNode.isRootNode()); + ModelNode childNode = rootModelNode.addChildNode("Qt/Rectangle", 4, 6, "data"); + QVERIFY(childNode.isValid()); + QVERIFY(rootModelNode.isValid()); + QVERIFY(rootModelNode.isRootNode()); + childNode.setParentProperty(topChildNode, "data"); + QVERIFY(topChildNode.isValid()); + QVERIFY(childNode.isValid()); + QVERIFY(rootModelNode.isValid()); + QVERIFY(rootModelNode.isRootNode()); + } catch (const QmlDesigner::Exception &exception) { + QString errorMsg = tr("Exception: %1 %2 %3:%4").arg(exception.type(), exception.function(), exception.file()).arg(exception.line()); + QFAIL(errorMsg.toLatin1().constData()); + } +} + +void TestCore::reparentingNodeInModificationGroup() +{ + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + ModelNode childNode = view->rootModelNode().addChildNode("Qt/Rectangle", 4, 6, "data"); + ModelNode childNode2 = view->rootModelNode().addChildNode("Qt/Item", 4, 6, "data"); + childNode.variantProperty("x").setValue(10); + childNode.variantProperty("y").setValue(10); + + QCOMPARE(childNode.parentProperty().parentModelNode(), view->rootModelNode()); + QCOMPARE(childNode2.parentProperty().parentModelNode(), view->rootModelNode()); + QVERIFY(view->rootModelNode().allDirectSubModelNodes().contains(childNode)); + QVERIFY(view->rootModelNode().allDirectSubModelNodes().contains(childNode2)); + +// ModificationGroupToken token = model->beginModificationGroup(); + childNode.variantProperty("x").setValue(20); + childNode.variantProperty("y").setValue(20); + childNode.setParentProperty(childNode2, "data"); + childNode.variantProperty("x").setValue(30); + childNode.variantProperty("y").setValue(30); + childNode.variantProperty("x").setValue(1000000); + childNode.variantProperty("y").setValue(1000000); +// model->endModificationGroup(token); + + QCOMPARE(childNode.parentProperty().parentModelNode(), childNode2); + QVERIFY(childNode2.allDirectSubModelNodes().contains(childNode)); + QVERIFY(!view->rootModelNode().allDirectSubModelNodes().contains(childNode)); + QVERIFY(view->rootModelNode().allDirectSubModelNodes().contains(childNode2)); + + childNode.setParentProperty(view->rootModelNode(), "data"); + QVERIFY(childNode.isValid()); + QCOMPARE(childNode2.parentProperty().parentModelNode(), view->rootModelNode()); + + childNode2.setParentProperty(childNode, "data"); + QCOMPARE(childNode2.parentProperty().parentModelNode(), childNode); + QVERIFY(childNode2.isValid()); + QVERIFY(childNode.allDirectSubModelNodes().contains(childNode2)); + + QCOMPARE(childNode2.parentProperty().parentModelNode(), childNode); + childNode2.setParentProperty(view->rootModelNode(), "data"); + QCOMPARE(childNode2.parentProperty().parentModelNode(), view->rootModelNode()); + QVERIFY(childNode2.isValid()); + QVERIFY(view->rootModelNode().allDirectSubModelNodes().contains(childNode2)); +} + +void TestCore::testModelAddAndRemoveProperty() +{ + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + ModelNode node = view->rootModelNode(); + QVERIFY(node.isValid()); + + NodeInstanceView *nodeInstanceView = new NodeInstanceView(model.data()); + model->attachView(nodeInstanceView); + + node.variantProperty("blah").setValue(-1); + QCOMPARE(node.variantProperty("blah").value().toInt(), -1); + QVERIFY(node.hasProperty("blah")); + QCOMPARE(node.variantProperty("blah").value().toInt(), -1); + + { + NodeInstance nodeInstance = nodeInstanceView->instanceForNode(node); +// QCOMPARE(nodeInstance.property("blah").toInt(), -1); + } + + node.variantProperty("customValue").setValue(42); + QCOMPARE(node.variantProperty("customValue").value().toInt(), 42); + + { + NodeInstance nodeInstance = nodeInstanceView->instanceForNode(node); +// QCOMPARE(!nodeInstance.property("customValue").toInt(), 42); + } + + node.variantProperty("x").setValue(42); + QCOMPARE(node.variantProperty("x").value().toInt(), 42); + + { + NodeInstance nodeInstance = nodeInstanceView->instanceForNode(node); + QCOMPARE(nodeInstance.property("x").toInt(), 42); + } + + node.removeProperty("customValue"); + QVERIFY(!node.hasProperty("customValue")); + + node.variantProperty("foo").setValue("bar"); + QVERIFY(node.hasProperty("foo")); + QCOMPARE(node.variantProperty("foo").value().toString(), QString("bar")); + + model->detachView(nodeInstanceView); +} + +void TestCore::testModelViewNotification() +{ + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view1(new TestView); + QVERIFY(view1.data()); + + QScopedPointer<TestView> view2(new TestView); + QVERIFY(view2.data()); + + model->attachView(view2.data()); + model->attachView(view1.data()); + + QCOMPARE(view1->methodCalls().size(), 1); + QCOMPARE(view1->methodCalls().at(0).name,QString("modelAttached")); + QCOMPARE(view2->methodCalls().size(), 1); + QCOMPARE(view2->methodCalls().at(0).name,QString("modelAttached")); + + QList<TestView::MethodCall> expectedCalls; + expectedCalls << TestView::MethodCall("modelAttached", QStringList() << QString::number(reinterpret_cast<long>(model.data()))); + QCOMPARE(view1->methodCalls(), expectedCalls); + QCOMPARE(view2->methodCalls(), expectedCalls); + + ModelNode childNode = view2->rootModelNode().addChildNode("Qt/Rectangle", 4, 6, "data"); + expectedCalls << TestView::MethodCall("nodeCreated", QStringList() << ""); + expectedCalls << TestView::MethodCall("nodeReparented", QStringList() << "" << "data" << "" << "PropertiesAdded"); + QCOMPARE(view1->methodCalls(), expectedCalls); + QCOMPARE(view2->methodCalls(), expectedCalls); + + childNode.setId("supernode"); + expectedCalls << TestView::MethodCall("nodeIdChanged", QStringList() << "supernode" << "supernode" << ""); + QCOMPARE(view1->methodCalls(), expectedCalls); + QCOMPARE(view2->methodCalls(), expectedCalls); + + childNode.variantProperty("visible").setValue(false); + expectedCalls << TestView::MethodCall("variantPropertiesChanged", QStringList() << "visible" << "PropertiesAdded"); + QCOMPARE(view1->methodCalls(), expectedCalls); + QCOMPARE(view2->methodCalls(), expectedCalls); + + childNode.setId("supernode2"); + expectedCalls << TestView::MethodCall("nodeIdChanged", QStringList() << "supernode2" << "supernode2" << "supernode"); + QCOMPARE(view1->methodCalls(), expectedCalls); + QCOMPARE(view2->methodCalls(), expectedCalls); + + childNode.variantProperty("visible").setValue(false); // its tae some value so no notification should be sent + QCOMPARE(view1->methodCalls(), expectedCalls); + QCOMPARE(view2->methodCalls(), expectedCalls); + + childNode.variantProperty("visible").setValue(true); + expectedCalls << TestView::MethodCall("variantPropertiesChanged", QStringList() << "visible" << ""); + QCOMPARE(view1->methodCalls(), expectedCalls); + QCOMPARE(view2->methodCalls(), expectedCalls); + + childNode.bindingProperty("visible").setExpression("false"); + expectedCalls << TestView::MethodCall("propertiesAboutToBeRemoved", QStringList() << "visible"); + expectedCalls << TestView::MethodCall("bindingPropertiesChanged", QStringList() << "visible" << "PropertiesAdded"); + QCOMPARE(view1->methodCalls(), expectedCalls); + QCOMPARE(view2->methodCalls(), expectedCalls); + + childNode.destroy(); + expectedCalls << TestView::MethodCall("nodeAboutToBeRemoved", QStringList() << "supernode2"); + expectedCalls << TestView::MethodCall("nodeRemoved", QStringList() << QString() << "data" << "EmptyPropertiesRemoved"); + QCOMPARE(view1->methodCalls(), expectedCalls); + QCOMPARE(view2->methodCalls(), expectedCalls); + + model->detachView(view1.data()); + expectedCalls << TestView::MethodCall("modelAboutToBeDetached", QStringList() << QString::number(reinterpret_cast<long>(model.data()))); + QCOMPARE(view1->methodCalls(), expectedCalls); +} + + +void TestCore::testRewriterTransaction() +{ + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + RewriterTransaction transaction = view->beginRewriterTransaction(); + QVERIFY(transaction.isValid()); + + ModelNode childNode = view->rootModelNode().addChildNode("Qt/Rectangle", 4, 6, "data"); + QVERIFY(childNode.isValid()); + + childNode.destroy(); + QVERIFY(!childNode.isValid()); + + { + RewriterTransaction transaction2 = view->beginRewriterTransaction(); + QVERIFY(transaction2.isValid()); + + ModelNode childNode = view->rootModelNode().addChildNode("Qt/Rectangle", 4, 6, "data"); + QVERIFY(childNode.isValid()); + + childNode.destroy(); + QVERIFY(!childNode.isValid()); + + RewriterTransaction transaction3(transaction2); + QVERIFY(!transaction2.isValid()); + QVERIFY(transaction3.isValid()); + + transaction2 = transaction3; + QVERIFY(transaction2.isValid()); + QVERIFY(!transaction3.isValid()); + + transaction3 = transaction3; + transaction2 = transaction2; + QVERIFY(transaction2.isValid()); + QVERIFY(!transaction3.isValid()); + } + + QVERIFY(transaction.isValid()); + transaction.commit(); + QVERIFY(!transaction.isValid()); +} + +void TestCore::testRewriterId() +{ + char qmlString[] = "import Qt 4.6\n" + "Rectangle {\n" + "}\n"; + + QPlainTextEdit textEdit; + textEdit.setPlainText(qmlString); + PlainTextEditModifier textModifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + ModelNode rootModelNode(view->rootModelNode()); + QVERIFY(rootModelNode.isValid()); + + QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + + + model->attachView(testRewriterView.data()); + QCOMPARE(rootModelNode.type(), QString("Qt/Rectangle")); + + QVERIFY(rootModelNode.isValid()); + + ModelNode newNode(view->createModelNode("Qt/Rectangle", 4, 6)); + newNode.setId("testId"); + + rootModelNode.nodeListProperty("data").reparentHere(newNode); + + const QLatin1String expected("import Qt 4.6\n" + "Rectangle {\n" + " Rectangle {\n" + " id: testId\n" + " }\n" + "}\n"); + + QCOMPARE(textEdit.toPlainText(), expected); +} + +void TestCore::testRewriterNodeReparentingTransaction1() +{ + char qmlString[] = "import Qt 4.6\n" + "Rectangle {\n" + "}\n"; + +// QSKIP("another asserting test", SkipAll); + + QPlainTextEdit textEdit; + textEdit.setPlainText(qmlString); + PlainTextEditModifier textModifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + ModelNode rootModelNode(view->rootModelNode()); + QVERIFY(rootModelNode.isValid()); + QCOMPARE(rootModelNode.type(), QString("Qt/Item")); + QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + + model->attachView(testRewriterView.data()); + + QVERIFY(rootModelNode.isValid()); + + ModelNode childNode1 = rootModelNode.addChildNode("Qt/Rectangle", 4, 6, "data"); + ModelNode childNode2 = rootModelNode.addChildNode("Qt/Rectangle", 4, 6, "data"); + ModelNode childNode3 = rootModelNode.addChildNode("Qt/Rectangle", 4, 6, "data"); + ModelNode childNode4 = rootModelNode.addChildNode("Qt/Rectangle", 4, 6, "data"); + + ModelNode reparentNode = childNode1.addChildNode("Qt/Rectangle", 4, 6, "data"); + + RewriterTransaction rewriterTransaction = view->beginRewriterTransaction(); + + childNode2.nodeListProperty("data").reparentHere(reparentNode); + childNode3.nodeListProperty("data").reparentHere(reparentNode); + childNode4.nodeListProperty("data").reparentHere(reparentNode); + + reparentNode.destroy(); + + rewriterTransaction.commit(); +} + +void TestCore::testRewriterNodeReparentingTransaction2() +{ + char qmlString[] = "import Qt 4.6\n" + "Rectangle {\n" + "}\n"; + + QPlainTextEdit textEdit; + textEdit.setPlainText(qmlString); + PlainTextEditModifier textModifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + ModelNode rootModelNode(view->rootModelNode()); + QVERIFY(rootModelNode.isValid()); + QCOMPARE(rootModelNode.type(), QString("Qt/Item")); + QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + + model->attachView(testRewriterView.data()); + + QVERIFY(rootModelNode.isValid()); + + ModelNode childNode1 = rootModelNode.addChildNode("Qt/Rectangle", 4, 6, "data"); + ModelNode childNode2 = rootModelNode.addChildNode("Qt/Rectangle", 4, 6, "data"); + + childNode2.variantProperty("x") = 200; + childNode2.variantProperty("y") = 50; + childNode2.variantProperty("color") = QColor(Qt::red); + childNode2.setId("childNode2"); + + childNode1.variantProperty("x") = 100; + childNode1.variantProperty("y") = 10; + childNode1.variantProperty("color") = QColor(Qt::blue); + childNode1.setId("childNode1"); + + RewriterTransaction rewriterTransaction = view->beginRewriterTransaction(); + + childNode1.nodeListProperty("data").reparentHere(childNode2); + childNode2.variantProperty("x") = 300; + childNode2.variantProperty("y") = 150; + + rewriterTransaction.commit(); + + rewriterTransaction = view->beginRewriterTransaction(); + + rootModelNode.nodeListProperty("data").reparentHere(childNode2); + childNode2.variantProperty("x") = 100; + childNode2.variantProperty("y") = 200; + + rewriterTransaction.commit(); + + rewriterTransaction = view->beginRewriterTransaction(); + + rootModelNode.nodeListProperty("data").reparentHere(childNode2); + childNode2.variantProperty("x") = 150; + childNode2.variantProperty("y") = 250; + childNode1.nodeListProperty("data").reparentHere(childNode2); + + rewriterTransaction.commit(); +} + +void TestCore::testRewriterNodeReparentingTransaction3() +{ + char qmlString[] = "import Qt 4.6\n" + "Rectangle {\n" + "}\n"; + + QPlainTextEdit textEdit; + textEdit.setPlainText(qmlString); + PlainTextEditModifier textModifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + ModelNode rootModelNode(view->rootModelNode()); + QVERIFY(rootModelNode.isValid()); + QCOMPARE(rootModelNode.type(), QString("Qt/Item")); + QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + + model->attachView(testRewriterView.data()); + + QVERIFY(rootModelNode.isValid()); + + ModelNode childNode1 = rootModelNode.addChildNode("Qt/Rectangle", 4, 6, "data"); + ModelNode childNode2 = rootModelNode.addChildNode("Qt/Rectangle", 4, 6, "data"); + ModelNode childNode3 = rootModelNode.addChildNode("Qt/Rectangle", 4, 6, "data"); + ModelNode childNode4 = rootModelNode.addChildNode("Qt/Rectangle", 4, 6, "data"); + + RewriterTransaction rewriterTransaction = view->beginRewriterTransaction(); + + childNode1.nodeListProperty("data").reparentHere(childNode4); + childNode4.variantProperty("x") = 151; + childNode4.variantProperty("y") = 251; + childNode2.nodeListProperty("data").reparentHere(childNode4); + childNode4.variantProperty("x") = 152; + childNode4.variantProperty("y") = 252; + childNode3.nodeListProperty("data").reparentHere(childNode4); + childNode4.variantProperty("x") = 153; + childNode4.variantProperty("y") = 253; + rootModelNode.nodeListProperty("data").reparentHere(childNode4); + childNode4.variantProperty("x") = 154; + childNode4.variantProperty("y") = 254; + + rewriterTransaction.commit(); +} + +void TestCore::testRewriterNodeReparentingTransaction4() +{ + char qmlString[] = "import Qt 4.6\n" + "Rectangle {\n" + "}\n"; + + QPlainTextEdit textEdit; + textEdit.setPlainText(qmlString); + PlainTextEditModifier textModifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + ModelNode rootModelNode(view->rootModelNode()); + QVERIFY(rootModelNode.isValid()); + QCOMPARE(rootModelNode.type(), QString("Qt/Item")); + QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + + model->attachView(testRewriterView.data()); + + QVERIFY(rootModelNode.isValid()); + + ModelNode childNode1 = rootModelNode.addChildNode("Qt/Rectangle", 4, 6, "data"); + ModelNode childNode2 = rootModelNode.addChildNode("Qt/Rectangle", 4, 6, "data"); + ModelNode childNode3 = rootModelNode.addChildNode("Qt/Rectangle", 4, 6, "data"); + ModelNode childNode4 = rootModelNode.addChildNode("Qt/Rectangle", 4, 6, "data"); + ModelNode childNode5 = childNode2.addChildNode("Qt/Rectangle", 4, 6, "data"); + + RewriterTransaction rewriterTransaction = view->beginRewriterTransaction(); + + childNode1.nodeListProperty("data").reparentHere(childNode4); + childNode4.variantProperty("x") = 151; + childNode4.variantProperty("y") = 251; + childNode2.nodeListProperty("data").reparentHere(childNode4); + childNode4.variantProperty("x") = 152; + childNode4.variantProperty("y") = 252; + childNode3.nodeListProperty("data").reparentHere(childNode4); + childNode4.variantProperty("x") = 153; + childNode4.variantProperty("y") = 253; + childNode5.nodeListProperty("data").reparentHere(childNode4); + childNode4.variantProperty("x") = 154; + childNode4.variantProperty("y") = 254; + + rewriterTransaction.commit(); +} + +void TestCore::testRewriterAddNodeTransaction() +{ + char qmlString[] = "import Qt 4.6\n" + "Rectangle {\n" + "}\n"; + + QPlainTextEdit textEdit; + textEdit.setPlainText(qmlString); + PlainTextEditModifier textModifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + ModelNode rootModelNode(view->rootModelNode()); + QVERIFY(rootModelNode.isValid()); + QCOMPARE(rootModelNode.type(), QString("Qt/Item")); + QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + + model->attachView(testRewriterView.data()); + + QVERIFY(rootModelNode.isValid()); + + + ModelNode childNode = rootModelNode.addChildNode("Qt/Rectangle", 4, 6, "data"); + + RewriterTransaction rewriterTransaction = view->beginRewriterTransaction(); + + ModelNode newNode = view->createModelNode("Qt/Rectangle", 4, 6); + newNode.variantProperty("x") = 100; + newNode.variantProperty("y") = 100; + + rootModelNode.nodeListProperty("data").reparentHere(newNode); + childNode.nodeListProperty("data").reparentHere(newNode); + + rewriterTransaction.commit(); +} + +void TestCore::testRewriterComponentId() +{ + char qmlString[] = "import Qt 4.6\n" + "Rectangle {\n" + " Component {\n" + " id: testComponent\n" + " Item {\n" + " }\n" + " }\n" + "}\n"; + + QPlainTextEdit textEdit; + textEdit.setPlainText(qmlString); + PlainTextEditModifier textModifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + model->attachView(testRewriterView.data()); + + ModelNode rootModelNode(view->rootModelNode()); + QVERIFY(rootModelNode.isValid()); + QCOMPARE(rootModelNode.type(), QString("Qt/Rectangle")); + + ModelNode component(rootModelNode.allDirectSubModelNodes().first()); + QVERIFY(component.isValid()); + QCOMPARE(component.type(), QString("Qt/Component")); + QCOMPARE(component.id(), QString("testComponent")); +} + +void TestCore::testRewriterTransactionRewriter() +{ + char qmlString[] = "import Qt 4.6\n" + "Rectangle {\n" + "}\n"; + + QPlainTextEdit textEdit; + textEdit.setPlainText(qmlString); + PlainTextEditModifier textModifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + ModelNode rootModelNode(view->rootModelNode()); + QVERIFY(rootModelNode.isValid()); + QCOMPARE(rootModelNode.type(), QString("Qt/Item")); + QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + + model->attachView(testRewriterView.data()); + + QVERIFY(rootModelNode.isValid()); + + ModelNode childNode1; + ModelNode childNode2; + + { + RewriterTransaction transaction = view->beginRewriterTransaction(); + childNode1 = rootModelNode.addChildNode("Qt/Rectangle", 4, 6, "data"); + childNode1.variantProperty("x") = "10"; + childNode1.variantProperty("y") = "10"; + } + + QVERIFY(childNode1.isValid()); + QCOMPARE(childNode1.variantProperty("x").value(), QVariant(10)); + QCOMPARE(childNode1.variantProperty("y").value(), QVariant(10)); + + + { + RewriterTransaction transaction = view->beginRewriterTransaction(); + childNode2 = childNode1.addChildNode("Qt/Rectangle", 4, 6, "data"); + childNode2.destroy(); + } + + QVERIFY(!childNode2.isValid()); + QVERIFY(childNode1.isValid()); +} + +void TestCore::testRewriterPropertyDeclarations() +{ +// QSKIP("Work in progress. See task https://qtrequirements.europe.nokia.com/browse/BAUHAUS-170", +// SkipAll); + + // + // test properties defined in qml + // + // [default] property <type> <name>[: defaultValue] + // + // where type is (int | bool | double | real | string | url | color | date | var | variant) + // + + // Unsupported: + // property var varProperty2: boolProperty + // property var myArray: [ Rectangle {} ] + // property var someGradient: Gradient {} + + char qmlString[] = "import Qt 4.6\n" + "Item {\n" + " property int intProperty\n" + " property bool boolProperty: true\n" + " property var varProperty1\n" + " property variant varProperty2: boolProperty\n" + " default property url urlProperty\n" + " intProperty: 2\n" + "}\n"; + + QPlainTextEdit textEdit; + textEdit.setPlainText(qmlString); + PlainTextEditModifier textModifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + model->attachView(testRewriterView.data()); + + // + // parsing + // + ModelNode rootModelNode(view->rootModelNode()); + QVERIFY(rootModelNode.isValid()); + QCOMPARE(rootModelNode.type(), QString("Qt/Item")); + + QCOMPARE(rootModelNode.properties().size(), 5); + + VariantProperty intProperty = rootModelNode.property(QLatin1String("intProperty")).toVariantProperty(); + QVERIFY(intProperty.isValid()); + QVERIFY(intProperty.isVariantProperty()); + + VariantProperty boolProperty = rootModelNode.property(QLatin1String("boolProperty")).toVariantProperty(); + QVERIFY(boolProperty.isValid()); + QVERIFY(boolProperty.isVariantProperty()); + QCOMPARE(boolProperty.value(), QVariant(true)); + + VariantProperty varProperty1 = rootModelNode.property(QLatin1String("varProperty1")).toVariantProperty(); + QVERIFY(varProperty1.isValid()); + QVERIFY(varProperty1.isVariantProperty()); + QCOMPARE(varProperty1.value(), QVariant()); + + VariantProperty urlProperty = rootModelNode.property(QLatin1String("urlProperty")).toVariantProperty(); + QVERIFY(urlProperty.isValid()); + QVERIFY(urlProperty.isVariantProperty()); + QCOMPARE(urlProperty.value(), QVariant()); +} + +void TestCore::testRewriterPropertyAliases() +{ + // + // test property aliases defined in qml, i.e. + // + // [default] property alias <name>: <alias reference> + // + // where type is (int | bool | double | real | string | url | color | date | var | variant) + // + + char qmlString[] = "import Qt 4.6\n" + "Item {\n" + " property alias theText: t.text\n" + " default alias property yPos: t.y\n" + " Text { id: t; text: \"zoo\"}\n" + "}\n"; + + QPlainTextEdit textEdit; + textEdit.setPlainText(qmlString); + PlainTextEditModifier textModifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + model->attachView(testRewriterView.data()); + + ModelNode rootModelNode(view->rootModelNode()); + QVERIFY(rootModelNode.isValid()); + QCOMPARE(rootModelNode.type(), QString("Qt/Item")); + + QList<AbstractProperty> properties = rootModelNode.properties(); + QCOMPARE(properties.size(), 0); // TODO: How to represent alias properties? As Bindings? +} + +void TestCore::testRewriterPositionAndOffset() +{ + const QLatin1String qmlString("\n" + "import Qt 4.6\n" + "\n" + "Rectangle {\n" + " id: Root\n" + " x: 10;\n" + " y: 10;\n" + " Rectangle {\n" + " id: Rectangle1\n" + " x: 10;\n" + " y: 10;\n" + " }\n" + " Rectangle {\n" + " id: Rectangle2\n" + " x: 100;\n" + " y: 100;\n" + " anchors.fill: Root\n" + " }\n" + " Rectangle {\n" + " id: Rectangle3\n" + " x: 140;\n" + " y: 180;\n" + " gradient: Gradient {\n" + " GradientStop {\n" + " position: 0\n" + " color: \"white\"\n" + " }\n" + " GradientStop {\n" + " position: 1\n" + " color: \"black\"\n" + " }\n" + " }\n" + " }\n" + "}"); + + QPlainTextEdit textEdit; + textEdit.setPlainText(qmlString); + PlainTextEditModifier textModifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item", 4, 6)); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + model->attachView(view.data()); + + // read in + QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + model->attachView(testRewriterView.data()); + + ModelNode rootNode = view->rootModelNode(); + QVERIFY(rootNode.isValid()); + QCOMPARE(rootNode.type(), QLatin1String("Qt/Rectangle")); + + QString string; + string = ""; + for (int i=testRewriterView->nodeOffset(rootNode); i < testRewriterView->nodeOffset(rootNode) + testRewriterView->nodeLength(rootNode);i++) + string +=QString(qmlString)[i]; + + const QString qmlExpected0("Rectangle {\n" + " id: Root\n" + " x: 10;\n" + " y: 10;\n" + " Rectangle {\n" + " id: Rectangle1\n" + " x: 10;\n" + " y: 10;\n" + " }\n" + " Rectangle {\n" + " id: Rectangle2\n" + " x: 100;\n" + " y: 100;\n" + " anchors.fill: Root\n" + " }\n" + " Rectangle {\n" + " id: Rectangle3\n" + " x: 140;\n" + " y: 180;\n" + " gradient: Gradient {\n" + " GradientStop {\n" + " position: 0\n" + " color: \"white\"\n" + " }\n" + " GradientStop {\n" + " position: 1\n" + " color: \"black\"\n" + " }\n" + " }\n" + " }\n" + "}"); + + QCOMPARE(string, qmlExpected0); + + ModelNode lastRectNode = rootNode.allDirectSubModelNodes().last(); + ModelNode gradientNode = lastRectNode.allDirectSubModelNodes().first(); + ModelNode gradientStop = gradientNode.allDirectSubModelNodes().first(); + + string = ""; + for (int i=testRewriterView->nodeOffset(gradientNode); i < testRewriterView->nodeOffset(gradientNode) + testRewriterView->nodeLength(gradientNode);i++) + string +=QString(qmlString)[i]; + + const QString qmlExpected1( "Gradient {\n" + " GradientStop {\n" + " position: 0\n" + " color: \"white\"\n" + " }\n" + " GradientStop {\n" + " position: 1\n" + " color: \"black\"\n" + " }\n" + " }"); + + QCOMPARE(string, qmlExpected1); + + string = ""; + for (int i=testRewriterView->nodeOffset(gradientStop); i < testRewriterView->nodeOffset(gradientStop) + testRewriterView->nodeLength(gradientStop);i++) + string +=QString(qmlString)[i]; + + const QString qmlExpected2( "GradientStop {\n" + " position: 0\n" + " color: \"white\"\n" + " }"); + + QCOMPARE(string, qmlExpected2); +} + +void TestCore::testRewriterComponentTextModifier() +{ + const QString qmlString("import Qt 4.6\n" + "Rectangle {\n" + " id: Root\n" + " x: 10;\n" + " y: 10;\n" + " Rectangle {\n" + " id: Rectangle1\n" + " x: 10;\n" + " y: 10;\n" + " }\n" + " Component {\n" + " id: RectangleComponent\n" + " Rectangle {\n" + " x: 100;\n" + " y: 100;\n" + " }\n" + " }\n" + "}"); + + QPlainTextEdit textEdit; + textEdit.setPlainText(qmlString); + PlainTextEditModifier textModifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item", 4, 6)); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + model->attachView(view.data()); + + // read in + QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + model->attachView(testRewriterView.data()); + + ModelNode rootNode = view->rootModelNode(); + QVERIFY(rootNode.isValid()); + QCOMPARE(rootNode.type(), QLatin1String("Qt/Rectangle")); + + ModelNode componentNode = rootNode.allDirectSubModelNodes().last(); + + int componentStartOffset = testRewriterView->nodeOffset(componentNode); + int componentEndOffset = componentStartOffset + testRewriterView->nodeLength(componentNode); + + int rootStartOffset = testRewriterView->nodeOffset(rootNode); + + ComponentTextModifier componentTextModifier(&textModifier, componentStartOffset, componentEndOffset, rootStartOffset); + + const QString qmlExpected("import Qt 4.6\n" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " Component {\n" + " id: RectangleComponent\n" + " Rectangle {\n" + " x: 100;\n" + " y: 100;\n" + " }\n" + " } " + " "); + + QCOMPARE(componentTextModifier.text(), qmlExpected); + + QScopedPointer<Model> componentModel(Model::create("Qt/Item", 4, 6)); + QScopedPointer<TestRewriterView> testRewriterViewComponent(new TestRewriterView()); + testRewriterViewComponent->setTextModifier(&componentTextModifier); + componentModel->attachView(testRewriterViewComponent.data()); + + ModelNode componentRootNode = testRewriterViewComponent->rootModelNode(); + QVERIFY(componentRootNode.isValid()); + QCOMPARE(componentRootNode.type(), QLatin1String("Qt/Component")); +} + +void TestCore::testRewriterPreserveType() +{ + const QString qmlString("import Qt 4.6\n" + "Rectangle {\n" + " id: root\n" + " Text {\n" + " font.bold: true\n" + " font.pointSize: 14\n" + " }\n" + "}"); + + QPlainTextEdit textEdit; + textEdit.setPlainText(qmlString); + PlainTextEditModifier textModifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item", 4, 6)); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + model->attachView(view.data()); + + // read in + QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + model->attachView(testRewriterView.data()); + + ModelNode rootNode = view->rootModelNode(); + QVERIFY(rootNode.isValid()); + QCOMPARE(rootNode.type(), QLatin1String("Qt/Rectangle")); + + ModelNode textNode = rootNode.allDirectSubModelNodes().first(); + QCOMPARE(QVariant::Bool, textNode.variantProperty("font.bold").value().type()); + QCOMPARE(QVariant::Double, textNode.variantProperty("font.pointSize").value().type()); + textNode.variantProperty("font.bold") = QVariant(false); + textNode.variantProperty("font.bold") = QVariant(true); + textNode.variantProperty("font.pointSize") = QVariant(13.0); + + ModelNode newTextNode = rootNode.addChildNode("Qt/Text", 4, 6, "data"); + + newTextNode.variantProperty("font.bold") = QVariant(true); + newTextNode.variantProperty("font.pointSize") = QVariant(13.0); + + QCOMPARE(QVariant::Bool, newTextNode.variantProperty("font.bold").value().type()); + QCOMPARE(QVariant::Double, newTextNode.variantProperty("font.pointSize").value().type()); +} + +void TestCore::testRewriterForArrayMagic() +{ + const QLatin1String qmlString("import Qt 4.6\n" + "\n" + "Rectangle {\n" + " states: State {\n" + " name: \"s1\"\n" + " }\n" + "}\n"); + QPlainTextEdit textEdit; + textEdit.setPlainText(qmlString); + PlainTextEditModifier textModifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item", 4, 6)); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + model->attachView(view.data()); + + // read in + QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + model->attachView(testRewriterView.data()); + + ModelNode rootNode = view->rootModelNode(); + QVERIFY(rootNode.isValid()); + QCOMPARE(rootNode.type(), QString("Qt/Rectangle")); + + QmlItemNode rootItem(rootNode); + QVERIFY(rootItem.isValid()); + + QmlModelState state1(rootItem.states().addState("s2")); + QCOMPARE(state1.modelNode().type(), QString("Qt/State")); + + const QLatin1String expected("import Qt 4.6\n" + "\n" + "Rectangle {\n" + " states: [\n" + " State {\n" + " name: \"s1\"\n" + " },\n" + " State {\n" + " name: \"s2\"\n" + " }\n" + " ]\n" + "}\n"); + QCOMPARE(textEdit.toPlainText(), expected); +} + +void TestCore::testRewriterWithSignals() +{ + const QLatin1String qmlString("import Qt 4.6\n" + "\n" + "TextEdit {\n" + " onTextChanged: { print(\"foo\"); }\n" + "}\n"); + QPlainTextEdit textEdit; + textEdit.setPlainText(qmlString); + PlainTextEditModifier textModifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item", 4, 6)); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + model->attachView(view.data()); + + // read in + QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + model->attachView(testRewriterView.data()); + + ModelNode rootNode = view->rootModelNode(); + QVERIFY(rootNode.isValid()); + QCOMPARE(rootNode.type(), QString("Qt/TextEdit")); + + QmlItemNode rootItem(rootNode); + QVERIFY(rootItem.isValid()); + + QCOMPARE(rootNode.properties().count(), 0); +} + +void TestCore::testRewriterNodeSliding() +{ + const QLatin1String qmlString("import Qt 4.6\n" + "Rectangle {\n" + " id: root\n" + " Rectangle {\n" + " id: rectangle1\n" + " x: 10;\n" + " y: 10;\n" + " }\n" + "\n" + " Rectangle {\n" + " id: rectangle2\n" + " x: 100;\n" + " y: 100;\n" + " }\n" + "}"); + + QPlainTextEdit textEdit; + textEdit.setPlainText(qmlString); + PlainTextEditModifier textModifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item", 4, 6)); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + model->attachView(view.data()); + + // read in + QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + model->attachView(testRewriterView.data()); + + ModelNode rootNode = view->rootModelNode(); + QVERIFY(rootNode.isValid()); + QCOMPARE(rootNode.type(), QLatin1String("Qt/Rectangle")); + QCOMPARE(rootNode.id(), QLatin1String("root")); + + QCOMPARE(rootNode.nodeListProperty(QLatin1String("data")).toModelNodeList().at(0).id(), QLatin1String("rectangle1")); + QCOMPARE(rootNode.nodeListProperty(QLatin1String("data")).toModelNodeList().at(1).id(), QLatin1String("rectangle2")); + + rootNode.nodeListProperty(QLatin1String("data")).slide(0, 1); + + QCOMPARE(rootNode.nodeListProperty(QLatin1String("data")).toModelNodeList().at(0).id(), QLatin1String("rectangle2")); + QCOMPARE(rootNode.nodeListProperty(QLatin1String("data")).toModelNodeList().at(1).id(), QLatin1String("rectangle1")); + + rootNode.nodeListProperty(QLatin1String("data")).slide(1, 0); + + QCOMPARE(rootNode.nodeListProperty(QLatin1String("data")).toModelNodeList().at(0).id(), QLatin1String("rectangle1")); + QCOMPARE(rootNode.nodeListProperty(QLatin1String("data")).toModelNodeList().at(1).id(), QLatin1String("rectangle2")); +} + +void TestCore::testRewriterFirstDefinitionInside() +{ + const QString qmlString("import Qt 4.6\n" + "Rectangle {\n" + " id: Root\n" + " x: 10;\n" + " y: 10;\n" + " Rectangle {\n" + " id: Rectangle1\n" + " x: 10;\n" + " y: 10;\n" + " }\n" + " Component {\n" + " id: RectangleComponent\n" + " Rectangle {\n" + " x: 100;\n" + " y: 100;\n" + " }\n" + " }\n" + "}"); + + + QPlainTextEdit textEdit; + textEdit.setPlainText(qmlString); + PlainTextEditModifier textModifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item", 4, 6)); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + model->attachView(view.data()); + + // read in + QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + model->attachView(testRewriterView.data()); + + ModelNode rootNode = view->rootModelNode(); + QVERIFY(rootNode.isValid()); + QCOMPARE(rootNode.type(), QLatin1String("Qt/Rectangle")); + + ModelNode componentNode = rootNode.allDirectSubModelNodes().last(); + + QString string = ""; + for (int i = testRewriterView->firstDefinitionInsideOffset(componentNode); i < testRewriterView->firstDefinitionInsideOffset(componentNode) + testRewriterView->firstDefinitionInsideLength(componentNode);i++) + string +=QString(qmlString)[i]; + + const QString qmlExpected("Rectangle {\n" + " x: 100;\n" + " y: 100;\n" + " }"); + QCOMPARE(string, qmlExpected); + +} + +void TestCore::testCopyModelRewriter1() +{ + const QLatin1String qmlString("\n" + "import Qt 4.6\n" + "\n" + "Rectangle {\n" + " id: Root\n" + " x: 10;\n" + " y: 10;\n" + " Rectangle {\n" + " id: Rectangle1\n" + " x: 10;\n" + " y: 10;\n" + " }\n" + " Rectangle {\n" + " id: Rectangle2\n" + " x: 100;\n" + " y: 100;\n" + " anchors.fill: Root\n" + " }\n" + " Rectangle {\n" + " id: Rectangle3\n" + " x: 140;\n" + " y: 180;\n" + " gradient: Gradient {\n" + " GradientStop {\n" + " position: 0\n" + " color: \"white\"\n" + " }\n" + " GradientStop {\n" + " position: 1\n" + " color: \"black\"\n" + " }\n" + " }\n" + " }\n" + "}"); + + QPlainTextEdit textEdit1; + textEdit1.setPlainText(qmlString); + PlainTextEditModifier textModifier1(&textEdit1); + + QScopedPointer<Model> model1(Model::create("Qt/Item", 4, 6)); + QVERIFY(model1.data()); + + QScopedPointer<TestView> view1(new TestView); + model1->attachView(view1.data()); + + // read in 1 + QScopedPointer<TestRewriterView> testRewriterView1(new TestRewriterView()); + testRewriterView1->setTextModifier(&textModifier1); + model1->attachView(testRewriterView1.data()); + + ModelNode rootNode1 = view1->rootModelNode(); + QVERIFY(rootNode1.isValid()); + QCOMPARE(rootNode1.type(), QLatin1String("Qt/Rectangle")); + + QPlainTextEdit textEdit2; + textEdit2.setPlainText(qmlString); + PlainTextEditModifier textModifier2(&textEdit2); + + QScopedPointer<Model> model2(Model::create("Qt/Item", 4, 6)); + QVERIFY(model2.data()); + + QScopedPointer<TestView> view2(new TestView); + model2->attachView(view2.data()); + + // read in 2 + QScopedPointer<TestRewriterView> testRewriterView2(new TestRewriterView()); + testRewriterView2->setTextModifier(&textModifier2); + model2->attachView(testRewriterView2.data()); + + ModelNode rootNode2 = view2->rootModelNode(); + QVERIFY(rootNode2.isValid()); + QCOMPARE(rootNode2.type(), QLatin1String("Qt/Rectangle")); + + + // + + ModelNode childNode(rootNode1.allDirectSubModelNodes().first()); + QVERIFY(childNode.isValid()); + + RewriterTransaction transaction(view1->beginRewriterTransaction()); + + ModelMerger merger(view1.data()); + + ModelNode insertedNode(merger.insertModel(rootNode2)); + transaction.commit(); + + QCOMPARE(insertedNode.id(), QString("Root1")); + + QVERIFY(insertedNode.isValid()); + childNode.nodeListProperty("data").reparentHere(insertedNode); + + + + const QLatin1String expected( + + "\n" + "import Qt 4.6\n" + "\n" + "Rectangle {\n" + " id: Root\n" + " x: 10;\n" + " y: 10;\n" + " Rectangle {\n" + " id: Rectangle1\n" + " x: 10;\n" + " y: 10;\n" + "\n" + " Rectangle {\n" + " id: Root1\n" + " x: 10\n" + " y: 10\n" + " Rectangle {\n" + " id: Rectangle11\n" + " x: 10\n" + " y: 10\n" + " }\n" + "\n" + " Rectangle {\n" + " id: Rectangle21\n" + " x: 100\n" + " y: 100\n" + " anchors.fill: Root1\n" + " }\n" + "\n" + " Rectangle {\n" + " id: Rectangle31\n" + " x: 140\n" + " y: 180\n" + " gradient: Gradient {\n" + " GradientStop {\n" + " position: 0\n" + " color: \"#ffffff\"\n" + " }\n" + "\n" + " GradientStop {\n" + " position: 1\n" + " color: \"#000000\"\n" + " }\n" + " }\n" + " }\n" + " }\n" + " }\n" + " Rectangle {\n" + " id: Rectangle2\n" + " x: 100;\n" + " y: 100;\n" + " anchors.fill: Root\n" + " }\n" + " Rectangle {\n" + " id: Rectangle3\n" + " x: 140;\n" + " y: 180;\n" + " gradient: Gradient {\n" + " GradientStop {\n" + " position: 0\n" + " color: \"white\"\n" + " }\n" + " GradientStop {\n" + " position: 1\n" + " color: \"black\"\n" + " }\n" + " }\n" + " }\n" + "}"); + + QCOMPARE(textEdit1.toPlainText(), expected); +} + +void TestCore::testCopyModelRewriter2() +{ + const QLatin1String qmlString1("\n" + "import Qt 4.6\n" + "\n" + "Rectangle {\n" + " id: Root\n" + " x: 10\n" + " y: 10\n" + "\n" + " Rectangle {\n" + " id: Rectangle1\n" + " x: 10\n" + " y: 10\n" + " }\n" + "\n" + " Rectangle {\n" + " id: Rectangle2\n" + " x: 100\n" + " y: 100\n" + " anchors.fill: Rectangle1\n" + " }\n" + "\n" + " Rectangle {\n" + " id: Rectangle3\n" + " x: 140\n" + " y: 180\n" + " gradient: Gradient {\n" + " GradientStop {\n" + " position: 0\n" + " color: \"#ffffff\"\n" + " }\n" + "\n" + " GradientStop {\n" + " position: 1\n" + " color: \"#000000\"\n" + " }\n" + " }\n" + " }\n" + "}"); + + + const QLatin1String qmlString2("\n" + "import Qt 4.6\n" + "\n" + "Rectangle {\n" + "}"); + + QPlainTextEdit textEdit1; + textEdit1.setPlainText(qmlString1); + PlainTextEditModifier textModifier1(&textEdit1); + + QScopedPointer<Model> model1(Model::create("Qt/Item", 4, 6)); + QVERIFY(model1.data()); + + QScopedPointer<TestView> view1(new TestView); + model1->attachView(view1.data()); + + // read in 1 + QScopedPointer<TestRewriterView> testRewriterView1(new TestRewriterView()); + testRewriterView1->setTextModifier(&textModifier1); + model1->attachView(testRewriterView1.data()); + + ModelNode rootNode1 = view1->rootModelNode(); + QVERIFY(rootNode1.isValid()); + QCOMPARE(rootNode1.type(), QLatin1String("Qt/Rectangle")); + + + // read in 2 + + QPlainTextEdit textEdit2; + textEdit2.setPlainText(qmlString2); + PlainTextEditModifier textModifier2(&textEdit2); + + QScopedPointer<Model> model2(Model::create("Qt/Item", 4, 6)); + QVERIFY(model2.data()); + + QScopedPointer<TestView> view2(new TestView); + model2->attachView(view2.data()); + + QScopedPointer<TestRewriterView> testRewriterView2(new TestRewriterView()); + testRewriterView2->setTextModifier(&textModifier2); + model2->attachView(testRewriterView2.data()); + + ModelNode rootNode2 = view2->rootModelNode(); + QVERIFY(rootNode2.isValid()); + QCOMPARE(rootNode2.type(), QLatin1String("Qt/Rectangle")); + + + // + + ModelMerger merger(view2.data()); + + merger.replaceModel(rootNode1); + QVERIFY(rootNode2.isValid()); + QCOMPARE(rootNode2.type(), QLatin1String("Qt/Rectangle")); + + QCOMPARE(textEdit2.toPlainText(), qmlString1); +} + + +void TestCore::loadQml() +{ +char qmlString[] = "import Qt 4.6\n" + "Rectangle {\n" + "id: root;\n" + "width: 200;\n" + "height: 200;\n" + "color: \"white\";\n" + "Text {\n" + "id: text1\n" + "text: \"Hello World\"\n" + "anchors.centerIn: parent\n" + "Item {\n" + "id: item\n" + "}\n" + "}\n" + "Rectangle {\n" + "id: rectangle;\n" + "gradient: Gradient {\n" + "GradientStop {\n" + "position: 0\n" + "color: \"white\"\n" + "}\n" + "GradientStop {\n" + "position: 1\n" + "color: \"black\"\n" + "}\n" + "}\n" + "}\n" + "Text {\n" + "text: \"text\"\n" + "x: 66\n" + "y: 43\n" + "width: 80\n" + "height: 20\n" + "id: text2\n" + "}\n" + "}\n"; + + QPlainTextEdit textEdit; + textEdit.setPlainText(qmlString); + PlainTextEditModifier textModifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + ModelNode rootModelNode(view->rootModelNode()); + QVERIFY(rootModelNode.isValid()); + QCOMPARE(rootModelNode.type(), QString("Qt/Item")); + QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + + model->attachView(testRewriterView.data()); + + QVERIFY(rootModelNode.isValid()); + QCOMPARE(rootModelNode.type(), QString("Qt/Rectangle")); + QCOMPARE(rootModelNode.id(), QString("root")); + QCOMPARE(rootModelNode.variantProperty("width").value().toInt(), 200); + QCOMPARE(rootModelNode.variantProperty("height").value().toInt(), 200); + QVERIFY(rootModelNode.hasProperty("data")); + QVERIFY(rootModelNode.property("data").isNodeListProperty()); + QVERIFY(!rootModelNode.nodeListProperty("data").toModelNodeList().isEmpty()); + QCOMPARE(rootModelNode.nodeListProperty("data").toModelNodeList().count(), 3); + ModelNode textNode1 = rootModelNode.nodeListProperty("data").toModelNodeList().first(); + QVERIFY(textNode1.isValid()); + QCOMPARE(textNode1.type(), QString("Qt/Text")); + QCOMPARE(textNode1.id(), QString("text1")); + QCOMPARE(textNode1.variantProperty("text").value().toString(), QString("Hello World")); + QVERIFY(textNode1.hasProperty("anchors.centerIn")); + QVERIFY(textNode1.property("anchors.centerIn").isBindingProperty()); + QCOMPARE(textNode1.bindingProperty("anchors.centerIn").expression(), QString("parent")); + QVERIFY(textNode1.hasProperty("data")); + QVERIFY(textNode1.property("data").isNodeListProperty()); + QVERIFY(!textNode1.nodeListProperty("data").toModelNodeList().isEmpty()); + ModelNode itemNode = textNode1.nodeListProperty("data").toModelNodeList().first(); + QVERIFY(itemNode.isValid()); + QCOMPARE(itemNode.id(), QString("item")); + QVERIFY(!itemNode.hasProperty("data")); + + ModelNode rectNode = rootModelNode.nodeListProperty("data").toModelNodeList().at(1); + QVERIFY(rectNode.isValid()); + QCOMPARE(rectNode.type(), QString("Qt/Rectangle")); + QCOMPARE(rectNode.id(), QString("rectangle")); + QVERIFY(rectNode.hasProperty("gradient")); + QVERIFY(rectNode.property("gradient").isNodeProperty()); + ModelNode gradientNode = rectNode.nodeProperty("gradient").modelNode(); + QVERIFY(gradientNode.isValid()); + QCOMPARE(gradientNode.type(), QString("Qt/Gradient")); + QVERIFY(gradientNode.hasProperty("stops")); + QVERIFY(gradientNode.property("stops").isNodeListProperty()); + QCOMPARE(gradientNode.nodeListProperty("stops").toModelNodeList().count(), 2); + + ModelNode stop1 = gradientNode.nodeListProperty("stops").toModelNodeList().first(); + ModelNode stop2 = gradientNode.nodeListProperty("stops").toModelNodeList().last(); + + QVERIFY(stop1.isValid()); + QVERIFY(stop2.isValid()); + + QCOMPARE(stop1.type(), QString("Qt/GradientStop")); + QCOMPARE(stop2.type(), QString("Qt/GradientStop")); + + QCOMPARE(stop1.variantProperty("position").value().toInt(), 0); + QCOMPARE(stop2.variantProperty("position").value().toInt(), 1); + + ModelNode textNode2 = rootModelNode.nodeListProperty("data").toModelNodeList().last(); + QVERIFY(textNode2.isValid()); + QCOMPARE(textNode2.type(), QString("Qt/Text")); + QCOMPARE(textNode2.id(), QString("text2")); + QCOMPARE(textNode2.variantProperty("width").value().toInt(), 80); + QCOMPARE(textNode2.variantProperty("height").value().toInt(), 20); + QCOMPARE(textNode2.variantProperty("x").value().toInt(), 66); + QCOMPARE(textNode2.variantProperty("y").value().toInt(), 43); + QCOMPARE(textNode2.variantProperty("text").value().toString(), QString("text")); +} + +void TestCore::testMetaInfo() +{ + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + QVERIFY(model->metaInfo().hasNodeMetaInfo("Qt/Item")); + QVERIFY(model->metaInfo().hasNodeMetaInfo("Qt/QtObject")); + QVERIFY(model->metaInfo().isSubclassOf("Qt/Text", "Qt/QtObject")); + QVERIFY(model->metaInfo().isSubclassOf("Qt/Text", "Qt/Item")); + QVERIFY(model->metaInfo().isSubclassOf("Qt/Rectangle", "Qt/QtObject")); + + QVERIFY(!model->metaInfo().isSubclassOf("Fooo", "Qt/QtObject")); + + QVERIFY(model->metaInfo().nodeMetaInfo("Qt/Item", 4, 6).isContainer()); + + QCOMPARE(view->rootModelNode().metaInfo().typeName(), QString("Qt/Item")); + QVERIFY(view->rootModelNode().metaInfo().hasProperty("x")); + QVERIFY(!view->rootModelNode().metaInfo().hasProperty("blah")); + QVERIFY(!view->rootModelNode().metaInfo().property("blah").isValid()); + QVERIFY(view->rootModelNode().metaInfo().isContainer()); + + ModelNode rectNode = view->rootModelNode().addChildNode("Qt/Rectangle", 4, 6, "data"); + + QVERIFY(rectNode.metaInfo().isSubclassOf("Qt/QtObject")); + QVERIFY(rectNode.metaInfo().isSubclassOf("Qt/Item")); + QVERIFY(!rectNode.metaInfo().isSubclassOf("Qt/Text")); + QVERIFY(rectNode.metaInfo().hasProperty("color")); + QVERIFY(rectNode.metaInfo().hasProperty("x")); + QVERIFY(!rectNode.metaInfo().hasProperty("blah")); + QVERIFY(rectNode.metaInfo().isContainer()); + + ModelNode textNode = view->rootModelNode().addChildNode("Qt/TextEdit", 4, 6, "data"); + NodeMetaInfo textNodeMetaInfo = textNode.metaInfo(); + QVERIFY(textNodeMetaInfo.hasProperty("text")); + QVERIFY(textNodeMetaInfo.property("text").isValid()); + QVERIFY(textNodeMetaInfo.property("text").isReadable()); + QVERIFY(textNodeMetaInfo.property("text").isWriteable()); + QVERIFY(textNodeMetaInfo.property("x").isReadable()); + QVERIFY(textNodeMetaInfo.property("x").isWriteable()); + QVERIFY(textNodeMetaInfo.isSubclassOf("Qt/Item", 4, 6)); + QVERIFY(!textNodeMetaInfo.isSubclassOf("Blah")); + QVERIFY(textNodeMetaInfo.isQmlGraphicsItem()); + QVERIFY(textNodeMetaInfo.isGraphicsObject()); + QVERIFY(!textNodeMetaInfo.isGraphicsWidget()); + + // test types declared with EXTENDED_OBJECT + NodeMetaInfo graphicsWidgetInfo = model->metaInfo().nodeMetaInfo("Qt/QGraphicsWidget", 4, 6); + QVERIFY(graphicsWidgetInfo.isValid()); + QVERIFY(graphicsWidgetInfo.hasProperty("layout")); // from QGraphicsWidgetDeclarativeUI + QVERIFY(graphicsWidgetInfo.hasProperty("font")); // from QGraphicsWidget + QVERIFY(graphicsWidgetInfo.hasProperty("enabled")); // from QGraphicsItem +} + +void TestCore::testMetaInfoDotProperties() +{ + QScopedPointer<Model> model(Model::create("Qt/Text")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + QVERIFY(model->metaInfo().hasNodeMetaInfo("Qt/Text")); + QCOMPARE(view->rootModelNode().metaInfo().typeName(), QString("Qt/Text")); + QVERIFY(!view->rootModelNode().metaInfo().property("text").isValueType()); + QVERIFY(view->rootModelNode().metaInfo().hasProperty("font")); + QVERIFY(view->rootModelNode().metaInfo().property("font").isValueType()); + + QVERIFY(view->rootModelNode().metaInfo().hasProperty("font.bold", true)); + QVERIFY(view->rootModelNode().metaInfo().properties(true).keys().contains("font.bold")); + QVERIFY(!view->rootModelNode().metaInfo().properties().keys().contains("font.bold")); + QVERIFY(view->rootModelNode().metaInfo().properties(true).keys().contains("font.pointSize")); + QVERIFY(view->rootModelNode().metaInfo().hasProperty("font.pointSize", true)); + QVERIFY(view->rootModelNode().metaInfo().property("font.pointSize", true).isValid()); + + ModelNode rectNode(view->rootModelNode().addChildNode("Qt/Rectangle", 4, 6, "data")); + + + QVERIFY(rectNode.metaInfo().properties(true).keys().contains("pos.x")); + QVERIFY(!rectNode.metaInfo().properties().keys().contains("pos.x")); + QVERIFY(rectNode.metaInfo().properties(true).keys().contains("pos.y")); + QVERIFY(!rectNode.metaInfo().properties().keys().contains("pos.y")); + QVERIFY(rectNode.metaInfo().properties(true).keys().contains("border.width")); + QVERIFY(rectNode.metaInfo().hasProperty("border")); + QVERIFY(!rectNode.metaInfo().property("border").isValueType()); + QVERIFY(rectNode.metaInfo().hasProperty("border.width", true)); + QVERIFY(rectNode.metaInfo().property("border.width", true).isValid()); +} + +void TestCore::testMetaInfoListProperties() +{ + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + QVERIFY(model->metaInfo().hasNodeMetaInfo("Qt/Item")); + QCOMPARE(view->rootModelNode().metaInfo().typeName(), QString("Qt/Item")); + + QVERIFY(view->rootModelNode().metaInfo().hasProperty("states")); + QVERIFY(view->rootModelNode().metaInfo().property("states").isListProperty()); + QVERIFY(view->rootModelNode().metaInfo().hasProperty("children")); + QVERIFY(view->rootModelNode().metaInfo().property("children").isListProperty()); + QVERIFY(view->rootModelNode().metaInfo().hasProperty("data")); + QVERIFY(view->rootModelNode().metaInfo().property("data").isListProperty()); + QVERIFY(view->rootModelNode().metaInfo().hasProperty("resources")); + QVERIFY(view->rootModelNode().metaInfo().property("resources").isListProperty()); + QVERIFY(view->rootModelNode().metaInfo().hasProperty("transitions")); + QVERIFY(view->rootModelNode().metaInfo().property("transitions").isListProperty()); + QVERIFY(view->rootModelNode().metaInfo().hasProperty("transform")); + QVERIFY(view->rootModelNode().metaInfo().property("transform").isListProperty()); + + QVERIFY(view->rootModelNode().metaInfo().hasProperty("effect")); + QVERIFY(!view->rootModelNode().metaInfo().property("effect").isListProperty()); + QVERIFY(view->rootModelNode().metaInfo().hasProperty("parent")); + QVERIFY(!view->rootModelNode().metaInfo().property("parent").isListProperty()); +} + +void TestCore::subItemMetaInfo() +{ + QSKIP("no rewriter anymore", SkipAll); +// QFile file(QCoreApplication::applicationDirPath() + "/../tests/data/fx/topitem.qml"); +// QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text)); +// +// QList<QmlError> errors; +// QScopedPointer<ByteArrayModifier> modifier1(ByteArrayModifier::create(QString(file.readAll()))); +// QScopedPointer<Model> model(Model::create(modifier1.data(), QUrl::fromLocalFile(file.fileName()), &errors)); +// QVERIFY(errors.isEmpty()); +// QVERIFY(model.data()); +// +// ModelNode firstChild = view->rootModelNode().childNodes().at(0); +// QVERIFY(firstChild.isValid()); +// QCOMPARE(firstChild.type(), QString("SubItem")); +// NodeMetaInfo firstMetaInfo = firstChild.metaInfo(); +// QVERIFY(firstMetaInfo.isValid()); +// QList<NodeMetaInfo> superClasses = firstMetaInfo.directSuperClasses(); +// QVERIFY(superClasses.contains(model->metaInfo().nodeMetaInfo("Qt/Rectangle", 4, 6, "data"))); +// QVERIFY(model->metaInfo().nodeMetaInfo("Qt/Rectangle", 4, 6, "data").property("x").isValid()); +// QVERIFY(firstMetaInfo.property("x").isValid()); +// +//#ifdef CUSTOM_PROPERTIES_SHOW_UP_IN_THE_DOM +// QVERIFY(firstMetaInfo.property("text").isValid()); +// QCOMPARE(firstMetaInfo.property("text").variantTypeId(), QVariant::String); +//#endif // CUSTOM_PROPERTIES_SHOW_UP_IN_THE_DOM +} + +void TestCore::testStatesRewriter() +{ + QPlainTextEdit textEdit; + textEdit.setPlainText("import Qt 4.6; Item {}\n"); + PlainTextEditModifier modifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + TestRewriterView *testRewriterView = new TestRewriterView(model.data()); + testRewriterView->setTextModifier(&modifier); + model->attachView(testRewriterView); + + QVERIFY(testRewriterView->errors().isEmpty()); + + ModelNode rootModelNode(view->rootModelNode()); + + QVERIFY(rootModelNode.isValid()); + + QVERIFY(QmlItemNode(rootModelNode).isValid()); + + QVERIFY(QmlItemNode(rootModelNode).states().allStates().isEmpty()); + + QmlModelState state1 = QmlItemNode(rootModelNode).states().addState("state 1"); + QVERIFY(state1.isValid()); + QVERIFY(!QmlItemNode(rootModelNode).states().allStates().isEmpty()); + QCOMPARE(QmlItemNode(rootModelNode).states().allStates().count(), 1); + + QmlModelState state2 = QmlItemNode(rootModelNode).states().addState("state 2"); + QVERIFY(state2.isValid()); + + QCOMPARE(QmlItemNode(rootModelNode).states().allStates().count(), 2); + + QmlItemNode(rootModelNode).states().removeState("state 1"); + QVERIFY(!state1.isValid()); + + QmlItemNode(rootModelNode).states().removeState("state 2"); + QVERIFY(!state2.isValid()); + + QVERIFY(QmlItemNode(rootModelNode).states().allStates().isEmpty()); + +} + + +void TestCore::testGradientsRewriter() +{ +// QSKIP("this crashes in the rewriter", SkipAll); + + QPlainTextEdit textEdit; + textEdit.setPlainText("\nimport Qt 4.6\n\nItem {\n}\n"); + PlainTextEditModifier modifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + TestRewriterView *testRewriterView = new TestRewriterView(model.data()); + testRewriterView->setTextModifier(&modifier); + model->attachView(testRewriterView); + + QVERIFY(testRewriterView->errors().isEmpty()); + + ModelNode rootModelNode(view->rootModelNode()); + + QVERIFY(rootModelNode.isValid()); + + ModelNode rectNode(rootModelNode.addChildNode("Qt/Rectangle", 4, 6, "data")); + + const QLatin1String expected1("\nimport Qt 4.6\n" + "\n" + "Item {\n" + " Rectangle {\n" + " }\n" + "}\n"); + QCOMPARE(textEdit.toPlainText(), expected1); + + ModelNode gradientNode(rectNode.addChildNode("Qt/Gradient", 4, 6, "gradient")); + + QVERIFY(rectNode.hasNodeProperty("gradient")); + + const QLatin1String expected2("\nimport Qt 4.6\n" + "\n" + "Item {\n" + " Rectangle {\n" + " gradient: Gradient {\n" + " }\n" + " }\n" + "}\n"); + QCOMPARE(textEdit.toPlainText(), expected2); + + NodeListProperty stops(gradientNode.nodeListProperty("stops")); + + QList<QPair<QString, QVariant> > propertyList; + + propertyList.append(qMakePair(QString("position"), QVariant::fromValue(0))); + propertyList.append(qMakePair(QString("color"), QVariant::fromValue(QColor(Qt::red)))); + + ModelNode gradientStop1(gradientNode.view()->createModelNode("Qt/GradientStop", 4, 6, propertyList)); + QVERIFY(gradientStop1.isValid()); + stops.reparentHere(gradientStop1); + + const QLatin1String expected3("\nimport Qt 4.6\n" + "\n" + "Item {\n" + " Rectangle {\n" + " gradient: Gradient {\n" + " GradientStop {\n" + " position: 0\n" + " color: \"#ff0000\"\n" + " }\n" + " }\n" + " }\n" + "}\n"); + + QCOMPARE(textEdit.toPlainText(), expected3); + + propertyList.clear(); + propertyList.append(qMakePair(QString("position"), QVariant::fromValue(0.5))); + propertyList.append(qMakePair(QString("color"), QVariant::fromValue(QColor(Qt::blue)))); + + ModelNode gradientStop2(gradientNode.view()->createModelNode("Qt/GradientStop", 4, 6, propertyList)); + QVERIFY(gradientStop2.isValid()); + stops.reparentHere(gradientStop2); + + const QLatin1String expected4("\nimport Qt 4.6\n" + "\n" + "Item {\n" + " Rectangle {\n" + " gradient: Gradient {\n" + " GradientStop {\n" + " position: 0\n" + " color: \"#ff0000\"\n" + " }\n" + "\n" + " GradientStop {\n" + " position: 0.5\n" + " color: \"#0000ff\"\n" + " }\n" + " }\n" + " }\n" + "}\n"); + + QCOMPARE(textEdit.toPlainText(), expected4); + + propertyList.clear(); + propertyList.append(qMakePair(QString("position"), QVariant::fromValue(0.8))); + propertyList.append(qMakePair(QString("color"), QVariant::fromValue(QColor(Qt::yellow)))); + + ModelNode gradientStop3(gradientNode.view()->createModelNode("Qt/GradientStop", 4, 6, propertyList)); + QVERIFY(gradientStop3.isValid()); + stops.reparentHere(gradientStop3); + + const QLatin1String expected5("\nimport Qt 4.6\n" + "\n" + "Item {\n" + " Rectangle {\n" + " gradient: Gradient {\n" + " GradientStop {\n" + " position: 0\n" + " color: \"#ff0000\"\n" + " }\n" + "\n" + " GradientStop {\n" + " position: 0.5\n" + " color: \"#0000ff\"\n" + " }\n" + "\n" + " GradientStop {\n" + " position: 0.8\n" + " color: \"#ffff00\"\n" + " }\n" + " }\n" + " }\n" + "}\n"); + + QCOMPARE(textEdit.toPlainText(), expected5); + + gradientNode.removeProperty("stops"); + + const QLatin1String expected6("\nimport Qt 4.6\n" + "\n" + "Item {\n" + " Rectangle {\n" + " gradient: Gradient {\n" + " }\n" + " }\n" + "}\n"); + + QCOMPARE(textEdit.toPlainText(), expected6); + + QVERIFY(!gradientNode.hasProperty(QLatin1String("stops"))); + + propertyList.clear(); + propertyList.append(qMakePair(QString("position"), QVariant::fromValue(0))); + propertyList.append(qMakePair(QString("color"), QVariant::fromValue(QColor(Qt::blue)))); + + gradientStop1 = gradientNode.view()->createModelNode("Qt/GradientStop", 4, 6, propertyList); + QVERIFY(gradientStop1.isValid()); + + stops.reparentHere(gradientStop1); +} + +void TestCore::testQmlModelStates() +{ + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + + + ModelNode rootModelNode(view->rootModelNode()); + + QVERIFY(rootModelNode.isValid()); + + QVERIFY(QmlItemNode(rootModelNode).isValid()); + + QVERIFY(QmlItemNode(rootModelNode).states().allStates().isEmpty()); + + QmlModelState newState = QmlItemNode(rootModelNode).states().addState("testState"); + QVERIFY(newState.isValid()); + QVERIFY(!QmlItemNode(rootModelNode).states().allStates().isEmpty()); + QCOMPARE(QmlItemNode(rootModelNode).states().allStates().count(), 1); + + QCOMPARE(newState.name(), QString("testState")); + QCOMPARE(newState, QmlItemNode(rootModelNode).states().state("testState")); + + QmlItemNode(rootModelNode).states().removeState("testState"); + QVERIFY(!newState.isValid()); + QVERIFY(QmlItemNode(rootModelNode).states().allStates().isEmpty()); + +} + +void TestCore::testInstancesStates() +{ +// +// import Qt 4.6 +// +// Rectangle { +// Text { +// id: targetObject +// text: "base state" +// } +// +// states: [ +// State { +// name: "state1" +// PropertyChanges { +// target: targetObj +// x: 10 +// text: "state1" +// } +// } +// State { +// name: "state2" +// PropertyChanges { +// target: targetObj +// text: "state2" +// } +// } +// ] +// } +// + + QScopedPointer<Model> model(Model::create("Qt/Rectangle", 4, 6)); + QVERIFY(model.data()); + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + // + // build up model + // + ModelNode rootNode = view->rootModelNode(); + + ModelNode textNode = view->createModelNode("Qt/Text", 4, 6); + textNode.setId("targetObject"); + textNode.variantProperty("text").setValue("base state"); + + rootNode.nodeListProperty("data").reparentHere(textNode); + + ModelNode propertyChanges1Node = view->createModelNode("Qt/PropertyChanges", 4, 6); + propertyChanges1Node.bindingProperty("target").setExpression("targetObject"); + propertyChanges1Node.variantProperty("x").setValue(10); + propertyChanges1Node.variantProperty("text").setValue("state1"); + + ModelNode state1Node = view->createModelNode("Qt/State", 4, 6); + state1Node.variantProperty("name").setValue("state1"); + state1Node.nodeListProperty("changes").reparentHere(propertyChanges1Node); + + rootNode.nodeListProperty("states").reparentHere(state1Node); + + ModelNode propertyChanges2Node = view->createModelNode("Qt/PropertyChanges", 4, 6); + propertyChanges2Node.bindingProperty("target").setExpression("targetObject"); + propertyChanges2Node.variantProperty("text").setValue("state2"); + + ModelNode state2Node = view->createModelNode("Qt/State", 4, 6); + state2Node.variantProperty("name").setValue("state2"); + state2Node.nodeListProperty("changes").reparentHere(propertyChanges2Node); + + rootNode.nodeListProperty("states").reparentHere(state2Node); + + // + // load into instance view + // + QScopedPointer<NodeInstanceView> instanceView(new NodeInstanceView); + + model->attachView(instanceView.data()); + + // + // check that list of actions is not empty (otherwise the CustomParser has not been run properly) + // + NodeInstance state1Instance = instanceView->instanceForNode(state1Node); + QVERIFY(state1Instance.isValid()); + QmlState *state1 = const_cast<QmlState*>(qobject_cast<const QmlState*>(state1Instance.testHandle())); + QVERIFY(state1); + QCOMPARE(state1->changes()->count(), 1); + QCOMPARE(state1->changes()->at(0)->actions().size(), 2); + + NodeInstance state2Instance = instanceView->instanceForNode(state2Node); + QVERIFY(state2Instance.isValid()); + QmlState *state2 = const_cast<QmlState*>(qobject_cast<const QmlState*>(state1Instance.testHandle())); + QVERIFY(state2); + QCOMPARE(state2->changes()->count(), 1); + QCOMPARE(state2->changes()->at(0)->actions().size(), 2); + + NodeInstance textInstance = instanceView->instanceForNode(textNode); + + // + // State switching + // + + // base state + QVERIFY(textInstance.isValid()); + QCOMPARE(state1Instance.property("__activateState").toBool(), false); + QCOMPARE(state2Instance.property("__activateState").toBool(), false); + QCOMPARE(textInstance.property("x").toInt(), 0); + QCOMPARE(textInstance.property("text").toString(), QString("base state")); + + // base state -> state 1 + state1Instance.setPropertyVariant("__activateState", true); + QCOMPARE(state1Instance.property("__activateState").toBool(), true); + QCOMPARE(state2Instance.property("__activateState").toBool(), false); + QCOMPARE(textInstance.property("x").toInt(), 10); + QCOMPARE(textInstance.property("text").toString(), QString("state1")); + + // state 1 -> state 2 + state2Instance.setPropertyVariant("__activateState", true); + QCOMPARE(state1Instance.property("__activateState").toBool(), false); + QCOMPARE(state2Instance.property("__activateState").toBool(), true); + QCOMPARE(textInstance.property("x").toInt(), 0); + QCOMPARE(textInstance.property("text").toString(), QString("state2")); + + // state 1 -> base state + state2Instance.setPropertyVariant("__activateState", false); + QCOMPARE(state1Instance.property("__activateState").toBool(), false); + QCOMPARE(state2Instance.property("__activateState").toBool(), false); + QCOMPARE(textInstance.property("x").toInt(), 0); + QCOMPARE(textInstance.property("text").toString(), QString("base state")); + + // + // Add/Change/Remove properties in current state + // + state1Instance.setPropertyVariant("__activateState", true); + + propertyChanges1Node.variantProperty("x").setValue(20); + QCOMPARE(textInstance.property("x").toInt(), 20); + propertyChanges1Node.variantProperty("x").setValue(10); // undo + QCOMPARE(textInstance.property("x").toInt(), 10); + + QCOMPARE(textInstance.property("y").toInt(), 0); + propertyChanges1Node.variantProperty("y").setValue(50); + QCOMPARE(textInstance.property("y").toInt(), 50); + propertyChanges1Node.removeProperty("y"); + QCOMPARE(textInstance.property("y").toInt(), 0); + + QCOMPARE(textInstance.property("text").toString(), QString("state1")); + propertyChanges1Node.removeProperty("text"); + QCOMPARE(textInstance.property("text").toString(), QString("base state")); + propertyChanges1Node.variantProperty("text").setValue("state1"); // undo + QCOMPARE(textInstance.property("text").toString(), QString("state1")); + +//// Following is not supported. We work around this +//// by _always_ changing to the base state before doing any changes to the +//// state structure. + + // + // Reparenting state actions (while state is active) + // + +// state1Instance.setPropertyVariant("__activateState", true); +// // move property changes of current state out of state +// ModelNode state3Node = view->createModelNode("Qt/State", 4, 6); +// state3Node.nodeListProperty("changes").reparentHere(propertyChanges1Node); +// +// QCOMPARE(state1->changes()->count(), 0); +// QCOMPARE(textInstance.property("text").toString(), QString("base state")); +// +// // undo +// state1Node.nodeListProperty("changes").reparentHere(propertyChanges1Node); +// QCOMPARE(state1->changes()->count(), 1); +// QCOMPARE(textInstance.property("text").toString(), QString("state 1")); + + // + // Removing state actions (while state is active) + // + +// state1Instance.setPropertyVariant("__activateState", true); +// QCOMPARE(textInstance.property("text").toString(), QString("state1")); +// propertyChanges1Node.destroy(); +// QCOMPARE(state1->changes()->count(), 0); +// QCOMPARE(textInstance.property("text").toString(), QString("base state")); + + // + // Removing state (while active) + // + +// state2Instance.setPropertyVariant("__activateState", true); +// QCOMPARE(textInstance.property("text").toString(), QString("state2")); +// state2Node.destroy(); +// QCOMPARE(textInstance.property("text").toString(), QString("base state")); +} + +void TestCore::testStates() +{ +// +// import Qt 4.6 +// +// Rectangle { +// Text { +// id: targetObject +// text: "base state" +// } +// states: [ +// State { +// name: "state 1" +// PropertyChanges { +// target: targetObj +// text: "state 1" +// } +// } +// ] +// } +// + + QScopedPointer<Model> model(Model::create("Qt/Rectangle", 4, 6)); + QVERIFY(model.data()); + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + // build up model + ModelNode rootNode = view->rootModelNode(); + + ModelNode textNode = view->createModelNode("Qt/Text", 4, 6); + textNode.setId("targetObject"); + textNode.variantProperty("text").setValue("base state"); + + rootNode.nodeListProperty("data").reparentHere(textNode); + + QVERIFY(rootNode.isValid()); + QVERIFY(textNode.isValid()); + + QmlItemNode rootItem(rootNode); + QmlItemNode textItem(textNode); + + QVERIFY(rootItem.isValid()); + QVERIFY(textItem.isValid()); + + QmlModelState state1(rootItem.states().addState("state 1")); //add state "state 1" + QCOMPARE(state1.modelNode().type(), QString("Qt/State")); + + QVERIFY(view->currentState().isBaseState()); + + NodeInstance textInstance = view->nodeInstanceView()->instanceForNode(textNode); + QVERIFY(textInstance.isValid()); + + NodeInstance state1Instance = view->nodeInstanceView()->instanceForNode(state1); + QVERIFY(state1Instance.isValid()); + QCOMPARE(state1Instance.property("__activateState").toBool(), false); + QCOMPARE(state1Instance.property("name").toString(), QString("state 1")); + + view->setCurrentState(state1); //set currentState "state 1" + + QCOMPARE(view->currentState(), state1); + + QCOMPARE(state1Instance.property("__activateState").toBool(), true); + + QVERIFY(!textItem.propertyAffectedByCurrentState("text")); + + textItem.setVariantProperty("text", QVariant("state 1")); //set text in state ! + + QmlState *qmlState1 = const_cast<QmlState*>(qobject_cast<const QmlState*>(state1Instance.testHandle())); + QVERIFY(qmlState1); + QCOMPARE(qmlState1->changes()->count(), 1); + QCOMPARE(qmlState1->changes()->at(0)->actions().size(), 1); + + QmlPropertyChanges changes(state1.propertyChanges(textNode)); + QVERIFY(changes.modelNode().hasProperty("text")); + QCOMPARE(changes.modelNode().variantProperty("text").value(), QVariant("state 1")); + QCOMPARE(changes.modelNode().bindingProperty("target").expression(), QString("targetObject")); + QCOMPARE(changes.target(), textNode); + QCOMPARE(changes.modelNode().type(), QString("Qt/PropertyChanges")); + + QCOMPARE(changes.modelNode().parentProperty().name(), QString("changes")); + QCOMPARE(changes.modelNode().parentProperty().parentModelNode(), state1.modelNode()); + + QCOMPARE(state1Instance.property("__activateState").toBool(), true); + + QVERIFY(textItem.propertyAffectedByCurrentState("text")); + + QCOMPARE(textInstance.property("text").toString(), QString("state 1")); + QCOMPARE(textItem.instanceValue("text"), QVariant("state 1")); + + QVERIFY(textNode.hasProperty("text")); + QCOMPARE(textNode.variantProperty("text").value(), QVariant("base state")); +} + +void TestCore::testStatesBaseState() +{ +// +// import Qt 4.6 +// +// Rectangle { +// Text { +// id: targetObject +// text: "base state" +// } +// states: [ +// State { +// name: "state 1" +// PropertyChanges { +// target: targetObj +// text: "state 1" +// } +// } +// ] +// } +// + + QScopedPointer<Model> model(Model::create("Qt/Rectangle", 4, 6)); + QVERIFY(model.data()); + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + // build up model + ModelNode rootNode = view->rootModelNode(); + + ModelNode textNode = view->createModelNode("Qt/Text", 4, 6); + textNode.setId("targetObject"); + textNode.variantProperty("text").setValue("base state"); + + rootNode.nodeListProperty("data").reparentHere(textNode); + + QVERIFY(rootNode.isValid()); + QVERIFY(textNode.isValid()); + + QmlItemNode rootItem(rootNode); + QmlItemNode textItem(textNode); + + QVERIFY(rootItem.isValid()); + QVERIFY(textItem.isValid()); + + QmlModelState state1(rootItem.states().addState("state 1")); //add state "state 1" + QCOMPARE(state1.modelNode().type(), QString("Qt/State")); + + QVERIFY(view->currentState().isBaseState()); + + view->setCurrentState(state1); //set currentState "state 1" + QCOMPARE(view->currentState(), state1); + textItem.setVariantProperty("text", QVariant("state 1")); //set text in state ! + QVERIFY(textItem.propertyAffectedByCurrentState("text")); + QCOMPARE(textItem.instanceValue("text"), QVariant("state 1")); + + view->setCurrentState(view->baseState()); //set currentState base state + QVERIFY(view->currentState().isBaseState()); + + textNode.variantProperty("text").setValue("base state changed"); + + view->setCurrentState(state1); //set currentState "state 1" + QCOMPARE(view->currentState(), state1); + QCOMPARE(textItem.instanceValue("text"), QVariant("state 1")); + + view->setCurrentState(view->baseState()); //set currentState base state + QVERIFY(view->currentState().isBaseState()); + + QCOMPARE(textItem.instanceValue("text"), QVariant("base state changed")); +} + + +void TestCore::testInstancesIdResolution() +{ + QScopedPointer<Model> model(Model::create("Qt/Rectangle", 4, 6)); + QVERIFY(model.data()); + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + // import Qt 4.6 + // + // Rectangle { + // id: root + // width: 100 + // height: 100 + // Rectangle { + // id: item1 + // width: root.width + // height: item2.height + // } + // } + + ModelNode rootNode = view->rootModelNode(); + rootNode.setId("root"); + rootNode.variantProperty("width").setValue(100); + rootNode.variantProperty("height").setValue(100); + + ModelNode item1Node = view->createModelNode("Qt/Rectangle", 4, 6); + item1Node.setId("item1"); + item1Node.bindingProperty("width").setExpression("root.width"); + item1Node.bindingProperty("height").setExpression("item2.height"); + + rootNode.nodeListProperty("data").reparentHere(item1Node); + + NodeInstance item1Instance = view->instanceForModelNode(item1Node); + QVERIFY(item1Instance.isValid()); + + QCOMPARE(item1Instance.property("width").toInt(), 100); + QCOMPARE(item1Instance.property("height").toInt(), 0); // item2 is still unknown + + // Add item2: + // Rectangle { + // id: item2 + // width: root.width / 2 + // height: root.height + // } + + ModelNode item2Node = view->createModelNode("Qt/Rectangle", 4, 6); + item2Node.setId("item2"); + item2Node.bindingProperty("width").setExpression("root.width / 2"); + item2Node.bindingProperty("height").setExpression("root.height"); + + rootNode.nodeListProperty("data").reparentHere(item2Node); + + NodeInstance item2Instance = view->instanceForModelNode(item2Node); + QVERIFY(item2Instance.isValid()); + + QCOMPARE(item2Instance.property("width").toInt(), 50); + QCOMPARE(item2Instance.property("height").toInt(), 100); + QCOMPARE(item1Instance.property("height").toInt(), 100); + + // Remove item2 again + item2Node.destroy(); + QVERIFY(!item2Instance.isValid()); + QVERIFY(!item2Instance.testHandle()); + + // Add item3: + // Rectangle { + // id: item3 + // height: 80 + // } + + ModelNode item3Node = view->createModelNode("Qt/Rectangle", 4, 6); + item3Node.setId("item3"); + item3Node.variantProperty("height").setValue(80); + rootNode.nodeListProperty("data").reparentHere(item3Node); + + // Change item1.height: item3.height + + item1Node.bindingProperty("height").setExpression("item3.height"); + QCOMPARE(item1Instance.property("height").toInt(), 80); +} + + +void TestCore::testInstancesNotInScene() +{ + // + // test whether deleting an instance which is not in the scene crashes + // + + QScopedPointer<Model> model(Model::create("Qt/Rectangle", 4, 6)); + QVERIFY(model.data()); + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + ModelNode node1 = view->createModelNode("Qt/Item", 4, 6); + node1.setId("node1"); + + ModelNode node2 = view->createModelNode("Qt/Item", 4, 6); + node2.setId("node2"); + + node1.nodeListProperty("children").reparentHere(node2); + + node1.destroy(); +} + +void TestCore::testQmlModelStatesInvalidForRemovedNodes() +{ + QScopedPointer<Model> model(Model::create("Qt/Rectangle", 4, 6)); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + ModelNode rootModelNode(view->rootModelNode()); + QVERIFY(rootModelNode.isValid()); + QVERIFY(QmlItemNode(rootModelNode).isValid()); + + rootModelNode.setId("rootModelNode"); + + QmlModelState state1 = QmlItemNode(rootModelNode).states().addState("state1"); + QVERIFY(state1.isValid()); + QCOMPARE(state1.name(), QString("state1")); + + ModelNode childNode = rootModelNode.addChildNode("Qt/Rectangle", 4, 6, "data"); + QVERIFY(childNode.isValid()); + childNode.setId("childNode"); + + ModelNode subChildNode = childNode.addChildNode("Qt/Rectangle", 4, 6, "data"); + QVERIFY(subChildNode.isValid()); + subChildNode.setId("subChildNode"); + + QVERIFY(!state1.hasPropertyChanges(subChildNode)); + QmlPropertyChanges changeSet = state1.propertyChanges(subChildNode); + QVERIFY(changeSet.isValid()); + + QmlItemNode(childNode).destroy(); + QVERIFY(!childNode.isValid()); + QVERIFY(!subChildNode.isValid()); + QVERIFY(!changeSet.isValid()); +} + +void TestCore::testInstancesAttachToExistingModel() +{ + // + // Test attaching nodeinstanceview to an existing model + // + + QScopedPointer<Model> model(Model::create("Qt/Item", 4, 6)); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + ModelNode rootNode = view->rootModelNode(); + ModelNode rectangleNode = rootNode.addChildNode("Qt/Rectangle", 4, 6, "data"); + + rectangleNode.variantProperty("width").setValue(100); + + QVERIFY(rectangleNode.isValid()); + QVERIFY(rectangleNode.hasProperty("width")); + + // Attach NodeInstanceView + + QScopedPointer<NodeInstanceView> instanceView(new NodeInstanceView); + QVERIFY(instanceView.data()); + model->attachView(instanceView.data()); + + NodeInstance rootInstance = instanceView->instanceForNode(rootNode); + NodeInstance rectangleInstance = instanceView->instanceForNode(rectangleNode); + QVERIFY(rootInstance.isValid()); + QVERIFY(rectangleInstance.isValid()); + QCOMPARE(QVariant(100), rectangleInstance.property("width")); + QVERIFY(rootInstance.testHandle()); + QVERIFY(rectangleInstance.testHandle()); + QCOMPARE(rootInstance.testHandle(), rectangleInstance.testHandle()->parent()); +} + +void TestCore::testQmlModelAddMultipleStates() +{ + QScopedPointer<Model> model(Model::create("Qt/Rectangle", 4, 6)); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + ModelNode rootModelNode(view->rootModelNode()); + QVERIFY(rootModelNode.isValid()); + QVERIFY(QmlItemNode(rootModelNode).isValid()); + + QmlModelState state1 = QmlItemNode(rootModelNode).states().addState("state1"); + QVERIFY(state1.isValid()); + QCOMPARE(state1.name(), QString("state1")); + + QmlModelState state2 = QmlItemNode(rootModelNode).states().addState("state2"); + QVERIFY(state2.isValid()); + QCOMPARE(state1.name(), QString("state1")); + QCOMPARE(state2.name(), QString("state2")); + + QmlModelState state3 = QmlItemNode(rootModelNode).states().addState("state3"); + QVERIFY(state3.isValid()); + QCOMPARE(state1.name(), QString("state1")); + QCOMPARE(state2.name(), QString("state2")); + QCOMPARE(state3.name(), QString("state3")); + + QCOMPARE(QmlItemNode(rootModelNode).states().allStates().count(), 3); +} + +void TestCore::testQmlModelRemoveStates() +{ + QScopedPointer<Model> model(Model::create("Qt/Rectangle", 4, 6)); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + ModelNode rootModelNode(view->rootModelNode()); + QVERIFY(rootModelNode.isValid()); + QVERIFY(QmlItemNode(rootModelNode).isValid()); + + QmlModelState state1 = QmlItemNode(rootModelNode).states().addState("state1"); + QmlModelState state2 = QmlItemNode(rootModelNode).states().addState("state2"); + QmlModelState state3 = QmlItemNode(rootModelNode).states().addState("state3"); + + QCOMPARE(QmlItemNode(rootModelNode).states().allStates().count(), 3); + + QmlItemNode(rootModelNode).states().removeState("state2"); + QVERIFY(!state2.isValid()); + QCOMPARE(QmlItemNode(rootModelNode).states().allStates().count(), 2); + + QmlItemNode(rootModelNode).states().removeState("state3"); + QVERIFY(!state3.isValid()); + state1.modelNode().destroy(); + QVERIFY(!state1.isValid()); + QCOMPARE(QmlItemNode(rootModelNode).states().allStates().count(), 0); +} + +void TestCore::testQmlModelStateWithName() +{ + QPlainTextEdit textEdit1; + textEdit1.setPlainText("import Qt 4.6; Rectangle { id: theRect; width: 100; states: [ State { name: \"a\"; PropertyChanges { target: theRect; width: 200; } } ] }\n"); + PlainTextEditModifier modifier1(&textEdit1); + + QScopedPointer<Model> model1(Model::create("Qt/Item")); + + TestRewriterView *testRewriterView1 = new TestRewriterView(model1.data()); + testRewriterView1->setTextModifier(&modifier1); + model1->attachView(testRewriterView1); + + QVERIFY(testRewriterView1->errors().isEmpty()); + + TestView *view = new TestView(model1.data()); + model1->attachView(view); + QmlItemNode rootNode = view->rootModelNode(); + QCOMPARE(rootNode.states().allStates().size(), 1); + + QmlModelState modelState = rootNode.states().allStates().at(0); + QVERIFY(!modelState.isBaseState()); + QCOMPARE(rootNode.instanceValue("width").toInt(), 100); + + QVERIFY(rootNode.isInBaseState()); + QVERIFY(rootNode.propertyAffectedByCurrentState("width")); + view->setCurrentState(rootNode.states().allStates().at(0)); + rootNode.setVariantProperty("width", 112); + + const QLatin1String expected1("import Qt 4.6; Rectangle { id: theRect; width: 100; states: [ State { name: \"a\"; PropertyChanges { target: theRect; width: 112 } } ] }\n"); + QCOMPARE(textEdit1.toPlainText(), expected1); + + QVERIFY(!rootNode.isInBaseState()); + QCOMPARE(rootNode.instanceValue("width").toInt(), 112); + + view->setCurrentState(view->baseState()); + QCOMPARE(rootNode.instanceValue("width").toInt(), 100); + + view->setCurrentState(rootNode.states().allStates().at(0)); + QCOMPARE(rootNode.instanceValue("width").toInt(), 112); + + + modelState.destroy(); + QCOMPARE(rootNode.states().allStates().size(), 0); +} + +void TestCore::testRewriterAutomaticSemicolonAfterChangedProperty() +{ + QPlainTextEdit textEdit1; + textEdit1.setPlainText("import Qt 4.6; Rectangle {\n width: 640\n height: 480\n}\n"); + PlainTextEditModifier modifier1(&textEdit1); + + QScopedPointer<Model> model1(Model::create("Qt/Item")); + + TestRewriterView *testRewriterView1 = new TestRewriterView(model1.data()); + testRewriterView1->setTextModifier(&modifier1); + model1->attachView(testRewriterView1); + + QVERIFY(testRewriterView1->errors().isEmpty()); + + ModelNode rootModelNode = testRewriterView1->rootModelNode(); + rootModelNode.variantProperty("height").setValue(1480); + QCOMPARE(rootModelNode.variantProperty("height").value().toInt(), 1480); + + QVERIFY(testRewriterView1->errors().isEmpty()); +} + +void TestCore::attributeChangeSynchronizer() +{ + QSKIP("no rewriter anymore", SkipAll); +// ByteArrayModifier* modifier; +// Model* model = 0; +// const QString originalQml = contentsTemplate.arg("text", ""); +// load(originalQml, model, modifier); +// QVERIFY(model); +// QCOMPARE(view->rootModelNode().childNodes().size(), 1); +// ModelNode textChild = view->rootModelNode().childNodes().first(); +// QVERIFY(textChild.id() == "textChild"); +// QVERIFY(textChild.property("text").value() == "text"); +// +// reload(contentsTemplate.arg("changed text", ""), modifier); +// +// QVERIFY2(textChild.property("text").value() == "changed text", QString("text was: \"%1\", expected \"changed text\"").arg(textChild.property("text").value().toString()).toLatin1().data()); +// +// textChild = view->rootModelNode().childNodes().first(); +// QVERIFY(textChild.property("text").value() == "changed text"); +// +// delete model; +// delete modifier; +} + +void TestCore::attributeAdditionSynchronizer() +{ + QSKIP("no rewriter anymore", SkipAll); +// ByteArrayModifier* modifier; +// Model* model = 0; +// load(contentsTemplate.arg("text", ""), model, modifier); +// QVERIFY(model); +// +// ModelNode textChild = view->rootModelNode().childNodes().first(); +// QVERIFY(textChild.id() == "textChild"); +// QVERIFY(textChild.property("text").value() == "text"); +// QVERIFY(!textChild.propertyNames().contains("width")); +// +// reload(contentsTemplate.arg("text", "width: 100;"), modifier); +// +// QVERIFY(textChild.property("text").value() == "text"); +// QVERIFY(textChild.property("width").value() == "100"); +// +// delete model; +// delete modifier; +} + +void TestCore::attributeRemovalSynchronizer() +{ + QSKIP("no rewriter anymore", SkipAll); +// ByteArrayModifier* modifier; +// Model* model = 0; +// load(contentsTemplate.arg("text", "width: 50;"), model, modifier); +// QVERIFY(model); +// +// ModelNode textChild = view->rootModelNode().childNodes().first(); +// QVERIFY(textChild.id() == "textChild"); +// QVERIFY(textChild.property("text").value() == "text"); +// QVERIFY(textChild.property("width").value() == "50"); +// +// reload(contentsTemplate.arg("text", ""), modifier); +// +// QVERIFY(textChild.property("text").value() == "text"); +// QVERIFY(!textChild.propertyNames().contains("width")); +// +// delete model; +// delete modifier; +} + +void TestCore::childAddedSynchronizer() +{ + QSKIP("no rewriter anymore", SkipAll); +// ByteArrayModifier* modifier; +// Model* model = 0; +// load(bareTemplate.arg(""), model, modifier); +// QVERIFY(model); +// QVERIFY(view->rootModelNode().childNodes().isEmpty()); +// +// reload(contentsTemplate.arg("text", ""), modifier); +// +// QVERIFY(view->rootModelNode().childNodes().size() == 1); +// QVERIFY(view->rootModelNode().childNodes().first().id() == "textChild"); +// +// delete model; +// delete modifier; +} + +void TestCore::childRemovedSynchronizer() +{ + QSKIP("no rewriter anymore", SkipAll); +// ByteArrayModifier* modifier; +// Model* model = 0; +// load(contentsTemplate.arg("text", ""), model, modifier); +// QVERIFY(model); +// QVERIFY(view->rootModelNode().childNodes().size() == 1); +// QVERIFY(view->rootModelNode().childNodes().first().id() == "textChild"); +// +// reload(bareTemplate.arg(""), modifier); +// +// QVERIFY(view->rootModelNode().childNodes().isEmpty()); +// +// delete model; +// delete modifier; +} + +void TestCore::childReplacedSynchronizer() +{ + QSKIP("no rewriter anymore", SkipAll); +// ByteArrayModifier* modifier; +// Model *model = 0; +// load(contentsTemplate.arg("text", ""), model, modifier); +// QVERIFY(model); +// QVERIFY(view->rootModelNode().childNodes().size() == 1); +// QVERIFY(view->rootModelNode().childNodes().first().id() == "textChild"); +// +// reload(bareTemplate.arg("Item { id:\"someOtherItem\"; }"), modifier); +// +// QVERIFY(view->rootModelNode().childNodes().size() == 1); +// QVERIFY(view->rootModelNode().childNodes().first().id() == "someOtherItem"); +// +// delete model; +// delete modifier; +} + +void TestCore::defaultPropertyValues() +{ + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + QCOMPARE(view->rootModelNode().variantProperty("x").value().toDouble(), 0.0); + QCOMPARE(view->rootModelNode().variantProperty("width").value().toDouble(), 0.0); + + ModelNode rectNode(view->rootModelNode().addChildNode("Qt/Rectangle", 4, 6, "data")); + + QCOMPARE(rectNode.variantProperty("y").value().toDouble(), 0.0); + QCOMPARE(rectNode.variantProperty("width").value().toDouble(), 0.0); + + ModelNode imageNode(view->rootModelNode().addChildNode("Qt/Image", 4, 6, "data")); + + QCOMPARE(imageNode.variantProperty("y").value().toDouble(), 0.0); + QCOMPARE(imageNode.variantProperty("width").value().toDouble(), 0.0); +} + +void TestCore::testModelPropertyValueTypes() +{ + QPlainTextEdit textEdit1; + textEdit1.setPlainText("import Qt 4.6; Rectangle { width: 100; radius: 1.5; color: \"red\"; }"); + PlainTextEditModifier modifier1(&textEdit1); + + QScopedPointer<Model> model1(Model::create("Qt/Item")); + + QScopedPointer<TestRewriterView> testRewriterView1(new TestRewriterView()); + testRewriterView1->setTextModifier(&modifier1); + model1->attachView(testRewriterView1.data()); + + QVERIFY(testRewriterView1->errors().isEmpty()); + + ModelNode rootModelNode(testRewriterView1->rootModelNode()); + QVERIFY(rootModelNode.isValid()); + + QCOMPARE(rootModelNode.variantProperty("width").value().type(), QVariant::Double); + QCOMPARE(rootModelNode.variantProperty("radius").value().type(), QVariant::Double); + QCOMPARE(rootModelNode.variantProperty("color").value().type(), QVariant::Color); +} + +void TestCore::testModelNodeInHierarchy() +{ + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + QVERIFY(view->rootModelNode().isInHierarchy()); + ModelNode node1 = view->rootModelNode().addChildNode("Qt/Item", 4, 6, "data"); + QVERIFY(node1.isInHierarchy()); + ModelNode node2 = view->createModelNode("Qt/Item", 4, 6); + QVERIFY(!node2.isInHierarchy()); + node2.nodeListProperty("data").reparentHere(node1); + QVERIFY(!node2.isInHierarchy()); + view->rootModelNode().nodeListProperty("data").reparentHere(node2); + QVERIFY(node1.isInHierarchy()); + QVERIFY(node2.isInHierarchy()); +} + +void TestCore::testModelDefaultProperties() +{ + QScopedPointer<Model> model(Model::create("Qt/Rectangle")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + ModelNode rootModelNode(view->rootModelNode()); + QVERIFY(rootModelNode.isValid()); + + QCOMPARE(rootModelNode.metaInfo().defaultProperty(), QString("data")); +} + +void TestCore::loadAnchors() +{ + QPlainTextEdit textEdit1; + textEdit1.setPlainText("import Qt 4.6; Item { width: 100; height: 100; Rectangle { anchors.left: parent.left; anchors.horizontalCenter: parent.horizontalCenter; anchors.rightMargin: 20; }}"); + PlainTextEditModifier modifier1(&textEdit1); + + QScopedPointer<Model> model(Model::create("Qt/Item")); + + QScopedPointer<TestRewriterView> testRewriterView1(new TestRewriterView()); + testRewriterView1->setTextModifier(&modifier1); + model->attachView(testRewriterView1.data()); + + TestView *testView = new TestView(model.data()); + model->attachView(testView); + + QmlItemNode rootQmlItemNode(testView->rootQmlItemNode()); + QmlItemNode anchoredNode(rootQmlItemNode.children().first()); + QVERIFY(anchoredNode.isValid()); + + QmlAnchors anchors(anchoredNode.anchors()); + QVERIFY(anchors.instanceHasAnchor(AnchorLine::Left)); + QVERIFY(!anchors.instanceHasAnchor(AnchorLine::Top)); + QVERIFY(!anchors.instanceHasAnchor(AnchorLine::Right)); + QVERIFY(!anchors.instanceHasAnchor(AnchorLine::Bottom)); + QVERIFY(anchors.instanceHasAnchor(AnchorLine::HorizontalCenter)); + QVERIFY(!anchors.instanceHasAnchor(AnchorLine::VerticalCenter)); + + QCOMPARE(anchors.instanceMargin(AnchorLine::Right), 20.0); + + + QCOMPARE(anchoredNode.instancePosition().x(), 0.0); + QCOMPARE(anchoredNode.instancePosition().y(), 0.0); + QCOMPARE(anchoredNode.instanceBoundingRect().width(), 100.0); + QCOMPARE(anchoredNode.instanceBoundingRect().height(), 0.0); + + + model->detachView(testView); +} + +void TestCore::changeAnchors() +{ + QPlainTextEdit textEdit1; + textEdit1.setPlainText("import Qt 4.6; Item { width: 100; height: 100; Rectangle { anchors.left: parent.left; anchors.horizontalCenter: parent.horizontalCenter; anchors.rightMargin: 20; }}"); + PlainTextEditModifier modifier1(&textEdit1); + + QScopedPointer<Model> model(Model::create("Qt/Item")); + + QScopedPointer<TestRewriterView> testRewriterView1(new TestRewriterView()); + testRewriterView1->setTextModifier(&modifier1); + model->attachView(testRewriterView1.data()); + + TestView *testView = new TestView(model.data()); + model->attachView(testView); + + QmlItemNode rootQmlItemNode(testView->rootQmlItemNode()); + QmlItemNode anchoredNode(rootQmlItemNode.children().first()); + QVERIFY(anchoredNode.isValid()); + + QmlAnchors anchors(anchoredNode.anchors()); + QVERIFY(anchors.instanceHasAnchor(AnchorLine::Left)); + QVERIFY(!anchors.instanceHasAnchor(AnchorLine::Top)); + QVERIFY(!anchors.instanceHasAnchor(AnchorLine::Right)); + QVERIFY(!anchors.instanceHasAnchor(AnchorLine::Bottom)); + QVERIFY(anchors.instanceHasAnchor(AnchorLine::HorizontalCenter)); + QVERIFY(!anchors.instanceHasAnchor(AnchorLine::VerticalCenter)); + QCOMPARE(anchors.instanceMargin(AnchorLine::Right), 20.0); + + + QCOMPARE(anchoredNode.instancePosition().x(), 0.0); + QCOMPARE(anchoredNode.instancePosition().y(), 0.0); + QCOMPARE(anchoredNode.instanceBoundingRect().width(), 100.0); + QCOMPARE(anchoredNode.instanceBoundingRect().height(), 0.0); + + + anchors.removeAnchor(AnchorLine::Top); + + anchoredNode.setBindingProperty("anchors.bottom", "parent.bottom"); + QVERIFY(anchors.instanceHasAnchor(AnchorLine::Bottom)); + anchors.setAnchor(AnchorLine::Right, rootQmlItemNode, AnchorLine::Right); + QVERIFY(!anchors.instanceHasAnchor(AnchorLine::Right)); + QCOMPARE(anchors.instanceAnchor(AnchorLine::Right).type(), AnchorLine::Invalid); + QVERIFY(!anchors.instanceAnchor(AnchorLine::Right).qmlItemNode().isValid()); + + anchors.setMargin(AnchorLine::Right, 10.0); + QCOMPARE(anchors.instanceMargin(AnchorLine::Right), 10.0); + + anchors.setMargin(AnchorLine::Right, 0.0); + QCOMPARE(anchors.instanceMargin(AnchorLine::Right), 0.0); + + anchors.setMargin(AnchorLine::Right, 20.0); + QCOMPARE(anchors.instanceMargin(AnchorLine::Right), 20.0); + + QCOMPARE(anchoredNode.instancePosition().x(), 0.0); + QCOMPARE(anchoredNode.instancePosition().y(), 100.0); + QCOMPARE(anchoredNode.instanceBoundingRect().width(), 100.0); + QCOMPARE(anchoredNode.instanceBoundingRect().height(), 0.0); + + model->detachView(testView); +} + +void TestCore::anchorToSibling() +{ + QPlainTextEdit textEdit1; + textEdit1.setPlainText("import Qt 4.6; Item { Rectangle {} Rectangle { id: secondChild } }"); + PlainTextEditModifier modifier1(&textEdit1); + + QScopedPointer<Model> model(Model::create("Qt/Item")); + + QScopedPointer<TestRewriterView> testRewriterView1(new TestRewriterView()); + testRewriterView1->setTextModifier(&modifier1); + model->attachView(testRewriterView1.data()); + + TestView *testView = new TestView(model.data()); + model->attachView(testView); + + QmlItemNode rootQmlItemNode(testView->rootQmlItemNode()); + QmlItemNode anchoredNode(rootQmlItemNode.children().first()); + QVERIFY(anchoredNode.isValid()); + + QmlAnchors anchors(anchoredNode.anchors()); + + QmlItemNode firstChild(rootQmlItemNode.children().first()); + QVERIFY(firstChild.isValid()); + QCOMPARE(firstChild.validId(), QString("rectangle1")); + + QmlItemNode secondChild(rootQmlItemNode.children().at(1)); + QVERIFY(secondChild.isValid()); + QCOMPARE(secondChild.validId(), QString("secondChild")); + + secondChild.anchors().setAnchor(AnchorLine::Top, firstChild, AnchorLine::Bottom); + + QmlItemNode secondChildTopAnchoredNode = secondChild.anchors().instanceAnchor(AnchorLine::Top).qmlItemNode(); + QVERIFY(secondChildTopAnchoredNode.isValid()); + QVERIFY2(secondChildTopAnchoredNode == firstChild, QString("expected %1 got %2").arg(firstChild.id(), secondChildTopAnchoredNode.id()).toLatin1().data()); + + secondChild.anchors().setMargin(AnchorLine::Top, 10.0); + QCOMPARE(secondChild.anchors().instanceMargin(AnchorLine::Top), 10.0); + + QVERIFY(firstChild.anchors().possibleAnchorLines(AnchorLine::Bottom, secondChild) == AnchorLine::Invalid); + QVERIFY(firstChild.anchors().possibleAnchorLines(AnchorLine::Left, secondChild) == AnchorLine::HorizontalMask); + QVERIFY(secondChild.anchors().possibleAnchorLines(AnchorLine::Top, firstChild) == AnchorLine::VerticalMask); + QVERIFY(secondChild.anchors().possibleAnchorLines(AnchorLine::Left, firstChild) == AnchorLine::HorizontalMask); +} + +void TestCore::anchorsAndStates() +{ + QSKIP("no anchor support anymore", SkipAll); + +// QScopedPointer<Model> model(create("import Qt 4.6; Item { id:rootModelNode }")); +// QVERIFY(model.data()); +// +// ModelNode childNode(view->rootModelNode().addChildNode("Qt/Rectangle", 4, 6, "data")); +// childNode.setId("childNode"); +// ModelState modelState(model->createModelState("anchoreteststate")); +// +// childNode.setId("childNode"); +// NodeState nodeState(modelState.nodeState(childNode)); +// NodeAnchors(nodeState).setAnchor(AnchorLine::Top, view->rootModelNode(), AnchorLine::Top); +} + +void TestCore::removeFillAnchorByDetaching() +{ + QPlainTextEdit textEdit1; + textEdit1.setPlainText("import Qt 4.6; Item { width: 100; height: 100; Rectangle { id: child; anchors.fill: parent } }"); + PlainTextEditModifier modifier1(&textEdit1); + + QScopedPointer<Model> model(Model::create("Qt/Item")); + + QScopedPointer<TestRewriterView> testRewriterView1(new TestRewriterView()); + testRewriterView1->setTextModifier(&modifier1); + model->attachView(testRewriterView1.data()); + + TestView *testView = new TestView(model.data()); + model->attachView(testView); + + QmlItemNode rootQmlItemNode(testView->rootQmlItemNode()); + + QmlItemNode firstChild(rootQmlItemNode.children().first()); + QVERIFY(firstChild.isValid()); + QCOMPARE(firstChild.id(), QString("child")); + + // Verify the synthesized anchors: + QmlAnchors anchors(firstChild); + QVERIFY(anchors.instanceHasAnchor(AnchorLine::Left)); + QVERIFY(anchors.instanceAnchor(AnchorLine::Left).isValid()); + QCOMPARE(anchors.instanceAnchor(AnchorLine::Left).qmlItemNode(), rootQmlItemNode); + QCOMPARE(anchors.instanceAnchor(AnchorLine::Left).type(), AnchorLine::Left); + + QVERIFY(anchors.instanceAnchor(AnchorLine::Top).isValid()); + QCOMPARE(anchors.instanceAnchor(AnchorLine::Top).qmlItemNode(), rootQmlItemNode); + QCOMPARE(anchors.instanceAnchor(AnchorLine::Top).type(), AnchorLine::Top); + + QVERIFY(anchors.instanceAnchor(AnchorLine::Right).isValid()); + QCOMPARE(anchors.instanceAnchor(AnchorLine::Right).qmlItemNode(), rootQmlItemNode); + QCOMPARE(anchors.instanceAnchor(AnchorLine::Right).type(), AnchorLine::Right); + + QVERIFY(anchors.instanceAnchor(AnchorLine::Bottom).isValid()); + QCOMPARE(anchors.instanceAnchor(AnchorLine::Bottom).qmlItemNode(), rootQmlItemNode); + QCOMPARE(anchors.instanceAnchor(AnchorLine::Bottom).type(), AnchorLine::Bottom); + + QVERIFY(!anchors.instanceAnchor(AnchorLine::HorizontalCenter).isValid()); + QVERIFY(!anchors.instanceAnchor(AnchorLine::VerticalCenter).isValid()); + + QCOMPARE(firstChild.instancePosition().x(), 0.0); + QCOMPARE(firstChild.instancePosition().y(), 0.0); + QCOMPARE(firstChild.instanceBoundingRect().width(), 100.0); + QCOMPARE(firstChild.instanceBoundingRect().height(), 100.0); + + // Remove 2 anchors: + RewriterTransaction transaction = testView->beginRewriterTransaction(); + anchors.removeAnchor(AnchorLine::Bottom); + anchors.removeAnchor(AnchorLine::Top); + transaction.commit(); + + // Verify again: + QVERIFY(anchors.instanceAnchor(AnchorLine::Left).isValid()); + QCOMPARE(anchors.instanceAnchor(AnchorLine::Left).qmlItemNode(), rootQmlItemNode); + QCOMPARE(anchors.instanceAnchor(AnchorLine::Left).type(), AnchorLine::Left); + + QVERIFY(!anchors.instanceHasAnchor(AnchorLine::Top)); + QVERIFY(!anchors.instanceAnchor(AnchorLine::Top).isValid()); + + QVERIFY(anchors.instanceAnchor(AnchorLine::Right).isValid()); + QCOMPARE(anchors.instanceAnchor(AnchorLine::Right).qmlItemNode(), rootQmlItemNode); + QCOMPARE(anchors.instanceAnchor(AnchorLine::Right).type(), AnchorLine::Right); + + QVERIFY(!anchors.instanceAnchor(AnchorLine::Bottom).isValid()); + + QVERIFY(!anchors.instanceAnchor(AnchorLine::HorizontalCenter).isValid()); + QVERIFY(!anchors.instanceAnchor(AnchorLine::VerticalCenter).isValid()); + + QCOMPARE(firstChild.instancePosition().x(), 0.0); + QCOMPARE(firstChild.instancePosition().y(), 0.0); + QCOMPARE(firstChild.instanceBoundingRect().width(), 100.0); + QSKIP("is this intended", SkipAll); + QCOMPARE(firstChild.instanceBoundingRect().height(), 0.0); + + + model->detachView(testView); +} + +void TestCore::removeFillAnchorByChanging() +{ + QPlainTextEdit textEdit1; + textEdit1.setPlainText("import Qt 4.6; Item { width: 100; height: 100; Rectangle { id: child; anchors.fill: parent } }"); + PlainTextEditModifier modifier1(&textEdit1); + + QScopedPointer<Model> model(Model::create("Qt/Item")); + + QScopedPointer<TestRewriterView> testRewriterView1(new TestRewriterView()); + testRewriterView1->setTextModifier(&modifier1); + model->attachView(testRewriterView1.data()); + + TestView *testView = new TestView(model.data()); + model->attachView(testView); + + QmlItemNode rootQmlItemNode(testView->rootQmlItemNode()); + + QmlItemNode firstChild(rootQmlItemNode.children().first()); + QVERIFY(firstChild.isValid()); + QCOMPARE(firstChild.id(), QString("child")); + + // Verify the synthesized anchors: + QmlAnchors anchors(firstChild); + QVERIFY(anchors.instanceAnchor(AnchorLine::Left).isValid()); + QCOMPARE(anchors.instanceAnchor(AnchorLine::Left).qmlItemNode(), rootQmlItemNode); + QCOMPARE(anchors.instanceAnchor(AnchorLine::Left).type(), AnchorLine::Left); + + QVERIFY(anchors.instanceAnchor(AnchorLine::Top).isValid()); + QCOMPARE(anchors.instanceAnchor(AnchorLine::Top).qmlItemNode(), rootQmlItemNode); + QCOMPARE(anchors.instanceAnchor(AnchorLine::Top).type(), AnchorLine::Top); + + QVERIFY(anchors.instanceAnchor(AnchorLine::Right).isValid()); + QCOMPARE(anchors.instanceAnchor(AnchorLine::Right).qmlItemNode(), rootQmlItemNode); + QCOMPARE(anchors.instanceAnchor(AnchorLine::Right).type(), AnchorLine::Right); + + QVERIFY(anchors.instanceAnchor(AnchorLine::Bottom).isValid()); + QCOMPARE(anchors.instanceAnchor(AnchorLine::Bottom).qmlItemNode(), rootQmlItemNode); + QCOMPARE(anchors.instanceAnchor(AnchorLine::Bottom).type(), AnchorLine::Bottom); + + QVERIFY(!anchors.instanceAnchor(AnchorLine::HorizontalCenter).isValid()); + QVERIFY(!anchors.instanceAnchor(AnchorLine::VerticalCenter).isValid()); + + QCOMPARE(firstChild.instancePosition().x(), 0.0); + QCOMPARE(firstChild.instancePosition().y(), 0.0); + QCOMPARE(firstChild.instanceBoundingRect().width(), 100.0); + QCOMPARE(firstChild.instanceBoundingRect().height(), 100.0); + + // Change 2 anchors: + anchors.setAnchor(AnchorLine::Bottom, rootQmlItemNode, AnchorLine::Top); + anchors.setAnchor(AnchorLine::Top, rootQmlItemNode, AnchorLine::Bottom); + + // Verify again: + QVERIFY(anchors.instanceAnchor(AnchorLine::Left).isValid()); + QCOMPARE(anchors.instanceAnchor(AnchorLine::Left).qmlItemNode(), rootQmlItemNode); + QCOMPARE(anchors.instanceAnchor(AnchorLine::Left).type(), AnchorLine::Left); + + QVERIFY(anchors.instanceAnchor(AnchorLine::Top).isValid()); + QCOMPARE(anchors.instanceAnchor(AnchorLine::Top).qmlItemNode(), rootQmlItemNode); + QCOMPARE(anchors.instanceAnchor(AnchorLine::Top).type(), AnchorLine::Bottom); + + QVERIFY(anchors.instanceAnchor(AnchorLine::Right).isValid()); + QCOMPARE(anchors.instanceAnchor(AnchorLine::Right).qmlItemNode(), rootQmlItemNode); + QCOMPARE(anchors.instanceAnchor(AnchorLine::Right).type(), AnchorLine::Right); + + QVERIFY(anchors.instanceAnchor(AnchorLine::Bottom).isValid()); + QCOMPARE(anchors.instanceAnchor(AnchorLine::Bottom).qmlItemNode(), rootQmlItemNode); + QCOMPARE(anchors.instanceAnchor(AnchorLine::Bottom).type(), AnchorLine::Top); + + QVERIFY(!anchors.instanceAnchor(AnchorLine::HorizontalCenter).isValid()); + QVERIFY(!anchors.instanceAnchor(AnchorLine::VerticalCenter).isValid()); + + + QCOMPARE(firstChild.instancePosition().x(), 0.0); + QCOMPARE(firstChild.instancePosition().y(), 100.0); + QCOMPARE(firstChild.instanceBoundingRect().width(), 100.0); + QCOMPARE(firstChild.instanceBoundingRect().height(), -100.0); + + + model->detachView(testView); +} + +void TestCore::testModelBindings() +{ + QScopedPointer<Model> model(Model::create("Qt/Item", 4, 6)); + QVERIFY(model.data()); + + NodeInstanceView *nodeInstanceView = new NodeInstanceView(model.data()); + model->attachView(nodeInstanceView); + + ModelNode rootModelNode = nodeInstanceView->rootModelNode(); + QCOMPARE(rootModelNode.allDirectSubModelNodes().count(), 0); + NodeInstance rootInstance = nodeInstanceView->instanceForNode(rootModelNode); + + QCOMPARE(rootInstance.size().width(), 0.0); + QCOMPARE(rootInstance.size().height(), 0.0); + + rootModelNode.variantProperty("width") = 200; + rootModelNode.variantProperty("height") = 100; + + QCOMPARE(rootInstance.size().width(), 200.0); + QCOMPARE(rootInstance.size().height(), 100.0); + + ModelNode childNode = rootModelNode.addChildNode("Qt/Rectangle", 4, 6, "data"); + + childNode.variantProperty("width") = 100; + childNode.variantProperty("height") = 100; + + childNode.variantProperty("x") = 10; + childNode.variantProperty("y") = 10; + + NodeInstance childInstance = nodeInstanceView->instanceForNode(childNode); + + QCOMPARE(childInstance.size().width(), 100.0); + QCOMPARE(childInstance.size().height(), 100.0); + + QCOMPARE(childInstance.position().x(), 10.0); + QCOMPARE(childInstance.position().y(), 10.0); + + childNode.bindingProperty("width").setExpression("parent.width"); + + QCOMPARE(childInstance.size().width(), 200.0); + + rootModelNode.setId("root"); + QCOMPARE(rootModelNode.id(), QString("root")); + QCOMPARE(rootInstance.testHandle()->objectName(), QString("root")); + + childNode.setId("child"); + QCOMPARE(childNode.id(), QString("child")); + QCOMPARE(childInstance.testHandle()->objectName(), QString("child")); + childNode.variantProperty("width") = 100; + + QCOMPARE(childInstance.size().width(), 100.0); + + childNode.bindingProperty("width").setExpression("child.height"); + QCOMPARE(childInstance.size().width(), 100.0); + + childNode.bindingProperty("width").setExpression("root.width"); + QCOMPARE(childInstance.size().width(), 200.0); +} + +void TestCore::testDynamicProperties() +{ + QScopedPointer<Model> model(Model::create("Qt/Item", 4, 6)); + QVERIFY(model.data()); + + TestView *testView = new TestView(model.data()); + model->attachView(testView); + + QmlItemNode rootQmlItemNode(testView->rootQmlItemNode()); + ModelNode rootModelNode = rootQmlItemNode.modelNode(); + + rootModelNode.variantProperty("x") = 10; + rootModelNode.variantProperty("myDouble") = qMakePair(QString("real"), QVariant(10)); + rootModelNode.variantProperty("myColor").setDynamicTypeNameAndValue("color", Qt::red); + + QVERIFY(!rootModelNode.property("x").isDynamic()); + QVERIFY(rootModelNode.property("myColor").isDynamic()); + QVERIFY(rootModelNode.property("myDouble").isDynamic()); + + QCOMPARE(rootModelNode.property("myColor").dynamicTypeName(), QString("color")); + QCOMPARE(rootModelNode.variantProperty("myColor").value(), QVariant(Qt::red)); + //QCOMPARE(rootQmlItemNode.instanceValue("myColor"), QVariant(Qt::red)); //not working yet + QCOMPARE(rootModelNode.property("myDouble").dynamicTypeName(), QString("real")); + QCOMPARE(rootModelNode.variantProperty("myDouble").value(), QVariant(10)); + //QCOMPARE(rootQmlItemNode.instanceValue("myDouble"), QVariant(10)); //not working yet + + QCOMPARE(rootModelNode.property("x").dynamicTypeName(), QString()); + + rootModelNode.variantProperty("myDouble") = QVariant(10); + QVERIFY(!rootModelNode.property("myDouble").isDynamic()); + + rootModelNode.bindingProperty("myBindingDouble") = qMakePair(QString("real"), QString("myDouble")); + rootModelNode.bindingProperty("myBindingColor").setDynamicTypeNameAndExpression("color", "myColor"); + + QCOMPARE(rootModelNode.property("myBindingColor").dynamicTypeName(), QString("color")); + QCOMPARE(rootModelNode.property("myBindingDouble").dynamicTypeName(), QString("real")); + + QVERIFY(rootModelNode.property("myBindingDouble").isDynamic()); + QVERIFY(rootModelNode.property("myBindingColor").isDynamic()); + + QCOMPARE(rootModelNode.bindingProperty("myBindingDouble").expression(), QString("myDouble")); + QCOMPARE(rootModelNode.bindingProperty("myBindingColor").expression(), QString("myColor")); +} + +void TestCore::sideIndex() +{ + QSKIP("not anymore needed", SkipAll); +// QList<QmlError> errors; +// +// char qmlString[] = "import Qt 4.6\n" +// "Rectangle {\n" +// " width: 200\n" +// " height: 200\n" +// " color: \"white\"\n" +// "Text {\n" +// " text: \"Hello World\"\n" +// " anchors.centerIn: parent\n" +// "}\n" +// " Text {\n" +// " text: \"text\"\n" +// " x: 66\n" +// " y: 43\n" +// " width: 80\n" +// " height: 20\n" +// " id: Text1;\n" +// "}\n" +// " Text {\n" +// " text: \"text\"\n" +// " width: 80\n" +// " height: 20\n" +// " x: 66\n" +// " y: 141\n" +// " id: Text2;\n" +// "}\n" +// " Text {\n" +// " height: 20\n" +// " text: \"text\"\n" +// " x: 74\n" +// " width: 80\n" +// " y: 68\n" +// " id: Text3;\n" +// "}\n" +// "}\n"; +// +// QScopedPointer<ByteArrayModifier> modifier1(ByteArrayModifier::create(QLatin1String(qmlString))); +// QScopedPointer<Model> model(Model::create(modifier1.data(), QUrl(), &errors)); +// foreach(QmlError error, errors) { +// QFAIL((QString("Line %1: %2").arg(error.line()).arg(error.description())).toLatin1()); +// } +// +// QVERIFY(model.data()); +// +// QCOMPARE(view->rootModelNode().childNodes().at(0).property("text").value().toString(), QString("Hello World")); +// QCOMPARE(view->rootModelNode().childNodes().at(1).id(), QString("Text1")); +// QCOMPARE(view->rootModelNode().childNodes().at(2).id(), QString("Text2")); +// QCOMPARE(view->rootModelNode().childNodes().at(3).id(), QString("Text3")); +// +// +// ModelNode childModelNode(view->rootModelNode().childNodes().at(2)); +// ModelNode childModelNode2(view->rootModelNode().childNodes().at(1)); +// +// QCOMPARE(childModelNode.id(), QString("Text2")); +// QCOMPARE(childModelNode2.id(), QString("Text1")); +// +// childModelNode.slideToIndex(0); +// QCOMPARE(childModelNode.id(), QString("Text2")); +// QCOMPARE(childModelNode2.id(), QString("Text1")); +// QCOMPARE(view->rootModelNode().childNodes().at(0).id(), QString("Text2")); +// QCOMPARE(view->rootModelNode().childNodes().at(1).property("text").value().toString(), QString("Hello World")); +// QCOMPARE(view->rootModelNode().childNodes().at(2).id(), QString("Text1")); +// QCOMPARE(view->rootModelNode().childNodes().at(3).id(), QString("Text3")); + +} + +void TestCore::testModelSliding() +{ + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + ModelNode rootModelNode(view->rootModelNode()); + + ModelNode rect00(rootModelNode.addChildNode("Qt/Rectangle", 4, 6, "data")); + ModelNode rect01(rootModelNode.addChildNode("Qt/Rectangle", 4, 6, "data")); + ModelNode rect02(rootModelNode.addChildNode("Qt/Rectangle", 4, 6, "data")); + ModelNode rect03(rootModelNode.addChildNode("Qt/Rectangle", 4, 6, "data")); + + QVERIFY(rect00.isValid()); + QVERIFY(rect01.isValid()); + QVERIFY(rect02.isValid()); + QVERIFY(rect03.isValid()); + + NodeListProperty listProperty(rootModelNode.nodeListProperty("data")); + QVERIFY(!listProperty.isEmpty()); + + QList<ModelNode> nodeList(listProperty.toModelNodeList()); + + QCOMPARE(rect00, nodeList.at(0)); + QCOMPARE(rect01, nodeList.at(1)); + QCOMPARE(rect02, nodeList.at(2)); + QCOMPARE(rect03, nodeList.at(3)); + + listProperty.slide(3,0); + + nodeList = listProperty.toModelNodeList(); + + QCOMPARE(rect03, nodeList.at(0)); + QCOMPARE(rect00, nodeList.at(1)); + QCOMPARE(rect01, nodeList.at(2)); + QCOMPARE(rect02, nodeList.at(3)); + + NodeListProperty childrenProperty(rootModelNode.nodeListProperty("children")); + + childrenProperty.reparentHere(rect00); + childrenProperty.reparentHere(rect01); + childrenProperty.reparentHere(rect02); + childrenProperty.reparentHere(rect03); + + QVERIFY(listProperty.isEmpty()); + + QVERIFY(!childrenProperty.isEmpty()); + + nodeList = childrenProperty.toModelNodeList(); + + QCOMPARE(rect00, nodeList.at(0)); + QCOMPARE(rect01, nodeList.at(1)); + QCOMPARE(rect02, nodeList.at(2)); + QCOMPARE(rect03, nodeList.at(3)); + + childrenProperty.slide(0,3); + + nodeList = childrenProperty.toModelNodeList(); + + QCOMPARE(rect01, nodeList.at(0)); + QCOMPARE(rect02, nodeList.at(1)); + QCOMPARE(rect03, nodeList.at(2)); + QCOMPARE(rect00, nodeList.at(3)); +} + +void TestCore::testRewriterChangeId() +{ + const char* qmlString = "import Qt 4.6\nRectangle { }"; + + QPlainTextEdit textEdit; + textEdit.setPlainText(qmlString); + PlainTextEditModifier textModifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + model->attachView(testRewriterView.data()); + + ModelNode rootModelNode(view->rootModelNode()); + QVERIFY(rootModelNode.isValid()); + QCOMPARE(rootModelNode.id(), QString()); + + rootModelNode.setId("rectId"); + + QCOMPARE(rootModelNode.id(), QString("rectId")); + + QString expected = "import Qt 4.6\n" + "Rectangle {\n" + " id: rectId\n" + " }"; + + QCOMPARE(textEdit.toPlainText(), expected); + + // change id for node outside of hierarchy + ModelNode node = view->createModelNode("Qt/Item", 4, 6); + node.setId("myId"); +} + +void TestCore::testRewriterChangeValueProperty() +{ + const char* qmlString = "import Qt 4.6\nRectangle { x: 10; y: 10 }"; + + QPlainTextEdit textEdit; + textEdit.setPlainText(qmlString); + PlainTextEditModifier textModifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + model->attachView(testRewriterView.data()); + + ModelNode rootModelNode(view->rootModelNode()); + QVERIFY(rootModelNode.isValid()); + + QCOMPARE(rootModelNode.property("x").toVariantProperty().value().toInt(), 10); + + rootModelNode.property("x").toVariantProperty().setValue(2); + + QCOMPARE(textEdit.toPlainText(), QString(QLatin1String(qmlString)).replace("x: 10;", "x: 2;")); + + // change property for node outside of hierarchy + PropertyListType properties; + properties.append(QPair<QString,QVariant>("x", 10)); + ModelNode node = view->createModelNode("Qt/Item", 4, 6, properties); + node.variantProperty("x").setValue(20); +} + +void TestCore::testRewriterRemoveValueProperty() +{ + const QLatin1String qmlString("\n" + "import Qt 4.6\n" + "Rectangle {\n" + " x: 10\n" + " y: 10;\n" + "}\n"); + + QPlainTextEdit textEdit; + textEdit.setPlainText(qmlString); + PlainTextEditModifier textModifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + model->attachView(testRewriterView.data()); + + ModelNode rootModelNode(view->rootModelNode()); + QVERIFY(rootModelNode.isValid()); + + QCOMPARE(rootModelNode.property("x").toVariantProperty().value().toInt(), 10); + + rootModelNode.property("x").toVariantProperty().setValue(2); + + rootModelNode.removeProperty("x"); + + const QLatin1String expected("\n" + "import Qt 4.6\n" + "Rectangle {\n" + " y: 10;\n" + "}\n"); + QCOMPARE(textEdit.toPlainText(), expected); + + // remove property for node outside of hierarchy + PropertyListType properties; + properties.append(QPair<QString,QVariant>("x", 10)); + ModelNode node = view->createModelNode("Qt/Item", 4, 6, properties); + node.removeProperty("x"); +} + +void TestCore::testRewriterSignalProperty() +{ + const char* qmlString = "import Qt 4.6\nRectangle { onColorChanged: {} }"; + + QPlainTextEdit textEdit; + textEdit.setPlainText(qmlString); + PlainTextEditModifier textModifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + model->attachView(testRewriterView.data()); + + ModelNode rootModelNode(view->rootModelNode()); + QVERIFY(rootModelNode.isValid()); + + // Signal properties are ignored for the time being + QCOMPARE(rootModelNode.properties().size(), 0); +} + +void TestCore::testRewriterObjectTypeProperty() +{ + const char* qmlString = "import Qt 4.6\nRectangle { x: 10; y: 10 }"; + + QPlainTextEdit textEdit; + textEdit.setPlainText(qmlString); + PlainTextEditModifier textModifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + model->attachView(testRewriterView.data()); + + ModelNode rootModelNode(view->rootModelNode()); + QVERIFY(rootModelNode.isValid()); + + QCOMPARE(rootModelNode.type(), QLatin1String("Qt/Rectangle")); + + rootModelNode.changeType(QLatin1String("Qt/Text"), 4, 6); + + QCOMPARE(rootModelNode.type(), QLatin1String("Qt/Text")); + + // change type outside of hierarchy + ModelNode node = view->createModelNode("Qt/Rectangle", 4, 6); + node.changeType(QLatin1String("Qt/Item"), 4, 6); +} + +void TestCore::testRewriterPropertyChanges() +{ + try { + // PropertyChanges uses a custom parser + + // Use a slightly more complicated example so that target properties are not resolved in default scope + const char* qmlString + = "import Qt 4.6\n" + "Rectangle {\n" + " Text {\n" + " id: targetObj\n" + " text: \"base State\"\n" + " }\n" + " states: [\n" + " State {\n" + " PropertyChanges {\n" + " target: targetObj\n" + " text: \"State 1\"\n" + " }\n" + " }\n" + " ]\n" + "}\n"; + + QPlainTextEdit textEdit; + textEdit.setPlainText(qmlString); + PlainTextEditModifier textModifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item", 4, 6)); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + model->attachView(view.data()); + + // read in + QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + model->attachView(testRewriterView.data()); + + ModelNode rootNode = view->rootModelNode(); + QVERIFY(rootNode.isValid()); + QCOMPARE(rootNode.type(), QString("Qt/Rectangle")); + QVERIFY(rootNode.propertyNames().contains(QLatin1String("data"))); + QVERIFY(rootNode.propertyNames().contains(QLatin1String("states"))); + QCOMPARE(rootNode.propertyNames().count(), 2); + + NodeListProperty statesProperty = rootNode.nodeListProperty("states"); + QVERIFY(statesProperty.isValid()); + QCOMPARE(statesProperty.toModelNodeList().size(), 1); + + ModelNode stateNode = statesProperty.toModelNodeList().first(); + QVERIFY(stateNode.isValid()); + QCOMPARE(stateNode.type(), QString("Qt/State")); + QCOMPARE(stateNode.propertyNames(), QStringList("changes")); + + NodeListProperty stateChangesProperty = stateNode.property("changes").toNodeListProperty(); + QVERIFY(stateChangesProperty.isValid()); + + ModelNode propertyChangesNode = stateChangesProperty.toModelNodeList().first(); + QVERIFY(propertyChangesNode.isValid()); + QCOMPARE(propertyChangesNode.properties().size(), 2); + + BindingProperty targetProperty = propertyChangesNode.bindingProperty("target"); + QVERIFY(targetProperty.isValid()); + + VariantProperty textProperty = propertyChangesNode.variantProperty("text"); + QVERIFY(textProperty.isValid()); + QCOMPARE(textProperty.value().toString(), QLatin1String("State 1")); + } catch (Exception &e) { + QFAIL(e.description().toAscii().data()); + } +} + +void TestCore::testRewriterListModel() +{ + QSKIP("See BAUHAUS-157", SkipAll); + + try { + // ListModel uses a custom parser + const char* qmlString = "import Qt 4.6; ListModel {\n ListElement {\n age: 12\n} \n}"; + + QPlainTextEdit textEdit; + textEdit.setPlainText(qmlString); + PlainTextEditModifier textModifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item", 4, 6)); + QVERIFY(model.data()); + + QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + model->attachView(testRewriterView.data()); + + QScopedPointer<TestView> view(new TestView); + model->attachView(view.data()); + + ModelNode listModelNode = view->rootModelNode(); + QCOMPARE(listModelNode.propertyNames(), QStringList() << "__elements"); // TODO: what should be the name? + + NodeListProperty elementListProperty = listModelNode.nodeListProperty("__elements"); + QCOMPARE(elementListProperty.toModelNodeList().size(), 1); + + ModelNode elementNode = elementListProperty.toModelNodeList().at(0); + QVERIFY(elementNode.isValid()); + QCOMPARE(elementNode.properties().size(), 1); + QVERIFY(elementNode.variantProperty("age").isValid()); + QCOMPARE(elementNode.variantProperty("age").value().toInt(), 12); + } catch (Exception &e) { + QFAIL(qPrintable(e.description())); + } +} + +void TestCore::testRewriterAddProperty() +{ + const QLatin1String qmlString("\n" + "import Qt 4.6\n" + "\n" + "Rectangle {\n" + "}"); + QPlainTextEdit textEdit; + textEdit.setPlainText(qmlString); + PlainTextEditModifier textModifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item", 4, 6)); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + model->attachView(view.data()); + + // read in + QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + model->attachView(testRewriterView.data()); + + ModelNode rootNode = view->rootModelNode(); + QVERIFY(rootNode.isValid()); + QCOMPARE(rootNode.type(), QString("Qt/Rectangle")); + + rootNode.variantProperty(QLatin1String("x")).setValue(123); + + QVERIFY(rootNode.hasProperty(QLatin1String("x"))); + QVERIFY(rootNode.property(QLatin1String("x")).isVariantProperty()); + QCOMPARE(rootNode.variantProperty(QLatin1String("x")).value(), QVariant(123)); + + const QLatin1String expected("\n" + "import Qt 4.6\n" + "\n" + "Rectangle {\n" + " x: 123\n" + "}"); + QCOMPARE(textEdit.toPlainText(), expected); +} + +void TestCore::testRewriterAddPropertyInNestedObject() +{ + const QLatin1String qmlString("\n" + "import Qt 4.6\n" + "\n" + "Rectangle {\n" + " Rectangle {\n" + " id: Rectangle1\n" + " }\n" + "}"); + QPlainTextEdit textEdit; + textEdit.setPlainText(qmlString); + PlainTextEditModifier textModifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item", 4, 6)); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + model->attachView(view.data()); + + // read in + QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + model->attachView(testRewriterView.data()); + + ModelNode rootNode = view->rootModelNode(); + QVERIFY(rootNode.isValid()); + QCOMPARE(rootNode.type(), QLatin1String("Qt/Rectangle")); + + ModelNode childNode = rootNode.nodeListProperty(QLatin1String("data")).toModelNodeList().at(0); + QVERIFY(childNode.isValid()); + QCOMPARE(childNode.type(), QLatin1String("Qt/Rectangle")); + QCOMPARE(childNode.id(), QLatin1String("Rectangle1")); + + childNode.variantProperty(QLatin1String("x")).setValue(10); + childNode.variantProperty(QLatin1String("y")).setValue(10); + + const QLatin1String expected("\n" + "import Qt 4.6\n" + "\n" + "Rectangle {\n" + " Rectangle {\n" + " id: Rectangle1\n" + " x: 10\n" + " y: 10\n" + " }\n" + "}"); + QCOMPARE(textEdit.toPlainText(), expected); +} + +void TestCore::testRewriterAddObjectDefinition() +{ + const QLatin1String qmlString("import Qt 4.6\n" + "\n" + "Rectangle {\n" + "}"); + QPlainTextEdit textEdit; + textEdit.setPlainText(qmlString); + PlainTextEditModifier textModifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item", 4, 6)); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + model->attachView(view.data()); + + // read in + QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + model->attachView(testRewriterView.data()); + + ModelNode rootNode = view->rootModelNode(); + QVERIFY(rootNode.isValid()); + QCOMPARE(rootNode.type(), QString("Qt/Rectangle")); + + ModelNode childNode = view->createModelNode("Qt/MouseRegion", 4, 6); + rootNode.nodeAbstractProperty(QLatin1String("data")).reparentHere(childNode); + + QCOMPARE(rootNode.nodeProperty(QLatin1String("data")).toNodeListProperty().toModelNodeList().size(), 1); + childNode = rootNode.nodeProperty(QLatin1String("data")).toNodeListProperty().toModelNodeList().at(0); + QCOMPARE(childNode.type(), QString(QLatin1String("Qt/MouseRegion"))); +} + +void TestCore::testRewriterAddStatesArray() +{ + const QLatin1String qmlString("\n" + "import Qt 4.6\n" + "\n" + "Rectangle {\n" + "}"); + QPlainTextEdit textEdit; + textEdit.setPlainText(qmlString); + PlainTextEditModifier textModifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item", 4, 6)); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + model->attachView(view.data()); + + QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + model->attachView(testRewriterView.data()); + + ModelNode rootNode = view->rootModelNode(); + QVERIFY(rootNode.isValid()); + QCOMPARE(rootNode.type(), QString("Qt/Rectangle")); + + ModelNode stateNode = view->createModelNode("Qt/State", 4, 6); + rootNode.nodeListProperty(QLatin1String("states")).reparentHere(stateNode); + + const QString expected1 = QLatin1String("\n" + "import Qt 4.6\n" + "\n" + "Rectangle {\n" + " states: [\n" + " State {\n" + " }\n" + " ]\n" + "}"); + QCOMPARE(textEdit.toPlainText(), expected1); + + ModelNode stateNode2 = view->createModelNode("Qt/State", 4, 6); + rootNode.nodeListProperty(QLatin1String("states")).reparentHere(stateNode2); + + const QString expected2 = QLatin1String("\n" + "import Qt 4.6\n" + "\n" + "Rectangle {\n" + " states: [\n" + " State {\n" + " },\n" + " State {\n" + " }\n" + " ]\n" + "}"); + QCOMPARE(textEdit.toPlainText(), expected2); +} + +void TestCore::testRewriterRemoveStates() +{ + const QLatin1String qmlString("\n" + "import Qt 4.6\n" + "\n" + "Rectangle {\n" + " states: [\n" + " State {\n" + " },\n" + " State {\n" + " }\n" + " ]\n" + "}"); + QPlainTextEdit textEdit; + textEdit.setPlainText(qmlString); + PlainTextEditModifier textModifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item", 4, 6)); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + model->attachView(view.data()); + + QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + model->attachView(testRewriterView.data()); + + ModelNode rootNode = view->rootModelNode(); + QVERIFY(rootNode.isValid()); + QCOMPARE(rootNode.type(), QString("Qt/Rectangle")); + + NodeListProperty statesProperty = rootNode.nodeListProperty(QLatin1String("states")); + QVERIFY(statesProperty.isValid()); + QCOMPARE(statesProperty.toModelNodeList().size(), 2); + + ModelNode state = statesProperty.toModelNodeList().at(1); + state.destroy(); + + const QLatin1String expected1("\n" + "import Qt 4.6\n" + "\n" + "Rectangle {\n" + " states: [\n" + " State {\n" + " }\n" + " ]\n" + "}"); + QCOMPARE(textEdit.toPlainText(), expected1); + + state = statesProperty.toModelNodeList().at(0); + state.destroy(); + + const QLatin1String expected2("\n" + "import Qt 4.6\n" + "\n" + "Rectangle {\n" + "}"); + QCOMPARE(textEdit.toPlainText(), expected2); +} + +void TestCore::testRewriterRemoveObjectDefinition() +{ + const QLatin1String qmlString("\n" + "import Qt 4.6\n" + "\n" + "Rectangle {\n" + " MouseRegion {\n" + " }\n" + " MouseRegion {\n" + " } // some comment here\n" + "}"); + QPlainTextEdit textEdit; + textEdit.setPlainText(qmlString); + PlainTextEditModifier textModifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item", 4, 6)); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + model->attachView(view.data()); + + // read in + QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + model->attachView(testRewriterView.data()); + + ModelNode rootNode = view->rootModelNode(); + QVERIFY(rootNode.isValid()); + QCOMPARE(rootNode.type(), QString("Qt/Rectangle")); + + QCOMPARE(rootNode.nodeProperty(QLatin1String("data")).toNodeListProperty().toModelNodeList().size(), 2); + ModelNode childNode = rootNode.nodeProperty(QLatin1String("data")).toNodeListProperty().toModelNodeList().at(1); + QCOMPARE(childNode.type(), QString(QLatin1String("Qt/MouseRegion"))); + + childNode.destroy(); + + QCOMPARE(rootNode.nodeProperty(QLatin1String("data")).toNodeListProperty().toModelNodeList().size(), 1); + childNode = rootNode.nodeProperty(QLatin1String("data")).toNodeListProperty().toModelNodeList().at(0); + QCOMPARE(childNode.type(), QString(QLatin1String("Qt/MouseRegion"))); + + childNode.destroy(); + + QVERIFY(!rootNode.hasProperty(QLatin1String("data"))); + + const QString expected = QLatin1String("\n" + "import Qt 4.6\n" + "\n" + "Rectangle {\n" + " // some comment here\n" + "}"); + QCOMPARE(textEdit.toPlainText(), expected); + + // don't crash when deleting nodes not in any hierarchy + ModelNode node1 = view->createModelNode("Qt/Rectangle", 4, 6); + ModelNode node2 = node1.addChildNode("Qt/Item", 4, 6, "data"); + ModelNode node3 = node2.addChildNode("Qt/Item", 4, 6, "data"); + + node3.destroy(); + node1.destroy(); +} + +void TestCore::testRewriterRemoveScriptBinding() +{ + const QLatin1String qmlString("\n" + "import Qt 4.6\n" + "\n" + "Rectangle {\n" + " x: 10; // some comment\n" + " y: 20\n" + "}"); + QPlainTextEdit textEdit; + textEdit.setPlainText(qmlString); + PlainTextEditModifier textModifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item", 4, 6)); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + model->attachView(view.data()); + + // read in + QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + model->attachView(testRewriterView.data()); + + ModelNode rootNode = view->rootModelNode(); + QVERIFY(rootNode.isValid()); + QCOMPARE(rootNode.type(), QString("Qt/Rectangle")); + + QCOMPARE(rootNode.properties().size(), 2); + QVERIFY(rootNode.hasProperty(QLatin1String("x"))); + QVERIFY(rootNode.hasProperty(QLatin1String("y"))); + + rootNode.removeProperty(QLatin1String("y")); + + QCOMPARE(rootNode.properties().size(), 1); + QVERIFY(rootNode.hasProperty(QLatin1String("x"))); + QVERIFY(!rootNode.hasProperty(QLatin1String("y"))); + + rootNode.removeProperty(QLatin1String("x")); + + QCOMPARE(rootNode.properties().size(), 0); + + const QString expected = QLatin1String("\n" + "import Qt 4.6\n" + "\n" + "Rectangle {\n" + " // some comment\n" + "}"); + QCOMPARE(textEdit.toPlainText(), expected); +} + +void TestCore::testRewriterNodeReparenting() +{ + const QLatin1String qmlString("\n" + "import Qt 4.6\n" + "\n" + "Rectangle {\n" + " Item {\n" + " MouseRegion {\n" + " }\n" + " }\n" + "}"); + QPlainTextEdit textEdit; + textEdit.setPlainText(qmlString); + PlainTextEditModifier textModifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item", 4, 6)); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + model->attachView(view.data()); + + // read in + QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + model->attachView(testRewriterView.data()); + + ModelNode rootNode = view->rootModelNode(); + QVERIFY(rootNode.isValid()); + QCOMPARE(rootNode.type(), QString("Qt/Rectangle")); + + ModelNode itemNode = rootNode.nodeListProperty("data").toModelNodeList().at(0); + QVERIFY(itemNode.isValid()); + QCOMPARE(itemNode.type(), QLatin1String("Qt/Item")); + + ModelNode mouseRegion = itemNode.nodeListProperty("data").toModelNodeList().at(0); + QVERIFY(mouseRegion.isValid()); + QCOMPARE(mouseRegion.type(), QLatin1String("Qt/MouseRegion")); + + rootNode.nodeListProperty("data").reparentHere(mouseRegion); + + QVERIFY(mouseRegion.isValid()); + QCOMPARE(rootNode.nodeListProperty("data").toModelNodeList().size(), 2); + + QString expected = "\n" + "import Qt 4.6\n" + "\n" + "Rectangle {\n" + " Item {\n" + " }\n" + "\n" + "MouseRegion {\n" + " }\n" + "}"; + QCOMPARE(textEdit.toPlainText(), expected); + + // reparenting outside of the hierarchy + ModelNode node1 = view->createModelNode("Qt/Rectangle", 4, 6); + ModelNode node2 = view->createModelNode("Qt/Item", 4, 6); + ModelNode node3 = view->createModelNode("Qt/Item", 4, 6); + node2.nodeListProperty("data").reparentHere(node3); + node1.nodeListProperty("data").reparentHere(node2); + + // reparent into the hierarchy + rootNode.nodeListProperty("data").reparentHere(node1); + + expected = "\n" + "import Qt 4.6\n" + "\n" + "Rectangle {\n" + " Item {\n" + " }\n" + "\n" + "MouseRegion {\n" + " }\n" + "\n" + " Rectangle {\n" + " Item {\n" + " Item {\n" + " }\n" + " }\n" + " }\n" + "}"; + + QCOMPARE(textEdit.toPlainText(), expected); + + // reparent out of the hierarchy + ModelNode node4 = view->createModelNode("Qt/Rectangle", 4, 6); + node4.nodeListProperty("data").reparentHere(node1); + + expected = "\n" + "import Qt 4.6\n" + "\n" + "Rectangle {\n" + " Item {\n" + " }\n" + "\n" + "MouseRegion {\n" + " }\n" + "}"; + + QCOMPARE(textEdit.toPlainText(), expected); +} + +void TestCore::testRewriterNodeReparentingWithTransaction() +{ + const QLatin1String qmlString("\n" + "import Qt 4.6\n" + "\n" + "Rectangle {\n" + " id: rootItem\n" + " Item {\n" + " id: firstItem\n" + " x: 10\n" + " }\n" + " Item {\n" + " id: secondItem\n" + " x: 20\n" + " }\n" + "}"); + QPlainTextEdit textEdit; + textEdit.setPlainText(qmlString); + PlainTextEditModifier textModifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item", 4, 6)); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + model->attachView(view.data()); + + // read in + QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + model->attachView(testRewriterView.data()); + + ModelNode rootNode = view->rootModelNode(); + QVERIFY(rootNode.isValid()); + QCOMPARE(rootNode.type(), QLatin1String("Qt/Rectangle")); + QCOMPARE(rootNode.id(), QLatin1String("rootItem")); + + ModelNode item1Node = rootNode.nodeListProperty("data").toModelNodeList().at(0); + QVERIFY(item1Node.isValid()); + QCOMPARE(item1Node.type(), QLatin1String("Qt/Item")); + QCOMPARE(item1Node.id(), QLatin1String("firstItem")); + + ModelNode item2Node = rootNode.nodeListProperty("data").toModelNodeList().at(1); + QVERIFY(item2Node.isValid()); + QCOMPARE(item2Node.type(), QLatin1String("Qt/Item")); + QCOMPARE(item2Node.id(), QLatin1String("secondItem")); + + RewriterTransaction transaction = testRewriterView->beginRewriterTransaction(); + + item1Node.nodeListProperty(QLatin1String("data")).reparentHere(item2Node); + item2Node.variantProperty(QLatin1String("x")).setValue(0); + + transaction.commit(); + + const QLatin1String expected("\n" + "import Qt 4.6\n" + "\n" + "Rectangle {\n" + " id: rootItem\n" + " Item {\n" + " id: firstItem\n" + " x: 10\n" + "\n" + "Item {\n" + " id: secondItem\n" + " x: 0\n" + " }\n" + " }\n" + "}"); + QCOMPARE(textEdit.toPlainText(), expected); +} +void TestCore::testRewriterMovingInOut() +{ + const QLatin1String qmlString("\n" + "import Qt 4.6\n" + "\n" + "Rectangle {\n" + "}"); + QPlainTextEdit textEdit; + textEdit.setPlainText(qmlString); + PlainTextEditModifier textModifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item", 4, 6)); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + model->attachView(view.data()); + + // read in + QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + model->attachView(testRewriterView.data()); + + ModelNode rootNode = view->rootModelNode(); + QVERIFY(rootNode.isValid()); + QCOMPARE(rootNode.type(), QString("Qt/Rectangle")); + + ModelNode newNode = view->createModelNode("Qt/MouseRegion", 4, 6); + rootNode.nodeListProperty(QLatin1String("data")).reparentHere(newNode); + + const QLatin1String expected1("\n" + "import Qt 4.6\n" + "\n" + "Rectangle {\n" + " MouseRegion {\n" + " }\n" + "}"); + QCOMPARE(textEdit.toPlainText(), expected1); + +#define move(node, x, y) {\ + node.variantProperty(QLatin1String("x")).setValue(x);\ + node.variantProperty(QLatin1String("y")).setValue(y);\ +} + move(newNode, 1, 1); + move(newNode, 2, 2); + move(newNode, 3, 3); +#undef move + newNode.destroy(); + + const QLatin1String expected2("\n" + "import Qt 4.6\n" + "\n" + "Rectangle {\n" + "}"); + QCOMPARE(textEdit.toPlainText(), expected2); +} + +void TestCore::testRewriterMovingInOutWithTransaction() +{ + const QLatin1String qmlString("\n" + "import Qt 4.6\n" + "\n" + "Rectangle {\n" + "}"); + QPlainTextEdit textEdit; + textEdit.setPlainText(qmlString); + PlainTextEditModifier textModifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item", 4, 6)); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + model->attachView(view.data()); + + // read in + QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + model->attachView(testRewriterView.data()); + + ModelNode rootNode = view->rootModelNode(); + QVERIFY(rootNode.isValid()); + QCOMPARE(rootNode.type(), QString("Qt/Rectangle")); + + RewriterTransaction transaction = view->beginRewriterTransaction(); + + ModelNode newNode = view->createModelNode("Qt/MouseRegion", 4, 6); + rootNode.nodeListProperty(QLatin1String("data")).reparentHere(newNode); + +#define move(node, x, y) {\ + node.variantProperty(QLatin1String("x")).setValue(x);\ + node.variantProperty(QLatin1String("y")).setValue(y);\ +} + move(newNode, 1, 1); + move(newNode, 2, 2); + move(newNode, 3, 3); +#undef move + newNode.destroy(); + + transaction.commit(); + + const QLatin1String expected2("\n" + "import Qt 4.6\n" + "\n" + "Rectangle {\n" + "}"); + QCOMPARE(textEdit.toPlainText(), expected2); +} + +void TestCore::testRewriterComplexMovingInOut() +{ + const QLatin1String qmlString("\n" + "import Qt 4.6\n" + "\n" + "Rectangle {\n" + " Item {\n" + " }\n" + "}"); + QPlainTextEdit textEdit; + textEdit.setPlainText(qmlString); + PlainTextEditModifier textModifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item", 4, 6)); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + model->attachView(view.data()); + + // read in + QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + model->attachView(testRewriterView.data()); + + ModelNode rootNode = view->rootModelNode(); + QVERIFY(rootNode.isValid()); + QCOMPARE(rootNode.type(), QString("Qt/Rectangle")); + ModelNode itemNode = rootNode.nodeListProperty(QLatin1String("data")).toModelNodeList().at(0); + + ModelNode newNode = view->createModelNode("Qt/MouseRegion", 4, 6); + rootNode.nodeListProperty(QLatin1String("data")).reparentHere(newNode); + + const QLatin1String expected1("\n" + "import Qt 4.6\n" + "\n" + "Rectangle {\n" + " Item {\n" + " }\n" + "\n" + " MouseRegion {\n" + " }\n" + "}"); + QCOMPARE(textEdit.toPlainText(), expected1); + +#define move(node, x, y) {\ + node.variantProperty(QLatin1String("x")).setValue(x);\ + node.variantProperty(QLatin1String("y")).setValue(y);\ +} + move(newNode, 1, 1); + move(newNode, 2, 2); + move(newNode, 3, 3); + + const QLatin1String expected2("\n" + "import Qt 4.6\n" + "\n" + "Rectangle {\n" + " Item {\n" + " }\n" + "\n" + " MouseRegion {\n" + " x: 3\n" + " y: 3\n" + " }\n" + "}"); + QCOMPARE(textEdit.toPlainText(), expected2); + + itemNode.nodeListProperty(QLatin1String("data")).reparentHere(newNode); + + const QLatin1String expected3("\n" + "import Qt 4.6\n" + "\n" + "Rectangle {\n" + " Item {\n" + "MouseRegion {\n" + " x: 3\n" + " y: 3\n" + " }\n" + " }\n" + "}"); + QCOMPARE(textEdit.toPlainText(), expected3); + + move(newNode, 0, 0); + move(newNode, 1, 1); + move(newNode, 2, 2); + move(newNode, 3, 3); +#undef move + newNode.destroy(); + + const QLatin1String expected4("\n" + "import Qt 4.6\n" + "\n" + "Rectangle {\n" + " Item {\n" + " }\n" + "}"); + QCOMPARE(textEdit.toPlainText(), expected4); +} + +void TestCore::removeCenteredInAnchorByDetaching() +{ + QPlainTextEdit textEdit1; + textEdit1.setPlainText("import Qt 4.6; Item { Rectangle { id: child; anchors.centerIn: parent } }"); + PlainTextEditModifier modifier1(&textEdit1); + + QScopedPointer<Model> model(Model::create("Qt/Item")); + + QScopedPointer<TestRewriterView> testRewriterView1(new TestRewriterView()); + testRewriterView1->setTextModifier(&modifier1); + model->attachView(testRewriterView1.data()); + + TestView *testView = new TestView(model.data()); + model->attachView(testView); + + QmlItemNode rootQmlItemNode(testView->rootQmlItemNode()); + + + QmlItemNode firstChild(rootQmlItemNode.children().first()); + QVERIFY(firstChild.isValid()); + QCOMPARE(firstChild.id(), QString("child")); + + // Verify the synthesized anchors: + QmlAnchors anchors(firstChild); + QVERIFY(!anchors.instanceAnchor(AnchorLine::Left).isValid()); + QVERIFY(!anchors.instanceAnchor(AnchorLine::Top).isValid()); + QVERIFY(!anchors.instanceAnchor(AnchorLine::Right).isValid()); + QVERIFY(!anchors.instanceAnchor(AnchorLine::Bottom).isValid()); + + QVERIFY(anchors.instanceAnchor(AnchorLine::HorizontalCenter).isValid()); + QCOMPARE(anchors.instanceAnchor(AnchorLine::HorizontalCenter).qmlItemNode(), rootQmlItemNode); + QCOMPARE(anchors.instanceAnchor(AnchorLine::HorizontalCenter).type(), AnchorLine::HorizontalCenter); + + QVERIFY(anchors.instanceAnchor(AnchorLine::VerticalCenter).isValid()); + QCOMPARE(anchors.instanceAnchor(AnchorLine::VerticalCenter).qmlItemNode(), rootQmlItemNode); + QCOMPARE(anchors.instanceAnchor(AnchorLine::VerticalCenter).type(), AnchorLine::VerticalCenter); + QSKIP("transaction are crashing", SkipAll); + // Remove horizontal anchor: + anchors.removeAnchor(AnchorLine::HorizontalCenter); + + // Verify again: + QVERIFY(!anchors.instanceAnchor(AnchorLine::Left).isValid()); + QVERIFY(!anchors.instanceAnchor(AnchorLine::Top).isValid()); + QVERIFY(!anchors.instanceAnchor(AnchorLine::Right).isValid()); + QVERIFY(!anchors.instanceAnchor(AnchorLine::Bottom).isValid()); + + QVERIFY(!anchors.instanceAnchor(AnchorLine::HorizontalCenter).isValid()); + + QVERIFY(anchors.instanceAnchor(AnchorLine::VerticalCenter).isValid()); + QCOMPARE(anchors.instanceAnchor(AnchorLine::VerticalCenter).qmlItemNode(), rootQmlItemNode); + QCOMPARE(anchors.instanceAnchor(AnchorLine::VerticalCenter).type(), AnchorLine::VerticalCenter); +} + +void TestCore::loadPropertyBinding() +{ + QSKIP("property bindings are disabled", SkipAll); +// QScopedPointer<Model> model(Model::create("Qt/Item")); +// QVERIFY(model.data()); +// ModelNode rootModelNode(view->rootModelNode()); +// +// +// ModelNode firstChild = rootModelNode.addChildNode("Qt/Rectangle", 4, 6, "data"); +// firstChild.addProperty("width", QString("parent.width")); +// QVERIFY(firstChild.isValid()); +// QVERIFY(firstChild.hasProperty("width")); +// QVERIFY(firstChild.property("width").isValueAPropertyBinding()); +// +// AbstractProperty width = firstChild.property("width"); +// QVERIFY(width.isValueAPropertyBinding()); +// +// QVERIFY(width.value().isValid() && !width.value().isNull()); +// QCOMPARE(width.value().type(), QVariant::UserType); +// QCOMPARE(width.value().userType(), qMetaTypeId<QmlDesigner::PropertyBinding>()); +// +// PropertyBinding widthBinding = width.valueToPropertyBinding(); +// QVERIFY(widthBinding.isValid()); +// QCOMPARE(widthBinding.value(), QString("parent.width")); +} + +void TestCore::changePropertyBinding() +{ + + QScopedPointer<Model> model(Model::create("Qt/Item")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + model->attachView(view.data()); + + ModelNode rootModelNode(view->rootModelNode()); + rootModelNode.variantProperty("width") = 20; + + ModelNode firstChild = rootModelNode.addChildNode("Qt/Rectangle", 4, 6, "data"); + firstChild.bindingProperty("width").setExpression(QString("parent.width")); + firstChild.variantProperty("height")= 10; + QVERIFY(firstChild.isValid()); + + { + QVERIFY(firstChild.hasProperty("width")); + + BindingProperty widthBinding = firstChild.bindingProperty("width"); + QVERIFY(widthBinding.isValid()); + QCOMPARE(widthBinding.expression(), QString("parent.width")); + QVERIFY(firstChild.variantProperty("height").value().toInt() == 10); + } + + firstChild.variantProperty("width") = 400; + firstChild.bindingProperty("height").setExpression("parent.width / 2"); + + { + QVERIFY(firstChild.hasProperty("width")); + QVariant width = firstChild.variantProperty("width").value(); + QVERIFY(width.isValid() && !width.isNull()); + QVERIFY(width == 400); + QVERIFY(firstChild.hasProperty("height")); + BindingProperty heightBinding = firstChild.bindingProperty("height"); + QVERIFY(heightBinding.isValid()); + QCOMPARE(heightBinding.expression(), QString("parent.width / 2")); + } +} + +void TestCore::loadObjectPropertyBinding() +{ + QSKIP("will be changed", SkipAll); + +// QList<QmlError> errors; +// QScopedPointer<ByteArrayModifier> modifier1(ByteArrayModifier::create(QString("import Qt 4.6; Item { ListView { delegate:componentId } Component { id:componentId;Rectangle {} } }"))); +// +// QVERIFY(model.data()); +// ModelNode rootModelNode(view->rootModelNode()); +// +// +// QVERIFY(rootModelNode.childNodes().at(1).id() == "componentId"); +// QVERIFY(rootModelNode.childNodes().first().property("delegate").value().canConvert<ObjectPropertyBinding>()); +} + +void TestCore::loadWelcomeScreen() +{ + QSKIP("there is no rewriter anymore", SkipAll); +// QFile file(QCoreApplication::applicationDirPath() + "/../shared/welcomescreen.qml"); +// QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text)); +// +// QList<QmlError> errors; +// QScopedPointer<ByteArrayModifier> modifier1(ByteArrayModifier::create(QString(file.readAll()))); +// QScopedPointer<Model> model1(Model::create(modifier1.data(), QUrl::fromLocalFile(file.fileName()), &errors)); +// if (!errors.isEmpty()) { +// printErrors(errors, file.fileName()); +// QVERIFY(errors.isEmpty()); +// } +// QVERIFY(model1.data()); +} + +static QString rectWithGradient = "import Qt 4.6\n" + "Rectangle {\n" + " gradient: Gradient {\n" + " id: pGradient\n" + " GradientStop { id: pOne; position: 0.0; color: \"lightsteelblue\" }\n" + " GradientStop { id: pTwo; position: 1.0; color: \"blue\" }\n" + " }\n" + " Gradient {\n" + " id: secondGradient\n" + " GradientStop { id: nOne; position: 0.0; color: \"blue\" }\n" + " GradientStop { id: nTwo; position: 1.0; color: \"lightsteelblue\" }\n" + " }\n" + "}"; + +void TestCore::loadGradient() +{ + QPlainTextEdit textEdit; + textEdit.setPlainText(rectWithGradient); + PlainTextEditModifier textModifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item", 4, 6)); + QVERIFY(model.data()); + + QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + model->attachView(testRewriterView.data()); + + + QVERIFY(model.data()); + ModelNode rootModelNode(testRewriterView->rootModelNode()); + QVERIFY(rootModelNode.isValid()); + QCOMPARE(rootModelNode.allDirectSubModelNodes().size(), 2); + + { + QVERIFY(rootModelNode.hasProperty("gradient")); + AbstractProperty gradientProperty = rootModelNode.property("gradient"); + QVERIFY(gradientProperty.isNodeProperty()); + ModelNode gradientPropertyModelNode = gradientProperty.toNodeProperty().modelNode(); + QVERIFY(gradientPropertyModelNode.isValid()); + QCOMPARE(gradientPropertyModelNode.type(), QString("Qt/Gradient")); + QCOMPARE(gradientPropertyModelNode.allDirectSubModelNodes().size(), 2); + + AbstractProperty stopsProperty = gradientPropertyModelNode.property("stops"); + QVERIFY(stopsProperty.isValid()); + + QVERIFY(stopsProperty.isNodeListProperty()); + QList<ModelNode> stops = stopsProperty.toNodeListProperty().toModelNodeList(); + QCOMPARE(stops.size(), 2); + + ModelNode pOne = stops.first(); + ModelNode pTwo = stops.last(); + + QCOMPARE(pOne.type(), QString("Qt/GradientStop")); + QCOMPARE(pOne.id(), QString("pOne")); + QCOMPARE(pOne.allDirectSubModelNodes().size(), 0); + QCOMPARE(pOne.propertyNames().size(), 2); + QCOMPARE(pOne.variantProperty("position").value().type(), QVariant::Double); + QCOMPARE(pOne.variantProperty("position").value().toDouble(), 0.0); + QCOMPARE(pOne.variantProperty("color").value().type(), QVariant::Color); + QCOMPARE(pOne.variantProperty("color").value().value<QColor>(), QColor("lightsteelblue")); + + QCOMPARE(pTwo.type(), QString("Qt/GradientStop")); + QCOMPARE(pTwo.id(), QString("pTwo")); + QCOMPARE(pTwo.allDirectSubModelNodes().size(), 0); + QCOMPARE(pTwo.propertyNames().size(), 2); + QCOMPARE(pTwo.variantProperty("position").value().type(), QVariant::Double); + QCOMPARE(pTwo.variantProperty("position").value().toDouble(), 1.0); + QCOMPARE(pTwo.variantProperty("color").value().type(), QVariant::Color); + QCOMPARE(pTwo.variantProperty("color").value().value<QColor>(), QColor("blue")); + } + + { + ModelNode gradientNode = rootModelNode.allDirectSubModelNodes().last(); + QVERIFY(gradientNode.isValid()); + QVERIFY(!gradientNode.metaInfo().isQmlGraphicsItem()); + QCOMPARE(gradientNode.type(), QString("Qt/Gradient")); + QCOMPARE(gradientNode.id(), QString("secondGradient")); + QCOMPARE(gradientNode.allDirectSubModelNodes().size(), 2); + + AbstractProperty stopsProperty = gradientNode.property("stops"); + QVERIFY(stopsProperty.isValid()); + + QVERIFY(stopsProperty.isNodeListProperty()); + QList<ModelNode> stops = stopsProperty.toNodeListProperty().toModelNodeList(); + QCOMPARE(stops.size(), 2); + + ModelNode nOne = stops.first(); + ModelNode nTwo = stops.last(); + + QCOMPARE(nOne.type(), QString("Qt/GradientStop")); + QCOMPARE(nOne.id(), QString("nOne")); + QCOMPARE(nOne.allDirectSubModelNodes().size(), 0); + QCOMPARE(nOne.propertyNames().size(), 2); + QCOMPARE(nOne.variantProperty("position").value().type(), QVariant::Double); + QCOMPARE(nOne.variantProperty("position").value().toDouble(), 0.0); + QCOMPARE(nOne.variantProperty("color").value().type(), QVariant::Color); + QCOMPARE(nOne.variantProperty("color").value().value<QColor>(), QColor("blue")); + + QCOMPARE(nTwo.type(), QString("Qt/GradientStop")); + QCOMPARE(nTwo.id(), QString("nTwo")); + QCOMPARE(nTwo.allDirectSubModelNodes().size(), 0); + QCOMPARE(nTwo.propertyNames().size(), 2); + QCOMPARE(nTwo.variantProperty("position").value().type(), QVariant::Double); + QCOMPARE(nTwo.variantProperty("position").value().toDouble(), 1.0); + QCOMPARE(nTwo.variantProperty("color").value().type(), QVariant::Color); + QCOMPARE(nTwo.variantProperty("color").value().value<QColor>(), QColor("lightsteelblue")); + } +} + +void TestCore::changeGradientId() +{ + try { + QPlainTextEdit textEdit; + textEdit.setPlainText(rectWithGradient); + PlainTextEditModifier textModifier(&textEdit); + + QScopedPointer<Model> model(Model::create("Qt/Item", 4, 6)); + QVERIFY(model.data()); + + QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + model->attachView(testRewriterView.data()); + + QVERIFY(model.data()); + ModelNode rootModelNode(testRewriterView->rootModelNode()); + QVERIFY(rootModelNode.isValid()); + QCOMPARE(rootModelNode.allDirectSubModelNodes().size(), 2); + + AbstractProperty gradientProperty = rootModelNode.property("gradient"); + QVERIFY(gradientProperty.isNodeProperty()); + ModelNode gradientNode = gradientProperty.toNodeProperty().modelNode(); + QVERIFY(gradientNode.isValid()); + QCOMPARE(gradientNode.id(), QString("pGradient")); + + gradientNode.setId("firstGradient"); + QCOMPARE(gradientNode.id(), QString("firstGradient")); + + AbstractProperty stopsProperty = gradientNode.property("stops"); + QVERIFY(stopsProperty.isValid()); + + QVERIFY(stopsProperty.isNodeListProperty()); + QList<ModelNode> stops = stopsProperty.toNodeListProperty().toModelNodeList(); + QCOMPARE(stops.size(), 2); + + ModelNode firstStop = stops.at(0); + QVERIFY(firstStop.isValid()); + firstStop.destroy(); + QVERIFY(!firstStop.isValid()); + + ModelNode gradientStop = gradientNode.addChildNode("Qt/GradientStop", 4, 6, "stops"); + gradientStop.variantProperty("position") = 0.5; + gradientStop.variantProperty("color") = QColor("yellow"); + + gradientStop.setId("newGradientStop"); + + QCOMPARE(gradientNode.allDirectSubModelNodes().size(), 2); + QCOMPARE(gradientNode.nodeListProperty("stops").toModelNodeList().size(), 2); + QCOMPARE(gradientStop.id(), QString("newGradientStop")); + QCOMPARE(gradientStop.variantProperty("position").value().toDouble(), 0.5); + QCOMPARE(gradientStop.variantProperty("color").value().value<QColor>(), QColor("yellow")); + + } catch (Exception &e) { + qDebug() << e; + QFAIL(e.description().toLatin1().data()); + } +} + +void TestCore::copyAndPasteInSingleModel() +{ + QSKIP("no anchor support anymore", SkipAll); + +// try { +// QScopedPointer<Model> model(create("import Qt 4.6; Item { width: 100; height: 200; Rectangle { x: 10; y: 20; width: 30; height: 40; color: \"red\"}}")); +// QVERIFY(model.data()); +// ModelNode rootModelNode(view->rootModelNode()); +// QVERIFY(rootModelNode.isValid()); +// ModelNode rectNode(rootModelNode.childNodes().at(0)); +// QVERIFY(rectNode.isValid()); +// +// QMimeData *transfer = model->copy(QList<NodeState>() << rectNode); +// QVERIFY(rectNode.isValid()); +// QCOMPARE(rectNode.childNodes().size(), 0); +// +// QVERIFY(transfer != 0); +// model->paste(transfer, rectNode); +// QVERIFY(rectNode.isValid()); +// QCOMPARE(rectNode.childNodes().size(), 1); +// +// QCOMPARE(rectNode.property("x").value().toDouble(), 10.0); +// QCOMPARE(rectNode.property("y").value().toDouble(), 20.0); +// QCOMPARE(rectNode.property("width").value().toDouble(), 30.0); +// QCOMPARE(rectNode.property("height").value().toDouble(), 40.0); +// QCOMPARE(rectNode.property("color").value().value<QColor>(), QColor("red")); +// +// ModelNode rectChild = rectNode.childNodes().at(0); +// QCOMPARE(rectChild.property("x").value().toDouble(), 10.0); +// QCOMPARE(rectChild.property("y").value().toDouble(), 20.0); +// QCOMPARE(rectChild.property("width").value().toDouble(), 30.0); +// QCOMPARE(rectChild.property("height").value().toDouble(), 40.0); +// QCOMPARE(rectChild.property("color").value().value<QColor>(), QColor("red")); +// } catch (Exception &e) { +// qDebug() << e; +// QFAIL(e.description().toLatin1().data()); +// } +} + +void TestCore::copyAndPasteBetweenModels() +{ + QSKIP("no anchor support anymore", SkipAll); + +// try { +// QScopedPointer<Model> model1(create("import Qt 4.6; Item { width: 100; height: 200; Rectangle { x: 10; y: 20; width: 30; height: 40; color: \"red\"}}")); +// QVERIFY(model1.data()); +// ModelNode rootModelNode1(model1->rootModelNode()); +// QVERIFY(rootModelNode1.isValid()); +// ModelNode rectNode1(rootModelNode1.childNodes().at(0)); +// QVERIFY(rectNode1.isValid()); +// +// QScopedPointer<Model> model2(create("import Qt 4.6; Item { width: 100; height: 200}")); +// QVERIFY(model2.data()); +// ModelNode rootModelNode2(model2->rootModelNode()); +// QVERIFY(rootModelNode2.isValid()); +// QCOMPARE(rootModelNode2.childNodes().size(), 0); +// +// QMimeData *transfer = model1->copy(QList<NodeState>() << rectNode1); +// QVERIFY(rectNode1.isValid()); +// +// QVERIFY(transfer != 0); +// model2->paste(transfer, rootModelNode2); +// QCOMPARE(rootModelNode2.childNodes().size(), 1); +// +// ModelNode rectNode2 = rootModelNode2.childNodes().at(0); +// QCOMPARE(rectNode2.property("x").value().toDouble(), 10.0); +// QCOMPARE(rectNode2.property("y").value().toDouble(), 20.0); +// QCOMPARE(rectNode2.property("width").value().toDouble(), 30.0); +// QCOMPARE(rectNode2.property("height").value().toDouble(), 40.0); +// QCOMPARE(rectNode2.property("color").value().value<QColor>(), QColor("red")); +// } catch (Exception &e) { +// qDebug() << e; +// QFAIL(e.description().toLatin1().data()); +// } +} + +void TestCore::changeSubModel() +{ + QSKIP("must be refactored", SkipAll); +// // create the orignal model: +// QScopedPointer<Model> model1(create("import Qt 4.6; Rectangle { id: rootRect; Rectangle { id: rect2 }}")); +// QVERIFY(model1.data()); +// +// // get the model node for the component from the "original" model +// ModelNode modelNode = model1->rootModelNode().childNodes().at(0); +// QVERIFY(modelNode.isValid()); +// QCOMPARE(modelNode.id(), QString("rect2")); +// +// QList<QmlError> errors; +// // Create a model on top of the component node: +// TextModifier *subModifier = model1->createTextModifier(modelNode); +// QScopedPointer<Model> subModel(Model::create(subModifier, model1->fileUrl(), &errors)); +// // Handle errors: +// if (!errors.isEmpty()) { +// printErrors(errors, "<sub model>"); +// qDebug() << "Data:"; +// qDebug() << subModifier->text(); +// QVERIFY(errors.isEmpty()); +// } +// +// // Check that changes in the model for the Component are reflected in both the model for the component, and the original model: +// ModelNode subModelNode = subview->rootModelNode(); +// Q_ASSERT(subModelNode.isValid()); +// subModelNode.setId("AnotherId"); +// QCOMPARE(subModelNode.id(), QString("AnotherId")); +// QCOMPARE(modelNode.id(), QString("AnotherId")); +// +// // Check that changes in the original model are reflected in the model for the component: +// modelNode.setId("YetAnotherId"); +// QCOMPARE(subModelNode.id(), QString("YetAnotherId")); +// QCOMPARE(modelNode.id(), QString("YetAnotherId")); +} + + +void TestCore::changeInlineComponent() +{ + // create the orignal model: + QSKIP("must be refactored", SkipAll); +// QScopedPointer<Model> model1(create("import Qt 4.6; Rectangle { id: rootRect; Component { id: MyComponent; Rectangle { id: myComponentRect }}}")); +// QVERIFY(model1.data()); +// +// // get the model node for the component from the "original" model +// ModelNode componentNode = model1->rootModelNode().childNodes().at(0); +// QVERIFY(componentNode.isValid()); +// QCOMPARE(componentNode.id(), QString("MyComponent")); +// QVERIFY(!componentNode.hasChildNodes()); // top model shouldn't go into subcomponents +// +// QScopedPointer<Model> componentModel(model1->createComponentModel(componentNode)); +// +// ModelNode componentRootNode = componentview->rootModelNode(); +// QVERIFY(componentRootNode.isValid()); +// QCOMPARE(componentRootNode.type(), QString("Qt/Rectangle")); +// +// ModificationGroupToken token = componentModel->beginModificationGroup(); +// ModelNode childNode(componentRootNode.addChildNode("Qt/Rectangle", 4, 6, "data")); +// QVERIFY(childNode.isValid()); +// componentModel->endModificationGroup(token); +// QVERIFY(childNode.isValid()); +// QVERIFY(!componentRootNode.childNodes().isEmpty()); +// QVERIFY(componentRootNode.childNodes().first() == childNode); + +} + +void TestCore::changeImports() +{ + QSKIP("imports are disabled", SkipAll); +// QScopedPointer<Model> model(Model::create("Qt/Rectangle")); +// QVERIFY(model.data()); +// QVERIFY(view->rootModelNode().isValid()); +// +// QSet<Import> imports = model->imports(); +// QCOMPARE(imports.size(), 1); +// const Import import = imports.toModelNodeList().at(0); +// QVERIFY(import.isLibraryImport()); +// QCOMPARE(import.url(), QUrl("Qt")); +// QVERIFY(import.file().isEmpty()); +// QCOMPARE(import.version(), QString("4.6")); +// QVERIFY(import.alias().isEmpty()); +// +// Import newImport = Import::createFileImport("Namespaces_the_final_frontier", QString(), "MySpace"); +// model->addImport(newImport); +// imports = model->imports(); +// QCOMPARE(imports.size(), 2); +// QVERIFY(imports.contains(import)); +// QVERIFY(imports.contains(newImport)); +// +// model->removeImport(newImport); +// imports = model->imports(); +// QCOMPARE(imports.size(), 1); +// QVERIFY(imports.contains(import)); +// QVERIFY(!imports.contains(newImport)); +} + +void TestCore::testIfChangePropertyIsRemoved() +{ + QSKIP("Fix me!!! See task BAUHAUS-139", SkipAll); +// QScopedPointer<Model> model(create("import Qt 4.6; Item { Rectangle { x: 10; y: 10; } }")); +// QVERIFY(model.data()); +// ModelNode rootModelNode(view->rootModelNode()); +// QVERIFY(rootModelNode.isValid()); +// ModelNode rectNode(rootModelNode.childNodes().at(0)); +// QVERIFY(rectNode.isValid()); +// ModelState newState = model->createModelState("new state"); +// QVERIFY(newState.isValid()); +// NodeState rectNodeState = newState.nodeState(rectNode); +// QVERIFY(newState.isValid()); +// try { +// rectNodeState.setPropertyValue("x", 100); +// QVERIFY(rectNodeState.hasProperty("x")); +// } catch (Exception &e) { +// // ok. +// } +// rectNode.setPropertyValue("x", 15); +// QVERIFY(newState.isValid()); +// NodeState rectNodeState2 = newState.nodeState(rectNode); +// QVERIFY(rectNodeState2.isValid()); +// QVERIFY(rectNodeState.isValid()); +// +// rectNode.setId("rect"); +// QVERIFY(rectNodeState2.isValid()); +// QVERIFY(rectNodeState.isValid()); +// +// rectNodeState.setPropertyValue("x", 105); +// +// QVERIFY(rectNodeState2.isValid()); +// QVERIFY(rectNodeState.isValid()); +// +// QCOMPARE(rectNodeState.property("x").value().toInt(), 105); +// +// rectNode.destroy(); +// +// QVERIFY(!rectNodeState2.isValid()); +// QVERIFY(!rectNodeState.isValid()); +// QVERIFY(!rectNode.isValid()); +// +// rectNode = rootModelNode.addChildNode("Qt/Rectangle", 4, 6, "data"); +// rectNode.setId("rect"); +// rectNodeState = newState.nodeState(rectNode); +// rectNode.setPropertyValue("x", 10); +// +// QCOMPARE(rectNodeState.property("x").value().toInt(), 10); +} + +void TestCore::testAnchorsAndStates() +{ + QSKIP("no states anymore", SkipAll); +// QScopedPointer<Model> model(create("import Qt 4.6; Item { Rectangle { x: 10; y: 10; } }")); +// QVERIFY(model.data()); +// ModelNode rootModelNode(view->rootModelNode()); +// QVERIFY(rootModelNode.isValid()); +// ModelNode rectNode(rootModelNode.childNodes().at(0)); +// QVERIFY(rectNode.isValid()); +// +// NodeAnchors anchors(rectNode); +// QVERIFY(!anchors.instanceHasAnchor(AnchorLine::Left)); +// QVERIFY(!anchors.instanceHasAnchor(AnchorLine::Top)); +// QVERIFY(!anchors.instanceHasAnchor(AnchorLine::Right)); +// QVERIFY(!anchors.instanceHasAnchor(AnchorLine::Bottom)); +// QVERIFY(!anchors.instanceHasAnchor(AnchorLine::HorizontalCenter)); +// QVERIFY(!anchors.instanceHasAnchor(AnchorLine::VerticalCenter)); +// +// anchors.setAnchor(AnchorLine::Right, rootModelNode, AnchorLine::Right); +// QVERIFY(anchors.instanceHasAnchor(AnchorLine::Right)); +// QCOMPARE(anchors.instanceAnchor(AnchorLine::Right).type(), AnchorLine::Right); +// QCOMPARE(anchors.instanceAnchor(AnchorLine::Right).modelNode(), rootModelNode); +// +// anchors.setMargin(AnchorLine::Right, 10.0); +// QCOMPARE(anchors.instanceMargin(AnchorLine::Right), 10.0); +// +// anchors.setMargin(AnchorLine::Right, 0.0); +// QCOMPARE(anchors.instanceMargin(AnchorLine::Right), 0.0); +// +// anchors.setMargin(AnchorLine::Right, 20.0); +// +// QCOMPARE(1, rectNode.nodeStates().count()); +// +// ModelState newState = model->createModelState("new state"); +// +// QCOMPARE(2, rectNode.nodeStates().count()); +// +// QCOMPARE(2, model->baseModelState().nodeStates().count()); +// QCOMPARE(2, newState.nodeStates().count()); +// +// QVERIFY(newState.isValid()); +// QVERIFY(rectNode.isValid()); +// NodeState rectNodeState = newState.nodeState(rectNode); +// QVERIFY(rectNodeState.isValid()); +} + + +void TestCore::testStatesWithAnonymousTargets() +{ + QSKIP("no states anymore", SkipAll); + //QScopedPointer<Model> model(create("import Qt 4.6; Rectangle { states:[State{name:\"s1\";PropertyChanges{target:;x:20}}]Rectangle{x:10} } ")); +// ByteArrayModifier* modifier = 0; +// Model *model_p = 0; +// load("import Qt 4.6; Rectangle { states:[State{name:\"s1\";PropertyChanges{target:;x:20}}]Rectangle{x:10} } ",model_p,modifier); +// +// QScopedPointer<Model> model(model_p); +// QVERIFY(model.data()); +// ModelNode rootModelNode(view->rootModelNode()); +// QVERIFY(rootModelNode.isValid()); +// ModelNode rectNode(rootModelNode.childNodes().at(0)); +// QVERIFY(rectNode.isValid()); +// // check item is anonymous +// QVERIFY(rectNode.id().isEmpty()); +// +// NodeState newState = model->modelStates().at(1).nodeStates().at(1); +// QVERIFY(newState.isValid()); +// // check target is anonymous +// QVERIFY(newState.modelNode().id().isEmpty()); +// // check base state has x=0 +// QCOMPARE(model->modelStates().at(0).nodeStates().at(1).property("x").value().toInt(),10); +// +// // now we "move" the rectangle, the model should force an id and generate a new propertychange +// NodeStateChangeSet change( newState ); +// change.setPropertyValue("x",QVariant(30)); +// QList <NodeStateChangeSet> changeList; +// changeList.append(change); +// model->changeNodeStates(changeList); +// +// QCOMPARE(newState.modelNode().id(),QString("Rectangle1")); +// QCOMPARE(rectNode.id(),QString("Rectangle1")); +// QCOMPARE(newState.property("x").value().toInt(),30); +// +// // now we manually erase the new id and target +// QString Text = modifier->text(); +// Text.remove(Text.indexOf(QString("id: Rectangle1;")),QString("id: Rectangle1;").length()); +// Text.remove(Text.indexOf(QString("Rectangle1")),QString("Rectangle1").length()); +// +// // text2model again +// reload(Text,modifier); +// +// // repeat checks +// QVERIFY(model.data()); +// QVERIFY(rootModelNode.isValid()); +// QVERIFY(rectNode.isValid()); +// QVERIFY(rectNode.id().isEmpty()); +// QCOMPARE(model->modelStates().at(0).nodeStates().at(1).property("x").value().toInt(),10); +// +// // see what happened with the new state +// QVERIFY(newState.isValid()); +// QVERIFY(newState.modelNode().id().isEmpty()); + +} + +QTEST_MAIN(TestCore); diff --git a/tests/auto/qml/qmldesigner/coretests/testcore.h b/tests/auto/qml/qmldesigner/coretests/testcore.h new file mode 100644 index 00000000000..8c62f8427dc --- /dev/null +++ b/tests/auto/qml/qmldesigner/coretests/testcore.h @@ -0,0 +1,233 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef TESTCORE_H +#define TESTCORE_H + +#include <QObject> + +#include <QtTest> + + +class TestCore : public QObject +{ + Q_OBJECT +public: + TestCore(); + +private slots: + void initTestCase(); + void cleanupTestCase(); + + // + // unit tests Model, ModelNode, NodeProperty, AbstractView + // + void testModelViewNotification(); + void testModelCreateSubNode(); + void testModelCreateCoreModel(); + void testModelCreateInvalidSubNode(); + void testModelCreateRect(); + void testModelRemoveNode(); + void testModelRootNode(); + void testModelReorderSiblings(); + void testModelAddAndRemoveProperty(); + void testModelSliding(); + void testModelBindings(); + void testDynamicProperties(); + void testModelDefaultProperties(); + void testModelBasicOperations(); + void testModelResolveIds(); + void testModelNodeListProperty(); + void testModelPropertyValueTypes(); + void testModelNodeInHierarchy(); + + // + // unit tests MetaInfo, NodeMetaInfo, PropertyMetaInfo + // + void testMetaInfo(); + void testMetaInfoDotProperties(); + void testMetaInfoListProperties(); + + // + // unit tests Rewriter + // + void testRewriterView(); + void testRewriterErrors(); + void testRewriterChangeId(); + void testRewriterChangeValueProperty(); + void testRewriterRemoveValueProperty(); + void testRewriterSignalProperty(); + void testRewriterObjectTypeProperty(); + void testRewriterPropertyChanges(); + void testRewriterListModel(); + void testRewriterAddProperty(); + void testRewriterAddPropertyInNestedObject(); + void testRewriterAddObjectDefinition(); + void testRewriterAddStatesArray(); + void testRewriterRemoveStates(); + void testRewriterRemoveObjectDefinition(); + void testRewriterRemoveScriptBinding(); + void testRewriterNodeReparenting(); + void testRewriterNodeReparentingWithTransaction(); + void testRewriterMovingInOut(); + void testRewriterMovingInOutWithTransaction(); + void testRewriterComplexMovingInOut(); + void testRewriterAutomaticSemicolonAfterChangedProperty(); + void testRewriterTransaction(); + void testRewriterId(); + void testRewriterNodeReparentingTransaction1(); + void testRewriterNodeReparentingTransaction2(); + void testRewriterNodeReparentingTransaction3(); + void testRewriterNodeReparentingTransaction4(); + void testRewriterAddNodeTransaction(); + void testRewriterComponentId(); + void testRewriterPropertyDeclarations(); + void testRewriterPropertyAliases(); + void testRewriterPositionAndOffset(); + void testRewriterFirstDefinitionInside(); + void testRewriterComponentTextModifier(); + void testRewriterPreserveType(); + void testRewriterForArrayMagic(); + void testRewriterWithSignals(); + void testRewriterNodeSliding(); + + // + // unit tests QmlModelNodeFacade/QmlModelState + // + + void testQmlModelStates(); + void testQmlModelAddMultipleStates(); + void testQmlModelRemoveStates(); + void testQmlModelStatesInvalidForRemovedNodes(); + void testQmlModelStateWithName(); + + // + // unit tests Instances + // + void testInstancesAttachToExistingModel(); + void testInstancesStates(); + void testInstancesIdResolution(); + void testInstancesNotInScene(); + + // + // integration tests + // + void testBasicStates(); + void testStates(); + void testStatesBaseState(); + void testStatesRewriter(); + void testGradientsRewriter(); + void testTypicalRewriterOperations(); + void testBasicOperationsWithView(); + void testQmlModelView(); + void reparentingNode(); + void reparentingNodeInModificationGroup(); + void testRewriterTransactionRewriter(); + void testCopyModelRewriter1(); + void testCopyModelRewriter2(); + + // + // regression tests + // + + void reparentingNodeLikeDragAndDrop(); + + // + // old tests + // + + void loadEmptyCoreModel(); + void saveEmptyCoreModel(); + void loadAttributesInCoreModel(); + void saveAttributesInCoreModel(); + void loadComponentPropertiesInCoreModel(); + void loadSubItems(); + + void attributeChangeSynchronizer(); + void attributeAdditionSynchronizer(); + void attributeRemovalSynchronizer(); + void childAddedSynchronizer(); + void childRemovedSynchronizer(); + void childReplacedSynchronizer(); + + void createInvalidCoreModel(); + + void loadQml(); + void subItemMetaInfo(); + + + + + void defaultPropertyValues(); + + // Detailed tests for the rewriting: + + // Anchor tests: + void loadAnchors(); + void changeAnchors(); + void anchorToSibling(); + void anchorsAndStates(); + void removeFillAnchorByDetaching(); + void removeFillAnchorByChanging(); + void removeCenteredInAnchorByDetaching(); + + void sideIndex(); + + // Property bindings: + void loadPropertyBinding(); + void changePropertyBinding(); + + void loadObjectPropertyBinding(); + + // Bigger tests: + void loadWelcomeScreen(); + + // Object bindings as properties: + void loadGradient(); + void changeGradientId(); + + // Copy & Paste: + void copyAndPasteInSingleModel(); + void copyAndPasteBetweenModels(); + + // Editing inline Components + void changeSubModel(); + void changeInlineComponent(); + + // Testing imports + void changeImports(); + + void testIfChangePropertyIsRemoved(); + void testAnchorsAndStates(); + + // Testing states + void testStatesWithAnonymousTargets(); +}; + +#endif // TESTCORE_H diff --git a/tests/auto/qml/qmldesigner/coretests/testrewriterview.cpp b/tests/auto/qml/qmldesigner/coretests/testrewriterview.cpp new file mode 100644 index 00000000000..5a3dae01d2e --- /dev/null +++ b/tests/auto/qml/qmldesigner/coretests/testrewriterview.cpp @@ -0,0 +1,113 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "abstractproperty.h" +#include "nodeproperty.h" +#include "testrewriterview.h" +#include <QObject> + +using namespace QmlDesigner; +using namespace QmlDesigner::Internal; + +bool TestModelToTextMerger::isNodeScheduledForRemoval(const ModelNode &node) const +{ + foreach (const RewriteAction *action, scheduledRewriteActions()) { + if (RemoveNodeRewriteAction const *removeAction = action->asRemoveNodeRewriteAction()) { + if (removeAction->node() == node) + return true; + } + } + + return false; +} + +bool TestModelToTextMerger::isNodeScheduledForAddition(const ModelNode &node) const +{ + foreach (const RewriteAction *action, scheduledRewriteActions()) { + if (AddPropertyRewriteAction const *addPropertyAction = action->asAddPropertyRewriteAction()) { + const AbstractProperty property = addPropertyAction->property(); + if (property.isNodeProperty() && property.toNodeProperty().modelNode() == node) + return true; + else if (property.isNodeListProperty() && property.toNodeListProperty().toModelNodeList().contains(node)) + return true; + } else if (ChangePropertyRewriteAction const *changePropertyAction = action->asChangePropertyRewriteAction()) { + const AbstractProperty property = changePropertyAction->property(); + if (property.isNodeProperty() && property.toNodeProperty().modelNode() == node) + return true; + else if (property.isNodeListProperty() && property.toNodeListProperty().toModelNodeList().contains(node)) + return true; + } + + } + + return false; +} + +VariantProperty TestModelToTextMerger::findAddedVariantProperty(const VariantProperty &property) const +{ + foreach (const RewriteAction *action, scheduledRewriteActions()) { + if (AddPropertyRewriteAction const * addPropertyAction = action->asAddPropertyRewriteAction()) { + const AbstractProperty candidate = addPropertyAction->property(); + + if (property.isVariantProperty() && property.toVariantProperty() == property) + return property.toVariantProperty(); + } + } + + return VariantProperty(); +} + +TestRewriterView::TestRewriterView(QObject *parent) : RewriterView(RewriterView::Validate, parent) +{ +} + +bool TestRewriterView::isModificationGroupActive() const +{ + return RewriterView::isModificationGroupActive(); +} + +void TestRewriterView::setModificationGroupActive(bool active) +{ + RewriterView::setModificationGroupActive(active); +} + +void TestRewriterView::applyModificationGroupChanges() +{ + RewriterView::applyModificationGroupChanges(); +} + +Internal::TestModelToTextMerger *TestRewriterView::modelToTextMerger() const +{ + return static_cast<Internal::TestModelToTextMerger*> (RewriterView::modelToTextMerger()); +} + +Internal::TextToModelMerger *TestRewriterView::textToModelMerger() const +{ + return RewriterView::textToModelMerger(); +} diff --git a/tests/auto/qml/qmldesigner/coretests/testrewriterview.h b/tests/auto/qml/qmldesigner/coretests/testrewriterview.h new file mode 100644 index 00000000000..2483a379504 --- /dev/null +++ b/tests/auto/qml/qmldesigner/coretests/testrewriterview.h @@ -0,0 +1,68 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef TESTREWRITERVIEW_H +#define TESTREWRITERVIEW_H + +#include <rewriterview.h> +#include <model/modeltotextmerger.h> + +namespace QmlDesigner { + +namespace Internal { + +class TestModelToTextMerger : public ModelToTextMerger +{ +public: + bool isNodeScheduledForRemoval(const ModelNode &node) const; + bool isNodeScheduledForAddition(const ModelNode &node) const; + VariantProperty findAddedVariantProperty(const VariantProperty &property) const; +}; + +} // Internal + +class TestRewriterView : public RewriterView +{ + +Q_OBJECT + +public: + TestRewriterView(QObject *parent = 0); + + Internal::TestModelToTextMerger *modelToTextMerger() const; + Internal::TextToModelMerger *textToModelMerger() const; + + bool isModificationGroupActive() const; + void setModificationGroupActive(bool active); + void applyModificationGroupChanges(); +}; + +} // QmlDesigner + +#endif // TESTREWRITERVIEW_H diff --git a/tests/auto/qml/qmldesigner/data/fx/attributes.qml b/tests/auto/qml/qmldesigner/data/fx/attributes.qml new file mode 100644 index 00000000000..79738ef98cf --- /dev/null +++ b/tests/auto/qml/qmldesigner/data/fx/attributes.qml @@ -0,0 +1,8 @@ +import Qt 4.6 + +Item { + id: id; + x: 300; + visible: true; + scale: 0.5 +} diff --git a/tests/auto/qml/qmldesigner/data/fx/bigtest.qml b/tests/auto/qml/qmldesigner/data/fx/bigtest.qml new file mode 100644 index 00000000000..8f19af47d45 --- /dev/null +++ b/tests/auto/qml/qmldesigner/data/fx/bigtest.qml @@ -0,0 +1,78 @@ +import Qt 4.6 + +Item { + width: 640; + height: 480; + Rectangle { + width: 295; + height: 264; + color: "#009920"; + x: 22; + y: 31; + id: Rectangle_1; + Rectangle { + width: 299; + color: "#009920"; + x: 109; + height: 334; + y: 42; + id: Rectangle_3; + Rectangle { + color: "#009920"; + height: 209; + width: 288; + x: 98; + y: 152; + id: Rectangle_5; + } + + + Image { + source: "qrc:/fxplugin/images/template_image.png"; + height: 379; + x: -154; + width: 330; + y: -31; + id: Image_1; + }Image { + width: 293; + x: 33; + height: 172; + y: 39; + source: "qrc:/fxplugin/images/template_image.png"; + id: Image_2; + }} + + } + Rectangle { + width: 238; + height: 302; + x: 360; + y: 42; + color: "#009920"; + id: Rectangle_2; + Image { + source: "qrc:/fxplugin/images/template_image.png"; + x: -40; + y: 12; + width: 281; + height: 280; + id: Image_3; + }} + Rectangle { + height: 206; + width: 276; + color: "#009920"; + x: 4; + y: 234; + id: Rectangle_4; + } + Text { + text: "text"; + width: 80; + height: 20; + x: 71; + y: 15; + id: Text_1; + } +} diff --git a/tests/auto/qml/qmldesigner/data/fx/components.qml b/tests/auto/qml/qmldesigner/data/fx/components.qml new file mode 100644 index 00000000000..4e0bd8ae4e6 --- /dev/null +++ b/tests/auto/qml/qmldesigner/data/fx/components.qml @@ -0,0 +1,26 @@ +import Qt 4.6 + +Rectangle { + width: 100 + height: 100 + + + Component { + id: componentOne + Rectangle { width: 80; height: 80; color: "lightsteelblue" } + } + + Component { + id: componentTwo + Rectangle { width: 80; height: 80; color: "lightsteelblue" } + } + + Loader { + x: 40 + y: 40 + width: 20 + height: 20 + sourceComponent: componentTwo + } + + } \ No newline at end of file diff --git a/tests/auto/qml/qmldesigner/data/fx/empty.qml b/tests/auto/qml/qmldesigner/data/fx/empty.qml new file mode 100644 index 00000000000..1472f01bfc0 --- /dev/null +++ b/tests/auto/qml/qmldesigner/data/fx/empty.qml @@ -0,0 +1,4 @@ +import Qt 4.6 + +Item { +} diff --git a/tests/auto/qml/qmldesigner/data/fx/helloworld.qml b/tests/auto/qml/qmldesigner/data/fx/helloworld.qml new file mode 100644 index 00000000000..0c1ea919398 --- /dev/null +++ b/tests/auto/qml/qmldesigner/data/fx/helloworld.qml @@ -0,0 +1,76 @@ +import Qt 4.6 + +Rectangle { + width: 300 + height: 300 + id: Page + color: "#ffff00" + Rectangle { + width: 183 + x: 31 + objectName: "Rectangle_1" + y: 76 + rotation: -20 + height: 193 + color: "#ffaa00" + gradient: Gradient { + GradientStop { position: 0.0; color: "#ffaa00" } + GradientStop { position: 1.0; color: "#ffaa7f" } + } + Text { + width: 78 + x: 11 + y: 10 + height: 20 + id: HelloText2 + text: "Hello QmlGraphics!" + } + Text { + width: 85 + x: 11 + objectName: "xt5" + y: 165 + height: 18 + id: HelloText5 + text: "blah!!!!!" + } + Image { + width: 422 + x: 47 + objectName: "Image_1" + opacity: 0.6 + y: 45.2 + rotation: 0 + scale: 0.2 + height: 498 + source: "http://farm4.static.flickr.com/3002/2722850010_02b62a030f_m.jpg" + smooth: true + fillMode: "Tile" + } + Text { + width: 100 + x: 178 + y: 11.64 + rotation: 90 + height: 20 + id: HelloText11 + text: "Hello QmlGraphics!" + } + } + Text { + width: 100 + x: 10 + y: 13 + height: 20 + id: HelloText3 + text: "Hello QmlGraphics!" + } + Text { + width: 127 + x: 163 + y: 184 + height: 106 + id: HelloText4 + text: "Hello QmlGraphics!" + } +} diff --git a/tests/auto/qml/qmldesigner/data/fx/image.qml b/tests/auto/qml/qmldesigner/data/fx/image.qml new file mode 100644 index 00000000000..1db0d811c26 --- /dev/null +++ b/tests/auto/qml/qmldesigner/data/fx/image.qml @@ -0,0 +1,10 @@ +import Qt 4.6 + +Rectangle { + color: "blue"; + width: 640; + height: 480; + Image { + source: "../qt-logo.png" + } +} diff --git a/tests/auto/qml/qmldesigner/data/fx/properties.qml b/tests/auto/qml/qmldesigner/data/fx/properties.qml new file mode 100644 index 00000000000..03d8ca50638 --- /dev/null +++ b/tests/auto/qml/qmldesigner/data/fx/properties.qml @@ -0,0 +1,5 @@ +import Qt 4.6 + +Item { + property bool pushed +} diff --git a/tests/auto/qml/qmldesigner/data/fx/selectiontest.qml b/tests/auto/qml/qmldesigner/data/fx/selectiontest.qml new file mode 100644 index 00000000000..631a0b6999f --- /dev/null +++ b/tests/auto/qml/qmldesigner/data/fx/selectiontest.qml @@ -0,0 +1,39 @@ +import Qt 4.6 + +Item { + id: RootItem + width: 640 + height: 480 +Rectangle { + height: 100; + x: 104; + y: 178; + color: "#ffffff"; + width: 100; + id: Rectangle_1; + } +Rectangle { + x: 110; + y: 44; + color: "#ffffff"; + height: 100; + width: 100; + id: Rectangle_2; + } +Rectangle { + width: 100; + color: "#ffffff"; + height: 100; + x: 323; + y: 160; + id: Rectangle_3; + } +Rectangle { + color: "#ffffff"; + width: 100; + height: 100; + x: 233; + y: 293; + id: Rectangle_4; + } +} diff --git a/tests/auto/qml/qmldesigner/data/fx/states.qml b/tests/auto/qml/qmldesigner/data/fx/states.qml new file mode 100644 index 00000000000..6c35f4ee2de --- /dev/null +++ b/tests/auto/qml/qmldesigner/data/fx/states.qml @@ -0,0 +1,59 @@ +import Qt 4.6 + +Item { + id: theRootItem; + width: 400; + height: 400; + Rectangle { + width: 157; + height: 120; + x: 145; + y: 157; + id: Rectangle_1; + } + + Rectangle { + x: 49; + y: 6; + width: 100; + id: Rectangle_2; + color: "#009920"; + height: 100; + } + + states: [ + State { + name: "State1"; + when: destination === "one"; + + PropertyChanges { + target: Rectangle_2; + height: 200 + width: 300 + } + }, + State { + name: "State2"; + when: false == true; + }, + State { + name: "State3"; + PropertyChanges { + target: Rectangle_2; + x: 200 + y: 300 + } + } + ] + + transitions: [ + Transition { + NumberAnimation { + properties: "width, height" + easing: "OutQuad" + duration: 500 + } + } + ] +} + diff --git a/tests/auto/qml/qmldesigner/data/fx/subitems/SubItem.qml b/tests/auto/qml/qmldesigner/data/fx/subitems/SubItem.qml new file mode 100644 index 00000000000..97b14f6ef46 --- /dev/null +++ b/tests/auto/qml/qmldesigner/data/fx/subitems/SubItem.qml @@ -0,0 +1,14 @@ +import Qt 4.6 + +Rectangle { + property string text + + width : {toprect.width} + height : 20 + color : "blue" + Text { + width : 100 + color : "white" + text : parent.text + } +} diff --git a/tests/auto/qml/qmldesigner/data/fx/topitem.qml b/tests/auto/qml/qmldesigner/data/fx/topitem.qml new file mode 100644 index 00000000000..dc5f10bc4d8 --- /dev/null +++ b/tests/auto/qml/qmldesigner/data/fx/topitem.qml @@ -0,0 +1,17 @@ +import Qt 4.6 +import Qt 4.6 as Qt46 +import "subitems" +import "subitems" as Subdir + +Qt46.Rectangle { + id : "toprect" + width : 100 + height : 50 + SubItem { + text: "item1" + } + Subdir.SubItem { + text : "item2" + y: 25 + } +} diff --git a/tests/auto/qml/qmldesigner/data/qt-logo.png b/tests/auto/qml/qmldesigner/data/qt-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..5ab3a1b0c4618ccce8321db7253b8dcdc86cfa98 GIT binary patch literal 23519 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 literal 0 HcmV?d00001 diff --git a/tests/auto/qml/qmldesigner/data/qwidget/test.qml b/tests/auto/qml/qmldesigner/data/qwidget/test.qml new file mode 100644 index 00000000000..6f0fb825540 --- /dev/null +++ b/tests/auto/qml/qmldesigner/data/qwidget/test.qml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<QWidget id="Form" x="300" y="200" width="455" height="376"> + <QPushButton id="pushButton" x="10" y="30" width="75" height="25"> + <text><?i18n?>PushButton</text> + </QPushButton> + <QPushButton id="toolButton" x="20" y="170" width="27" height="23"> + <text><?i18n?>...</text> + </QPushButton> + <QPushButton id="pushButton_2" x="180" y="170" width="75" height="25"> + <text><?i18n?>PushButton</text> + </QPushButton> + <QPushButton id="pushButton_3" x="180" y="200" width="75" height="25"> + <text><?i18n?>PushButton</text> + </QPushButton> + <QPushButton id="pushButton_4" x="180" y="230" width="75" height="25"> + <text><?i18n?>PushButton</text> + </QPushButton> + <QPushButton id="pushButton_5" x="180" y="260" width="75" height="25"> + <text><?i18n?>PushButton</text> + </QPushButton> + <QPushButton id="pushButton_6" x="180" y="290" width="75" height="25"> + <text><?i18n?>PushButton</text> + </QPushButton> + <QPushButton id="pushButton_7" x="180" y="330" width="75" height="25"> + <text><?i18n?>PushButton</text> + </QPushButton> +</QWidget> diff --git a/tests/auto/qml/qmldesigner/data/testfiles.qrc b/tests/auto/qml/qmldesigner/data/testfiles.qrc new file mode 100644 index 00000000000..b607beb73f9 --- /dev/null +++ b/tests/auto/qml/qmldesigner/data/testfiles.qrc @@ -0,0 +1,11 @@ +<RCC> + <qresource prefix="/" > + <file>fx/attributes.qml</file> + <file>fx/empty.qml</file> + <file>fx/helloworld.qml</file> + <file>fx/properties.qml</file> + <file>qwidget/test.qml</file> + <file>fx/topitem.qml</file> + <file>fx/subitems/SubItem.qml</file> + </qresource> +</RCC> diff --git a/tests/auto/qml/qmldesigner/propertyeditortests/propertyeditortests.pro b/tests/auto/qml/qmldesigner/propertyeditortests/propertyeditortests.pro new file mode 100644 index 00000000000..9daa18c38fb --- /dev/null +++ b/tests/auto/qml/qmldesigner/propertyeditortests/propertyeditortests.pro @@ -0,0 +1,24 @@ +include(../../../../../src/plugins/qmldesigner/config.pri) +include(../../../../../src/plugins/qmldesigner/components/propertyeditor/propertyeditor.pri) + +QT += testlib \ + script \ + declarative +DESTDIR = $$DESIGNER_BINARY_DIRECTORY +include(../../../../../src/plugins/qmldesigner/core/core.pri) + +DEPENDPATH += ../../../../../src/plugins/qmldesigner/core/include +DEPENDPATH += .. + +TARGET = tst_propertyeditor +CONFIG += console +CONFIG -= app_bundle +TEMPLATE = app +SOURCES += testpropertyeditor.cpp +SOURCES += ../testview.cpp +HEADERS += testpropertyeditor.h +HEADERS += ../testview.h +RESOURCES += ../data/testfiles.qrc + +HEADERS -= allpropertiesbox.h +SOURCES -= allpropertiesbox.cpp diff --git a/tests/auto/qml/qmldesigner/propertyeditortests/testpropertyeditor.cpp b/tests/auto/qml/qmldesigner/propertyeditortests/testpropertyeditor.cpp new file mode 100644 index 00000000000..b8c3166f2cf --- /dev/null +++ b/tests/auto/qml/qmldesigner/propertyeditortests/testpropertyeditor.cpp @@ -0,0 +1,370 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "testpropertyeditor.h" +#include "../testview.h" + +#include <memory> +#include <cstdio> + +#include <metainfo.h> +#include <model.h> +#include <modelnode.h> +#include <variantproperty.h> +#include <bytearraymodifier.h> +#include <invalididexception.h> +#include <invalidmodelnodeexception.h> +#include <propertyeditor.h> +#include <QDebug> +#include <QDir> +#include <QSpinBox> +#include <QLineEdit> +#include <QStackedWidget> +#include <QmlView> +#include <QVariant> + +using namespace QmlDesigner; + +#include "../common/statichelpers.cpp" + + +static void inspectPropertyEditor(ModelNode node, QWidget* propWidget) +{ + if (!propWidget) + return; + + QStackedWidget * stackedWidget = qobject_cast<QStackedWidget *> (propWidget); + QVERIFY(stackedWidget); + QmlView *view = qobject_cast<QmlView*>(stackedWidget->currentWidget()); + QVERIFY(view); + + QLineEdit * idLineEdit = view->findChild<QLineEdit* >("IdLineEdit"); + + if (!idLineEdit) + return ; + + QCOMPARE(idLineEdit->text(), node.id()); + + if (node.hasProperty("width")) { + QSpinBox * widthSpinBox = view->findChild<QSpinBox* >("WidthSpinBox"); + QVERIFY(widthSpinBox); + QCOMPARE(widthSpinBox->value(), node.variantProperty("width").value().toInt()); + } + + if (node.hasProperty("height")) { + QSpinBox * heightSpinBox = view->findChild<QSpinBox* >("HeightSpinBox"); + QVERIFY(heightSpinBox); + QCOMPARE(heightSpinBox->value(), node.variantProperty("height").value().toInt());// this can be dangerous + } + + if (node.hasProperty("x")) { + QSpinBox * xSpinBox = view->findChild<QSpinBox* >("XSpinBox"); + QVERIFY(xSpinBox); + QCOMPARE(xSpinBox->value(), node.variantProperty("x").value().toInt()); // this can be dangerous + } + + if (node.hasProperty("y")) { + QSpinBox * ySpinBox = view->findChild<QSpinBox* >("YSpinBox"); + QVERIFY(ySpinBox); + QCOMPARE(ySpinBox->value(), node.variantProperty("y").value().toInt()); + } +} +static void selectThrough(ModelNode node, QWidget* propWidget = 0) +{ + QVERIFY(node.isValid()); + int numberOfProperties = node.propertyNames().count(); + QList<AbstractProperty> properties = node.properties(); + node.view()->clearSelectedModelNodes(); + node.view()->selectModelNode(node); + QString name = node.id(); + qApp->processEvents(); + QTest::qSleep(100); + qApp->processEvents(); + QTest::qSleep(100); + qApp->processEvents(); + inspectPropertyEditor(node, propWidget); + //selecting should not effect any properties at all! + QCOMPARE(node.propertyNames().count(), numberOfProperties); + foreach (const AbstractProperty &property, properties) + if (property.isVariantProperty()) { + QCOMPARE(property.toVariantProperty().value(), node.variantProperty(property.name()).value()); + } + QList<ModelNode> childNodes = node.allDirectSubModelNodes(); + foreach (const ModelNode &childNode, childNodes) + selectThrough(childNode, propWidget); +} + +static QWidget * setupPropertyEditor(QWidget *widget, Model *model) +{ + PropertyEditor *properties = new PropertyEditor(widget); + + QString qmlDir = QDir::cleanPath(qApp->applicationDirPath() + QString("/../shared/propertyeditor/")); + qDebug() << qmlDir; + properties->setQmlDir(qmlDir); + model->attachView(properties); + QWidget *pane = properties->createPropertiesPage(); + pane->setParent(widget); + widget->show(); + widget->resize(300, 800); + qApp->processEvents(); + pane->resize(300, 800); + pane->move(0,0); + qApp->processEvents(); + QTest::qSleep(100); + + return pane; +} + +static void loadFileAndTest(const QString &fileName) +{ + QFile file(fileName); + QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text)); + + QList<QmlError> errors; + //std::auto_ptr<ByteArrayModifier> modifier1(ByteArrayModifier::create(QString(file.readAll()))); + //std::auto_ptr<Model> model1(Model::create(modifier1.get(), QUrl::fromLocalFile(file.fileName()), &errors)); + + QScopedPointer<Model> model1(Model::create("Item")); + QVERIFY(model1.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model1->attachView(view.data()); + + + QVERIFY(model1.data()); + + std::auto_ptr<QWidget> widget(new QWidget()); + QWidget *propWidget = setupPropertyEditor(widget.get(), model1.data()); + + selectThrough(view->rootModelNode(), propWidget); +} + +TestPropertyEditor::TestPropertyEditor() + : QObject() +{ +} + +void TestPropertyEditor::initTestCase() +{ + qInstallMsgHandler(testMessageOutput); + MetaInfo::setPluginPaths(pluginPaths()); + Exception::setShouldAssert(false); +} + + +void TestPropertyEditor::createCoreModel() +{ + try { + std::auto_ptr<QWidget> widget(new QWidget()); + + QScopedPointer<Model> model(Model::create("import Qt 4.6\n Item {}")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + QVERIFY(model.data()); + setupPropertyEditor(widget.get(), model.data()); + + QVERIFY(view->rootModelNode().isValid()); + int numberOfProperties = view->rootModelNode().propertyNames().count(); + selectThrough(view->rootModelNode()); + QCOMPARE(view->rootModelNode().propertyNames().count(), numberOfProperties); + } catch (Exception &exception) { + QFAIL("Exception thrown"); + } +} + +void TestPropertyEditor::loadEmptyCoreModel() +{ + /* QList<QmlError> errors; + QFile file(":/fx/empty.qml"); + QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text)); + + std::auto_ptr<QWidget> widget(new QWidget()); + std::auto_ptr<ByteArrayModifier> modifier1(ByteArrayModifier::create(QString(file.readAll()))); + std::auto_ptr<Model> model1(Model::create(modifier1.get(), QUrl(), &errors)); + foreach (const QmlError &error, errors) + QFAIL(error.toString().toLatin1()); + QVERIFY(model1.get()); + setupPropertyEditor(widget.get(), model1.get()); + + selectThrough(model1->rootNode()); + + std::auto_ptr<ByteArrayModifier> modifier2(ByteArrayModifier::create("import Qt 4.6\n Item{}")); + std::auto_ptr<Model> model2(Model::create(modifier2.get(), QUrl(), &errors)); + foreach (const QmlError &error, errors) + QFAIL(error.toString().toLatin1()); + QVERIFY(model2.get()); + + QVERIFY(compareTree(model1->rootNode(), model2->rootNode()));*/ +} + +void TestPropertyEditor::createSubNode() +{ + std::auto_ptr<QWidget> widget(new QWidget()); + + QScopedPointer<Model> model(Model::create("import Qt 4.6\n Item {}")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + setupPropertyEditor(widget.get(), model.data()); + QVERIFY(view->rootModelNode().isValid()); + selectThrough(view->rootModelNode()); + ModelNode childNode = view->rootModelNode().addChildNode("Qt/Rectangle", 4, 6, "data"); + QVERIFY(childNode.isValid()); + QVERIFY(view->rootModelNode().allDirectSubModelNodes().contains(childNode)); + QVERIFY(childNode.parentProperty().parentModelNode() == view->rootModelNode()); + QCOMPARE(childNode.type(), QString("Qt/Rectangle")); + + selectThrough(childNode); + + QVERIFY(childNode.id().isEmpty()); + + childNode.setId("Blah"); + QCOMPARE(childNode.id(), QString("Blah")); + + + QCOMPARE(childNode.id(), QString("Blah")); +} + +void TestPropertyEditor::createRect() +{ + try { + + std::auto_ptr<QWidget> widget(new QWidget()); + + QScopedPointer<Model> model(Model::create("import Qt 4.6\n Item {}")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + setupPropertyEditor(widget.get(), model.data()); + + QVERIFY(view->rootModelNode().isValid()); + + //selectThrough(view->rootModelNode()); + + ModelNode childNode = view->rootModelNode().addChildNode("Qt/Rectangle", 4, 6, "data"); + QVERIFY(childNode.isValid()); + QVERIFY(view->rootModelNode().allDirectSubModelNodes().contains(childNode)); + QVERIFY(childNode.parentProperty().parentModelNode() == view->rootModelNode()); + QCOMPARE(childNode.type(), QString("Qt/Rectangle")); + + QVERIFY(childNode.id().isEmpty()); + + childNode.setId("Rect01"); + QCOMPARE(childNode.id(), QString("Rect01")); + + childNode.variantProperty("x") = 100; + QCOMPARE(QmlObjectNode(childNode).instanceValue("x").toInt(), 100); + childNode.variantProperty("y") = 100; + QCOMPARE(QmlObjectNode(childNode).instanceValue("y").toInt(), 100); + childNode.variantProperty("width") = 100; + QCOMPARE(QmlObjectNode(childNode).instanceValue("width").toInt(), 100); + childNode.variantProperty("height") = 100; + QCOMPARE(QmlObjectNode(childNode).instanceValue("height").toInt(), 100); + + selectThrough(childNode); + + QCOMPARE(childNode.propertyNames().count(), 4); + QCOMPARE(childNode.variantProperty("scale").value(), QVariant()); + + } catch (Exception &exception) { + QFAIL("Exception thrown"); + } +} + +void TestPropertyEditor::removeNode() +{ + std::auto_ptr<QWidget> widget(new QWidget()); + + QScopedPointer<Model> model(Model::create("import Qt 4.6\n Item {}")); + QVERIFY(model.data()); + + QScopedPointer<TestView> view(new TestView); + QVERIFY(view.data()); + model->attachView(view.data()); + + setupPropertyEditor(widget.get(), model.data()); + + QCOMPARE(view->rootModelNode().allDirectSubModelNodes().count(), 0); + + selectThrough(view->rootModelNode()); + + ModelNode childNode = view->rootModelNode().addChildNode("Qt/Rectangle", 4, 6, "data"); + QVERIFY(childNode.isValid()); + QCOMPARE(view->rootModelNode().allDirectSubModelNodes().count(), 1); + QVERIFY(view->rootModelNode().allDirectSubModelNodes().contains(childNode)); + QVERIFY(childNode.parentProperty().parentModelNode() == view->rootModelNode()); + + selectThrough(childNode); + + ModelNode subChildNode = childNode.addChildNode("Qt/Rectangle", 4, 6, "data"); + QVERIFY(subChildNode.isValid()); + QCOMPARE(childNode.allDirectSubModelNodes().count(), 1); + QVERIFY(childNode.allDirectSubModelNodes().contains(subChildNode)); + QVERIFY(subChildNode.parentProperty().parentModelNode() == childNode); + + selectThrough(subChildNode); + + childNode.destroy(); + + QCOMPARE(view->rootModelNode().allDirectSubModelNodes().count(), 0); + QVERIFY(!view->rootModelNode().allDirectSubModelNodes().contains(childNode)); + QVERIFY(!childNode.isValid()); + QVERIFY(!subChildNode.isValid()); +} + +void TestPropertyEditor::loadWelcomeScreen() +{ + loadFileAndTest(QCoreApplication::applicationDirPath() + "/../shared/welcomescreen.qml"); +} + +void TestPropertyEditor::loadHelloWorld() +{ + loadFileAndTest(":/fx/helloworld.qml"); +} + + + +void TestPropertyEditor::cleanupTestCase() +{ + MetaInfo::clearGlobal(); +} + + +QTEST_MAIN(TestPropertyEditor); diff --git a/tests/auto/qml/qmldesigner/propertyeditortests/testpropertyeditor.h b/tests/auto/qml/qmldesigner/propertyeditortests/testpropertyeditor.h new file mode 100644 index 00000000000..bb1e3dfd377 --- /dev/null +++ b/tests/auto/qml/qmldesigner/propertyeditortests/testpropertyeditor.h @@ -0,0 +1,57 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef TESTPROPERTYEDITOR_H +#define TESTPROPERTYEDITOR_H + +#include <QObject> + +#include <QtTest> + + +class TestPropertyEditor : public QObject +{ + Q_OBJECT +public: + TestPropertyEditor(); +private slots: + void initTestCase(); + void cleanupTestCase(); + + void createCoreModel(); + void loadEmptyCoreModel(); + + void createSubNode(); + void createRect(); + void removeNode(); + void loadWelcomeScreen(); + void loadHelloWorld(); +}; + +#endif // TESTPROPERTYEDITOR_H diff --git a/tests/auto/qml/qmldesigner/qmldesigner.pro b/tests/auto/qml/qmldesigner/qmldesigner.pro new file mode 100644 index 00000000000..eaeb28fdaef --- /dev/null +++ b/tests/auto/qml/qmldesigner/qmldesigner.pro @@ -0,0 +1,5 @@ +TEMPLATE = subdirs + +SUBDIRS += coretests +SUBDIRS += propertyeditortests +SUBDIRS += bauhaustests diff --git a/tests/auto/qml/qmldesigner/testview.cpp b/tests/auto/qml/qmldesigner/testview.cpp new file mode 100644 index 00000000000..b1e424d9db0 --- /dev/null +++ b/tests/auto/qml/qmldesigner/testview.cpp @@ -0,0 +1,213 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "testview.h" + +#include <QtDebug> +#include <modelnode.h> +#include <model.h> +#include <modelnode.h> +#include <nodeabstractproperty.h> +#include <nodelistproperty.h> +#include <qtestcase.h> + +TestView::TestView(QObject *parent) + : QmlDesigner::QmlModelView(parent) +{ +} + +void TestView::modelAttached(QmlDesigner::Model *model) +{ + QmlDesigner::QmlModelView::modelAttached(model); + m_methodCalls += MethodCall("modelAttached", QStringList() << QString::number(reinterpret_cast<long>(model))); +} + +void TestView::modelAboutToBeDetached(QmlDesigner::Model *model) +{ + QmlDesigner::QmlModelView::modelAboutToBeDetached(model); + m_methodCalls += MethodCall("modelAboutToBeDetached", QStringList() << QString::number(reinterpret_cast<long>(model))); +} + +void TestView::nodeIdChanged(const QmlDesigner::ModelNode &node, const QString& newId, const QString &oldId) +{ + QmlDesigner::QmlModelView::nodeIdChanged(node, newId, oldId); + m_methodCalls += MethodCall("nodeIdChanged", QStringList() << node.id() << newId << oldId); +} + +void TestView::nodeTypeChanged(const QmlDesigner::ModelNode &node,const QString &type, int majorVersion, int minorVersion) +{ + QmlDesigner::QmlModelView::nodeTypeChanged(node, type, majorVersion, minorVersion); + m_methodCalls += MethodCall("nodeTypeChanged", QStringList() << node.id() << type << QString::number(majorVersion) << QString::number(minorVersion)); +} + +void TestView::fileUrlChanged(const QUrl & oldBaseUrl, const QUrl &newBaseUrl) +{ + QmlDesigner::QmlModelView::fileUrlChanged(oldBaseUrl, newBaseUrl); + m_methodCalls += MethodCall("fileUrlChanged", QStringList() << oldBaseUrl.toString() << newBaseUrl.toString()); +} + +void TestView::propertiesAboutToBeRemoved(const QList<QmlDesigner::AbstractProperty>& propertyList) +{ + QmlDesigner::QmlModelView::propertiesAboutToBeRemoved(propertyList); + QStringList propertyNames; + foreach (const QmlDesigner::AbstractProperty &property, propertyList) + propertyNames += property.name(); + m_methodCalls += MethodCall("propertiesAboutToBeRemoved", QStringList() << propertyNames.join(", ")); +} + +void TestView::nodeCreated(const QmlDesigner::ModelNode &createdNode) +{ + QmlDesigner::QmlModelView::nodeCreated(createdNode); + m_methodCalls += MethodCall("nodeCreated", QStringList() << createdNode.id()); +} + +void TestView::nodeAboutToBeRemoved(const QmlDesigner::ModelNode &removedNode) +{ + QmlDesigner::QmlModelView::nodeAboutToBeRemoved(removedNode); + m_methodCalls += MethodCall("nodeAboutToBeRemoved", QStringList() << removedNode.id()); +} + +void TestView::nodeRemoved(const QmlDesigner::ModelNode &removedNode, const QmlDesigner::NodeAbstractProperty &parentProperty, AbstractView::PropertyChangeFlags propertyChange) +{ + QmlDesigner::QmlModelView::nodeRemoved(removedNode, parentProperty, propertyChange); + const QString parentPropertyName = parentProperty.isValid() ? parentProperty.name() : ""; + + m_methodCalls += MethodCall("nodeRemoved", QStringList() << QString() << parentPropertyName << serialize(propertyChange)); +} + + +void TestView::nodeReparented(const QmlDesigner::ModelNode & node, const QmlDesigner::NodeAbstractProperty &newPropertyParent, const QmlDesigner::NodeAbstractProperty & oldPropertyParent, AbstractView::PropertyChangeFlags propertyChange) +{ + QmlDesigner::QmlModelView::nodeReparented(node, newPropertyParent, oldPropertyParent, propertyChange); + m_methodCalls += MethodCall("nodeReparented", QStringList() << node.id() << newPropertyParent.name() << oldPropertyParent.name() << serialize(propertyChange)); +} + +void TestView::bindingPropertiesChanged(const QList<QmlDesigner::BindingProperty>& propertyList, PropertyChangeFlags propertyChange) +{ + QmlDesigner::QmlModelView::bindingPropertiesChanged(propertyList, propertyChange); + QStringList propertyNames; + foreach (const QmlDesigner::BindingProperty &property, propertyList) + propertyNames += property.name(); + m_methodCalls += MethodCall("bindingPropertiesChanged", QStringList() << propertyNames.join(", ") << serialize(propertyChange)); +} + +void TestView::variantPropertiesChanged(const QList<QmlDesigner::VariantProperty>& propertyList, PropertyChangeFlags propertyChange) +{ + QmlDesigner::QmlModelView::variantPropertiesChanged(propertyList, propertyChange); + QStringList propertyNames; + foreach (const QmlDesigner::VariantProperty &property, propertyList) + propertyNames += property.name(); + + m_methodCalls += MethodCall("variantPropertiesChanged", QStringList() << propertyNames.join(", ") << serialize(propertyChange)); +} + +void TestView::selectedNodesChanged(const QList<QmlDesigner::ModelNode> &selectedNodeList, + const QList<QmlDesigner::ModelNode> &lastSelectedNodeList) +{ + QmlDesigner::QmlModelView::selectedNodesChanged(selectedNodeList, lastSelectedNodeList); + QStringList selectedNodes; + foreach (const QmlDesigner::ModelNode &node, selectedNodeList) + selectedNodes += node.id(); + QStringList lastSelectedNodes; + foreach (const QmlDesigner::ModelNode &node, lastSelectedNodeList) + lastSelectedNodes += node.id(); + m_methodCalls += MethodCall("selectedNodesChanged", QStringList() << selectedNodes.join(", ") << lastSelectedNodes.join(", ")); +} + +void TestView::nodeSlidedToIndex(const QmlDesigner::NodeListProperty &listProperty, int newIndex, int oldIndex) +{ + QmlDesigner::QmlModelView::nodeSlidedToIndex(listProperty, newIndex, oldIndex); + m_methodCalls += MethodCall("nodeSlidedToIndex", QStringList() << listProperty.name() << QString::number(newIndex) << QString::number(oldIndex)); +} + +void TestView::stateChanged(const QmlDesigner::QmlModelState &newQmlModelState, const QmlDesigner::QmlModelState &oldQmlModelState) +{ + QmlDesigner::QmlModelView::stateChanged(newQmlModelState, oldQmlModelState); + m_methodCalls += MethodCall("stateChanged", QStringList() << newQmlModelState.name() << oldQmlModelState.name()); +} + +QList<TestView::MethodCall> &TestView::methodCalls() +{ + return m_methodCalls; +} + +QString TestView::lastFunction() const +{ + return m_methodCalls.last().name; +} + +QmlDesigner::NodeInstanceView *TestView::nodeInstanceView() const +{ + return QmlDesigner::QmlModelView::nodeInstanceView(); + +} + +QmlDesigner::NodeInstance TestView::instanceForModelNode(const QmlDesigner::ModelNode &modelNode) +{ + return QmlDesigner::QmlModelView::instanceForModelNode(modelNode); +} + + +QString TestView::serialize(AbstractView::PropertyChangeFlags change) +{ + QStringList tokenList; + + if (change.testFlag(PropertiesAdded)) + tokenList.append(QLatin1String("PropertiesAdded")); + + if (change.testFlag(EmptyPropertiesRemoved)) + tokenList.append(QLatin1String("EmptyPropertiesRemoved")); + + return tokenList.join(" "); +} + + +bool operator==(TestView::MethodCall call1, TestView::MethodCall call2) +{ + if (call1.name != call2.name) + return false; +// if (call1.arguments != call2.arguments) +// return false; + if (call1.arguments.size() != call2.arguments.size()) { + qWarning() << "different method names:" << call1.arguments.size() << call2.arguments.size(); + return false; + } + for (int i = 0; i < call1.arguments.size(); ++i) + if (call1.arguments.at(i) != call2.arguments.at(i)) { + qDebug() << "different argument values:" << call1.arguments.at(i) << call2.arguments.at(i); + return false; + } + return true; +} + +QDebug operator<<(QDebug debug, TestView::MethodCall call) +{ + return debug.nospace() << call.name << "(" << call.arguments.join(", ") << ")"; +} + diff --git a/tests/auto/qml/qmldesigner/testview.h b/tests/auto/qml/qmldesigner/testview.h new file mode 100644 index 00000000000..c667f8b0415 --- /dev/null +++ b/tests/auto/qml/qmldesigner/testview.h @@ -0,0 +1,93 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef TESTVIEW_H +#define TESTVIEW_H + +#include <modelnode.h> +#include <qmlmodelview.h> +#include <QVariant> +#include <QStringList> + +class TestView : public QmlDesigner::QmlModelView +{ + Q_OBJECT +public: + struct MethodCall { + MethodCall(const QString &n, const QStringList &args) : + name(n), arguments(args) + { + } + + QString name; + QStringList arguments; + }; + + TestView(QObject *parent = 0); + + void modelAttached(QmlDesigner::Model *model); + void modelAboutToBeDetached(QmlDesigner::Model *model); + void nodeCreated(const QmlDesigner::ModelNode &createdNode); + void nodeAboutToBeRemoved(const QmlDesigner::ModelNode &removedNode); + void nodeRemoved(const QmlDesigner::ModelNode &removedNode, const QmlDesigner::NodeAbstractProperty &parentProperty, AbstractView::PropertyChangeFlags propertyChange); + void nodeReparented(const QmlDesigner::ModelNode &node, const QmlDesigner::NodeAbstractProperty &newPropertyParent, const QmlDesigner::NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags propertyChange); + void nodeIdChanged(const QmlDesigner::ModelNode& node, const QString& newId, const QString& oldId); + void nodeTypeChanged(const QmlDesigner::ModelNode &node,const QString &type, int majorVersion, int minorVersion); + + void bindingPropertiesChanged(const QList<QmlDesigner::BindingProperty>& propertyList, PropertyChangeFlags propertyChange); + void variantPropertiesChanged(const QList<QmlDesigner::VariantProperty>& propertyList, PropertyChangeFlags propertyChange); + void propertiesAboutToBeRemoved(const QList<QmlDesigner::AbstractProperty> &propertyList); + + + + void fileUrlChanged(const QUrl &oldBaseUrl, const QUrl &newBaseUrl); + void selectedNodesChanged(const QList<QmlDesigner::ModelNode> &selectedNodeList, + const QList<QmlDesigner::ModelNode> &lastSelectedNodeList); + + void nodeSlidedToIndex(const QmlDesigner::NodeListProperty &listProperty, int newIndex, int oldIndex); + + void stateChanged(const QmlDesigner::QmlModelState &newQmlModelState, const QmlDesigner::QmlModelState &oldQmlModelState); + + QList<MethodCall> &methodCalls(); + + QString lastFunction() const; + + QmlDesigner::NodeInstanceView *nodeInstanceView() const; + + QmlDesigner::NodeInstance instanceForModelNode(const QmlDesigner::ModelNode &modelNode); + +private: + QList<MethodCall> m_methodCalls; + static QString serialize(AbstractView::PropertyChangeFlags change); +}; + +bool operator==(TestView::MethodCall call1, TestView::MethodCall call2); +QDebug operator<<(QDebug debug, TestView::MethodCall call); + +#endif // TESTVIEW_H -- GitLab