From 12a5ddf456ba8549645a8cb28a8b4ed6197a14da Mon Sep 17 00:00:00 2001 From: Matthew Vogt Date: Mon, 30 Jan 2012 14:16:15 +1000 Subject: Import relevant source from Qt 4.8 Change-Id: I5078db4081d95290c54f39d3c0efc2fc2f62e6a6 --- src/declarative/QmlChanges.txt | 364 ++ src/declarative/debugger/debugger.pri | 33 + src/declarative/debugger/qdeclarativedebug.h | 67 + .../debugger/qdeclarativedebugclient.cpp | 292 ++ .../debugger/qdeclarativedebugclient_p.h | 103 + .../debugger/qdeclarativedebuggerstatus.cpp | 54 + .../debugger/qdeclarativedebuggerstatus_p.h | 68 + .../debugger/qdeclarativedebughelper.cpp | 76 + .../debugger/qdeclarativedebughelper_p.h | 73 + .../debugger/qdeclarativedebugserver.cpp | 426 +++ .../debugger/qdeclarativedebugserver_p.h | 91 + .../debugger/qdeclarativedebugserverconnection_p.h | 74 + .../debugger/qdeclarativedebugservice.cpp | 230 ++ .../debugger/qdeclarativedebugservice_p.h | 96 + .../debugger/qdeclarativedebugservice_p_p.h | 71 + .../debugger/qdeclarativedebugtrace.cpp | 225 ++ .../debugger/qdeclarativedebugtrace_p.h | 132 + .../debugger/qdeclarativeenginedebug.cpp | 1068 ++++++ .../debugger/qdeclarativeenginedebug_p.h | 387 ++ .../debugger/qdeclarativeenginedebugservice.cpp | 747 ++++ .../debugger/qdeclarativeenginedebugservice_p.h | 134 + .../debugger/qdeclarativeinspectorinterface_p.h | 69 + .../debugger/qdeclarativeinspectorservice.cpp | 147 + .../debugger/qdeclarativeinspectorservice_p.h | 93 + src/declarative/debugger/qjsdebuggeragent.cpp | 614 ++++ src/declarative/debugger/qjsdebuggeragent_p.h | 229 ++ src/declarative/debugger/qjsdebugservice.cpp | 259 ++ src/declarative/debugger/qjsdebugservice_p.h | 123 + src/declarative/debugger/qpacketprotocol.cpp | 557 +++ src/declarative/debugger/qpacketprotocol_p.h | 127 + src/declarative/declarative.pro | 43 + src/declarative/graphicsitems/graphicsitems.pri | 94 + .../graphicsitems/qdeclarativeanchors.cpp | 1165 ++++++ .../graphicsitems/qdeclarativeanchors_p.h | 204 ++ .../graphicsitems/qdeclarativeanchors_p_p.h | 170 + .../graphicsitems/qdeclarativeanimatedimage.cpp | 404 +++ .../graphicsitems/qdeclarativeanimatedimage_p.h | 116 + .../graphicsitems/qdeclarativeanimatedimage_p_p.h | 87 + .../graphicsitems/qdeclarativeborderimage.cpp | 623 ++++ .../graphicsitems/qdeclarativeborderimage_p.h | 109 + .../graphicsitems/qdeclarativeborderimage_p_p.h | 106 + .../graphicsitems/qdeclarativeevents.cpp | 237 ++ .../graphicsitems/qdeclarativeevents_p_p.h | 141 + .../graphicsitems/qdeclarativeflickable.cpp | 1805 ++++++++++ .../graphicsitems/qdeclarativeflickable_p.h | 229 ++ .../graphicsitems/qdeclarativeflickable_p_p.h | 244 ++ .../graphicsitems/qdeclarativeflipable.cpp | 254 ++ .../graphicsitems/qdeclarativeflipable_p.h | 100 + .../graphicsitems/qdeclarativefocuspanel.cpp | 89 + .../graphicsitems/qdeclarativefocuspanel_p.h | 78 + .../graphicsitems/qdeclarativefocusscope.cpp | 73 + .../graphicsitems/qdeclarativefocusscope_p.h | 69 + .../graphicsitems/qdeclarativegraphicswidget.cpp | 125 + .../graphicsitems/qdeclarativegraphicswidget_p.h | 90 + .../graphicsitems/qdeclarativegridview.cpp | 3172 +++++++++++++++++ .../graphicsitems/qdeclarativegridview_p.h | 286 ++ .../graphicsitems/qdeclarativeimage.cpp | 584 +++ .../graphicsitems/qdeclarativeimage_p.h | 100 + .../graphicsitems/qdeclarativeimage_p_p.h | 79 + .../graphicsitems/qdeclarativeimagebase.cpp | 284 ++ .../graphicsitems/qdeclarativeimagebase_p.h | 116 + .../graphicsitems/qdeclarativeimagebase_p_p.h | 93 + .../graphicsitems/qdeclarativeimplicitsizeitem.cpp | 92 + .../graphicsitems/qdeclarativeimplicitsizeitem_p.h | 100 + .../qdeclarativeimplicitsizeitem_p_p.h | 90 + src/declarative/graphicsitems/qdeclarativeitem.cpp | 3759 ++++++++++++++++++++ src/declarative/graphicsitems/qdeclarativeitem.h | 234 ++ src/declarative/graphicsitems/qdeclarativeitem_p.h | 624 ++++ .../qdeclarativeitemchangelistener_p.h | 76 + .../graphicsitems/qdeclarativeitemsmodule.cpp | 261 ++ .../graphicsitems/qdeclarativeitemsmodule_p.h | 63 + .../graphicsitems/qdeclarativelayoutitem.cpp | 112 + .../graphicsitems/qdeclarativelayoutitem_p.h | 94 + .../graphicsitems/qdeclarativelistview.cpp | 3651 +++++++++++++++++++ .../graphicsitems/qdeclarativelistview_p.h | 370 ++ .../graphicsitems/qdeclarativeloader.cpp | 597 ++++ .../graphicsitems/qdeclarativeloader_p.h | 108 + .../graphicsitems/qdeclarativeloader_p_p.h | 91 + .../graphicsitems/qdeclarativemousearea.cpp | 988 +++++ .../graphicsitems/qdeclarativemousearea_p.h | 218 ++ .../graphicsitems/qdeclarativemousearea_p_p.h | 128 + .../graphicsitems/qdeclarativepainteditem.cpp | 497 +++ .../graphicsitems/qdeclarativepainteditem_p.h | 118 + .../graphicsitems/qdeclarativepainteditem_p_p.h | 90 + src/declarative/graphicsitems/qdeclarativepath.cpp | 922 +++++ src/declarative/graphicsitems/qdeclarativepath_p.h | 286 ++ .../graphicsitems/qdeclarativepath_p_p.h | 82 + .../graphicsitems/qdeclarativepathview.cpp | 1729 +++++++++ .../graphicsitems/qdeclarativepathview_p.h | 252 ++ .../graphicsitems/qdeclarativepathview_p_p.h | 192 + .../graphicsitems/qdeclarativepincharea.cpp | 607 ++++ .../graphicsitems/qdeclarativepincharea_p.h | 313 ++ .../graphicsitems/qdeclarativepincharea_p_p.h | 115 + .../graphicsitems/qdeclarativepositioners.cpp | 1371 +++++++ .../graphicsitems/qdeclarativepositioners_p.h | 233 ++ .../graphicsitems/qdeclarativepositioners_p_p.h | 173 + .../graphicsitems/qdeclarativerectangle.cpp | 587 +++ .../graphicsitems/qdeclarativerectangle_p.h | 188 + .../graphicsitems/qdeclarativerectangle_p_p.h | 112 + .../graphicsitems/qdeclarativerepeater.cpp | 447 +++ .../graphicsitems/qdeclarativerepeater_p.h | 110 + .../graphicsitems/qdeclarativerepeater_p_p.h | 82 + .../graphicsitems/qdeclarativescalegrid.cpp | 213 ++ .../graphicsitems/qdeclarativescalegrid_p_p.h | 134 + src/declarative/graphicsitems/qdeclarativetext.cpp | 1635 +++++++++ src/declarative/graphicsitems/qdeclarativetext_p.h | 211 ++ .../graphicsitems/qdeclarativetext_p_p.h | 143 + .../graphicsitems/qdeclarativetextedit.cpp | 1895 ++++++++++ .../graphicsitems/qdeclarativetextedit_p.h | 305 ++ .../graphicsitems/qdeclarativetextedit_p_p.h | 138 + .../graphicsitems/qdeclarativetextinput.cpp | 2009 +++++++++++ .../graphicsitems/qdeclarativetextinput_p.h | 304 ++ .../graphicsitems/qdeclarativetextinput_p_p.h | 157 + .../graphicsitems/qdeclarativetextlayout.cpp | 391 ++ .../graphicsitems/qdeclarativetextlayout_p.h | 75 + .../graphicsitems/qdeclarativetranslate.cpp | 125 + .../graphicsitems/qdeclarativetranslate_p.h | 89 + .../graphicsitems/qdeclarativevisualitemmodel.cpp | 1425 ++++++++ .../graphicsitems/qdeclarativevisualitemmodel_p.h | 257 ++ src/declarative/qml/parser/parser.pri | 21 + src/declarative/qml/parser/qdeclarativejs.g | 3149 ++++++++++++++++ src/declarative/qml/parser/qdeclarativejsast.cpp | 956 +++++ src/declarative/qml/parser/qdeclarativejsast_p.h | 2546 +++++++++++++ .../qml/parser/qdeclarativejsastfwd_p.h | 189 + .../qml/parser/qdeclarativejsastvisitor.cpp | 58 + .../qml/parser/qdeclarativejsastvisitor_p.h | 335 ++ .../qml/parser/qdeclarativejsengine_p.cpp | 212 ++ .../qml/parser/qdeclarativejsengine_p.h | 167 + .../qml/parser/qdeclarativejsglobal_p.h | 64 + .../qml/parser/qdeclarativejsgrammar.cpp | 989 +++++ .../qml/parser/qdeclarativejsgrammar_p.h | 210 ++ src/declarative/qml/parser/qdeclarativejslexer.cpp | 1258 +++++++ src/declarative/qml/parser/qdeclarativejslexer_p.h | 249 ++ .../qml/parser/qdeclarativejsmemorypool_p.h | 133 + .../qml/parser/qdeclarativejsnodepool_p.h | 139 + .../qml/parser/qdeclarativejsparser.cpp | 1904 ++++++++++ .../qml/parser/qdeclarativejsparser_p.h | 246 ++ src/declarative/qml/qbitfield_p.h | 165 + src/declarative/qml/qdeclarative.h | 415 +++ src/declarative/qml/qdeclarativebinding.cpp | 573 +++ src/declarative/qml/qdeclarativebinding_p.h | 193 + src/declarative/qml/qdeclarativebinding_p_p.h | 85 + src/declarative/qml/qdeclarativeboundsignal.cpp | 306 ++ src/declarative/qml/qdeclarativeboundsignal_p.h | 103 + src/declarative/qml/qdeclarativecleanup.cpp | 87 + src/declarative/qml/qdeclarativecleanup_p.h | 79 + .../qml/qdeclarativecompiledbindings.cpp | 2921 +++++++++++++++ .../qml/qdeclarativecompiledbindings_p.h | 116 + src/declarative/qml/qdeclarativecompileddata.cpp | 255 ++ src/declarative/qml/qdeclarativecompiler.cpp | 3129 ++++++++++++++++ src/declarative/qml/qdeclarativecompiler_p.h | 351 ++ src/declarative/qml/qdeclarativecomponent.cpp | 1086 ++++++ src/declarative/qml/qdeclarativecomponent.h | 132 + src/declarative/qml/qdeclarativecomponent_p.h | 161 + src/declarative/qml/qdeclarativecontext.cpp | 774 ++++ src/declarative/qml/qdeclarativecontext.h | 114 + src/declarative/qml/qdeclarativecontext_p.h | 292 ++ .../qml/qdeclarativecontextscriptclass.cpp | 335 ++ .../qml/qdeclarativecontextscriptclass_p.h | 106 + src/declarative/qml/qdeclarativecustomparser.cpp | 317 ++ src/declarative/qml/qdeclarativecustomparser_p.h | 167 + src/declarative/qml/qdeclarativecustomparser_p_p.h | 89 + src/declarative/qml/qdeclarativedata_p.h | 165 + src/declarative/qml/qdeclarativedirparser.cpp | 285 ++ src/declarative/qml/qdeclarativedirparser_p.h | 149 + src/declarative/qml/qdeclarativedom.cpp | 1835 ++++++++++ src/declarative/qml/qdeclarativedom_p.h | 362 ++ src/declarative/qml/qdeclarativedom_p_p.h | 157 + src/declarative/qml/qdeclarativeengine.cpp | 2523 +++++++++++++ src/declarative/qml/qdeclarativeengine.h | 130 + src/declarative/qml/qdeclarativeengine_p.h | 387 ++ src/declarative/qml/qdeclarativeerror.cpp | 285 ++ src/declarative/qml/qdeclarativeerror.h | 86 + src/declarative/qml/qdeclarativeexpression.cpp | 875 +++++ src/declarative/qml/qdeclarativeexpression.h | 119 + src/declarative/qml/qdeclarativeexpression_p.h | 233 ++ .../qml/qdeclarativeextensioninterface.h | 68 + .../qml/qdeclarativeextensionplugin.cpp | 171 + src/declarative/qml/qdeclarativeextensionplugin.h | 76 + src/declarative/qml/qdeclarativefastproperties.cpp | 101 + src/declarative/qml/qdeclarativefastproperties_p.h | 75 + src/declarative/qml/qdeclarativeglobal_p.h | 110 + .../qml/qdeclarativeglobalscriptclass.cpp | 147 + .../qml/qdeclarativeglobalscriptclass_p.h | 86 + src/declarative/qml/qdeclarativeguard_p.h | 214 ++ src/declarative/qml/qdeclarativeimageprovider.cpp | 259 ++ src/declarative/qml/qdeclarativeimageprovider.h | 80 + src/declarative/qml/qdeclarativeimport.cpp | 1083 ++++++ src/declarative/qml/qdeclarativeimport_p.h | 144 + src/declarative/qml/qdeclarativeinclude.cpp | 310 ++ src/declarative/qml/qdeclarativeinclude_p.h | 115 + src/declarative/qml/qdeclarativeinfo.cpp | 179 + src/declarative/qml/qdeclarativeinfo.h | 105 + src/declarative/qml/qdeclarativeinstruction.cpp | 230 ++ src/declarative/qml/qdeclarativeinstruction_p.h | 359 ++ src/declarative/qml/qdeclarativeintegercache.cpp | 96 + src/declarative/qml/qdeclarativeintegercache_p.h | 112 + src/declarative/qml/qdeclarativelist.cpp | 417 +++ src/declarative/qml/qdeclarativelist.h | 152 + src/declarative/qml/qdeclarativelist_p.h | 85 + .../qml/qdeclarativelistscriptclass.cpp | 149 + .../qml/qdeclarativelistscriptclass_p.h | 87 + src/declarative/qml/qdeclarativemetatype.cpp | 1536 ++++++++ src/declarative/qml/qdeclarativemetatype_p.h | 174 + .../qdeclarativenetworkaccessmanagerfactory.cpp | 103 + .../qml/qdeclarativenetworkaccessmanagerfactory.h | 66 + src/declarative/qml/qdeclarativenotifier.cpp | 126 + src/declarative/qml/qdeclarativenotifier_p.h | 268 ++ .../qml/qdeclarativeobjectscriptclass.cpp | 1242 +++++++ .../qml/qdeclarativeobjectscriptclass_p.h | 166 + src/declarative/qml/qdeclarativeparser.cpp | 437 +++ src/declarative/qml/qdeclarativeparser_p.h | 381 ++ src/declarative/qml/qdeclarativeparserstatus.cpp | 107 + src/declarative/qml/qdeclarativeparserstatus.h | 75 + src/declarative/qml/qdeclarativeprivate.h | 249 ++ src/declarative/qml/qdeclarativeproperty.cpp | 1654 +++++++++ src/declarative/qml/qdeclarativeproperty.h | 143 + src/declarative/qml/qdeclarativeproperty_p.h | 147 + src/declarative/qml/qdeclarativepropertycache.cpp | 471 +++ src/declarative/qml/qdeclarativepropertycache_p.h | 240 ++ .../qml/qdeclarativepropertyvalueinterceptor.cpp | 79 + .../qml/qdeclarativepropertyvalueinterceptor.h | 68 + .../qml/qdeclarativepropertyvaluesource.cpp | 76 + .../qml/qdeclarativepropertyvaluesource.h | 67 + .../qml/qdeclarativeproxymetaobject.cpp | 124 + .../qml/qdeclarativeproxymetaobject_p.h | 100 + src/declarative/qml/qdeclarativerefcount.cpp | 70 + src/declarative/qml/qdeclarativerefcount_p.h | 80 + src/declarative/qml/qdeclarativerewrite.cpp | 273 ++ src/declarative/qml/qdeclarativerewrite_p.h | 127 + src/declarative/qml/qdeclarativescriptparser.cpp | 1206 +++++++ src/declarative/qml/qdeclarativescriptparser_p.h | 148 + src/declarative/qml/qdeclarativescriptstring.cpp | 166 + src/declarative/qml/qdeclarativescriptstring.h | 87 + src/declarative/qml/qdeclarativesqldatabase.cpp | 448 +++ src/declarative/qml/qdeclarativesqldatabase_p.h | 67 + .../qml/qdeclarativestringconverters.cpp | 278 ++ .../qml/qdeclarativestringconverters_p.h | 91 + src/declarative/qml/qdeclarativetypeloader.cpp | 1196 +++++++ src/declarative/qml/qdeclarativetypeloader_p.h | 330 ++ src/declarative/qml/qdeclarativetypenamecache.cpp | 118 + src/declarative/qml/qdeclarativetypenamecache_p.h | 119 + .../qml/qdeclarativetypenamescriptclass.cpp | 165 + .../qml/qdeclarativetypenamescriptclass_p.h | 92 + .../qml/qdeclarativetypenotavailable.cpp | 53 + .../qml/qdeclarativetypenotavailable_p.h | 65 + src/declarative/qml/qdeclarativevaluetype.cpp | 1011 ++++++ src/declarative/qml/qdeclarativevaluetype_p.h | 556 +++ .../qml/qdeclarativevaluetypescriptclass.cpp | 242 ++ .../qml/qdeclarativevaluetypescriptclass_p.h | 87 + src/declarative/qml/qdeclarativevme.cpp | 1124 ++++++ src/declarative/qml/qdeclarativevme_p.h | 95 + src/declarative/qml/qdeclarativevmemetaobject.cpp | 903 +++++ src/declarative/qml/qdeclarativevmemetaobject_p.h | 197 + src/declarative/qml/qdeclarativewatcher.cpp | 188 + src/declarative/qml/qdeclarativewatcher_p.h | 94 + src/declarative/qml/qdeclarativeworkerscript.cpp | 753 ++++ src/declarative/qml/qdeclarativeworkerscript_p.h | 129 + src/declarative/qml/qdeclarativexmlhttprequest.cpp | 1740 +++++++++ src/declarative/qml/qdeclarativexmlhttprequest_p.h | 71 + src/declarative/qml/qmetaobjectbuilder.cpp | 2601 ++++++++++++++ src/declarative/qml/qmetaobjectbuilder_p.h | 325 ++ src/declarative/qml/qml.pri | 139 + src/declarative/qml/qperformancetimer.cpp | 227 ++ src/declarative/qml/qperformancetimer_p.h | 79 + src/declarative/qml/qpodvector_p.h | 173 + src/declarative/qml/rewriter/rewriter.pri | 4 + src/declarative/qml/rewriter/textwriter.cpp | 217 ++ src/declarative/qml/rewriter/textwriter_p.h | 101 + src/declarative/util/qdeclarativeanimation.cpp | 2952 +++++++++++++++ src/declarative/util/qdeclarativeanimation_p.h | 528 +++ src/declarative/util/qdeclarativeanimation_p_p.h | 397 +++ src/declarative/util/qdeclarativeapplication.cpp | 112 + src/declarative/util/qdeclarativeapplication_p.h | 86 + src/declarative/util/qdeclarativebehavior.cpp | 230 ++ src/declarative/util/qdeclarativebehavior_p.h | 98 + src/declarative/util/qdeclarativebind.cpp | 214 ++ src/declarative/util/qdeclarativebind_p.h | 96 + src/declarative/util/qdeclarativeconnections.cpp | 289 ++ src/declarative/util/qdeclarativeconnections_p.h | 103 + src/declarative/util/qdeclarativefontloader.cpp | 338 ++ src/declarative/util/qdeclarativefontloader_p.h | 97 + src/declarative/util/qdeclarativelistaccessor.cpp | 138 + src/declarative/util/qdeclarativelistaccessor_p.h | 80 + src/declarative/util/qdeclarativelistmodel.cpp | 1628 +++++++++ src/declarative/util/qdeclarativelistmodel_p.h | 158 + src/declarative/util/qdeclarativelistmodel_p_p.h | 281 ++ .../util/qdeclarativelistmodelworkeragent.cpp | 278 ++ .../util/qdeclarativelistmodelworkeragent_p.h | 163 + .../util/qdeclarativenullablevalue_p_p.h | 81 + .../util/qdeclarativeopenmetaobject.cpp | 380 ++ .../util/qdeclarativeopenmetaobject_p.h | 129 + src/declarative/util/qdeclarativepackage.cpp | 201 ++ src/declarative/util/qdeclarativepackage_p.h | 98 + src/declarative/util/qdeclarativepixmapcache.cpp | 1119 ++++++ src/declarative/util/qdeclarativepixmapcache_p.h | 124 + .../util/qdeclarativepropertychanges.cpp | 798 +++++ .../util/qdeclarativepropertychanges_p.h | 112 + src/declarative/util/qdeclarativepropertymap.cpp | 296 ++ src/declarative/util/qdeclarativepropertymap.h | 90 + .../util/qdeclarativesmoothedanimation.cpp | 493 +++ .../util/qdeclarativesmoothedanimation_p.h | 103 + .../util/qdeclarativesmoothedanimation_p_p.h | 135 + .../util/qdeclarativespringanimation.cpp | 462 +++ .../util/qdeclarativespringanimation_p.h | 111 + src/declarative/util/qdeclarativestate.cpp | 742 ++++ src/declarative/util/qdeclarativestate_p.h | 210 ++ src/declarative/util/qdeclarativestate_p_p.h | 255 ++ src/declarative/util/qdeclarativestategroup.cpp | 502 +++ src/declarative/util/qdeclarativestategroup_p.h | 95 + .../util/qdeclarativestateoperations.cpp | 1587 +++++++++ .../util/qdeclarativestateoperations_p.h | 299 ++ src/declarative/util/qdeclarativestyledtext.cpp | 347 ++ src/declarative/util/qdeclarativestyledtext_p.h | 69 + src/declarative/util/qdeclarativesystempalette.cpp | 312 ++ src/declarative/util/qdeclarativesystempalette_p.h | 122 + src/declarative/util/qdeclarativetimeline.cpp | 947 +++++ src/declarative/util/qdeclarativetimeline_p_p.h | 200 ++ src/declarative/util/qdeclarativetimer.cpp | 324 ++ src/declarative/util/qdeclarativetimer_p.h | 115 + src/declarative/util/qdeclarativetransition.cpp | 345 ++ src/declarative/util/qdeclarativetransition_p.h | 106 + .../util/qdeclarativetransitionmanager.cpp | 276 ++ .../util/qdeclarativetransitionmanager_p_p.h | 85 + src/declarative/util/qdeclarativeutilmodule.cpp | 174 + src/declarative/util/qdeclarativeutilmodule_p.h | 63 + src/declarative/util/qdeclarativeview.cpp | 739 ++++ src/declarative/util/qdeclarativeview.h | 119 + src/declarative/util/qdeclarativexmllistmodel.cpp | 1157 ++++++ src/declarative/util/qdeclarativexmllistmodel_p.h | 213 ++ src/declarative/util/qlistmodelinterface.cpp | 105 + src/declarative/util/qlistmodelinterface_p.h | 84 + src/declarative/util/util.pri | 72 + src/imports/folderlistmodel/folderlistmodel.pro | 26 + src/imports/folderlistmodel/plugin.cpp | 70 + .../qdeclarativefolderlistmodel.cpp | 485 +++ .../folderlistmodel/qdeclarativefolderlistmodel.h | 159 + src/imports/folderlistmodel/qmldir | 1 + src/imports/gestures/gestures.pro | 26 + src/imports/gestures/plugin.cpp | 73 + src/imports/gestures/qdeclarativegesturearea.cpp | 282 ++ src/imports/gestures/qdeclarativegesturearea_p.h | 104 + src/imports/gestures/qmldir | 1 + src/imports/imports.pro | 5 + src/imports/particles/particles.cpp | 69 + src/imports/particles/particles.pro | 30 + src/imports/particles/qdeclarativeparticles.cpp | 1296 +++++++ src/imports/particles/qdeclarativeparticles_p.h | 258 ++ src/imports/particles/qmldir | 1 + src/imports/qimportbase.pri | 36 + src/imports/shaders/glfunctions.h | 75 + src/imports/shaders/qmldir | 2 + src/imports/shaders/qmlshadersplugin_plugin.cpp | 55 + src/imports/shaders/qmlshadersplugin_plugin.h | 56 + src/imports/shaders/scenegraph/qsggeometry.cpp | 194 + src/imports/shaders/scenegraph/qsggeometry.h | 234 ++ src/imports/shaders/shadereffect.cpp | 196 + src/imports/shaders/shadereffect.h | 81 + src/imports/shaders/shadereffectbuffer.cpp | 52 + src/imports/shaders/shadereffectbuffer.h | 62 + src/imports/shaders/shadereffectitem.cpp | 955 +++++ src/imports/shaders/shadereffectitem.h | 154 + src/imports/shaders/shadereffectsource.cpp | 451 +++ src/imports/shaders/shadereffectsource.h | 159 + src/imports/shaders/shaders.pro | 38 + .../platforms/uikit/examples/qmltest/main.mm | 78 + .../platforms/uikit/examples/qmltest/qml/main.qml | 112 + .../uikit/examples/qmltest/qmltest-Info.plist | 36 + .../qmltest/qmltest.xcodeproj/project.pbxproj | 494 +++ .../moc_qmlapplicationviewer.cpp | 122 + .../qmlapplicationviewer/qmlapplicationviewer.cpp | 198 ++ .../qmlapplicationviewer/qmlapplicationviewer.h | 83 + src/plugins/plugins.pro | 16 + .../qmltooling/qmldbg_inspector/abstracttool.cpp | 54 + .../qmltooling/qmldbg_inspector/abstracttool.h | 85 + .../qmldbg_inspector/abstractviewinspector.cpp | 511 +++ .../qmldbg_inspector/abstractviewinspector.h | 173 + .../editor/abstractliveedittool.cpp | 196 + .../qmldbg_inspector/editor/abstractliveedittool.h | 104 + .../editor/boundingrecthighlighter.cpp | 240 ++ .../editor/boundingrecthighlighter.h | 115 + .../qmldbg_inspector/editor/colorpickertool.cpp | 100 + .../qmldbg_inspector/editor/colorpickertool.h | 92 + .../qmldbg_inspector/editor/livelayeritem.cpp | 92 + .../qmldbg_inspector/editor/livelayeritem.h | 67 + .../editor/liverubberbandselectionmanipulator.cpp | 165 + .../editor/liverubberbandselectionmanipulator.h | 96 + .../editor/liveselectionindicator.cpp | 118 + .../editor/liveselectionindicator.h | 80 + .../editor/liveselectionrectangle.cpp | 113 + .../editor/liveselectionrectangle.h | 77 + .../qmldbg_inspector/editor/liveselectiontool.cpp | 425 +++ .../qmldbg_inspector/editor/liveselectiontool.h | 120 + .../editor/livesingleselectionmanipulator.cpp | 151 + .../editor/livesingleselectionmanipulator.h | 89 + .../qmldbg_inspector/editor/qmltoolbar.cpp | 328 ++ .../qmldbg_inspector/editor/qmltoolbar.h | 132 + .../editor/subcomponentmasklayeritem.cpp | 130 + .../editor/subcomponentmasklayeritem.h | 71 + .../qmldbg_inspector/editor/toolbarcolorbox.cpp | 134 + .../qmldbg_inspector/editor/toolbarcolorbox.h | 81 + .../qmldbg_inspector/editor/zoomtool.cpp | 330 ++ .../qmltooling/qmldbg_inspector/editor/zoomtool.h | 107 + .../qdeclarativeinspectorplugin.cpp | 80 + .../qmldbg_inspector/qdeclarativeinspectorplugin.h | 71 + .../qdeclarativeinspectorprotocol.h | 137 + .../qmldbg_inspector/qdeclarativeviewinspector.cpp | 436 +++ .../qmldbg_inspector/qdeclarativeviewinspector.h | 100 + .../qmldbg_inspector/qdeclarativeviewinspector_p.h | 117 + .../qmldbg_inspector/qmldbg_inspector.pro | 49 + .../qmldbg_inspector/qmlinspectorconstants.h | 77 + src/plugins/qmltooling/qmldbg_ost/qmldbg_ost.pro | 21 + src/plugins/qmltooling/qmldbg_ost/qmlostplugin.cpp | 149 + src/plugins/qmltooling/qmldbg_ost/qmlostplugin.h | 82 + src/plugins/qmltooling/qmldbg_ost/qostdevice.cpp | 220 ++ src/plugins/qmltooling/qmldbg_ost/qostdevice.h | 77 + src/plugins/qmltooling/qmldbg_ost/usbostcomm.h | 191 + src/plugins/qmltooling/qmldbg_tcp/qmldbg_tcp.pro | 18 + .../qmltooling/qmldbg_tcp/qtcpserverconnection.cpp | 198 ++ .../qmltooling/qmldbg_tcp/qtcpserverconnection.h | 84 + src/plugins/qmltooling/qmltooling.pro | 4 + src/plugins/qpluginbase.pri | 41 + src/qt_targets.pri | 4 + 423 files changed, 146396 insertions(+) create mode 100644 src/declarative/QmlChanges.txt create mode 100644 src/declarative/debugger/debugger.pri create mode 100644 src/declarative/debugger/qdeclarativedebug.h create mode 100644 src/declarative/debugger/qdeclarativedebugclient.cpp create mode 100644 src/declarative/debugger/qdeclarativedebugclient_p.h create mode 100644 src/declarative/debugger/qdeclarativedebuggerstatus.cpp create mode 100644 src/declarative/debugger/qdeclarativedebuggerstatus_p.h create mode 100644 src/declarative/debugger/qdeclarativedebughelper.cpp create mode 100644 src/declarative/debugger/qdeclarativedebughelper_p.h create mode 100644 src/declarative/debugger/qdeclarativedebugserver.cpp create mode 100644 src/declarative/debugger/qdeclarativedebugserver_p.h create mode 100644 src/declarative/debugger/qdeclarativedebugserverconnection_p.h create mode 100644 src/declarative/debugger/qdeclarativedebugservice.cpp create mode 100644 src/declarative/debugger/qdeclarativedebugservice_p.h create mode 100644 src/declarative/debugger/qdeclarativedebugservice_p_p.h create mode 100644 src/declarative/debugger/qdeclarativedebugtrace.cpp create mode 100644 src/declarative/debugger/qdeclarativedebugtrace_p.h create mode 100644 src/declarative/debugger/qdeclarativeenginedebug.cpp create mode 100644 src/declarative/debugger/qdeclarativeenginedebug_p.h create mode 100644 src/declarative/debugger/qdeclarativeenginedebugservice.cpp create mode 100644 src/declarative/debugger/qdeclarativeenginedebugservice_p.h create mode 100644 src/declarative/debugger/qdeclarativeinspectorinterface_p.h create mode 100644 src/declarative/debugger/qdeclarativeinspectorservice.cpp create mode 100644 src/declarative/debugger/qdeclarativeinspectorservice_p.h create mode 100644 src/declarative/debugger/qjsdebuggeragent.cpp create mode 100644 src/declarative/debugger/qjsdebuggeragent_p.h create mode 100644 src/declarative/debugger/qjsdebugservice.cpp create mode 100644 src/declarative/debugger/qjsdebugservice_p.h create mode 100644 src/declarative/debugger/qpacketprotocol.cpp create mode 100644 src/declarative/debugger/qpacketprotocol_p.h create mode 100644 src/declarative/declarative.pro create mode 100644 src/declarative/graphicsitems/graphicsitems.pri create mode 100644 src/declarative/graphicsitems/qdeclarativeanchors.cpp create mode 100644 src/declarative/graphicsitems/qdeclarativeanchors_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativeanchors_p_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativeanimatedimage.cpp create mode 100644 src/declarative/graphicsitems/qdeclarativeanimatedimage_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativeanimatedimage_p_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativeborderimage.cpp create mode 100644 src/declarative/graphicsitems/qdeclarativeborderimage_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativeborderimage_p_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativeevents.cpp create mode 100644 src/declarative/graphicsitems/qdeclarativeevents_p_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativeflickable.cpp create mode 100644 src/declarative/graphicsitems/qdeclarativeflickable_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativeflickable_p_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativeflipable.cpp create mode 100644 src/declarative/graphicsitems/qdeclarativeflipable_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativefocuspanel.cpp create mode 100644 src/declarative/graphicsitems/qdeclarativefocuspanel_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativefocusscope.cpp create mode 100644 src/declarative/graphicsitems/qdeclarativefocusscope_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativegraphicswidget.cpp create mode 100644 src/declarative/graphicsitems/qdeclarativegraphicswidget_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativegridview.cpp create mode 100644 src/declarative/graphicsitems/qdeclarativegridview_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativeimage.cpp create mode 100644 src/declarative/graphicsitems/qdeclarativeimage_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativeimage_p_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativeimagebase.cpp create mode 100644 src/declarative/graphicsitems/qdeclarativeimagebase_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativeimagebase_p_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativeimplicitsizeitem.cpp create mode 100644 src/declarative/graphicsitems/qdeclarativeimplicitsizeitem_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativeimplicitsizeitem_p_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativeitem.cpp create mode 100644 src/declarative/graphicsitems/qdeclarativeitem.h create mode 100644 src/declarative/graphicsitems/qdeclarativeitem_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativeitemchangelistener_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativeitemsmodule.cpp create mode 100644 src/declarative/graphicsitems/qdeclarativeitemsmodule_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativelayoutitem.cpp create mode 100644 src/declarative/graphicsitems/qdeclarativelayoutitem_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativelistview.cpp create mode 100644 src/declarative/graphicsitems/qdeclarativelistview_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativeloader.cpp create mode 100644 src/declarative/graphicsitems/qdeclarativeloader_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativeloader_p_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativemousearea.cpp create mode 100644 src/declarative/graphicsitems/qdeclarativemousearea_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativemousearea_p_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativepainteditem.cpp create mode 100644 src/declarative/graphicsitems/qdeclarativepainteditem_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativepainteditem_p_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativepath.cpp create mode 100644 src/declarative/graphicsitems/qdeclarativepath_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativepath_p_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativepathview.cpp create mode 100644 src/declarative/graphicsitems/qdeclarativepathview_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativepathview_p_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativepincharea.cpp create mode 100644 src/declarative/graphicsitems/qdeclarativepincharea_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativepincharea_p_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativepositioners.cpp create mode 100644 src/declarative/graphicsitems/qdeclarativepositioners_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativepositioners_p_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativerectangle.cpp create mode 100644 src/declarative/graphicsitems/qdeclarativerectangle_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativerectangle_p_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativerepeater.cpp create mode 100644 src/declarative/graphicsitems/qdeclarativerepeater_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativerepeater_p_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativescalegrid.cpp create mode 100644 src/declarative/graphicsitems/qdeclarativescalegrid_p_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativetext.cpp create mode 100644 src/declarative/graphicsitems/qdeclarativetext_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativetext_p_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativetextedit.cpp create mode 100644 src/declarative/graphicsitems/qdeclarativetextedit_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativetextedit_p_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativetextinput.cpp create mode 100644 src/declarative/graphicsitems/qdeclarativetextinput_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativetextinput_p_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativetextlayout.cpp create mode 100644 src/declarative/graphicsitems/qdeclarativetextlayout_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativetranslate.cpp create mode 100644 src/declarative/graphicsitems/qdeclarativetranslate_p.h create mode 100644 src/declarative/graphicsitems/qdeclarativevisualitemmodel.cpp create mode 100644 src/declarative/graphicsitems/qdeclarativevisualitemmodel_p.h create mode 100644 src/declarative/qml/parser/parser.pri create mode 100644 src/declarative/qml/parser/qdeclarativejs.g create mode 100644 src/declarative/qml/parser/qdeclarativejsast.cpp create mode 100644 src/declarative/qml/parser/qdeclarativejsast_p.h create mode 100644 src/declarative/qml/parser/qdeclarativejsastfwd_p.h create mode 100644 src/declarative/qml/parser/qdeclarativejsastvisitor.cpp create mode 100644 src/declarative/qml/parser/qdeclarativejsastvisitor_p.h create mode 100644 src/declarative/qml/parser/qdeclarativejsengine_p.cpp create mode 100644 src/declarative/qml/parser/qdeclarativejsengine_p.h create mode 100644 src/declarative/qml/parser/qdeclarativejsglobal_p.h create mode 100644 src/declarative/qml/parser/qdeclarativejsgrammar.cpp create mode 100644 src/declarative/qml/parser/qdeclarativejsgrammar_p.h create mode 100644 src/declarative/qml/parser/qdeclarativejslexer.cpp create mode 100644 src/declarative/qml/parser/qdeclarativejslexer_p.h create mode 100644 src/declarative/qml/parser/qdeclarativejsmemorypool_p.h create mode 100644 src/declarative/qml/parser/qdeclarativejsnodepool_p.h create mode 100644 src/declarative/qml/parser/qdeclarativejsparser.cpp create mode 100644 src/declarative/qml/parser/qdeclarativejsparser_p.h create mode 100644 src/declarative/qml/qbitfield_p.h create mode 100644 src/declarative/qml/qdeclarative.h create mode 100644 src/declarative/qml/qdeclarativebinding.cpp create mode 100644 src/declarative/qml/qdeclarativebinding_p.h create mode 100644 src/declarative/qml/qdeclarativebinding_p_p.h create mode 100644 src/declarative/qml/qdeclarativeboundsignal.cpp create mode 100644 src/declarative/qml/qdeclarativeboundsignal_p.h create mode 100644 src/declarative/qml/qdeclarativecleanup.cpp create mode 100644 src/declarative/qml/qdeclarativecleanup_p.h create mode 100644 src/declarative/qml/qdeclarativecompiledbindings.cpp create mode 100644 src/declarative/qml/qdeclarativecompiledbindings_p.h create mode 100644 src/declarative/qml/qdeclarativecompileddata.cpp create mode 100644 src/declarative/qml/qdeclarativecompiler.cpp create mode 100644 src/declarative/qml/qdeclarativecompiler_p.h create mode 100644 src/declarative/qml/qdeclarativecomponent.cpp create mode 100644 src/declarative/qml/qdeclarativecomponent.h create mode 100644 src/declarative/qml/qdeclarativecomponent_p.h create mode 100644 src/declarative/qml/qdeclarativecontext.cpp create mode 100644 src/declarative/qml/qdeclarativecontext.h create mode 100644 src/declarative/qml/qdeclarativecontext_p.h create mode 100644 src/declarative/qml/qdeclarativecontextscriptclass.cpp create mode 100644 src/declarative/qml/qdeclarativecontextscriptclass_p.h create mode 100644 src/declarative/qml/qdeclarativecustomparser.cpp create mode 100644 src/declarative/qml/qdeclarativecustomparser_p.h create mode 100644 src/declarative/qml/qdeclarativecustomparser_p_p.h create mode 100644 src/declarative/qml/qdeclarativedata_p.h create mode 100644 src/declarative/qml/qdeclarativedirparser.cpp create mode 100644 src/declarative/qml/qdeclarativedirparser_p.h create mode 100644 src/declarative/qml/qdeclarativedom.cpp create mode 100644 src/declarative/qml/qdeclarativedom_p.h create mode 100644 src/declarative/qml/qdeclarativedom_p_p.h create mode 100644 src/declarative/qml/qdeclarativeengine.cpp create mode 100644 src/declarative/qml/qdeclarativeengine.h create mode 100644 src/declarative/qml/qdeclarativeengine_p.h create mode 100644 src/declarative/qml/qdeclarativeerror.cpp create mode 100644 src/declarative/qml/qdeclarativeerror.h create mode 100644 src/declarative/qml/qdeclarativeexpression.cpp create mode 100644 src/declarative/qml/qdeclarativeexpression.h create mode 100644 src/declarative/qml/qdeclarativeexpression_p.h create mode 100644 src/declarative/qml/qdeclarativeextensioninterface.h create mode 100644 src/declarative/qml/qdeclarativeextensionplugin.cpp create mode 100644 src/declarative/qml/qdeclarativeextensionplugin.h create mode 100644 src/declarative/qml/qdeclarativefastproperties.cpp create mode 100644 src/declarative/qml/qdeclarativefastproperties_p.h create mode 100644 src/declarative/qml/qdeclarativeglobal_p.h create mode 100644 src/declarative/qml/qdeclarativeglobalscriptclass.cpp create mode 100644 src/declarative/qml/qdeclarativeglobalscriptclass_p.h create mode 100644 src/declarative/qml/qdeclarativeguard_p.h create mode 100644 src/declarative/qml/qdeclarativeimageprovider.cpp create mode 100644 src/declarative/qml/qdeclarativeimageprovider.h create mode 100644 src/declarative/qml/qdeclarativeimport.cpp create mode 100644 src/declarative/qml/qdeclarativeimport_p.h create mode 100644 src/declarative/qml/qdeclarativeinclude.cpp create mode 100644 src/declarative/qml/qdeclarativeinclude_p.h create mode 100644 src/declarative/qml/qdeclarativeinfo.cpp create mode 100644 src/declarative/qml/qdeclarativeinfo.h create mode 100644 src/declarative/qml/qdeclarativeinstruction.cpp create mode 100644 src/declarative/qml/qdeclarativeinstruction_p.h create mode 100644 src/declarative/qml/qdeclarativeintegercache.cpp create mode 100644 src/declarative/qml/qdeclarativeintegercache_p.h create mode 100644 src/declarative/qml/qdeclarativelist.cpp create mode 100644 src/declarative/qml/qdeclarativelist.h create mode 100644 src/declarative/qml/qdeclarativelist_p.h create mode 100644 src/declarative/qml/qdeclarativelistscriptclass.cpp create mode 100644 src/declarative/qml/qdeclarativelistscriptclass_p.h create mode 100644 src/declarative/qml/qdeclarativemetatype.cpp create mode 100644 src/declarative/qml/qdeclarativemetatype_p.h create mode 100644 src/declarative/qml/qdeclarativenetworkaccessmanagerfactory.cpp create mode 100644 src/declarative/qml/qdeclarativenetworkaccessmanagerfactory.h create mode 100644 src/declarative/qml/qdeclarativenotifier.cpp create mode 100644 src/declarative/qml/qdeclarativenotifier_p.h create mode 100644 src/declarative/qml/qdeclarativeobjectscriptclass.cpp create mode 100644 src/declarative/qml/qdeclarativeobjectscriptclass_p.h create mode 100644 src/declarative/qml/qdeclarativeparser.cpp create mode 100644 src/declarative/qml/qdeclarativeparser_p.h create mode 100644 src/declarative/qml/qdeclarativeparserstatus.cpp create mode 100644 src/declarative/qml/qdeclarativeparserstatus.h create mode 100644 src/declarative/qml/qdeclarativeprivate.h create mode 100644 src/declarative/qml/qdeclarativeproperty.cpp create mode 100644 src/declarative/qml/qdeclarativeproperty.h create mode 100644 src/declarative/qml/qdeclarativeproperty_p.h create mode 100644 src/declarative/qml/qdeclarativepropertycache.cpp create mode 100644 src/declarative/qml/qdeclarativepropertycache_p.h create mode 100644 src/declarative/qml/qdeclarativepropertyvalueinterceptor.cpp create mode 100644 src/declarative/qml/qdeclarativepropertyvalueinterceptor.h create mode 100644 src/declarative/qml/qdeclarativepropertyvaluesource.cpp create mode 100644 src/declarative/qml/qdeclarativepropertyvaluesource.h create mode 100644 src/declarative/qml/qdeclarativeproxymetaobject.cpp create mode 100644 src/declarative/qml/qdeclarativeproxymetaobject_p.h create mode 100644 src/declarative/qml/qdeclarativerefcount.cpp create mode 100644 src/declarative/qml/qdeclarativerefcount_p.h create mode 100644 src/declarative/qml/qdeclarativerewrite.cpp create mode 100644 src/declarative/qml/qdeclarativerewrite_p.h create mode 100644 src/declarative/qml/qdeclarativescriptparser.cpp create mode 100644 src/declarative/qml/qdeclarativescriptparser_p.h create mode 100644 src/declarative/qml/qdeclarativescriptstring.cpp create mode 100644 src/declarative/qml/qdeclarativescriptstring.h create mode 100644 src/declarative/qml/qdeclarativesqldatabase.cpp create mode 100644 src/declarative/qml/qdeclarativesqldatabase_p.h create mode 100644 src/declarative/qml/qdeclarativestringconverters.cpp create mode 100644 src/declarative/qml/qdeclarativestringconverters_p.h create mode 100644 src/declarative/qml/qdeclarativetypeloader.cpp create mode 100644 src/declarative/qml/qdeclarativetypeloader_p.h create mode 100644 src/declarative/qml/qdeclarativetypenamecache.cpp create mode 100644 src/declarative/qml/qdeclarativetypenamecache_p.h create mode 100644 src/declarative/qml/qdeclarativetypenamescriptclass.cpp create mode 100644 src/declarative/qml/qdeclarativetypenamescriptclass_p.h create mode 100644 src/declarative/qml/qdeclarativetypenotavailable.cpp create mode 100644 src/declarative/qml/qdeclarativetypenotavailable_p.h create mode 100644 src/declarative/qml/qdeclarativevaluetype.cpp create mode 100644 src/declarative/qml/qdeclarativevaluetype_p.h create mode 100644 src/declarative/qml/qdeclarativevaluetypescriptclass.cpp create mode 100644 src/declarative/qml/qdeclarativevaluetypescriptclass_p.h create mode 100644 src/declarative/qml/qdeclarativevme.cpp create mode 100644 src/declarative/qml/qdeclarativevme_p.h create mode 100644 src/declarative/qml/qdeclarativevmemetaobject.cpp create mode 100644 src/declarative/qml/qdeclarativevmemetaobject_p.h create mode 100644 src/declarative/qml/qdeclarativewatcher.cpp create mode 100644 src/declarative/qml/qdeclarativewatcher_p.h create mode 100644 src/declarative/qml/qdeclarativeworkerscript.cpp create mode 100644 src/declarative/qml/qdeclarativeworkerscript_p.h create mode 100644 src/declarative/qml/qdeclarativexmlhttprequest.cpp create mode 100644 src/declarative/qml/qdeclarativexmlhttprequest_p.h create mode 100644 src/declarative/qml/qmetaobjectbuilder.cpp create mode 100644 src/declarative/qml/qmetaobjectbuilder_p.h create mode 100644 src/declarative/qml/qml.pri create mode 100644 src/declarative/qml/qperformancetimer.cpp create mode 100644 src/declarative/qml/qperformancetimer_p.h create mode 100644 src/declarative/qml/qpodvector_p.h create mode 100644 src/declarative/qml/rewriter/rewriter.pri create mode 100644 src/declarative/qml/rewriter/textwriter.cpp create mode 100644 src/declarative/qml/rewriter/textwriter_p.h create mode 100644 src/declarative/util/qdeclarativeanimation.cpp create mode 100644 src/declarative/util/qdeclarativeanimation_p.h create mode 100644 src/declarative/util/qdeclarativeanimation_p_p.h create mode 100644 src/declarative/util/qdeclarativeapplication.cpp create mode 100644 src/declarative/util/qdeclarativeapplication_p.h create mode 100644 src/declarative/util/qdeclarativebehavior.cpp create mode 100644 src/declarative/util/qdeclarativebehavior_p.h create mode 100644 src/declarative/util/qdeclarativebind.cpp create mode 100644 src/declarative/util/qdeclarativebind_p.h create mode 100644 src/declarative/util/qdeclarativeconnections.cpp create mode 100644 src/declarative/util/qdeclarativeconnections_p.h create mode 100644 src/declarative/util/qdeclarativefontloader.cpp create mode 100644 src/declarative/util/qdeclarativefontloader_p.h create mode 100644 src/declarative/util/qdeclarativelistaccessor.cpp create mode 100644 src/declarative/util/qdeclarativelistaccessor_p.h create mode 100644 src/declarative/util/qdeclarativelistmodel.cpp create mode 100644 src/declarative/util/qdeclarativelistmodel_p.h create mode 100644 src/declarative/util/qdeclarativelistmodel_p_p.h create mode 100644 src/declarative/util/qdeclarativelistmodelworkeragent.cpp create mode 100644 src/declarative/util/qdeclarativelistmodelworkeragent_p.h create mode 100644 src/declarative/util/qdeclarativenullablevalue_p_p.h create mode 100644 src/declarative/util/qdeclarativeopenmetaobject.cpp create mode 100644 src/declarative/util/qdeclarativeopenmetaobject_p.h create mode 100644 src/declarative/util/qdeclarativepackage.cpp create mode 100644 src/declarative/util/qdeclarativepackage_p.h create mode 100644 src/declarative/util/qdeclarativepixmapcache.cpp create mode 100644 src/declarative/util/qdeclarativepixmapcache_p.h create mode 100644 src/declarative/util/qdeclarativepropertychanges.cpp create mode 100644 src/declarative/util/qdeclarativepropertychanges_p.h create mode 100644 src/declarative/util/qdeclarativepropertymap.cpp create mode 100644 src/declarative/util/qdeclarativepropertymap.h create mode 100644 src/declarative/util/qdeclarativesmoothedanimation.cpp create mode 100644 src/declarative/util/qdeclarativesmoothedanimation_p.h create mode 100644 src/declarative/util/qdeclarativesmoothedanimation_p_p.h create mode 100644 src/declarative/util/qdeclarativespringanimation.cpp create mode 100644 src/declarative/util/qdeclarativespringanimation_p.h create mode 100644 src/declarative/util/qdeclarativestate.cpp create mode 100644 src/declarative/util/qdeclarativestate_p.h create mode 100644 src/declarative/util/qdeclarativestate_p_p.h create mode 100644 src/declarative/util/qdeclarativestategroup.cpp create mode 100644 src/declarative/util/qdeclarativestategroup_p.h create mode 100644 src/declarative/util/qdeclarativestateoperations.cpp create mode 100644 src/declarative/util/qdeclarativestateoperations_p.h create mode 100644 src/declarative/util/qdeclarativestyledtext.cpp create mode 100644 src/declarative/util/qdeclarativestyledtext_p.h create mode 100644 src/declarative/util/qdeclarativesystempalette.cpp create mode 100644 src/declarative/util/qdeclarativesystempalette_p.h create mode 100644 src/declarative/util/qdeclarativetimeline.cpp create mode 100644 src/declarative/util/qdeclarativetimeline_p_p.h create mode 100644 src/declarative/util/qdeclarativetimer.cpp create mode 100644 src/declarative/util/qdeclarativetimer_p.h create mode 100644 src/declarative/util/qdeclarativetransition.cpp create mode 100644 src/declarative/util/qdeclarativetransition_p.h create mode 100644 src/declarative/util/qdeclarativetransitionmanager.cpp create mode 100644 src/declarative/util/qdeclarativetransitionmanager_p_p.h create mode 100644 src/declarative/util/qdeclarativeutilmodule.cpp create mode 100644 src/declarative/util/qdeclarativeutilmodule_p.h create mode 100644 src/declarative/util/qdeclarativeview.cpp create mode 100644 src/declarative/util/qdeclarativeview.h create mode 100644 src/declarative/util/qdeclarativexmllistmodel.cpp create mode 100644 src/declarative/util/qdeclarativexmllistmodel_p.h create mode 100644 src/declarative/util/qlistmodelinterface.cpp create mode 100644 src/declarative/util/qlistmodelinterface_p.h create mode 100644 src/declarative/util/util.pri create mode 100644 src/imports/folderlistmodel/folderlistmodel.pro create mode 100644 src/imports/folderlistmodel/plugin.cpp create mode 100644 src/imports/folderlistmodel/qdeclarativefolderlistmodel.cpp create mode 100644 src/imports/folderlistmodel/qdeclarativefolderlistmodel.h create mode 100644 src/imports/folderlistmodel/qmldir create mode 100644 src/imports/gestures/gestures.pro create mode 100644 src/imports/gestures/plugin.cpp create mode 100644 src/imports/gestures/qdeclarativegesturearea.cpp create mode 100644 src/imports/gestures/qdeclarativegesturearea_p.h create mode 100644 src/imports/gestures/qmldir create mode 100644 src/imports/imports.pro create mode 100644 src/imports/particles/particles.cpp create mode 100644 src/imports/particles/particles.pro create mode 100644 src/imports/particles/qdeclarativeparticles.cpp create mode 100644 src/imports/particles/qdeclarativeparticles_p.h create mode 100644 src/imports/particles/qmldir create mode 100644 src/imports/qimportbase.pri create mode 100644 src/imports/shaders/glfunctions.h create mode 100644 src/imports/shaders/qmldir create mode 100644 src/imports/shaders/qmlshadersplugin_plugin.cpp create mode 100644 src/imports/shaders/qmlshadersplugin_plugin.h create mode 100644 src/imports/shaders/scenegraph/qsggeometry.cpp create mode 100644 src/imports/shaders/scenegraph/qsggeometry.h create mode 100644 src/imports/shaders/shadereffect.cpp create mode 100644 src/imports/shaders/shadereffect.h create mode 100644 src/imports/shaders/shadereffectbuffer.cpp create mode 100644 src/imports/shaders/shadereffectbuffer.h create mode 100644 src/imports/shaders/shadereffectitem.cpp create mode 100644 src/imports/shaders/shadereffectitem.h create mode 100644 src/imports/shaders/shadereffectsource.cpp create mode 100644 src/imports/shaders/shadereffectsource.h create mode 100644 src/imports/shaders/shaders.pro create mode 100644 src/plugins/platforms/uikit/examples/qmltest/main.mm create mode 100644 src/plugins/platforms/uikit/examples/qmltest/qml/main.qml create mode 100644 src/plugins/platforms/uikit/examples/qmltest/qmltest-Info.plist create mode 100644 src/plugins/platforms/uikit/examples/qmltest/qmltest.xcodeproj/project.pbxproj create mode 100644 src/plugins/platforms/uikit/examples/share/qmlapplicationviewer/moc_qmlapplicationviewer.cpp create mode 100644 src/plugins/platforms/uikit/examples/share/qmlapplicationviewer/qmlapplicationviewer.cpp create mode 100644 src/plugins/platforms/uikit/examples/share/qmlapplicationviewer/qmlapplicationviewer.h create mode 100644 src/plugins/plugins.pro create mode 100644 src/plugins/qmltooling/qmldbg_inspector/abstracttool.cpp create mode 100644 src/plugins/qmltooling/qmldbg_inspector/abstracttool.h create mode 100644 src/plugins/qmltooling/qmldbg_inspector/abstractviewinspector.cpp create mode 100644 src/plugins/qmltooling/qmldbg_inspector/abstractviewinspector.h create mode 100644 src/plugins/qmltooling/qmldbg_inspector/editor/abstractliveedittool.cpp create mode 100644 src/plugins/qmltooling/qmldbg_inspector/editor/abstractliveedittool.h create mode 100644 src/plugins/qmltooling/qmldbg_inspector/editor/boundingrecthighlighter.cpp create mode 100644 src/plugins/qmltooling/qmldbg_inspector/editor/boundingrecthighlighter.h create mode 100644 src/plugins/qmltooling/qmldbg_inspector/editor/colorpickertool.cpp create mode 100644 src/plugins/qmltooling/qmldbg_inspector/editor/colorpickertool.h create mode 100644 src/plugins/qmltooling/qmldbg_inspector/editor/livelayeritem.cpp create mode 100644 src/plugins/qmltooling/qmldbg_inspector/editor/livelayeritem.h create mode 100644 src/plugins/qmltooling/qmldbg_inspector/editor/liverubberbandselectionmanipulator.cpp create mode 100644 src/plugins/qmltooling/qmldbg_inspector/editor/liverubberbandselectionmanipulator.h create mode 100644 src/plugins/qmltooling/qmldbg_inspector/editor/liveselectionindicator.cpp create mode 100644 src/plugins/qmltooling/qmldbg_inspector/editor/liveselectionindicator.h create mode 100644 src/plugins/qmltooling/qmldbg_inspector/editor/liveselectionrectangle.cpp create mode 100644 src/plugins/qmltooling/qmldbg_inspector/editor/liveselectionrectangle.h create mode 100644 src/plugins/qmltooling/qmldbg_inspector/editor/liveselectiontool.cpp create mode 100644 src/plugins/qmltooling/qmldbg_inspector/editor/liveselectiontool.h create mode 100644 src/plugins/qmltooling/qmldbg_inspector/editor/livesingleselectionmanipulator.cpp create mode 100644 src/plugins/qmltooling/qmldbg_inspector/editor/livesingleselectionmanipulator.h create mode 100644 src/plugins/qmltooling/qmldbg_inspector/editor/qmltoolbar.cpp create mode 100644 src/plugins/qmltooling/qmldbg_inspector/editor/qmltoolbar.h create mode 100644 src/plugins/qmltooling/qmldbg_inspector/editor/subcomponentmasklayeritem.cpp create mode 100644 src/plugins/qmltooling/qmldbg_inspector/editor/subcomponentmasklayeritem.h create mode 100644 src/plugins/qmltooling/qmldbg_inspector/editor/toolbarcolorbox.cpp create mode 100644 src/plugins/qmltooling/qmldbg_inspector/editor/toolbarcolorbox.h create mode 100644 src/plugins/qmltooling/qmldbg_inspector/editor/zoomtool.cpp create mode 100644 src/plugins/qmltooling/qmldbg_inspector/editor/zoomtool.h create mode 100644 src/plugins/qmltooling/qmldbg_inspector/qdeclarativeinspectorplugin.cpp create mode 100644 src/plugins/qmltooling/qmldbg_inspector/qdeclarativeinspectorplugin.h create mode 100644 src/plugins/qmltooling/qmldbg_inspector/qdeclarativeinspectorprotocol.h create mode 100644 src/plugins/qmltooling/qmldbg_inspector/qdeclarativeviewinspector.cpp create mode 100644 src/plugins/qmltooling/qmldbg_inspector/qdeclarativeviewinspector.h create mode 100644 src/plugins/qmltooling/qmldbg_inspector/qdeclarativeviewinspector_p.h create mode 100644 src/plugins/qmltooling/qmldbg_inspector/qmldbg_inspector.pro create mode 100644 src/plugins/qmltooling/qmldbg_inspector/qmlinspectorconstants.h create mode 100644 src/plugins/qmltooling/qmldbg_ost/qmldbg_ost.pro create mode 100644 src/plugins/qmltooling/qmldbg_ost/qmlostplugin.cpp create mode 100644 src/plugins/qmltooling/qmldbg_ost/qmlostplugin.h create mode 100644 src/plugins/qmltooling/qmldbg_ost/qostdevice.cpp create mode 100644 src/plugins/qmltooling/qmldbg_ost/qostdevice.h create mode 100644 src/plugins/qmltooling/qmldbg_ost/usbostcomm.h create mode 100644 src/plugins/qmltooling/qmldbg_tcp/qmldbg_tcp.pro create mode 100644 src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.cpp create mode 100644 src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.h create mode 100644 src/plugins/qmltooling/qmltooling.pro create mode 100644 src/plugins/qpluginbase.pri create mode 100644 src/qt_targets.pri (limited to 'src') diff --git a/src/declarative/QmlChanges.txt b/src/declarative/QmlChanges.txt new file mode 100644 index 00000000..6e073305 --- /dev/null +++ b/src/declarative/QmlChanges.txt @@ -0,0 +1,364 @@ +============================================================================= +The changes below are pre Qt 4.7.0 RC1 + +TextInput + - copy(), cut() and paste() functions added +Font.letterSpacing + - was percentage based. Now specified in pixels. +Item + - wantsFocus renamed to activeFocus + - forceFocus() renamed to forceActiveFocus() + - focus now returns the scoped focus (i.e. focus read/write now manipulate + the same value) +TextInput and TextEdit: + - focusOnPress renamed to activeFocusOnPress + +============================================================================= +The changes below are pre Qt 4.7.0 beta 2 + +QDeclarativeView + - initialSize() function added +TextInput and TextEdit: + - openSoftwareInputPanel() and closeSoftwareInputPanel() functions added +Flickable: + - overShoot is replaced by boundsBehavior enumeration + - flickingHorizontally and flickingVertically properties added + - movingHorizontally and movingVertically properties added + - flickDirection is renamed flickableDirection +Component: + - isReady, isLoading, isError and isNull properties removed, use + status property instead + - errorsString() renamed to errorString() +ListView: + - ListView.prevSection property changed to ListView.previousSection +TextInput: + - xToPosition -> positionAt (to match TextEdit.positionAt) +Image: + - pixmap property removed, use QDeclarativeImageProvider to serve pixmaps + instead + +QList models no longer provide properties in model object. The +properties are now updated when the object changes. An object's property +"foo" may now be accessed as "foo", modelData.foo" or model.modelData.foo" +component.createObject has gained a mandatory "parent" argument +TextEdit and TextInput now have a "selectByMouse" property that defaults to false. + +C++ API +------- +QDeclarativeExpression::value() has been renamed to +QDeclarativeExpression::evaluate() + +The QDeclarativeExpression constructor has changed from + QDeclarativeExpression(context, expression, scope) +to + QDeclarativeExpression(context, scope, expression, parent = 0) + +QDeclarativeImageProvider::request() has been renamed to +QDeclarativeImageProvider::image() and the class can now provide +both qimages and qpixmaps, and qimages can be served synchronously + +QML Viewer +------------ +The standalone qml executable has been renamed back to Qml Viewer. Runtime warnings +can be now accessed via the menu (Debugging->Show Warnings). + +============================================================================= +The changes below are pre Qt 4.7.0 beta 1 + +TextEdit: wrap property is replaced by wrapMode enumeration. +Text: wrap property is replaced by wrapMode enumeration. +Removed Q-prefix from validators (IntValidator, DoubleValidator, and RegExpValidator) +PathView: offset property now uses range 0-1.0 rather than 0-100 +ListView, GridView::positionViewAtIndex() gained a 'mode' parameter +Removed Qt.playSound (replaced by SoundEffect element) +Removed Qt.closestAngle (use RotationAnimation instead) +Removed NumberFormatter +Removed DateTimeFormatter (use Qt.formatDateTime() instead) +Using WebView now requires "import QtWebKit 1.0" +Using Particles now requires "import Qt.labs.particles 1.0" +AnchorAnimation must now be used to animate anchor changes (and not NumberAnimation) +Removed ParentAction (use ParentAnimation instead) +ScriptAction: renamed stateChangeScriptName -> scriptName +Animation: replace repeat with loops (loops: Animation.Infinite gives the old repeat behavior) +AnchorChanges: use natural form to specify anchors (anchors.left instead of left) +AnchorChanges: removed reset property. (reset: "left" should now be anchors.left: undefined) +PathView: snapPosition replaced by preferredHighlightBegin, preferredHighlightEnd +createQmlObject: Moved to the Qt object, now use Qt.createQmlObject() +createComponent: Moved to the Qt object, now use Qt.createComponent() + +C++ API +------- +QDeclarativeContext::addDefaultObject() has been replaced with +QDeclarativeContext::setContextObject() + +Behavior and Animation syntax +----------------------------- +Previously animations and behaviors could be "assigned" to properties like this: + Item { x: Behavior {}; y: NumberAnimation {} } +To make it more obvious that these are not regular value assignments a new "on" +syntax has been introduced: + Item { Behavior on x {}; NumberAnimation on y {} } +Only the syntax has changed, the behavior is identical. + +EaseFollow renamed to SmoothedFollow +--------------------------------------- +This element shares the internal implementation with SmoothedAnimation, +both providing the same easing function, but with SmoothedFollow it's +easier to set a start value to animate intially and then start to follow, +while SmoothedAnimation is still convenient for using inside Behaviors +and Transitions. + + +Add SmoothedAnimation element +--------------------------------------- +SmoothedAnimation inherits from NumberAnimaton and as a +consequence SmoothedAnimation can be used inside Behaviors, +as PropertySourceValues or in state transitions, like any other animation. + +The old EaseFollow properties changed to comply with the other declarative +animations ('source' changed to 'to'), so now 'to' changes are not +automatically 'followed' anymore. + +If you want to follow an hypothetical rect1, you should do now: + + Rectangle { + color: "green" + width: 60; height: 60; + x: rect1.x - 5; y: rect1.y - 5; + Behavior on x { SmoothedAnimation { velocity: 200 } } + Behavior on y { SmoothedAnimation { velocity: 200 } } + } + +instead of the old automatic source changed tracking: + + Rectangle { + color: "green" + width: 60; height: 60; + EaseFollow on x { source: rect1.x - 5; velocity: 200 } + EaseFollow on y { source: rect1.y - 5; velocity: 200 } + } + +This is a syntax and behavior change. + + +Script element removed +---------------------- +Inline Script{} blocks have been deprecated, and will soon be removed entirely. +If you used Script to write inline javascript code, it can simply be removed. +For example + +Item { + Script { + function doSomething() {} + } +} + +becomes + +Item { + function doSomething() {} +} + +If you used Script to include external JavaScript files, you can replace the +Script element with an “import” line. For example + +MouseArea { + Script { + source: “foo.js” + } + onClicked: foo() +} + +becomes + +import “foo.js” as Foo +MouseArea { + onClicked: Foo.foo() +} + +The “as” qualifier is mandatory for script imports (as opposed to type +imports where it is optional). + + +============================================================================= +The changes below are pre Qt 4.7.0 alpha + +Flickable: renamed viewportWidth -> contentWidth +Flickable: renamed viewportHeight -> contentHeight +Flickable: renamed viewportX -> contentX +Flickable: renamed viewportY -> contentY +Removed Flickable.reportedVelocitySmoothing +Renamed MouseRegion -> MouseArea +Connection: syntax and rename: + Connection { sender: a; signal: foo(); script: xxx } + Connection { sender: a; signal: bar(); script: yyy } + becomes: + Connections { target: a; onFoo: xxx; onBar: yyy } + +ListView::sectionExpression has been replaced by section.property, section.criteria + +ListModel +--------- +- types are strictly checked (previously, everything was a string) + - foo: "bar" continues to work as before + - foo: bar is now invalid, use foo: "bar" + - foo: true is now a bool (not string "true") + - foo: false is now a bool (not string "false" == true!) + +PropertyAnimation +------------------ +matchProperties and matchTargets have been renamed back to properties and targets. +The semantics are explained in the PropertyAnimation::properties documentation +and the animation overview documentation. + +Easing curves and their parameters are now specified via dot properties: +* easing.type : enum +* easing.amplitude : real +* easing.overshoot : real +* easing.period : real +For example: +PropertyAnimation { properties: "y"; easing.type: "InOutElastic"; easing.amplitude: 2.0; easing.period: 1.5 } + +C++ API +------- +QML_DEFINE_... definition macros, previously global macros, are replaced by +qmlRegisterType registration functions, which must be called explicitly. +C++ API users should also consider using the QmlExtensionPlugin (previously +named QmlModulePlugin) as a cleaner mechanism for publishing libraries of QML +types, or the upcoming application plugin features of the qmlviewer / +qmlruntime / qml. + +QmlView +------- +The API of QmlView has been narrowed and its role as a convenience class +reinforced. +- remove addItem() +- remove clearItems() - use 'delete root()' +- remove reset() +- resizeContent -> enum ResizeMode { SizeViewToRootObject, SizeRootObjectToView } +- remove setQml(), qml() +- rename setUrl(), ur() to setSource(), source() +- root() -> rootObject(), returns QGraphicsObject rather than QmlGraphicsItem +- remove quit() signal -> use quit() signal of engine() +- initialSize() signal removed +- Added status() to determine status of the internal QmlComponent +- removed execute() - setSource() will also execute the QML. + + +============================================================================= +The changes below are pre-4.6.0 release. + +QML API Review +============== + +The QML API is being reviewed. This file documents the changes. +Note that the changes are incremental, so a rename A->B for example may be followed +by another subsequent rename B->C, if later reviews override earlier reviews. + +API Changes +=========== + +Renamed Elements: +LineEdit -> TextInput +VerticalLayout -> Column +HorizontalLayout -> Row +VerticalPositioner -> Column +HorizontalPositioner -> Row +GridLayout -> Grid +GridPositioner -> Grid +Rect -> Rectangle +FocusRealm -> FocusScope +FontFamily -> FontLoader +Palette -> SystemPalette +Bind -> Binding +SetProperties -> PropertyChanges +RunScript -> StateChangeScript +SetAnchors -> AnchorChanges +SetPropertyAction -> PropertyAction +RunScriptAction -> ScriptAction +ParentChangeAction -> ParentAction +VisualModel -> VisualDataModel +Follow -> SpringFollow + +Renamed properties: +Item: contents -> childrenRect +MouseRegion: xmin -> minimumX +MouseRegion: xmax -> maximumX +MouseRegion: ymin -> minimumY +MouseRegion: ymin -> maximumY +Text elements: hAlign -> horizontalAlignment +Text elements: vAlign -> verticalAlignment +Text elements: highlightColor -> selectionColor +Text elements: highlightedTextColor -> selectedTextColor +Text elements: preserveSelection -> persistentSelection +State: operations -> changes +Transition: operations -> animations +Transition: fromState -> from +Transition: toState -> to +Follow: followValue -> value +Flickable: xPosition -> viewportX +Flickable: yPosition -> viewportY +Flickable: xVelocity -> horizontalVelocity +Flickable: yVelocity -> verticalVelocity +Flickable: velocityDecay -> reportedVelocitySmoothing +Flickable: locked -> interactive (note reversal of meaning) +Flickable: pageXPosition -> visibleArea.xPosition +Flickable: pageYPosition -> visibleArea.yPosition +Flickable: pageWidth -> visibleArea.widthRatio +Flickable: pageHeight -> visibleArea.heightRatio +WebView: idealWidth -> preferredWidth +WebView: idealHeight -> preferredHeight +WebView: status -> statusText +WebView: mouseX -> clickX (parameter to onDoubleClick) +WebView: mouseY -> clickY (parameter to onDoubleClick) +WebView: cacheSize -> pixelCacheSize +Repeater: component -> delegate +Repeater: dataSource -> model +ListView: current -> currentItem +GridView: current -> currentItem +ListView: wrap -> keyNavigationWraps +ListView: autoHighlight -> highlightFollowsCurrentItem +GridView: wrap -> keyNavigationWraps +GridView: autoHighlight -> highlightFollowsCurrentItem +Animation: targets -> matchTargets +Animation: properties -> matchProperties + +Additions: +MouseRegion: add "acceptedButtons" property +MouseRegion: add "hoverEnabled" property +MouseRegion: add "pressedButtons" property +Timer: add start() and stop() slots +WebView: add newWindowComponent and newWindowParent properties +Loader: add status() and progress() properties +Loader: add sourceComponent property +Loader: add resizeMode property +ListView: preferredHighlightBegin, preferredHighlightEnd +ListView: strictlyEnforceHighlightRange +Particles: Added emissionRate, emissionVariance and burst() + +Deletions: +Column/VerticalPositioner: lost "margins" property +Row/HorizontalPositioner: lost "margins" property +Grid/Positioner/Layout: lost "margins" property +WebView: lost "interactive" property (always true now) +Flickable: removed "dragMode" property +ComponentInstance: removed. Replaced by Loader.sourceComponent +ListView: removed currentItemMode. Replaced by highligh range. +ListView: removed snapPos. +Particles: removed streamIn. Replaced by emissionRate + +Other Changes: +ids must be lowercase: Text { id: foo }, not Text { id: Foo } +Drag: axis becomes an enum with values "XAxis", "YAxis", "XandYAxis" +Image: scaleGrid property removed. New item called BorderImage instead. +KeyActions: changed to a Keys attached property on any item. +KeyProxy: changed to a Keys.forwardTo property on any item. +Script: now an intrinsic type in the language + - cannot be assigned to properties + - good: Item { Script { ... } } + - bad: Item { resources: Script { ... } } +Script: delay-loaded of the QML file until their source has been loaded (this only effects QML files loaded across the network.) +Scope: declared properties shadow a property of the same name (was previously the reverse) +ScriptAction and StateChangeScript: the script property now takes script rather than a string containing script (script: doSomething() rather than script: "doSomething()") +QmlGraphicsItem::transformOrigin default changed from TopLeft to Center +Animations used as PropertySourceValues are set to 'running: true' as default diff --git a/src/declarative/debugger/debugger.pri b/src/declarative/debugger/debugger.pri new file mode 100644 index 00000000..c81968ee --- /dev/null +++ b/src/declarative/debugger/debugger.pri @@ -0,0 +1,33 @@ +INCLUDEPATH += $$PWD + +SOURCES += \ + $$PWD/qdeclarativedebuggerstatus.cpp \ + $$PWD/qpacketprotocol.cpp \ + $$PWD/qdeclarativedebugservice.cpp \ + $$PWD/qdeclarativedebugclient.cpp \ + $$PWD/qdeclarativeenginedebug.cpp \ + $$PWD/qdeclarativedebugtrace.cpp \ + $$PWD/qdeclarativedebughelper.cpp \ + $$PWD/qdeclarativedebugserver.cpp \ + $$PWD/qdeclarativeinspectorservice.cpp \ + $$PWD/qjsdebuggeragent.cpp \ + $$PWD/qjsdebugservice.cpp \ + $$PWD/qdeclarativeenginedebugservice.cpp + +HEADERS += \ + $$PWD/qdeclarativedebuggerstatus_p.h \ + $$PWD/qpacketprotocol_p.h \ + $$PWD/qdeclarativedebugservice_p.h \ + $$PWD/qdeclarativedebugservice_p_p.h \ + $$PWD/qdeclarativedebugclient_p.h \ + $$PWD/qdeclarativeenginedebug_p.h \ + $$PWD/qdeclarativedebugtrace_p.h \ + $$PWD/qdeclarativedebughelper_p.h \ + $$PWD/qdeclarativedebugserver_p.h \ + $$PWD/qdeclarativedebugserverconnection_p.h \ + $$PWD/qdeclarativeinspectorservice_p.h \ + $$PWD/qdeclarativeinspectorinterface_p.h \ + $$PWD/qjsdebuggeragent_p.h \ + $$PWD/qjsdebugservice_p.h \ + $$PWD/qdeclarativedebug.h \ + $$PWD/qdeclarativeenginedebugservice_p.h diff --git a/src/declarative/debugger/qdeclarativedebug.h b/src/declarative/debugger/qdeclarativedebug.h new file mode 100644 index 00000000..8b0a65c2 --- /dev/null +++ b/src/declarative/debugger/qdeclarativedebug.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEDEBUG_H +#define QDECLARATIVEDEBUG_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +struct Q_DECLARATIVE_EXPORT QDeclarativeDebuggingEnabler +{ + QDeclarativeDebuggingEnabler(); +}; + +// Execute code in constructor before first QDeclarativeEngine is instantiated +#if defined(QT_DECLARATIVE_DEBUG) +static QDeclarativeDebuggingEnabler qmlEnableDebuggingHelper; +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEDEBUG_H diff --git a/src/declarative/debugger/qdeclarativedebugclient.cpp b/src/declarative/debugger/qdeclarativedebugclient.cpp new file mode 100644 index 00000000..9c3d8144 --- /dev/null +++ b/src/declarative/debugger/qdeclarativedebugclient.cpp @@ -0,0 +1,292 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativedebugclient_p.h" + +#include "private/qpacketprotocol_p.h" + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +const int protocolVersion = 1; +const QString serverId = QLatin1String("QDeclarativeDebugServer"); +const QString clientId = QLatin1String("QDeclarativeDebugClient"); + +class QDeclarativeDebugClientPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeDebugClient) +public: + QDeclarativeDebugClientPrivate(); + + QString name; + QDeclarativeDebugConnection *connection; +}; + +class QDeclarativeDebugConnectionPrivate : public QObject +{ + Q_OBJECT +public: + QDeclarativeDebugConnectionPrivate(QDeclarativeDebugConnection *c); + QDeclarativeDebugConnection *q; + QPacketProtocol *protocol; + + bool gotHello; + QStringList serverPlugins; + QHash plugins; + + void advertisePlugins(); + +public Q_SLOTS: + void connected(); + void readyRead(); +}; + +QDeclarativeDebugConnectionPrivate::QDeclarativeDebugConnectionPrivate(QDeclarativeDebugConnection *c) +: QObject(c), q(c), protocol(0), gotHello(false) +{ + protocol = new QPacketProtocol(q, this); + QObject::connect(c, SIGNAL(connected()), this, SLOT(connected())); + QObject::connect(protocol, SIGNAL(readyRead()), this, SLOT(readyRead())); +} + +void QDeclarativeDebugConnectionPrivate::advertisePlugins() +{ + if (!q->isConnected()) + return; + + QPacket pack; + pack << serverId << 1 << plugins.keys(); + protocol->send(pack); + q->flush(); +} + +void QDeclarativeDebugConnectionPrivate::connected() +{ + QPacket pack; + pack << serverId << 0 << protocolVersion << plugins.keys(); + protocol->send(pack); + q->flush(); +} + +void QDeclarativeDebugConnectionPrivate::readyRead() +{ + if (!gotHello) { + QPacket pack = protocol->read(); + QString name; + + pack >> name; + + bool validHello = false; + if (name == clientId) { + int op = -1; + pack >> op; + if (op == 0) { + int version = -1; + pack >> version; + if (version == protocolVersion) { + pack >> serverPlugins; + validHello = true; + } + } + } + + if (!validHello) { + qWarning("QDeclarativeDebugConnection: Invalid hello message"); + QObject::disconnect(protocol, SIGNAL(readyRead()), this, SLOT(readyRead())); + return; + } + + gotHello = true; + + QHash::Iterator iter = plugins.begin(); + for (; iter != plugins.end(); ++iter) { + QDeclarativeDebugClient::Status newStatus = QDeclarativeDebugClient::Unavailable; + if (serverPlugins.contains(iter.key())) + newStatus = QDeclarativeDebugClient::Enabled; + iter.value()->statusChanged(newStatus); + } + } + + while (protocol->packetsAvailable()) { + QPacket pack = protocol->read(); + QString name; + pack >> name; + + if (name == clientId) { + int op = -1; + pack >> op; + + if (op == 1) { + // Service Discovery + QStringList oldServerPlugins = serverPlugins; + pack >> serverPlugins; + + QHash::Iterator iter = plugins.begin(); + for (; iter != plugins.end(); ++iter) { + const QString pluginName = iter.key(); + QDeclarativeDebugClient::Status newStatus = QDeclarativeDebugClient::Unavailable; + if (serverPlugins.contains(pluginName)) + newStatus = QDeclarativeDebugClient::Enabled; + + if (oldServerPlugins.contains(pluginName) + != serverPlugins.contains(pluginName)) { + iter.value()->statusChanged(newStatus); + } + } + } else { + qWarning() << "QDeclarativeDebugConnection: Unknown control message id" << op; + } + } else { + QByteArray message; + pack >> message; + + QHash::Iterator iter = + plugins.find(name); + if (iter == plugins.end()) { + qWarning() << "QDeclarativeDebugConnection: Message received for missing plugin" << name; + } else { + (*iter)->messageReceived(message); + } + } + } +} + +QDeclarativeDebugConnection::QDeclarativeDebugConnection(QObject *parent) +: QTcpSocket(parent), d(new QDeclarativeDebugConnectionPrivate(this)) +{ +} + +QDeclarativeDebugConnection::~QDeclarativeDebugConnection() +{ + QHash::iterator iter = d->plugins.begin(); + for (; iter != d->plugins.end(); ++iter) { + iter.value()->d_func()->connection = 0; + iter.value()->statusChanged(QDeclarativeDebugClient::NotConnected); + } +} + +bool QDeclarativeDebugConnection::isConnected() const +{ + return state() == ConnectedState; +} + +QDeclarativeDebugClientPrivate::QDeclarativeDebugClientPrivate() +: connection(0) +{ +} + +QDeclarativeDebugClient::QDeclarativeDebugClient(const QString &name, + QDeclarativeDebugConnection *parent) +: QObject(*(new QDeclarativeDebugClientPrivate), parent) +{ + Q_D(QDeclarativeDebugClient); + d->name = name; + d->connection = parent; + + if (!d->connection) + return; + + if (d->connection->d->plugins.contains(name)) { + qWarning() << "QDeclarativeDebugClient: Conflicting plugin name" << name; + d->connection = 0; + } else { + d->connection->d->plugins.insert(name, this); + d->connection->d->advertisePlugins(); + } +} + +QDeclarativeDebugClient::~QDeclarativeDebugClient() +{ + Q_D(const QDeclarativeDebugClient); + if (d->connection && d->connection->d) { + d->connection->d->plugins.remove(d->name); + d->connection->d->advertisePlugins(); + } +} + +QString QDeclarativeDebugClient::name() const +{ + Q_D(const QDeclarativeDebugClient); + return d->name; +} + +QDeclarativeDebugClient::Status QDeclarativeDebugClient::status() const +{ + Q_D(const QDeclarativeDebugClient); + if (!d->connection + || !d->connection->isConnected() + || !d->connection->d->gotHello) + return NotConnected; + + if (d->connection->d->serverPlugins.contains(d->name)) + return Enabled; + + return Unavailable; +} + +void QDeclarativeDebugClient::sendMessage(const QByteArray &message) +{ + Q_D(QDeclarativeDebugClient); + + if (status() != Enabled) + return; + + QPacket pack; + pack << d->name << message; + d->connection->d->protocol->send(pack); + d->connection->d->q->flush(); +} + +void QDeclarativeDebugClient::statusChanged(Status) +{ +} + +void QDeclarativeDebugClient::messageReceived(const QByteArray &) +{ +} + +QT_END_NAMESPACE + +#include diff --git a/src/declarative/debugger/qdeclarativedebugclient_p.h b/src/declarative/debugger/qdeclarativedebugclient_p.h new file mode 100644 index 00000000..ea099fe2 --- /dev/null +++ b/src/declarative/debugger/qdeclarativedebugclient_p.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEDEBUGCLIENT_H +#define QDECLARATIVEDEBUGCLIENT_H + +#include + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeDebugConnectionPrivate; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeDebugConnection : public QTcpSocket +{ + Q_OBJECT + Q_DISABLE_COPY(QDeclarativeDebugConnection) +public: + QDeclarativeDebugConnection(QObject * = 0); + ~QDeclarativeDebugConnection(); + + bool isConnected() const; +private: + QDeclarativeDebugConnectionPrivate *d; + friend class QDeclarativeDebugClient; + friend class QDeclarativeDebugClientPrivate; +}; + +class QDeclarativeDebugClientPrivate; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeDebugClient : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeDebugClient) + Q_DISABLE_COPY(QDeclarativeDebugClient) + +public: + enum Status { NotConnected, Unavailable, Enabled }; + + QDeclarativeDebugClient(const QString &, QDeclarativeDebugConnection *parent); + ~QDeclarativeDebugClient(); + + QString name() const; + + Status status() const; + + void sendMessage(const QByteArray &); + +protected: + virtual void statusChanged(Status); + virtual void messageReceived(const QByteArray &); + +private: + friend class QDeclarativeDebugConnection; + friend class QDeclarativeDebugConnectionPrivate; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEDEBUGCLIENT_H diff --git a/src/declarative/debugger/qdeclarativedebuggerstatus.cpp b/src/declarative/debugger/qdeclarativedebuggerstatus.cpp new file mode 100644 index 00000000..2e2f4f5b --- /dev/null +++ b/src/declarative/debugger/qdeclarativedebuggerstatus.cpp @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativedebuggerstatus_p.h" + +QT_BEGIN_NAMESPACE + +QDeclarativeDebuggerStatus::~QDeclarativeDebuggerStatus() +{ +} + +void QDeclarativeDebuggerStatus::setSelectedState(bool) +{ +} + +QT_END_NAMESPACE diff --git a/src/declarative/debugger/qdeclarativedebuggerstatus_p.h b/src/declarative/debugger/qdeclarativedebuggerstatus_p.h new file mode 100644 index 00000000..0404e0f8 --- /dev/null +++ b/src/declarative/debugger/qdeclarativedebuggerstatus_p.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEDEBUGGERSTATUS_P_H +#define QDECLARATIVEDEBUGGERSTATUS_P_H + +#include + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeDebuggerStatus +{ +public: + virtual ~QDeclarativeDebuggerStatus(); + + virtual void setSelectedState(bool); +}; +Q_DECLARE_INTERFACE(QDeclarativeDebuggerStatus, "com.trolltech.qml.QDeclarativeDebuggerStatus") + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QLMDEBUGGERSTATUS_P_H diff --git a/src/declarative/debugger/qdeclarativedebughelper.cpp b/src/declarative/debugger/qdeclarativedebughelper.cpp new file mode 100644 index 00000000..1a460bfb --- /dev/null +++ b/src/declarative/debugger/qdeclarativedebughelper.cpp @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "private/qdeclarativedebughelper_p.h" + +#include +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QScriptEngine *QDeclarativeDebugHelper::getScriptEngine(QDeclarativeEngine *engine) +{ + return QDeclarativeEnginePrivate::getScriptEngine(engine); +} + +void QDeclarativeDebugHelper::setAnimationSlowDownFactor(qreal factor) +{ + QUnifiedTimer *timer = QUnifiedTimer::instance(); + timer->setSlowModeEnabled(factor != 1.0); + timer->setSlowdownFactor(factor); +} + +void QDeclarativeDebugHelper::enableDebugging() { +#ifndef QDECLARATIVE_NO_DEBUG_PROTOCOL + if (!QDeclarativeEnginePrivate::qml_debugging_enabled) { + qWarning("Qml debugging is enabled. Only use this in a safe environment!"); + } + QDeclarativeEnginePrivate::qml_debugging_enabled = true; +#endif +} + +QT_END_NAMESPACE diff --git a/src/declarative/debugger/qdeclarativedebughelper_p.h b/src/declarative/debugger/qdeclarativedebughelper_p.h new file mode 100644 index 00000000..93370982 --- /dev/null +++ b/src/declarative/debugger/qdeclarativedebughelper_p.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEDEBUGHELPER_P_H +#define QDECLARATIVEDEBUGHELPER_P_H + +#include + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QScriptEngine; +class QDeclarativeEngine; + +// Helper methods to access private API through a stable interface +// This is used in the qmljsdebugger library of QtCreator. +class Q_DECLARATIVE_EXPORT QDeclarativeDebugHelper +{ +public: + static QScriptEngine *getScriptEngine(QDeclarativeEngine *engine); + static void setAnimationSlowDownFactor(qreal factor); + + // Enables remote debugging functionality + // Only use this for debugging in a safe environment! + static void enableDebugging(); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEDEBUGHELPER_P_H diff --git a/src/declarative/debugger/qdeclarativedebugserver.cpp b/src/declarative/debugger/qdeclarativedebugserver.cpp new file mode 100644 index 00000000..c664f960 --- /dev/null +++ b/src/declarative/debugger/qdeclarativedebugserver.cpp @@ -0,0 +1,426 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativedebugserver_p.h" +#include "private/qdeclarativedebugservice_p.h" +#include "private/qdeclarativedebugservice_p_p.h" +#include "private/qdeclarativeengine_p.h" + +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +/* + QDeclarativeDebug Protocol (Version 1): + + handshake: + 1. Client sends + "QDeclarativeDebugServer" 0 version pluginNames + version: an int representing the highest protocol version the client knows + pluginNames: plugins available on client side + 2. Server sends + "QDeclarativeDebugClient" 0 version pluginNames + version: an int representing the highest protocol version the client & server know + pluginNames: plugins available on server side. plugins both in the client and server message are enabled. + client plugin advertisement + 1. Client sends + "QDeclarativeDebugServer" 1 pluginNames + server plugin advertisement + 1. Server sends + "QDeclarativeDebugClient" 1 pluginNames + plugin communication: + Everything send with a header different to "QDeclarativeDebugServer" is sent to the appropriate plugin. + */ + +const int protocolVersion = 1; + + +class QDeclarativeDebugServerPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeDebugServer) +public: + QDeclarativeDebugServerPrivate(); + + void advertisePlugins(); + + QDeclarativeDebugServerConnection *connection; + QHash plugins; + QStringList clientPlugins; + bool gotHello; + QString waitingForMsgFromService; + bool waitingForMsgSucceeded; + +private: + // private slot + void _q_deliverMessage(const QString &serviceName, const QByteArray &message); + static QDeclarativeDebugServerConnection *loadConnectionPlugin(QPluginLoader *loader, const QString &pluginName); +}; + +QDeclarativeDebugServerPrivate::QDeclarativeDebugServerPrivate() : + connection(0), + gotHello(false), + waitingForMsgSucceeded(false) +{ +} + +void QDeclarativeDebugServerPrivate::advertisePlugins() +{ + if (!gotHello) + return; + + QByteArray message; + { + QDataStream out(&message, QIODevice::WriteOnly); + out << QString(QLatin1String("QDeclarativeDebugClient")) << 1 << plugins.keys(); + } + connection->send(message); +} + +QDeclarativeDebugServerConnection *QDeclarativeDebugServerPrivate::loadConnectionPlugin( + QPluginLoader *loader, const QString &pluginName) +{ +#ifndef QT_NO_LIBRARY + QStringList pluginCandidates; + const QStringList paths = QCoreApplication::libraryPaths(); + foreach (const QString &libPath, paths) { + const QDir dir(libPath + QLatin1String("/qmltooling")); + if (dir.exists()) { + QStringList plugins(dir.entryList(QDir::Files)); + foreach (const QString &pluginPath, plugins) { + if (QFileInfo(pluginPath).fileName().contains(pluginName)) + pluginCandidates << dir.absoluteFilePath(pluginPath); + } + } + } + + foreach (const QString &pluginPath, pluginCandidates) { + loader->setFileName(pluginPath); + if (!loader->load()) { + continue; + } + QDeclarativeDebugServerConnection *connection = 0; + if (QObject *instance = loader->instance()) + connection = qobject_cast(instance); + + if (connection) + return connection; + loader->unload(); + } +#endif + return 0; +} + +bool QDeclarativeDebugServer::hasDebuggingClient() const +{ + Q_D(const QDeclarativeDebugServer); + return d->connection + && d->connection->isConnected() + && d->gotHello; +} + +QDeclarativeDebugServer *QDeclarativeDebugServer::instance() +{ + static bool commandLineTested = false; + static QDeclarativeDebugServer *server = 0; + + if (!commandLineTested) { + commandLineTested = true; + + QApplicationPrivate *appD = static_cast(QObjectPrivate::get(qApp)); +#ifndef QDECLARATIVE_NO_DEBUG_PROTOCOL + // ### remove port definition when protocol is changed + int port = 0; + bool block = false; + bool ok = false; + + // format: qmljsdebugger=port:3768[,block] OR qmljsdebugger=ost[,block] + if (!appD->qmljsDebugArgumentsString().isEmpty()) { + if (!QDeclarativeEnginePrivate::qml_debugging_enabled) { + qWarning() << QString::fromLatin1( + "QDeclarativeDebugServer: Ignoring \"-qmljsdebugger=%1\". " + "Debugging has not been enabled.").arg( + appD->qmljsDebugArgumentsString()); + return 0; + } + + QString pluginName; + if (appD->qmljsDebugArgumentsString().indexOf(QLatin1String("port:")) == 0) { + int separatorIndex = appD->qmljsDebugArgumentsString().indexOf(QLatin1Char(',')); + port = appD->qmljsDebugArgumentsString().mid(5, separatorIndex - 5).toInt(&ok); + pluginName = QLatin1String("qmldbg_tcp"); + } else if (appD->qmljsDebugArgumentsString().contains(QLatin1String("ost"))) { + pluginName = QLatin1String("qmldbg_ost"); + ok = true; + } + + block = appD->qmljsDebugArgumentsString().contains(QLatin1String("block")); + + if (ok) { + server = new QDeclarativeDebugServer(); + + QPluginLoader *loader = new QPluginLoader(server); + QDeclarativeDebugServerConnection *connection + = QDeclarativeDebugServerPrivate::loadConnectionPlugin(loader, pluginName); + if (connection) { + server->d_func()->connection = connection; + + connection->setServer(server); + connection->setPort(port, block); + } else { + qWarning() << QString::fromLatin1( + "QDeclarativeDebugServer: Ignoring \"-qmljsdebugger=%1\". " + "Remote debugger plugin has not been found.").arg( + appD->qmljsDebugArgumentsString()); + } + + } else { + qWarning() << QString::fromLatin1( + "QDeclarativeDebugServer: Ignoring \"-qmljsdebugger=%1\". " + "Format is -qmljsdebugger=port:[,block]").arg( + appD->qmljsDebugArgumentsString()); + } + } +#else + if (!appD->qmljsDebugArgumentsString().isEmpty()) { + qWarning() << QString::fromLatin1( + "QDeclarativeDebugServer: Ignoring \"-qmljsdebugger=%1\". " + "QtDeclarative is not configured for debugging.").arg( + appD->qmljsDebugArgumentsString()); + } +#endif + } + + return server; +} + +QDeclarativeDebugServer::QDeclarativeDebugServer() +: QObject(*(new QDeclarativeDebugServerPrivate)) +{ +} + +void QDeclarativeDebugServer::receiveMessage(const QByteArray &message) +{ + Q_D(QDeclarativeDebugServer); + + QDataStream in(message); + if (!d->gotHello) { + QString name; + int op; + in >> name >> op; + + if (name != QLatin1String("QDeclarativeDebugServer") + || op != 0) { + qWarning("QDeclarativeDebugServer: Invalid hello message"); + d->connection->disconnect(); + return; + } + + int version; + in >> version >> d->clientPlugins; + + // Send the hello answer immediately, since it needs to arrive before + // the plugins below start sending messages. + QByteArray helloAnswer; + { + QDataStream out(&helloAnswer, QIODevice::WriteOnly); + out << QString(QLatin1String("QDeclarativeDebugClient")) << 0 << protocolVersion << d->plugins.keys(); + } + d->connection->send(helloAnswer); + + d->gotHello = true; + + QHash::Iterator iter = d->plugins.begin(); + for (; iter != d->plugins.end(); ++iter) { + QDeclarativeDebugService::Status newStatus = QDeclarativeDebugService::Unavailable; + if (d->clientPlugins.contains(iter.key())) + newStatus = QDeclarativeDebugService::Enabled; + iter.value()->d_func()->status = newStatus; + iter.value()->statusChanged(newStatus); + } + + qDebug("QDeclarativeDebugServer: Connection established"); + } else { + + QString debugServer(QLatin1String("QDeclarativeDebugServer")); + + QString name; + in >> name; + + if (name == debugServer) { + int op = -1; + in >> op; + + if (op == 1) { + // Service Discovery + QStringList oldClientPlugins = d->clientPlugins; + in >> d->clientPlugins; + + QHash::Iterator iter = d->plugins.begin(); + for (; iter != d->plugins.end(); ++iter) { + const QString pluginName = iter.key(); + QDeclarativeDebugService::Status newStatus = QDeclarativeDebugService::Unavailable; + if (d->clientPlugins.contains(pluginName)) + newStatus = QDeclarativeDebugService::Enabled; + + if (oldClientPlugins.contains(pluginName) + != d->clientPlugins.contains(pluginName)) { + iter.value()->d_func()->status = newStatus; + iter.value()->statusChanged(newStatus); + } + } + } else { + qWarning("QDeclarativeDebugServer: Invalid control message %d", op); + } + } else { + QByteArray message; + in >> message; + + if (d->waitingForMsgFromService == name) { + // deliver directly so that it is delivered before waitForMessage is returning. + d->_q_deliverMessage(name, message); + d->waitingForMsgSucceeded = true; + } else { + // deliver message in next event loop run. + // Fixes the case that the service does start it's own event loop ..., + // but the networking code doesn't deliver any new messages because readyRead + // hasn't returned. + QMetaObject::invokeMethod(this, "_q_deliverMessage", Qt::QueuedConnection, + Q_ARG(QString, name), + Q_ARG(QByteArray, message)); + } + } + } +} + +void QDeclarativeDebugServerPrivate::_q_deliverMessage(const QString &serviceName, const QByteArray &message) +{ + QHash::Iterator iter = plugins.find(serviceName); + if (iter == plugins.end()) { + qWarning() << "QDeclarativeDebugServer: Message received for missing plugin" << serviceName; + } else { + (*iter)->messageReceived(message); + } +} + +QList QDeclarativeDebugServer::services() const +{ + const Q_D(QDeclarativeDebugServer); + return d->plugins.values(); +} + +QStringList QDeclarativeDebugServer::serviceNames() const +{ + const Q_D(QDeclarativeDebugServer); + return d->plugins.keys(); +} + +bool QDeclarativeDebugServer::addService(QDeclarativeDebugService *service) +{ + Q_D(QDeclarativeDebugServer); + if (!service || d->plugins.contains(service->name())) + return false; + + d->plugins.insert(service->name(), service); + d->advertisePlugins(); + + QDeclarativeDebugService::Status newStatus = QDeclarativeDebugService::Unavailable; + if (d->clientPlugins.contains(service->name())) + newStatus = QDeclarativeDebugService::Enabled; + service->d_func()->status = newStatus; + service->statusChanged(newStatus); + return true; +} + +bool QDeclarativeDebugServer::removeService(QDeclarativeDebugService *service) +{ + Q_D(QDeclarativeDebugServer); + if (!service || !d->plugins.contains(service->name())) + return false; + + d->plugins.remove(service->name()); + d->advertisePlugins(); + + QDeclarativeDebugService::Status newStatus = QDeclarativeDebugService::NotConnected; + service->d_func()->server = 0; + service->d_func()->status = newStatus; + service->statusChanged(newStatus); + return true; +} + +void QDeclarativeDebugServer::sendMessage(QDeclarativeDebugService *service, + const QByteArray &message) +{ + Q_D(QDeclarativeDebugServer); + QByteArray msg; + { + QDataStream out(&msg, QIODevice::WriteOnly); + out << service->name() << message; + } + d->connection->send(msg); +} + +bool QDeclarativeDebugServer::waitForMessage(QDeclarativeDebugService *service) +{ + Q_D(QDeclarativeDebugServer); + + if (!service + || !d->plugins.contains(service->name()) + || !d->waitingForMsgFromService.isEmpty()) + return false; + + d->waitingForMsgFromService = service->name(); + + do { + d->connection->waitForMessage(); + } while (!d->waitingForMsgSucceeded); + d->waitingForMsgSucceeded = false; + d->waitingForMsgFromService.clear(); + return true; +} + +QT_END_NAMESPACE + +#include "moc_qdeclarativedebugserver_p.cpp" diff --git a/src/declarative/debugger/qdeclarativedebugserver_p.h b/src/declarative/debugger/qdeclarativedebugserver_p.h new file mode 100644 index 00000000..922f981d --- /dev/null +++ b/src/declarative/debugger/qdeclarativedebugserver_p.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEDEBUGSERVER_H +#define QDECLARATIVEDEBUGSERVER_H + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeDebugService; + +class QDeclarativeDebugServerPrivate; +class Q_DECLARATIVE_EXPORT QDeclarativeDebugServer : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeDebugServer) + Q_DISABLE_COPY(QDeclarativeDebugServer) +public: + static QDeclarativeDebugServer *instance(); + + void setConnection(QDeclarativeDebugServerConnection *connection); + + bool hasDebuggingClient() const; + + QList services() const; + QStringList serviceNames() const; + + bool addService(QDeclarativeDebugService *service); + bool removeService(QDeclarativeDebugService *service); + + void sendMessage(QDeclarativeDebugService *service, const QByteArray &message); + void receiveMessage(const QByteArray &message); + + bool waitForMessage(QDeclarativeDebugService *service); + +private: + friend class QDeclarativeDebugService; + friend class QDeclarativeDebugServicePrivate; + QDeclarativeDebugServer(); + Q_PRIVATE_SLOT(d_func(), void _q_deliverMessage(QString, QByteArray)) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEDEBUGSERVICE_H diff --git a/src/declarative/debugger/qdeclarativedebugserverconnection_p.h b/src/declarative/debugger/qdeclarativedebugserverconnection_p.h new file mode 100644 index 00000000..16fa798e --- /dev/null +++ b/src/declarative/debugger/qdeclarativedebugserverconnection_p.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEDEBUGSERVERCONNECTION_H +#define QDECLARATIVEDEBUGSERVERCONNECTION_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeDebugServer; +class Q_DECLARATIVE_EXPORT QDeclarativeDebugServerConnection +{ +public: + QDeclarativeDebugServerConnection() {} + virtual ~QDeclarativeDebugServerConnection() {} + + virtual void setServer(QDeclarativeDebugServer *server) = 0; + virtual void setPort(int port, bool bock) = 0; + virtual bool isConnected() const = 0; + virtual void send(const QByteArray &message) = 0; + virtual void disconnect() = 0; + virtual bool waitForMessage() = 0; +}; + +Q_DECLARE_INTERFACE(QDeclarativeDebugServerConnection, "com.trolltech.Qt.QDeclarativeDebugServerConnection/1.0") + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEDEBUGSERVERCONNECTION_H diff --git a/src/declarative/debugger/qdeclarativedebugservice.cpp b/src/declarative/debugger/qdeclarativedebugservice.cpp new file mode 100644 index 00000000..111bd24c --- /dev/null +++ b/src/declarative/debugger/qdeclarativedebugservice.cpp @@ -0,0 +1,230 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativedebugservice_p.h" +#include "private/qdeclarativedebugservice_p_p.h" +#include "private/qdeclarativedebugserver_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +QDeclarativeDebugServicePrivate::QDeclarativeDebugServicePrivate() +: server(0) +{ +} + +QDeclarativeDebugService::QDeclarativeDebugService(const QString &name, QObject *parent) +: QObject(*(new QDeclarativeDebugServicePrivate), parent) +{ + Q_D(QDeclarativeDebugService); + d->name = name; + d->server = QDeclarativeDebugServer::instance(); + d->status = QDeclarativeDebugService::NotConnected; + + if (!d->server) + return; + + if (d->server->serviceNames().contains(name)) { + qWarning() << "QDeclarativeDebugService: Conflicting plugin name" << name; + d->server = 0; + } else { + d->server->addService(this); + } +} + +QDeclarativeDebugService::~QDeclarativeDebugService() +{ + Q_D(const QDeclarativeDebugService); + if (d->server) { + d->server->removeService(this); + } +} + +QString QDeclarativeDebugService::name() const +{ + Q_D(const QDeclarativeDebugService); + return d->name; +} + +QDeclarativeDebugService::Status QDeclarativeDebugService::status() const +{ + Q_D(const QDeclarativeDebugService); + return d->status; +} + +namespace { + + struct ObjectReference + { + QPointer object; + int id; + }; + + struct ObjectReferenceHash + { + ObjectReferenceHash() : nextId(0) {} + + QHash objects; + QHash ids; + + int nextId; + }; + +} +Q_GLOBAL_STATIC(ObjectReferenceHash, objectReferenceHash); + + +/*! + Returns a unique id for \a object. Calling this method multiple times + for the same object will return the same id. +*/ +int QDeclarativeDebugService::idForObject(QObject *object) +{ + if (!object) + return -1; + + ObjectReferenceHash *hash = objectReferenceHash(); + QHash::Iterator iter = + hash->objects.find(object); + + if (iter == hash->objects.end()) { + int id = hash->nextId++; + + hash->ids.insert(id, object); + iter = hash->objects.insert(object, ObjectReference()); + iter->object = object; + iter->id = id; + } else if (iter->object != object) { + int id = hash->nextId++; + + hash->ids.remove(iter->id); + + hash->ids.insert(id, object); + iter->object = object; + iter->id = id; + } + return iter->id; +} + +/*! + Returns the object for unique \a id. If the object has not previously been + assigned an id, through idForObject(), then 0 is returned. If the object + has been destroyed, 0 is returned. +*/ +QObject *QDeclarativeDebugService::objectForId(int id) +{ + ObjectReferenceHash *hash = objectReferenceHash(); + + QHash::Iterator iter = hash->ids.find(id); + if (iter == hash->ids.end()) + return 0; + + + QHash::Iterator objIter = + hash->objects.find(*iter); + Q_ASSERT(objIter != hash->objects.end()); + + if (objIter->object == 0) { + hash->ids.erase(iter); + hash->objects.erase(objIter); + return 0; + } else { + return *iter; + } +} + +bool QDeclarativeDebugService::isDebuggingEnabled() +{ + return QDeclarativeDebugServer::instance() != 0; +} + +bool QDeclarativeDebugService::hasDebuggingClient() +{ + return QDeclarativeDebugServer::instance() != 0 + && QDeclarativeDebugServer::instance()->hasDebuggingClient(); +} + +QString QDeclarativeDebugService::objectToString(QObject *obj) +{ + if(!obj) + return QLatin1String("NULL"); + + QString objectName = obj->objectName(); + if(objectName.isEmpty()) + objectName = QLatin1String(""); + + QString rv = QString::fromUtf8(obj->metaObject()->className()) + + QLatin1String(": ") + objectName; + + return rv; +} + +void QDeclarativeDebugService::sendMessage(const QByteArray &message) +{ + Q_D(QDeclarativeDebugService); + + if (status() != Enabled) + return; + + d->server->sendMessage(this, message); +} + +bool QDeclarativeDebugService::waitForMessage() +{ + Q_D(QDeclarativeDebugService); + + if (status() != Enabled) + return false; + + return d->server->waitForMessage(this); +} + +void QDeclarativeDebugService::statusChanged(Status) +{ +} + +void QDeclarativeDebugService::messageReceived(const QByteArray &) +{ +} + +QT_END_NAMESPACE diff --git a/src/declarative/debugger/qdeclarativedebugservice_p.h b/src/declarative/debugger/qdeclarativedebugservice_p.h new file mode 100644 index 00000000..2abef713 --- /dev/null +++ b/src/declarative/debugger/qdeclarativedebugservice_p.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEDEBUGSERVICE_H +#define QDECLARATIVEDEBUGSERVICE_H + +#include + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeDebugServicePrivate; +class Q_DECLARATIVE_EXPORT QDeclarativeDebugService : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeDebugService) + Q_DISABLE_COPY(QDeclarativeDebugService) + +public: + explicit QDeclarativeDebugService(const QString &, QObject *parent = 0); + ~QDeclarativeDebugService(); + + QString name() const; + + enum Status { NotConnected, Unavailable, Enabled }; + Status status() const; + + void sendMessage(const QByteArray &); + bool waitForMessage(); + + static int idForObject(QObject *); + static QObject *objectForId(int); + + static QString objectToString(QObject *obj); + + static bool isDebuggingEnabled(); + static bool hasDebuggingClient(); + +protected: + virtual void statusChanged(Status); + virtual void messageReceived(const QByteArray &); + +private: + friend class QDeclarativeDebugServer; + friend class QDeclarativeDebugServerPrivate; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEDEBUGSERVICE_H + diff --git a/src/declarative/debugger/qdeclarativedebugservice_p_p.h b/src/declarative/debugger/qdeclarativedebugservice_p_p.h new file mode 100644 index 00000000..1a169334 --- /dev/null +++ b/src/declarative/debugger/qdeclarativedebugservice_p_p.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEDEBUGSERVICE_P_H +#define QDECLARATIVEDEBUGSERVICE_P_H + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeDebugServer; + +class QDeclarativeDebugServicePrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeDebugService) +public: + QDeclarativeDebugServicePrivate(); + + QString name; + QDeclarativeDebugServer *server; + QDeclarativeDebugService::Status status; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEDEBUGSERVICE_P_H diff --git a/src/declarative/debugger/qdeclarativedebugtrace.cpp b/src/declarative/debugger/qdeclarativedebugtrace.cpp new file mode 100644 index 00000000..16e6dfb1 --- /dev/null +++ b/src/declarative/debugger/qdeclarativedebugtrace.cpp @@ -0,0 +1,225 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativedebugtrace_p.h" + +#include +#include +#include + +Q_GLOBAL_STATIC(QDeclarativeDebugTrace, traceInstance); + +// convert to a QByteArray that can be sent to the debug client +// use of QDataStream can skew results if m_deferredSend == false +// (see tst_qperformancetimer::trace() benchmark) +QByteArray QDeclarativeDebugData::toByteArray() const +{ + QByteArray data; + //### using QDataStream is relatively expensive + QDataStream ds(&data, QIODevice::WriteOnly); + ds << time << messageType << detailType; + if (messageType == (int)QDeclarativeDebugTrace::RangeData) + ds << detailData; + if (messageType == (int)QDeclarativeDebugTrace::RangeLocation) + ds << detailData << line; + return data; +} + +QDeclarativeDebugTrace::QDeclarativeDebugTrace() +: QDeclarativeDebugService(QLatin1String("CanvasFrameRate")), + m_enabled(false), m_deferredSend(true), m_messageReceived(false) +{ + m_timer.start(); + if (status() == Enabled) { + // wait for first message indicating whether to trace or not + while (!m_messageReceived) + waitForMessage(); + } +} + +void QDeclarativeDebugTrace::addEvent(EventType t) +{ + if (QDeclarativeDebugService::isDebuggingEnabled()) + traceInstance()->addEventImpl(t); +} + +void QDeclarativeDebugTrace::startRange(RangeType t) +{ + if (QDeclarativeDebugService::isDebuggingEnabled()) + traceInstance()->startRangeImpl(t); +} + +void QDeclarativeDebugTrace::rangeData(RangeType t, const QString &data) +{ + if (QDeclarativeDebugService::isDebuggingEnabled()) + traceInstance()->rangeDataImpl(t, data); +} + +void QDeclarativeDebugTrace::rangeData(RangeType t, const QUrl &data) +{ + if (QDeclarativeDebugService::isDebuggingEnabled()) + traceInstance()->rangeDataImpl(t, data); +} + +void QDeclarativeDebugTrace::rangeLocation(RangeType t, const QString &fileName, int line) +{ + if (QDeclarativeDebugService::isDebuggingEnabled()) + traceInstance()->rangeLocationImpl(t, fileName, line); +} + +void QDeclarativeDebugTrace::rangeLocation(RangeType t, const QUrl &fileName, int line) +{ + if (QDeclarativeDebugService::isDebuggingEnabled()) + traceInstance()->rangeLocationImpl(t, fileName, line); +} + +void QDeclarativeDebugTrace::endRange(RangeType t) +{ + if (QDeclarativeDebugService::isDebuggingEnabled()) + traceInstance()->endRangeImpl(t); +} + +void QDeclarativeDebugTrace::addEventImpl(EventType event) +{ + if (status() != Enabled || !m_enabled) + return; + + QDeclarativeDebugData ed = {m_timer.elapsed(), (int)Event, (int)event, QString(), -1}; + processMessage(ed); +} + +void QDeclarativeDebugTrace::startRangeImpl(RangeType range) +{ + if (status() != Enabled || !m_enabled) + return; + + QDeclarativeDebugData rd = {m_timer.elapsed(), (int)RangeStart, (int)range, QString(), -1}; + processMessage(rd); +} + +void QDeclarativeDebugTrace::rangeDataImpl(RangeType range, const QString &rData) +{ + if (status() != Enabled || !m_enabled) + return; + + QDeclarativeDebugData rd = {m_timer.elapsed(), (int)RangeData, (int)range, rData, -1}; + processMessage(rd); +} + +void QDeclarativeDebugTrace::rangeDataImpl(RangeType range, const QUrl &rData) +{ + if (status() != Enabled || !m_enabled) + return; + + QDeclarativeDebugData rd = {m_timer.elapsed(), (int)RangeData, (int)range, rData.toString(QUrl::FormattingOption(0x100)), -1}; + processMessage(rd); +} + +void QDeclarativeDebugTrace::rangeLocationImpl(RangeType range, const QString &fileName, int line) +{ + if (status() != Enabled || !m_enabled) + return; + + QDeclarativeDebugData rd = {m_timer.elapsed(), (int)RangeLocation, (int)range, fileName, line}; + processMessage(rd); +} + +void QDeclarativeDebugTrace::rangeLocationImpl(RangeType range, const QUrl &fileName, int line) +{ + if (status() != Enabled || !m_enabled) + return; + + QDeclarativeDebugData rd = {m_timer.elapsed(), (int)RangeLocation, (int)range, fileName.toString(QUrl::FormattingOption(0x100)), line}; + processMessage(rd); +} + +void QDeclarativeDebugTrace::endRangeImpl(RangeType range) +{ + if (status() != Enabled || !m_enabled) + return; + + QDeclarativeDebugData rd = {m_timer.elapsed(), (int)RangeEnd, (int)range, QString(), -1}; + processMessage(rd); +} + +/* + Either send the message directly, or queue up + a list of messages to send later (via sendMessages) +*/ +void QDeclarativeDebugTrace::processMessage(const QDeclarativeDebugData &message) +{ + if (m_deferredSend) + m_data.append(message); + else + sendMessage(message.toByteArray()); +} + +/* + Send the messages queued up by processMessage +*/ +void QDeclarativeDebugTrace::sendMessages() +{ + if (m_deferredSend) { + //### this is a suboptimal way to send batched messages + for (int i = 0; i < m_data.count(); ++i) + sendMessage(m_data.at(i).toByteArray()); + m_data.clear(); + + //indicate completion + QByteArray data; + QDataStream ds(&data, QIODevice::WriteOnly); + ds << (qint64)-1 << (int)Complete; + sendMessage(data); + } +} + +void QDeclarativeDebugTrace::messageReceived(const QByteArray &message) +{ + QByteArray rwData = message; + QDataStream stream(&rwData, QIODevice::ReadOnly); + + stream >> m_enabled; + + m_messageReceived = true; + + if (!m_enabled) + sendMessages(); +} diff --git a/src/declarative/debugger/qdeclarativedebugtrace_p.h b/src/declarative/debugger/qdeclarativedebugtrace_p.h new file mode 100644 index 00000000..3cd73b07 --- /dev/null +++ b/src/declarative/debugger/qdeclarativedebugtrace_p.h @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEDEBUGTRACE_P_H +#define QDECLARATIVEDEBUGTRACE_P_H + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +struct QDeclarativeDebugData +{ + qint64 time; + int messageType; + int detailType; + + //### + QString detailData; //used by RangeData and RangeLocation + int line; //used by RangeLocation + + QByteArray toByteArray() const; +}; + +class QUrl; +class Q_AUTOTEST_EXPORT QDeclarativeDebugTrace : public QDeclarativeDebugService +{ +public: + enum Message { + Event, + RangeStart, + RangeData, + RangeLocation, + RangeEnd, + Complete, + + MaximumMessage + }; + + enum EventType { + FramePaint, + Mouse, + Key, + + MaximumEventType + }; + + enum RangeType { + Painting, + Compiling, + Creating, + Binding, //running a binding + HandlingSignal, //running a signal handler + + MaximumRangeType + }; + + static void addEvent(EventType); + + static void startRange(RangeType); + static void rangeData(RangeType, const QString &); + static void rangeData(RangeType, const QUrl &); + static void rangeLocation(RangeType, const QString &, int); + static void rangeLocation(RangeType, const QUrl &, int); + static void endRange(RangeType); + + QDeclarativeDebugTrace(); +protected: + virtual void messageReceived(const QByteArray &); +private: + void addEventImpl(EventType); + void startRangeImpl(RangeType); + void rangeDataImpl(RangeType, const QString &); + void rangeDataImpl(RangeType, const QUrl &); + void rangeLocationImpl(RangeType, const QString &, int); + void rangeLocationImpl(RangeType, const QUrl &, int); + void endRangeImpl(RangeType); + void processMessage(const QDeclarativeDebugData &); + void sendMessages(); + QPerformanceTimer m_timer; + bool m_enabled; + bool m_deferredSend; + bool m_messageReceived; + QList m_data; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEDEBUGTRACE_P_H + diff --git a/src/declarative/debugger/qdeclarativeenginedebug.cpp b/src/declarative/debugger/qdeclarativeenginedebug.cpp new file mode 100644 index 00000000..74316574 --- /dev/null +++ b/src/declarative/debugger/qdeclarativeenginedebug.cpp @@ -0,0 +1,1068 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativeenginedebug_p.h" + +#include "private/qdeclarativedebugclient_p.h" + +#include + +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeEngineDebugClient : public QDeclarativeDebugClient +{ +public: + QDeclarativeEngineDebugClient(QDeclarativeDebugConnection *client, QDeclarativeEngineDebugPrivate *p); + +protected: + virtual void statusChanged(Status status); + virtual void messageReceived(const QByteArray &); + +private: + QDeclarativeEngineDebugPrivate *priv; + friend class QDeclarativeEngineDebugPrivate; +}; + +class QDeclarativeEngineDebugPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeEngineDebug) +public: + QDeclarativeEngineDebugPrivate(QDeclarativeDebugConnection *); + ~QDeclarativeEngineDebugPrivate(); + + void statusChanged(QDeclarativeEngineDebug::Status status); + void message(const QByteArray &); + + QDeclarativeEngineDebugClient *client; + int nextId; + int getId(); + + void decode(QDataStream &, QDeclarativeDebugContextReference &); + void decode(QDataStream &, QDeclarativeDebugObjectReference &, bool simple); + + static void remove(QDeclarativeEngineDebug *, QDeclarativeDebugEnginesQuery *); + static void remove(QDeclarativeEngineDebug *, QDeclarativeDebugRootContextQuery *); + static void remove(QDeclarativeEngineDebug *, QDeclarativeDebugObjectQuery *); + static void remove(QDeclarativeEngineDebug *, QDeclarativeDebugExpressionQuery *); + static void remove(QDeclarativeEngineDebug *, QDeclarativeDebugWatch *); + + QHash enginesQuery; + QHash rootContextQuery; + QHash objectQuery; + QHash expressionQuery; + + QHash watched; +}; + +QDeclarativeEngineDebugClient::QDeclarativeEngineDebugClient(QDeclarativeDebugConnection *client, + QDeclarativeEngineDebugPrivate *p) +: QDeclarativeDebugClient(QLatin1String("QDeclarativeEngine"), client), priv(p) +{ +} + +void QDeclarativeEngineDebugClient::statusChanged(Status status) +{ + if (priv) + priv->statusChanged(static_cast(status)); +} + +void QDeclarativeEngineDebugClient::messageReceived(const QByteArray &data) +{ + if (priv) + priv->message(data); +} + +QDeclarativeEngineDebugPrivate::QDeclarativeEngineDebugPrivate(QDeclarativeDebugConnection *c) +: client(new QDeclarativeEngineDebugClient(c, this)), nextId(0) +{ +} + +QDeclarativeEngineDebugPrivate::~QDeclarativeEngineDebugPrivate() +{ + if (client) + client->priv = 0; + delete client; + + QHash::iterator enginesIter = enginesQuery.begin(); + for (; enginesIter != enginesQuery.end(); ++enginesIter) { + enginesIter.value()->m_client = 0; + if (enginesIter.value()->state() == QDeclarativeDebugQuery::Waiting) + enginesIter.value()->setState(QDeclarativeDebugQuery::Error); + } + + QHash::iterator rootContextIter = rootContextQuery.begin(); + for (; rootContextIter != rootContextQuery.end(); ++rootContextIter) { + rootContextIter.value()->m_client = 0; + if (rootContextIter.value()->state() == QDeclarativeDebugQuery::Waiting) + rootContextIter.value()->setState(QDeclarativeDebugQuery::Error); + } + + QHash::iterator objectIter = objectQuery.begin(); + for (; objectIter != objectQuery.end(); ++objectIter) { + objectIter.value()->m_client = 0; + if (objectIter.value()->state() == QDeclarativeDebugQuery::Waiting) + objectIter.value()->setState(QDeclarativeDebugQuery::Error); + } + + QHash::iterator exprIter = expressionQuery.begin(); + for (; exprIter != expressionQuery.end(); ++exprIter) { + exprIter.value()->m_client = 0; + if (exprIter.value()->state() == QDeclarativeDebugQuery::Waiting) + exprIter.value()->setState(QDeclarativeDebugQuery::Error); + } + + QHash::iterator watchIter = watched.begin(); + for (; watchIter != watched.end(); ++watchIter) { + watchIter.value()->m_client = 0; + watchIter.value()->setState(QDeclarativeDebugWatch::Dead); + } +} + +int QDeclarativeEngineDebugPrivate::getId() +{ + return nextId++; +} + +void QDeclarativeEngineDebugPrivate::remove(QDeclarativeEngineDebug *c, QDeclarativeDebugEnginesQuery *q) +{ + if (c && q) { + QDeclarativeEngineDebugPrivate *p = (QDeclarativeEngineDebugPrivate *)QObjectPrivate::get(c); + p->enginesQuery.remove(q->m_queryId); + } +} + +void QDeclarativeEngineDebugPrivate::remove(QDeclarativeEngineDebug *c, + QDeclarativeDebugRootContextQuery *q) +{ + if (c && q) { + QDeclarativeEngineDebugPrivate *p = (QDeclarativeEngineDebugPrivate *)QObjectPrivate::get(c); + p->rootContextQuery.remove(q->m_queryId); + } +} + +void QDeclarativeEngineDebugPrivate::remove(QDeclarativeEngineDebug *c, QDeclarativeDebugObjectQuery *q) +{ + if (c && q) { + QDeclarativeEngineDebugPrivate *p = (QDeclarativeEngineDebugPrivate *)QObjectPrivate::get(c); + p->objectQuery.remove(q->m_queryId); + } +} + +void QDeclarativeEngineDebugPrivate::remove(QDeclarativeEngineDebug *c, QDeclarativeDebugExpressionQuery *q) +{ + if (c && q) { + QDeclarativeEngineDebugPrivate *p = (QDeclarativeEngineDebugPrivate *)QObjectPrivate::get(c); + p->expressionQuery.remove(q->m_queryId); + } +} + +void QDeclarativeEngineDebugPrivate::remove(QDeclarativeEngineDebug *c, QDeclarativeDebugWatch *w) +{ + if (c && w) { + QDeclarativeEngineDebugPrivate *p = (QDeclarativeEngineDebugPrivate *)QObjectPrivate::get(c); + p->watched.remove(w->m_queryId); + } +} + +void QDeclarativeEngineDebugPrivate::decode(QDataStream &ds, QDeclarativeDebugObjectReference &o, + bool simple) +{ + QDeclarativeEngineDebugService::QDeclarativeObjectData data; + ds >> data; + o.m_debugId = data.objectId; + o.m_class = data.objectType; + o.m_idString = data.idString; + o.m_name = data.objectName; + o.m_source.m_url = data.url; + o.m_source.m_lineNumber = data.lineNumber; + o.m_source.m_columnNumber = data.columnNumber; + o.m_contextDebugId = data.contextId; + + if (simple) + return; + + int childCount; + bool recur; + ds >> childCount >> recur; + + for (int ii = 0; ii < childCount; ++ii) { + o.m_children.append(QDeclarativeDebugObjectReference()); + decode(ds, o.m_children.last(), !recur); + } + + int propCount; + ds >> propCount; + + for (int ii = 0; ii < propCount; ++ii) { + QDeclarativeEngineDebugService::QDeclarativeObjectProperty data; + ds >> data; + QDeclarativeDebugPropertyReference prop; + prop.m_objectDebugId = o.m_debugId; + prop.m_name = data.name; + prop.m_binding = data.binding; + prop.m_hasNotifySignal = data.hasNotifySignal; + prop.m_valueTypeName = data.valueTypeName; + switch (data.type) { + case QDeclarativeEngineDebugService::QDeclarativeObjectProperty::Basic: + case QDeclarativeEngineDebugService::QDeclarativeObjectProperty::List: + case QDeclarativeEngineDebugService::QDeclarativeObjectProperty::SignalProperty: + { + prop.m_value = data.value; + break; + } + case QDeclarativeEngineDebugService::QDeclarativeObjectProperty::Object: + { + QDeclarativeDebugObjectReference obj; + obj.m_debugId = prop.m_value.toInt(); + prop.m_value = QVariant::fromValue(obj); + break; + } + case QDeclarativeEngineDebugService::QDeclarativeObjectProperty::Unknown: + break; + } + o.m_properties << prop; + } +} + +void QDeclarativeEngineDebugPrivate::decode(QDataStream &ds, QDeclarativeDebugContextReference &c) +{ + ds >> c.m_name >> c.m_debugId; + + int contextCount; + ds >> contextCount; + + for (int ii = 0; ii < contextCount; ++ii) { + c.m_contexts.append(QDeclarativeDebugContextReference()); + decode(ds, c.m_contexts.last()); + } + + int objectCount; + ds >> objectCount; + + for (int ii = 0; ii < objectCount; ++ii) { + QDeclarativeDebugObjectReference obj; + decode(ds, obj, true); + + obj.m_contextDebugId = c.m_debugId; + c.m_objects << obj; + } +} + +void QDeclarativeEngineDebugPrivate::statusChanged(QDeclarativeEngineDebug::Status status) +{ + emit q_func()->statusChanged(status); +} + +void QDeclarativeEngineDebugPrivate::message(const QByteArray &data) +{ + QDataStream ds(data); + + QByteArray type; + ds >> type; + + //qDebug() << "QDeclarativeEngineDebugPrivate::message()" << type; + + if (type == "LIST_ENGINES_R") { + int queryId; + ds >> queryId; + + QDeclarativeDebugEnginesQuery *query = enginesQuery.value(queryId); + if (!query) + return; + enginesQuery.remove(queryId); + + int count; + ds >> count; + + for (int ii = 0; ii < count; ++ii) { + QDeclarativeDebugEngineReference ref; + ds >> ref.m_name; + ds >> ref.m_debugId; + query->m_engines << ref; + } + + query->m_client = 0; + query->setState(QDeclarativeDebugQuery::Completed); + } else if (type == "LIST_OBJECTS_R") { + int queryId; + ds >> queryId; + + QDeclarativeDebugRootContextQuery *query = rootContextQuery.value(queryId); + if (!query) + return; + rootContextQuery.remove(queryId); + + if (!ds.atEnd()) + decode(ds, query->m_context); + + query->m_client = 0; + query->setState(QDeclarativeDebugQuery::Completed); + } else if (type == "FETCH_OBJECT_R") { + int queryId; + ds >> queryId; + + QDeclarativeDebugObjectQuery *query = objectQuery.value(queryId); + if (!query) + return; + objectQuery.remove(queryId); + + if (!ds.atEnd()) + decode(ds, query->m_object, false); + + query->m_client = 0; + query->setState(QDeclarativeDebugQuery::Completed); + } else if (type == "EVAL_EXPRESSION_R") { + int queryId; + QVariant result; + ds >> queryId >> result; + + QDeclarativeDebugExpressionQuery *query = expressionQuery.value(queryId); + if (!query) + return; + expressionQuery.remove(queryId); + + query->m_result = result; + query->m_client = 0; + query->setState(QDeclarativeDebugQuery::Completed); + } else if (type == "WATCH_PROPERTY_R") { + int queryId; + bool ok; + ds >> queryId >> ok; + + QDeclarativeDebugWatch *watch = watched.value(queryId); + if (!watch) + return; + + watch->setState(ok ? QDeclarativeDebugWatch::Active : QDeclarativeDebugWatch::Inactive); + } else if (type == "WATCH_OBJECT_R") { + int queryId; + bool ok; + ds >> queryId >> ok; + + QDeclarativeDebugWatch *watch = watched.value(queryId); + if (!watch) + return; + + watch->setState(ok ? QDeclarativeDebugWatch::Active : QDeclarativeDebugWatch::Inactive); + } else if (type == "WATCH_EXPR_OBJECT_R") { + int queryId; + bool ok; + ds >> queryId >> ok; + + QDeclarativeDebugWatch *watch = watched.value(queryId); + if (!watch) + return; + + watch->setState(ok ? QDeclarativeDebugWatch::Active : QDeclarativeDebugWatch::Inactive); + } else if (type == "UPDATE_WATCH") { + int queryId; + int debugId; + QByteArray name; + QVariant value; + ds >> queryId >> debugId >> name >> value; + + QDeclarativeDebugWatch *watch = watched.value(queryId, 0); + if (!watch) + return; + emit watch->valueChanged(name, value); + } else if (type == "OBJECT_CREATED") { + emit q_func()->newObjects(); + } +} + +QDeclarativeEngineDebug::QDeclarativeEngineDebug(QDeclarativeDebugConnection *client, QObject *parent) +: QObject(*(new QDeclarativeEngineDebugPrivate(client)), parent) +{ +} + +QDeclarativeEngineDebug::Status QDeclarativeEngineDebug::status() const +{ + Q_D(const QDeclarativeEngineDebug); + + return static_cast(d->client->status()); +} + +QDeclarativeDebugPropertyWatch *QDeclarativeEngineDebug::addWatch(const QDeclarativeDebugPropertyReference &property, QObject *parent) +{ + Q_D(QDeclarativeEngineDebug); + + QDeclarativeDebugPropertyWatch *watch = new QDeclarativeDebugPropertyWatch(parent); + if (d->client->status() == QDeclarativeDebugClient::Enabled) { + int queryId = d->getId(); + watch->m_queryId = queryId; + watch->m_client = this; + watch->m_objectDebugId = property.objectDebugId(); + watch->m_name = property.name(); + d->watched.insert(queryId, watch); + + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + ds << QByteArray("WATCH_PROPERTY") << queryId << property.objectDebugId() << property.name().toUtf8(); + d->client->sendMessage(message); + } else { + watch->m_state = QDeclarativeDebugWatch::Dead; + } + + return watch; +} + +QDeclarativeDebugWatch *QDeclarativeEngineDebug::addWatch(const QDeclarativeDebugContextReference &, const QString &, QObject *) +{ + qWarning("QDeclarativeEngineDebug::addWatch(): Not implemented"); + return 0; +} + +QDeclarativeDebugObjectExpressionWatch *QDeclarativeEngineDebug::addWatch(const QDeclarativeDebugObjectReference &object, const QString &expr, QObject *parent) +{ + Q_D(QDeclarativeEngineDebug); + QDeclarativeDebugObjectExpressionWatch *watch = new QDeclarativeDebugObjectExpressionWatch(parent); + if (d->client->status() == QDeclarativeDebugClient::Enabled) { + int queryId = d->getId(); + watch->m_queryId = queryId; + watch->m_client = this; + watch->m_objectDebugId = object.debugId(); + watch->m_expr = expr; + d->watched.insert(queryId, watch); + + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + ds << QByteArray("WATCH_EXPR_OBJECT") << queryId << object.debugId() << expr; + d->client->sendMessage(message); + } else { + watch->m_state = QDeclarativeDebugWatch::Dead; + } + return watch; +} + +QDeclarativeDebugWatch *QDeclarativeEngineDebug::addWatch(const QDeclarativeDebugObjectReference &object, QObject *parent) +{ + Q_D(QDeclarativeEngineDebug); + + QDeclarativeDebugWatch *watch = new QDeclarativeDebugWatch(parent); + if (d->client->status() == QDeclarativeDebugClient::Enabled) { + int queryId = d->getId(); + watch->m_queryId = queryId; + watch->m_client = this; + watch->m_objectDebugId = object.debugId(); + d->watched.insert(queryId, watch); + + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + ds << QByteArray("WATCH_OBJECT") << queryId << object.debugId(); + d->client->sendMessage(message); + } else { + watch->m_state = QDeclarativeDebugWatch::Dead; + } + + return watch; +} + +QDeclarativeDebugWatch *QDeclarativeEngineDebug::addWatch(const QDeclarativeDebugFileReference &, QObject *) +{ + qWarning("QDeclarativeEngineDebug::addWatch(): Not implemented"); + return 0; +} + +void QDeclarativeEngineDebug::removeWatch(QDeclarativeDebugWatch *watch) +{ + Q_D(QDeclarativeEngineDebug); + + if (!watch || !watch->m_client) + return; + + watch->m_client = 0; + watch->setState(QDeclarativeDebugWatch::Inactive); + + d->watched.remove(watch->queryId()); + + if (d->client && d->client->status() == QDeclarativeDebugClient::Enabled) { + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + ds << QByteArray("NO_WATCH") << watch->queryId(); + d->client->sendMessage(message); + } +} + +QDeclarativeDebugEnginesQuery *QDeclarativeEngineDebug::queryAvailableEngines(QObject *parent) +{ + Q_D(QDeclarativeEngineDebug); + + QDeclarativeDebugEnginesQuery *query = new QDeclarativeDebugEnginesQuery(parent); + if (d->client->status() == QDeclarativeDebugClient::Enabled) { + query->m_client = this; + int queryId = d->getId(); + query->m_queryId = queryId; + d->enginesQuery.insert(queryId, query); + + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + ds << QByteArray("LIST_ENGINES") << queryId; + d->client->sendMessage(message); + } else { + query->m_state = QDeclarativeDebugQuery::Error; + } + + return query; +} + +QDeclarativeDebugRootContextQuery *QDeclarativeEngineDebug::queryRootContexts(const QDeclarativeDebugEngineReference &engine, QObject *parent) +{ + Q_D(QDeclarativeEngineDebug); + + QDeclarativeDebugRootContextQuery *query = new QDeclarativeDebugRootContextQuery(parent); + if (d->client->status() == QDeclarativeDebugClient::Enabled && engine.debugId() != -1) { + query->m_client = this; + int queryId = d->getId(); + query->m_queryId = queryId; + d->rootContextQuery.insert(queryId, query); + + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + ds << QByteArray("LIST_OBJECTS") << queryId << engine.debugId(); + d->client->sendMessage(message); + } else { + query->m_state = QDeclarativeDebugQuery::Error; + } + + return query; +} + +QDeclarativeDebugObjectQuery *QDeclarativeEngineDebug::queryObject(const QDeclarativeDebugObjectReference &object, QObject *parent) +{ + Q_D(QDeclarativeEngineDebug); + + QDeclarativeDebugObjectQuery *query = new QDeclarativeDebugObjectQuery(parent); + if (d->client->status() == QDeclarativeDebugClient::Enabled && object.debugId() != -1) { + query->m_client = this; + int queryId = d->getId(); + query->m_queryId = queryId; + d->objectQuery.insert(queryId, query); + + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + ds << QByteArray("FETCH_OBJECT") << queryId << object.debugId() + << false << true; + d->client->sendMessage(message); + } else { + query->m_state = QDeclarativeDebugQuery::Error; + } + + return query; +} + +QDeclarativeDebugObjectQuery *QDeclarativeEngineDebug::queryObjectRecursive(const QDeclarativeDebugObjectReference &object, QObject *parent) +{ + Q_D(QDeclarativeEngineDebug); + + QDeclarativeDebugObjectQuery *query = new QDeclarativeDebugObjectQuery(parent); + if (d->client->status() == QDeclarativeDebugClient::Enabled && object.debugId() != -1) { + query->m_client = this; + int queryId = d->getId(); + query->m_queryId = queryId; + d->objectQuery.insert(queryId, query); + + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + ds << QByteArray("FETCH_OBJECT") << queryId << object.debugId() + << true << true; + d->client->sendMessage(message); + } else { + query->m_state = QDeclarativeDebugQuery::Error; + } + + return query; +} + +QDeclarativeDebugExpressionQuery *QDeclarativeEngineDebug::queryExpressionResult(int objectDebugId, const QString &expr, QObject *parent) +{ + Q_D(QDeclarativeEngineDebug); + + QDeclarativeDebugExpressionQuery *query = new QDeclarativeDebugExpressionQuery(parent); + if (d->client->status() == QDeclarativeDebugClient::Enabled && objectDebugId != -1) { + query->m_client = this; + query->m_expr = expr; + int queryId = d->getId(); + query->m_queryId = queryId; + d->expressionQuery.insert(queryId, query); + + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + ds << QByteArray("EVAL_EXPRESSION") << queryId << objectDebugId << expr; + d->client->sendMessage(message); + } else { + query->m_state = QDeclarativeDebugQuery::Error; + } + + return query; +} + +bool QDeclarativeEngineDebug::setBindingForObject(int objectDebugId, const QString &propertyName, + const QVariant &bindingExpression, + bool isLiteralValue, + QString source, int line) +{ + Q_D(QDeclarativeEngineDebug); + + if (d->client->status() == QDeclarativeDebugClient::Enabled && objectDebugId != -1) { + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + ds << QByteArray("SET_BINDING") << objectDebugId << propertyName << bindingExpression << isLiteralValue << source << line; + d->client->sendMessage(message); + return true; + } else { + return false; + } +} + +bool QDeclarativeEngineDebug::resetBindingForObject(int objectDebugId, const QString &propertyName) +{ + Q_D(QDeclarativeEngineDebug); + + if (d->client->status() == QDeclarativeDebugClient::Enabled && objectDebugId != -1) { + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + ds << QByteArray("RESET_BINDING") << objectDebugId << propertyName; + d->client->sendMessage(message); + return true; + } else { + return false; + } +} + +bool QDeclarativeEngineDebug::setMethodBody(int objectDebugId, const QString &methodName, + const QString &methodBody) +{ + Q_D(QDeclarativeEngineDebug); + + if (d->client->status() == QDeclarativeDebugClient::Enabled && objectDebugId != -1) { + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + ds << QByteArray("SET_METHOD_BODY") << objectDebugId << methodName << methodBody; + d->client->sendMessage(message); + return true; + } else { + return false; + } +} + +QDeclarativeDebugWatch::QDeclarativeDebugWatch(QObject *parent) +: QObject(parent), m_state(Waiting), m_queryId(-1), m_client(0), m_objectDebugId(-1) +{ +} + +QDeclarativeDebugWatch::~QDeclarativeDebugWatch() +{ + if (m_client && m_queryId != -1) + QDeclarativeEngineDebugPrivate::remove(m_client, this); +} + +int QDeclarativeDebugWatch::queryId() const +{ + return m_queryId; +} + +int QDeclarativeDebugWatch::objectDebugId() const +{ + return m_objectDebugId; +} + +QDeclarativeDebugWatch::State QDeclarativeDebugWatch::state() const +{ + return m_state; +} + +void QDeclarativeDebugWatch::setState(State s) +{ + if (m_state == s) + return; + m_state = s; + emit stateChanged(m_state); +} + +QDeclarativeDebugPropertyWatch::QDeclarativeDebugPropertyWatch(QObject *parent) + : QDeclarativeDebugWatch(parent) +{ +} + +QString QDeclarativeDebugPropertyWatch::name() const +{ + return m_name; +} + + +QDeclarativeDebugObjectExpressionWatch::QDeclarativeDebugObjectExpressionWatch(QObject *parent) + : QDeclarativeDebugWatch(parent) +{ +} + +QString QDeclarativeDebugObjectExpressionWatch::expression() const +{ + return m_expr; +} + + +QDeclarativeDebugQuery::QDeclarativeDebugQuery(QObject *parent) +: QObject(parent), m_state(Waiting) +{ +} + +QDeclarativeDebugQuery::State QDeclarativeDebugQuery::state() const +{ + return m_state; +} + +bool QDeclarativeDebugQuery::isWaiting() const +{ + return m_state == Waiting; +} + +void QDeclarativeDebugQuery::setState(State s) +{ + if (m_state == s) + return; + m_state = s; + emit stateChanged(m_state); +} + +QDeclarativeDebugEnginesQuery::QDeclarativeDebugEnginesQuery(QObject *parent) +: QDeclarativeDebugQuery(parent), m_client(0), m_queryId(-1) +{ +} + +QDeclarativeDebugEnginesQuery::~QDeclarativeDebugEnginesQuery() +{ + if (m_client && m_queryId != -1) + QDeclarativeEngineDebugPrivate::remove(m_client, this); +} + +QList QDeclarativeDebugEnginesQuery::engines() const +{ + return m_engines; +} + +QDeclarativeDebugRootContextQuery::QDeclarativeDebugRootContextQuery(QObject *parent) +: QDeclarativeDebugQuery(parent), m_client(0), m_queryId(-1) +{ +} + +QDeclarativeDebugRootContextQuery::~QDeclarativeDebugRootContextQuery() +{ + if (m_client && m_queryId != -1) + QDeclarativeEngineDebugPrivate::remove(m_client, this); +} + +QDeclarativeDebugContextReference QDeclarativeDebugRootContextQuery::rootContext() const +{ + return m_context; +} + +QDeclarativeDebugObjectQuery::QDeclarativeDebugObjectQuery(QObject *parent) +: QDeclarativeDebugQuery(parent), m_client(0), m_queryId(-1) +{ +} + +QDeclarativeDebugObjectQuery::~QDeclarativeDebugObjectQuery() +{ + if (m_client && m_queryId != -1) + QDeclarativeEngineDebugPrivate::remove(m_client, this); +} + +QDeclarativeDebugObjectReference QDeclarativeDebugObjectQuery::object() const +{ + return m_object; +} + +QDeclarativeDebugExpressionQuery::QDeclarativeDebugExpressionQuery(QObject *parent) +: QDeclarativeDebugQuery(parent), m_client(0), m_queryId(-1) +{ +} + +QDeclarativeDebugExpressionQuery::~QDeclarativeDebugExpressionQuery() +{ + if (m_client && m_queryId != -1) + QDeclarativeEngineDebugPrivate::remove(m_client, this); +} + +QVariant QDeclarativeDebugExpressionQuery::expression() const +{ + return m_expr; +} + +QVariant QDeclarativeDebugExpressionQuery::result() const +{ + return m_result; +} + +QDeclarativeDebugEngineReference::QDeclarativeDebugEngineReference() +: m_debugId(-1) +{ +} + +QDeclarativeDebugEngineReference::QDeclarativeDebugEngineReference(int debugId) +: m_debugId(debugId) +{ +} + +QDeclarativeDebugEngineReference::QDeclarativeDebugEngineReference(const QDeclarativeDebugEngineReference &o) +: m_debugId(o.m_debugId), m_name(o.m_name) +{ +} + +QDeclarativeDebugEngineReference & +QDeclarativeDebugEngineReference::operator=(const QDeclarativeDebugEngineReference &o) +{ + m_debugId = o.m_debugId; m_name = o.m_name; + return *this; +} + +int QDeclarativeDebugEngineReference::debugId() const +{ + return m_debugId; +} + +QString QDeclarativeDebugEngineReference::name() const +{ + return m_name; +} + +QDeclarativeDebugObjectReference::QDeclarativeDebugObjectReference() +: m_debugId(-1), m_contextDebugId(-1) +{ +} + +QDeclarativeDebugObjectReference::QDeclarativeDebugObjectReference(int debugId) +: m_debugId(debugId), m_contextDebugId(-1) +{ +} + +QDeclarativeDebugObjectReference::QDeclarativeDebugObjectReference(const QDeclarativeDebugObjectReference &o) +: m_debugId(o.m_debugId), m_class(o.m_class), m_idString(o.m_idString), + m_name(o.m_name), m_source(o.m_source), m_contextDebugId(o.m_contextDebugId), + m_properties(o.m_properties), m_children(o.m_children) +{ +} + +QDeclarativeDebugObjectReference & +QDeclarativeDebugObjectReference::operator=(const QDeclarativeDebugObjectReference &o) +{ + m_debugId = o.m_debugId; m_class = o.m_class; m_idString = o.m_idString; + m_name = o.m_name; m_source = o.m_source; m_contextDebugId = o.m_contextDebugId; + m_properties = o.m_properties; m_children = o.m_children; + return *this; +} + +int QDeclarativeDebugObjectReference::debugId() const +{ + return m_debugId; +} + +QString QDeclarativeDebugObjectReference::className() const +{ + return m_class; +} + +QString QDeclarativeDebugObjectReference::idString() const +{ + return m_idString; +} + +QString QDeclarativeDebugObjectReference::name() const +{ + return m_name; +} + +QDeclarativeDebugFileReference QDeclarativeDebugObjectReference::source() const +{ + return m_source; +} + +int QDeclarativeDebugObjectReference::contextDebugId() const +{ + return m_contextDebugId; +} + +QList QDeclarativeDebugObjectReference::properties() const +{ + return m_properties; +} + +QList QDeclarativeDebugObjectReference::children() const +{ + return m_children; +} + +QDeclarativeDebugContextReference::QDeclarativeDebugContextReference() +: m_debugId(-1) +{ +} + +QDeclarativeDebugContextReference::QDeclarativeDebugContextReference(const QDeclarativeDebugContextReference &o) +: m_debugId(o.m_debugId), m_name(o.m_name), m_objects(o.m_objects), m_contexts(o.m_contexts) +{ +} + +QDeclarativeDebugContextReference &QDeclarativeDebugContextReference::operator=(const QDeclarativeDebugContextReference &o) +{ + m_debugId = o.m_debugId; m_name = o.m_name; m_objects = o.m_objects; + m_contexts = o.m_contexts; + return *this; +} + +int QDeclarativeDebugContextReference::debugId() const +{ + return m_debugId; +} + +QString QDeclarativeDebugContextReference::name() const +{ + return m_name; +} + +QList QDeclarativeDebugContextReference::objects() const +{ + return m_objects; +} + +QList QDeclarativeDebugContextReference::contexts() const +{ + return m_contexts; +} + +QDeclarativeDebugFileReference::QDeclarativeDebugFileReference() +: m_lineNumber(-1), m_columnNumber(-1) +{ +} + +QDeclarativeDebugFileReference::QDeclarativeDebugFileReference(const QDeclarativeDebugFileReference &o) +: m_url(o.m_url), m_lineNumber(o.m_lineNumber), m_columnNumber(o.m_columnNumber) +{ +} + +QDeclarativeDebugFileReference &QDeclarativeDebugFileReference::operator=(const QDeclarativeDebugFileReference &o) +{ + m_url = o.m_url; m_lineNumber = o.m_lineNumber; m_columnNumber = o.m_columnNumber; + return *this; +} + +QUrl QDeclarativeDebugFileReference::url() const +{ + return m_url; +} + +void QDeclarativeDebugFileReference::setUrl(const QUrl &u) +{ + m_url = u; +} + +int QDeclarativeDebugFileReference::lineNumber() const +{ + return m_lineNumber; +} + +void QDeclarativeDebugFileReference::setLineNumber(int l) +{ + m_lineNumber = l; +} + +int QDeclarativeDebugFileReference::columnNumber() const +{ + return m_columnNumber; +} + +void QDeclarativeDebugFileReference::setColumnNumber(int c) +{ + m_columnNumber = c; +} + +QDeclarativeDebugPropertyReference::QDeclarativeDebugPropertyReference() +: m_objectDebugId(-1), m_hasNotifySignal(false) +{ +} + +QDeclarativeDebugPropertyReference::QDeclarativeDebugPropertyReference(const QDeclarativeDebugPropertyReference &o) +: m_objectDebugId(o.m_objectDebugId), m_name(o.m_name), m_value(o.m_value), + m_valueTypeName(o.m_valueTypeName), m_binding(o.m_binding), + m_hasNotifySignal(o.m_hasNotifySignal) +{ +} + +QDeclarativeDebugPropertyReference &QDeclarativeDebugPropertyReference::operator=(const QDeclarativeDebugPropertyReference &o) +{ + m_objectDebugId = o.m_objectDebugId; m_name = o.m_name; m_value = o.m_value; + m_valueTypeName = o.m_valueTypeName; m_binding = o.m_binding; + m_hasNotifySignal = o.m_hasNotifySignal; + return *this; +} + +int QDeclarativeDebugPropertyReference::objectDebugId() const +{ + return m_objectDebugId; +} + +QString QDeclarativeDebugPropertyReference::name() const +{ + return m_name; +} + +QString QDeclarativeDebugPropertyReference::valueTypeName() const +{ + return m_valueTypeName; +} + +QVariant QDeclarativeDebugPropertyReference::value() const +{ + return m_value; +} + +QString QDeclarativeDebugPropertyReference::binding() const +{ + return m_binding; +} + +bool QDeclarativeDebugPropertyReference::hasNotifySignal() const +{ + return m_hasNotifySignal; +} + +QT_END_NAMESPACE + diff --git a/src/declarative/debugger/qdeclarativeenginedebug_p.h b/src/declarative/debugger/qdeclarativeenginedebug_p.h new file mode 100644 index 00000000..6c00efca --- /dev/null +++ b/src/declarative/debugger/qdeclarativeenginedebug_p.h @@ -0,0 +1,387 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QDECLARATIVEENGINEDEBUG_H +#define QDECLARATIVEENGINEDEBUG_H + +#include +#include +#include + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeDebugConnection; +class QDeclarativeDebugWatch; +class QDeclarativeDebugPropertyWatch; +class QDeclarativeDebugObjectExpressionWatch; +class QDeclarativeDebugEnginesQuery; +class QDeclarativeDebugRootContextQuery; +class QDeclarativeDebugObjectQuery; +class QDeclarativeDebugExpressionQuery; +class QDeclarativeDebugPropertyReference; +class QDeclarativeDebugContextReference; +class QDeclarativeDebugObjectReference; +class QDeclarativeDebugFileReference; +class QDeclarativeDebugEngineReference; +class QDeclarativeEngineDebugPrivate; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeEngineDebug : public QObject +{ +Q_OBJECT +public: + enum Status { NotConnected, Unavailable, Enabled }; + + explicit QDeclarativeEngineDebug(QDeclarativeDebugConnection *, QObject * = 0); + + Status status() const; + + QDeclarativeDebugPropertyWatch *addWatch(const QDeclarativeDebugPropertyReference &, + QObject *parent = 0); + QDeclarativeDebugWatch *addWatch(const QDeclarativeDebugContextReference &, const QString &, + QObject *parent = 0); + QDeclarativeDebugObjectExpressionWatch *addWatch(const QDeclarativeDebugObjectReference &, const QString &, + QObject *parent = 0); + QDeclarativeDebugWatch *addWatch(const QDeclarativeDebugObjectReference &, + QObject *parent = 0); + QDeclarativeDebugWatch *addWatch(const QDeclarativeDebugFileReference &, + QObject *parent = 0); + + void removeWatch(QDeclarativeDebugWatch *watch); + + QDeclarativeDebugEnginesQuery *queryAvailableEngines(QObject *parent = 0); + QDeclarativeDebugRootContextQuery *queryRootContexts(const QDeclarativeDebugEngineReference &, + QObject *parent = 0); + QDeclarativeDebugObjectQuery *queryObject(const QDeclarativeDebugObjectReference &, + QObject *parent = 0); + QDeclarativeDebugObjectQuery *queryObjectRecursive(const QDeclarativeDebugObjectReference &, + QObject *parent = 0); + QDeclarativeDebugExpressionQuery *queryExpressionResult(int objectDebugId, + const QString &expr, + QObject *parent = 0); + bool setBindingForObject(int objectDebugId, const QString &propertyName, + const QVariant &bindingExpression, bool isLiteralValue, + QString source = QString(), int line = -1); + bool resetBindingForObject(int objectDebugId, const QString &propertyName); + bool setMethodBody(int objectDebugId, const QString &methodName, const QString &methodBody); + +Q_SIGNALS: + void newObjects(); + void statusChanged(Status status); + +private: + Q_DECLARE_PRIVATE(QDeclarativeEngineDebug) +}; + +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeDebugWatch : public QObject +{ +Q_OBJECT +public: + enum State { Waiting, Active, Inactive, Dead }; + + QDeclarativeDebugWatch(QObject *); + ~QDeclarativeDebugWatch(); + + int queryId() const; + int objectDebugId() const; + State state() const; + +Q_SIGNALS: + void stateChanged(QDeclarativeDebugWatch::State); + //void objectChanged(int, const QDeclarativeDebugObjectReference &); + //void valueChanged(int, const QVariant &); + + // Server sends value as string if it is a user-type variant + void valueChanged(const QByteArray &name, const QVariant &value); + +private: + friend class QDeclarativeEngineDebug; + friend class QDeclarativeEngineDebugPrivate; + void setState(State); + State m_state; + int m_queryId; + QDeclarativeEngineDebug *m_client; + int m_objectDebugId; +}; + +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeDebugPropertyWatch : public QDeclarativeDebugWatch +{ + Q_OBJECT +public: + QDeclarativeDebugPropertyWatch(QObject *parent); + + QString name() const; + +private: + friend class QDeclarativeEngineDebug; + QString m_name; +}; + +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeDebugObjectExpressionWatch : public QDeclarativeDebugWatch +{ + Q_OBJECT +public: + QDeclarativeDebugObjectExpressionWatch(QObject *parent); + + QString expression() const; + +private: + friend class QDeclarativeEngineDebug; + QString m_expr; + int m_debugId; +}; + + +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeDebugQuery : public QObject +{ +Q_OBJECT +public: + enum State { Waiting, Error, Completed }; + + State state() const; + bool isWaiting() const; + +// bool waitUntilCompleted(); + +Q_SIGNALS: + void stateChanged(QDeclarativeDebugQuery::State); + +protected: + QDeclarativeDebugQuery(QObject *); + +private: + friend class QDeclarativeEngineDebug; + friend class QDeclarativeEngineDebugPrivate; + void setState(State); + State m_state; +}; + +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeDebugFileReference +{ +public: + QDeclarativeDebugFileReference(); + QDeclarativeDebugFileReference(const QDeclarativeDebugFileReference &); + QDeclarativeDebugFileReference &operator=(const QDeclarativeDebugFileReference &); + + QUrl url() const; + void setUrl(const QUrl &); + int lineNumber() const; + void setLineNumber(int); + int columnNumber() const; + void setColumnNumber(int); + +private: + friend class QDeclarativeEngineDebugPrivate; + QUrl m_url; + int m_lineNumber; + int m_columnNumber; +}; + +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeDebugEngineReference +{ +public: + QDeclarativeDebugEngineReference(); + QDeclarativeDebugEngineReference(int); + QDeclarativeDebugEngineReference(const QDeclarativeDebugEngineReference &); + QDeclarativeDebugEngineReference &operator=(const QDeclarativeDebugEngineReference &); + + int debugId() const; + QString name() const; + +private: + friend class QDeclarativeEngineDebugPrivate; + int m_debugId; + QString m_name; +}; + +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeDebugObjectReference +{ +public: + QDeclarativeDebugObjectReference(); + QDeclarativeDebugObjectReference(int); + QDeclarativeDebugObjectReference(const QDeclarativeDebugObjectReference &); + QDeclarativeDebugObjectReference &operator=(const QDeclarativeDebugObjectReference &); + + int debugId() const; + QString className() const; + QString idString() const; + QString name() const; + + QDeclarativeDebugFileReference source() const; + int contextDebugId() const; + + QList properties() const; + QList children() const; + +private: + friend class QDeclarativeEngineDebugPrivate; + int m_debugId; + QString m_class; + QString m_idString; + QString m_name; + QDeclarativeDebugFileReference m_source; + int m_contextDebugId; + QList m_properties; + QList m_children; +}; + +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeDebugContextReference +{ +public: + QDeclarativeDebugContextReference(); + QDeclarativeDebugContextReference(const QDeclarativeDebugContextReference &); + QDeclarativeDebugContextReference &operator=(const QDeclarativeDebugContextReference &); + + int debugId() const; + QString name() const; + + QList objects() const; + QList contexts() const; + +private: + friend class QDeclarativeEngineDebugPrivate; + int m_debugId; + QString m_name; + QList m_objects; + QList m_contexts; +}; + +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeDebugPropertyReference +{ +public: + QDeclarativeDebugPropertyReference(); + QDeclarativeDebugPropertyReference(const QDeclarativeDebugPropertyReference &); + QDeclarativeDebugPropertyReference &operator=(const QDeclarativeDebugPropertyReference &); + + int objectDebugId() const; + QString name() const; + QVariant value() const; + QString valueTypeName() const; + QString binding() const; + bool hasNotifySignal() const; + +private: + friend class QDeclarativeEngineDebugPrivate; + int m_objectDebugId; + QString m_name; + QVariant m_value; + QString m_valueTypeName; + QString m_binding; + bool m_hasNotifySignal; +}; + + +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeDebugEnginesQuery : public QDeclarativeDebugQuery +{ +Q_OBJECT +public: + virtual ~QDeclarativeDebugEnginesQuery(); + QList engines() const; +private: + friend class QDeclarativeEngineDebug; + friend class QDeclarativeEngineDebugPrivate; + QDeclarativeDebugEnginesQuery(QObject *); + QDeclarativeEngineDebug *m_client; + int m_queryId; + QList m_engines; +}; + +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeDebugRootContextQuery : public QDeclarativeDebugQuery +{ +Q_OBJECT +public: + virtual ~QDeclarativeDebugRootContextQuery(); + QDeclarativeDebugContextReference rootContext() const; +private: + friend class QDeclarativeEngineDebug; + friend class QDeclarativeEngineDebugPrivate; + QDeclarativeDebugRootContextQuery(QObject *); + QDeclarativeEngineDebug *m_client; + int m_queryId; + QDeclarativeDebugContextReference m_context; +}; + +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeDebugObjectQuery : public QDeclarativeDebugQuery +{ +Q_OBJECT +public: + virtual ~QDeclarativeDebugObjectQuery(); + QDeclarativeDebugObjectReference object() const; +private: + friend class QDeclarativeEngineDebug; + friend class QDeclarativeEngineDebugPrivate; + QDeclarativeDebugObjectQuery(QObject *); + QDeclarativeEngineDebug *m_client; + int m_queryId; + QDeclarativeDebugObjectReference m_object; + +}; + +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeDebugExpressionQuery : public QDeclarativeDebugQuery +{ +Q_OBJECT +public: + virtual ~QDeclarativeDebugExpressionQuery(); + QVariant expression() const; + QVariant result() const; +private: + friend class QDeclarativeEngineDebug; + friend class QDeclarativeEngineDebugPrivate; + QDeclarativeDebugExpressionQuery(QObject *); + QDeclarativeEngineDebug *m_client; + int m_queryId; + QVariant m_expr; + QVariant m_result; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QDeclarativeDebugEngineReference) +Q_DECLARE_METATYPE(QDeclarativeDebugObjectReference) +Q_DECLARE_METATYPE(QDeclarativeDebugContextReference) +Q_DECLARE_METATYPE(QDeclarativeDebugPropertyReference) + +QT_END_HEADER + +#endif // QDECLARATIVEENGINEDEBUG_H diff --git a/src/declarative/debugger/qdeclarativeenginedebugservice.cpp b/src/declarative/debugger/qdeclarativeenginedebugservice.cpp new file mode 100644 index 00000000..421e3994 --- /dev/null +++ b/src/declarative/debugger/qdeclarativeenginedebugservice.cpp @@ -0,0 +1,747 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativeenginedebugservice_p.h" + +#include "private/qdeclarativeboundsignal_p.h" +#include "qdeclarativeengine.h" +#include "private/qdeclarativemetatype_p.h" +#include "qdeclarativeproperty.h" +#include "private/qdeclarativeproperty_p.h" +#include "private/qdeclarativebinding_p.h" +#include "private/qdeclarativecontext_p.h" +#include "private/qdeclarativewatcher_p.h" +#include "private/qdeclarativevaluetype_p.h" +#include "private/qdeclarativevmemetaobject_p.h" +#include "private/qdeclarativeexpression_p.h" +#include "private/qdeclarativepropertychanges_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +Q_GLOBAL_STATIC(QDeclarativeEngineDebugService, qmlEngineDebugService); + +QDeclarativeEngineDebugService *QDeclarativeEngineDebugService::instance() +{ + return qmlEngineDebugService(); +} + +QDeclarativeEngineDebugService::QDeclarativeEngineDebugService(QObject *parent) +: QDeclarativeDebugService(QLatin1String("QDeclarativeEngine"), parent), + m_watch(new QDeclarativeWatcher(this)) +{ + QObject::connect(m_watch, SIGNAL(propertyChanged(int,int,QMetaProperty,QVariant)), + this, SLOT(propertyChanged(int,int,QMetaProperty,QVariant))); +} + +QDataStream &operator<<(QDataStream &ds, + const QDeclarativeEngineDebugService::QDeclarativeObjectData &data) +{ + ds << data.url << data.lineNumber << data.columnNumber << data.idString + << data.objectName << data.objectType << data.objectId << data.contextId; + return ds; +} + +QDataStream &operator>>(QDataStream &ds, + QDeclarativeEngineDebugService::QDeclarativeObjectData &data) +{ + ds >> data.url >> data.lineNumber >> data.columnNumber >> data.idString + >> data.objectName >> data.objectType >> data.objectId >> data.contextId; + return ds; +} + +QDataStream &operator<<(QDataStream &ds, + const QDeclarativeEngineDebugService::QDeclarativeObjectProperty &data) +{ + ds << (int)data.type << data.name << data.value << data.valueTypeName + << data.binding << data.hasNotifySignal; + return ds; +} + +QDataStream &operator>>(QDataStream &ds, + QDeclarativeEngineDebugService::QDeclarativeObjectProperty &data) +{ + int type; + ds >> type >> data.name >> data.value >> data.valueTypeName + >> data.binding >> data.hasNotifySignal; + data.type = (QDeclarativeEngineDebugService::QDeclarativeObjectProperty::Type)type; + return ds; +} + +static inline bool isSignalPropertyName(const QString &signalName) +{ + // see QmlCompiler::isSignalPropertyName + return signalName.length() >= 3 && signalName.startsWith(QLatin1String("on")) && + signalName.at(2).isLetter() && signalName.at(2).isUpper(); +} + +static bool hasValidSignal(QObject *object, const QString &propertyName) +{ + if (!isSignalPropertyName(propertyName)) + return false; + + QString signalName = propertyName.mid(2); + signalName[0] = signalName.at(0).toLower(); + + int sigIdx = QDeclarativePropertyPrivate::findSignalByName(object->metaObject(), signalName.toLatin1()).methodIndex(); + + if (sigIdx == -1) + return false; + + return true; +} + +QDeclarativeEngineDebugService::QDeclarativeObjectProperty +QDeclarativeEngineDebugService::propertyData(QObject *obj, int propIdx) +{ + QDeclarativeObjectProperty rv; + + QMetaProperty prop = obj->metaObject()->property(propIdx); + + rv.type = QDeclarativeObjectProperty::Unknown; + rv.valueTypeName = QString::fromUtf8(prop.typeName()); + rv.name = QString::fromUtf8(prop.name()); + rv.hasNotifySignal = prop.hasNotifySignal(); + QDeclarativeAbstractBinding *binding = + QDeclarativePropertyPrivate::binding(QDeclarativeProperty(obj, rv.name)); + if (binding) + rv.binding = binding->expression(); + + if (QDeclarativeValueTypeFactory::isValueType(prop.userType())) { + rv.type = QDeclarativeObjectProperty::Basic; + } else if (QDeclarativeMetaType::isQObject(prop.userType())) { + rv.type = QDeclarativeObjectProperty::Object; + } else if (QDeclarativeMetaType::isList(prop.userType())) { + rv.type = QDeclarativeObjectProperty::List; + } + + QVariant value; + if (rv.type != QDeclarativeObjectProperty::Unknown && prop.userType() != 0) { + value = prop.read(obj); + } + rv.value = valueContents(value); + + return rv; +} + +QVariant QDeclarativeEngineDebugService::valueContents(const QVariant &value) const +{ + int userType = value.userType(); + + if (value.type() == QVariant::List) { + QVariantList contents; + QVariantList list = value.toList(); + int count = list.size(); + for (int i = 0; i < count; i++) + contents << valueContents(list.at(i)); + return contents; + } + + if (QDeclarativeValueTypeFactory::isValueType(userType)) + return value; + + if (QDeclarativeMetaType::isQObject(userType)) { + QObject *o = QDeclarativeMetaType::toQObject(value); + if (o) { + QString name = o->objectName(); + if (name.isEmpty()) + name = QLatin1String(""); + return name; + } + } + + return QLatin1String(""); +} + +void QDeclarativeEngineDebugService::buildObjectDump(QDataStream &message, + QObject *object, bool recur, bool dumpProperties) +{ + message << objectData(object); + + QObjectList children = object->children(); + + int childrenCount = children.count(); + for (int ii = 0; ii < children.count(); ++ii) { + if (qobject_cast(children[ii]) || QDeclarativeBoundSignal::cast(children[ii])) + --childrenCount; + } + + message << childrenCount << recur; + + QList fakeProperties; + + for (int ii = 0; ii < children.count(); ++ii) { + QObject *child = children.at(ii); + if (qobject_cast(child)) + continue; + QDeclarativeBoundSignal *signal = QDeclarativeBoundSignal::cast(child); + if (signal) { + if (!dumpProperties) + continue; + QDeclarativeObjectProperty prop; + prop.type = QDeclarativeObjectProperty::SignalProperty; + prop.hasNotifySignal = false; + QDeclarativeExpression *expr = signal->expression(); + if (expr) { + prop.value = expr->expression(); + QObject *scope = expr->scopeObject(); + if (scope) { + QString sig = QLatin1String(scope->metaObject()->method(signal->index()).signature()); + int lparen = sig.indexOf(QLatin1Char('(')); + if (lparen >= 0) { + QString methodName = sig.mid(0, lparen); + prop.name = QLatin1String("on") + methodName[0].toUpper() + + methodName.mid(1); + } + } + } + fakeProperties << prop; + } else { + if (recur) + buildObjectDump(message, child, recur, dumpProperties); + else + message << objectData(child); + } + } + + if (!dumpProperties) { + message << 0; + return; + } + + QList propertyIndexes; + for (int ii = 0; ii < object->metaObject()->propertyCount(); ++ii) { + if (object->metaObject()->property(ii).isScriptable()) + propertyIndexes << ii; + } + + message << propertyIndexes.size() + fakeProperties.count(); + + for (int ii = 0; ii < propertyIndexes.size(); ++ii) + message << propertyData(object, propertyIndexes.at(ii)); + + for (int ii = 0; ii < fakeProperties.count(); ++ii) + message << fakeProperties[ii]; +} + +void QDeclarativeEngineDebugService::prepareDeferredObjects(QObject *obj) +{ + qmlExecuteDeferred(obj); + + QObjectList children = obj->children(); + for (int ii = 0; ii < children.count(); ++ii) { + QObject *child = children.at(ii); + prepareDeferredObjects(child); + } + +} + +void QDeclarativeEngineDebugService::buildObjectList(QDataStream &message, QDeclarativeContext *ctxt) +{ + QDeclarativeContextData *p = QDeclarativeContextData::get(ctxt); + + QString ctxtName = ctxt->objectName(); + int ctxtId = QDeclarativeDebugService::idForObject(ctxt); + + message << ctxtName << ctxtId; + + int count = 0; + + QDeclarativeContextData *child = p->childContexts; + while (child) { + ++count; + child = child->nextChild; + } + + message << count; + + child = p->childContexts; + while (child) { + buildObjectList(message, child->asQDeclarativeContext()); + child = child->nextChild; + } + + // Clean deleted objects + QDeclarativeContextPrivate *ctxtPriv = QDeclarativeContextPrivate::get(ctxt); + for (int ii = 0; ii < ctxtPriv->instances.count(); ++ii) { + if (!ctxtPriv->instances.at(ii)) { + ctxtPriv->instances.removeAt(ii); + --ii; + } + } + + message << ctxtPriv->instances.count(); + for (int ii = 0; ii < ctxtPriv->instances.count(); ++ii) { + message << objectData(ctxtPriv->instances.at(ii)); + } +} + +void QDeclarativeEngineDebugService::buildStatesList(QDeclarativeContext *ctxt, bool cleanList=false) +{ + if (cleanList) + m_allStates.clear(); + + QDeclarativeContextPrivate *ctxtPriv = QDeclarativeContextPrivate::get(ctxt); + for (int ii = 0; ii < ctxtPriv->instances.count(); ++ii) { + buildStatesList(ctxtPriv->instances.at(ii)); + } + + QDeclarativeContextData *child = QDeclarativeContextData::get(ctxt)->childContexts; + while (child) { + buildStatesList(child->asQDeclarativeContext()); + child = child->nextChild; + } +} + +void QDeclarativeEngineDebugService::buildStatesList(QObject *obj) +{ + if (QDeclarativeState *state = qobject_cast(obj)) { + m_allStates.append(state); + } + + QObjectList children = obj->children(); + for (int ii = 0; ii < children.count(); ++ii) { + buildStatesList(children.at(ii)); + } +} + +QDeclarativeEngineDebugService::QDeclarativeObjectData +QDeclarativeEngineDebugService::objectData(QObject *object) +{ + QDeclarativeData *ddata = QDeclarativeData::get(object); + QDeclarativeObjectData rv; + if (ddata && ddata->outerContext) { + rv.url = ddata->outerContext->url; + rv.lineNumber = ddata->lineNumber; + rv.columnNumber = ddata->columnNumber; + } else { + rv.lineNumber = -1; + rv.columnNumber = -1; + } + + QDeclarativeContext *context = qmlContext(object); + if (context) { + QDeclarativeContextData *cdata = QDeclarativeContextData::get(context); + if (cdata) + rv.idString = cdata->findObjectId(object); + } + + rv.objectName = object->objectName(); + rv.objectId = QDeclarativeDebugService::idForObject(object); + rv.contextId = QDeclarativeDebugService::idForObject(qmlContext(object)); + + QDeclarativeType *type = QDeclarativeMetaType::qmlType(object->metaObject()); + if (type) { + QString typeName = QLatin1String(type->qmlTypeName()); + int lastSlash = typeName.lastIndexOf(QLatin1Char('/')); + rv.objectType = lastSlash < 0 ? typeName : typeName.mid(lastSlash+1); + } else { + rv.objectType = QString::fromUtf8(object->metaObject()->className()); + int marker = rv.objectType.indexOf(QLatin1String("_QMLTYPE_")); + if (marker != -1) + rv.objectType = rv.objectType.left(marker); + } + + return rv; +} + +void QDeclarativeEngineDebugService::messageReceived(const QByteArray &message) +{ + QDataStream ds(message); + + QByteArray type; + ds >> type; + + if (type == "LIST_ENGINES") { + int queryId; + ds >> queryId; + + QByteArray reply; + QDataStream rs(&reply, QIODevice::WriteOnly); + rs << QByteArray("LIST_ENGINES_R"); + rs << queryId << m_engines.count(); + + for (int ii = 0; ii < m_engines.count(); ++ii) { + QDeclarativeEngine *engine = m_engines.at(ii); + + QString engineName = engine->objectName(); + int engineId = QDeclarativeDebugService::idForObject(engine); + + rs << engineName << engineId; + } + + sendMessage(reply); + } else if (type == "LIST_OBJECTS") { + int queryId; + int engineId = -1; + ds >> queryId >> engineId; + + QDeclarativeEngine *engine = + qobject_cast(QDeclarativeDebugService::objectForId(engineId)); + + QByteArray reply; + QDataStream rs(&reply, QIODevice::WriteOnly); + rs << QByteArray("LIST_OBJECTS_R") << queryId; + + if (engine) { + buildObjectList(rs, engine->rootContext()); + buildStatesList(engine->rootContext(), true); + } + + sendMessage(reply); + } else if (type == "FETCH_OBJECT") { + int queryId; + int objectId; + bool recurse; + bool dumpProperties = true; + + ds >> queryId >> objectId >> recurse >> dumpProperties; + + QObject *object = QDeclarativeDebugService::objectForId(objectId); + + QByteArray reply; + QDataStream rs(&reply, QIODevice::WriteOnly); + rs << QByteArray("FETCH_OBJECT_R") << queryId; + + if (object) { + if (recurse) + prepareDeferredObjects(object); + buildObjectDump(rs, object, recurse, dumpProperties); + } + + sendMessage(reply); + } else if (type == "WATCH_OBJECT") { + int queryId; + int objectId; + + ds >> queryId >> objectId; + bool ok = m_watch->addWatch(queryId, objectId); + + QByteArray reply; + QDataStream rs(&reply, QIODevice::WriteOnly); + rs << QByteArray("WATCH_OBJECT_R") << queryId << ok; + + sendMessage(reply); + } else if (type == "WATCH_PROPERTY") { + int queryId; + int objectId; + QByteArray property; + + ds >> queryId >> objectId >> property; + bool ok = m_watch->addWatch(queryId, objectId, property); + + QByteArray reply; + QDataStream rs(&reply, QIODevice::WriteOnly); + rs << QByteArray("WATCH_PROPERTY_R") << queryId << ok; + + sendMessage(reply); + } else if (type == "WATCH_EXPR_OBJECT") { + int queryId; + int debugId; + QString expr; + + ds >> queryId >> debugId >> expr; + bool ok = m_watch->addWatch(queryId, debugId, expr); + + QByteArray reply; + QDataStream rs(&reply, QIODevice::WriteOnly); + rs << QByteArray("WATCH_EXPR_OBJECT_R") << queryId << ok; + sendMessage(reply); + } else if (type == "NO_WATCH") { + int queryId; + + ds >> queryId; + m_watch->removeWatch(queryId); + } else if (type == "EVAL_EXPRESSION") { + int queryId; + int objectId; + QString expr; + + ds >> queryId >> objectId >> expr; + + QObject *object = QDeclarativeDebugService::objectForId(objectId); + QDeclarativeContext *context = qmlContext(object); + QVariant result; + if (object && context) { + QDeclarativeExpression exprObj(context, object, expr); + bool undefined = false; + QVariant value = exprObj.evaluate(&undefined); + if (undefined) + result = QLatin1String(""); + else + result = valueContents(value); + } else { + result = QLatin1String(""); + } + + QByteArray reply; + QDataStream rs(&reply, QIODevice::WriteOnly); + rs << QByteArray("EVAL_EXPRESSION_R") << queryId << result; + + sendMessage(reply); + } else if (type == "SET_BINDING") { + int objectId; + QString propertyName; + QVariant expr; + bool isLiteralValue; + QString filename; + int line; + ds >> objectId >> propertyName >> expr >> isLiteralValue; + if (!ds.atEnd()) { // backward compatibility from 2.1, 2.2 + ds >> filename >> line; + } + setBinding(objectId, propertyName, expr, isLiteralValue, filename, line); + } else if (type == "RESET_BINDING") { + int objectId; + QString propertyName; + ds >> objectId >> propertyName; + resetBinding(objectId, propertyName); + } else if (type == "SET_METHOD_BODY") { + int objectId; + QString methodName; + QString methodBody; + ds >> objectId >> methodName >> methodBody; + setMethodBody(objectId, methodName, methodBody); + } +} + +void QDeclarativeEngineDebugService::setBinding(int objectId, + const QString &propertyName, + const QVariant &expression, + bool isLiteralValue, + QString filename, + int line) +{ + QObject *object = objectForId(objectId); + QDeclarativeContext *context = qmlContext(object); + + if (object && context) { + QDeclarativeProperty property(object, propertyName, context); + if (property.isValid()) { + + bool inBaseState = true; + + foreach(QWeakPointer statePointer, m_allStates) { + if (QDeclarativeState *state = statePointer.data()) { + // here we assume that the revert list on itself defines the base state + if (state->isStateActive() && state->containsPropertyInRevertList(object, propertyName)) { + inBaseState = false; + + QDeclarativeBinding *newBinding = 0; + if (!isLiteralValue) { + newBinding = new QDeclarativeBinding(expression.toString(), object, context); + newBinding->setTarget(property); + newBinding->setNotifyOnValueChanged(true); + newBinding->setSourceLocation(filename, line); + } + + state->changeBindingInRevertList(object, propertyName, newBinding); + + if (isLiteralValue) + state->changeValueInRevertList(object, propertyName, expression); + } + } + } + + if (inBaseState) { + if (isLiteralValue) { + property.write(expression); + } else if (hasValidSignal(object, propertyName)) { + QDeclarativeExpression *declarativeExpression = new QDeclarativeExpression(context, object, expression.toString()); + QDeclarativePropertyPrivate::setSignalExpression(property, declarativeExpression); + declarativeExpression->setSourceLocation(filename, line); + } else if (property.isProperty()) { + QDeclarativeBinding *binding = new QDeclarativeBinding(expression.toString(), object, context); + binding->setTarget(property); + binding->setSourceLocation(filename, line); + binding->setNotifyOnValueChanged(true); + QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::setBinding(property, binding); + if (oldBinding) + oldBinding->destroy(); + binding->update(); + } else { + qWarning() << "QDeclarativeEngineDebugService::setBinding: unable to set property" << propertyName << "on object" << object; + } + } + + } else { + // not a valid property + if (QDeclarativePropertyChanges *propertyChanges = qobject_cast(object)) { + if (isLiteralValue) { + propertyChanges->changeValue(propertyName, expression); + } else { + propertyChanges->changeExpression(propertyName, expression.toString()); + } + } else { + qWarning() << "QDeclarativeEngineDebugService::setBinding: unable to set property" << propertyName << "on object" << object; + } + } + } +} + +void QDeclarativeEngineDebugService::resetBinding(int objectId, const QString &propertyName) +{ + QObject *object = objectForId(objectId); + QDeclarativeContext *context = qmlContext(object); + + if (object && context) { + if (object->property(propertyName.toLatin1()).isValid()) { + QDeclarativeProperty property(object, propertyName); + QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(property); + if (oldBinding) { + QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::setBinding(property, 0); + if (oldBinding) + oldBinding->destroy(); + } + if (property.isResettable()) { + // Note: this will reset the property in any case, without regard to states + // Right now almost no QDeclarativeItem has reset methods for its properties (with the + // notable exception of QDeclarativeAnchors), so this is not a big issue + // later on, setBinding does take states into account + property.reset(); + } else { + // overwrite with default value + if (QDeclarativeType *objType = QDeclarativeMetaType::qmlType(object->metaObject())) { + if (QObject *emptyObject = objType->create()) { + if (emptyObject->property(propertyName.toLatin1()).isValid()) { + QVariant defaultValue = QDeclarativeProperty(emptyObject, propertyName).read(); + if (defaultValue.isValid()) { + setBinding(objectId, propertyName, defaultValue, true); + } + } + delete emptyObject; + } + } + } + } else if (hasValidSignal(object, propertyName)) { + QDeclarativeProperty property(object, propertyName, context); + QDeclarativePropertyPrivate::setSignalExpression(property, 0); + } else { + if (QDeclarativePropertyChanges *propertyChanges = qobject_cast(object)) { + propertyChanges->removeProperty(propertyName); + } + } + } +} + +void QDeclarativeEngineDebugService::setMethodBody(int objectId, const QString &method, const QString &body) +{ + QObject *object = objectForId(objectId); + QDeclarativeContext *context = qmlContext(object); + if (!object || !context || !context->engine()) + return; + QDeclarativeContextData *contextData = QDeclarativeContextData::get(context); + if (!contextData) + return; + + QDeclarativePropertyCache::Data dummy; + QDeclarativePropertyCache::Data *prop = + QDeclarativePropertyCache::property(context->engine(), object, method, dummy); + + if (!prop || !(prop->flags & QDeclarativePropertyCache::Data::IsVMEFunction)) + return; + + QMetaMethod metaMethod = object->metaObject()->method(prop->coreIndex); + QList paramNames = metaMethod.parameterNames(); + + QString paramStr; + for (int ii = 0; ii < paramNames.count(); ++ii) { + if (ii != 0) paramStr.append(QLatin1String(",")); + paramStr.append(QString::fromUtf8(paramNames.at(ii))); + } + + QString jsfunction = QLatin1String("(function ") + method + QLatin1String("(") + paramStr + + QLatin1String(") {"); + jsfunction += body; + jsfunction += QLatin1String("\n})"); + + QDeclarativeVMEMetaObject *vmeMetaObject = + static_cast(QObjectPrivate::get(object)->metaObject); + Q_ASSERT(vmeMetaObject); // the fact we found the property above should guarentee this + + int lineNumber = vmeMetaObject->vmeMethodLineNumber(prop->coreIndex); + vmeMetaObject->setVmeMethod(prop->coreIndex, QDeclarativeExpressionPrivate::evalInObjectScope(contextData, object, jsfunction, contextData->url.toString(), lineNumber, 0)); +} + +void QDeclarativeEngineDebugService::propertyChanged(int id, int objectId, const QMetaProperty &property, const QVariant &value) +{ + QByteArray reply; + QDataStream rs(&reply, QIODevice::WriteOnly); + + rs << QByteArray("UPDATE_WATCH") << id << objectId << QByteArray(property.name()) << valueContents(value); + + sendMessage(reply); +} + +void QDeclarativeEngineDebugService::addEngine(QDeclarativeEngine *engine) +{ + Q_ASSERT(engine); + Q_ASSERT(!m_engines.contains(engine)); + + m_engines.append(engine); +} + +void QDeclarativeEngineDebugService::remEngine(QDeclarativeEngine *engine) +{ + Q_ASSERT(engine); + Q_ASSERT(m_engines.contains(engine)); + + m_engines.removeAll(engine); +} + +void QDeclarativeEngineDebugService::objectCreated(QDeclarativeEngine *engine, QObject *object) +{ + Q_ASSERT(engine); + Q_ASSERT(m_engines.contains(engine)); + + int engineId = QDeclarativeDebugService::idForObject(engine); + int objectId = QDeclarativeDebugService::idForObject(object); + + QByteArray reply; + QDataStream rs(&reply, QIODevice::WriteOnly); + + rs << QByteArray("OBJECT_CREATED") << engineId << objectId; + sendMessage(reply); +} + +QT_END_NAMESPACE diff --git a/src/declarative/debugger/qdeclarativeenginedebugservice_p.h b/src/declarative/debugger/qdeclarativeenginedebugservice_p.h new file mode 100644 index 00000000..864fa13a --- /dev/null +++ b/src/declarative/debugger/qdeclarativeenginedebugservice_p.h @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEENGINEDEBUGSERVICE_P_H +#define QDECLARATIVEENGINEDEBUGSERVICE_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 + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeEngine; +class QDeclarativeContext; +class QDeclarativeWatcher; +class QDataStream; +class QDeclarativeState; + +class QDeclarativeEngineDebugService : public QDeclarativeDebugService +{ + Q_OBJECT +public: + QDeclarativeEngineDebugService(QObject * = 0); + + struct QDeclarativeObjectData { + QUrl url; + int lineNumber; + int columnNumber; + QString idString; + QString objectName; + QString objectType; + int objectId; + int contextId; + }; + + struct QDeclarativeObjectProperty { + enum Type { Unknown, Basic, Object, List, SignalProperty }; + Type type; + QString name; + QVariant value; + QString valueTypeName; + QString binding; + bool hasNotifySignal; + }; + + void addEngine(QDeclarativeEngine *); + void remEngine(QDeclarativeEngine *); + void objectCreated(QDeclarativeEngine *, QObject *); + + static QDeclarativeEngineDebugService *instance(); + +protected: + virtual void messageReceived(const QByteArray &); + +private Q_SLOTS: + void propertyChanged(int id, int objectId, const QMetaProperty &property, const QVariant &value); + +private: + void prepareDeferredObjects(QObject *); + void buildObjectList(QDataStream &, QDeclarativeContext *); + void buildObjectDump(QDataStream &, QObject *, bool, bool); + void buildStatesList(QDeclarativeContext *, bool); + void buildStatesList(QObject *obj); + QDeclarativeObjectData objectData(QObject *); + QDeclarativeObjectProperty propertyData(QObject *, int); + QVariant valueContents(const QVariant &defaultValue) const; + void setBinding(int objectId, const QString &propertyName, const QVariant &expression, bool isLiteralValue, QString filename = QString(), int line = -1); + void resetBinding(int objectId, const QString &propertyName); + void setMethodBody(int objectId, const QString &method, const QString &body); + + QList m_engines; + QDeclarativeWatcher *m_watch; + QList > m_allStates; +}; +Q_DECLARATIVE_PRIVATE_EXPORT QDataStream &operator<<(QDataStream &, const QDeclarativeEngineDebugService::QDeclarativeObjectData &); +Q_DECLARATIVE_PRIVATE_EXPORT QDataStream &operator>>(QDataStream &, QDeclarativeEngineDebugService::QDeclarativeObjectData &); +Q_DECLARATIVE_PRIVATE_EXPORT QDataStream &operator<<(QDataStream &, const QDeclarativeEngineDebugService::QDeclarativeObjectProperty &); +Q_DECLARATIVE_PRIVATE_EXPORT QDataStream &operator>>(QDataStream &, QDeclarativeEngineDebugService::QDeclarativeObjectProperty &); + +QT_END_NAMESPACE + +#endif // QDECLARATIVEENGINEDEBUGSERVICE_P_H + diff --git a/src/declarative/debugger/qdeclarativeinspectorinterface_p.h b/src/declarative/debugger/qdeclarativeinspectorinterface_p.h new file mode 100644 index 00000000..bd7cd954 --- /dev/null +++ b/src/declarative/debugger/qdeclarativeinspectorinterface_p.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEOBSERVERINTERFACE_H +#define QDECLARATIVEOBSERVERINTERFACE_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class Q_DECLARATIVE_EXPORT QDeclarativeInspectorInterface +{ +public: + QDeclarativeInspectorInterface() {} + virtual ~QDeclarativeInspectorInterface() {} + + virtual void activate() = 0; + virtual void deactivate() = 0; +}; + +Q_DECLARE_INTERFACE(QDeclarativeInspectorInterface, "com.trolltech.Qt.QDeclarativeInspectorInterface/1.0") + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEOBSERVERINTERFACE_H diff --git a/src/declarative/debugger/qdeclarativeinspectorservice.cpp b/src/declarative/debugger/qdeclarativeinspectorservice.cpp new file mode 100644 index 00000000..b651b259 --- /dev/null +++ b/src/declarative/debugger/qdeclarativeinspectorservice.cpp @@ -0,0 +1,147 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativeinspectorservice_p.h" +#include "private/qdeclarativeinspectorinterface_p.h" + +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +Q_GLOBAL_STATIC(QDeclarativeInspectorService, serviceInstance) + +QDeclarativeInspectorService::QDeclarativeInspectorService() + : QDeclarativeDebugService(QLatin1String("QDeclarativeObserverMode")) + , m_inspectorPlugin(0) +{ +} + +QDeclarativeInspectorService *QDeclarativeInspectorService::instance() +{ + return serviceInstance(); +} + +void QDeclarativeInspectorService::addView(QDeclarativeView *view) +{ + m_views.append(view); + updateStatus(); +} + +void QDeclarativeInspectorService::removeView(QDeclarativeView *view) +{ + m_views.removeAll(view); + updateStatus(); +} + +void QDeclarativeInspectorService::sendMessage(const QByteArray &message) +{ + if (status() != Enabled) + return; + + QDeclarativeDebugService::sendMessage(message); +} + +void QDeclarativeInspectorService::statusChanged(Status status) +{ + updateStatus(); +} + +void QDeclarativeInspectorService::updateStatus() +{ + if (m_views.isEmpty()) { + if (m_inspectorPlugin) + m_inspectorPlugin->deactivate(); + return; + } + + if (status() == Enabled) { + if (!m_inspectorPlugin) + m_inspectorPlugin = loadInspectorPlugin(); + + if (!m_inspectorPlugin) { + qWarning() << "Error while loading inspector plugin"; + return; + } + + m_inspectorPlugin->activate(); + } else { + if (m_inspectorPlugin) + m_inspectorPlugin->deactivate(); + } +} + +void QDeclarativeInspectorService::messageReceived(const QByteArray &message) +{ + emit gotMessage(message); +} + +QDeclarativeInspectorInterface *QDeclarativeInspectorService::loadInspectorPlugin() +{ + QStringList pluginCandidates; + const QStringList paths = QCoreApplication::libraryPaths(); + foreach (const QString &libPath, paths) { + const QDir dir(libPath + QLatin1String("/qmltooling")); + if (dir.exists()) + foreach (const QString &pluginPath, dir.entryList(QDir::Files)) + pluginCandidates << dir.absoluteFilePath(pluginPath); + } + + foreach (const QString &pluginPath, pluginCandidates) { + QPluginLoader loader(pluginPath); + if (!loader.load()) + continue; + + QDeclarativeInspectorInterface *inspector = + qobject_cast(loader.instance()); + + if (inspector) + return inspector; + loader.unload(); + } + return 0; +} + +QT_END_NAMESPACE diff --git a/src/declarative/debugger/qdeclarativeinspectorservice_p.h b/src/declarative/debugger/qdeclarativeinspectorservice_p.h new file mode 100644 index 00000000..623e850a --- /dev/null +++ b/src/declarative/debugger/qdeclarativeinspectorservice_p.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEOBSERVERSERVICE_H +#define QDECLARATIVEOBSERVERSERVICE_H + +#include "private/qdeclarativedebugservice_p.h" +#include + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeView; +class QDeclarativeInspectorInterface; + +class Q_DECLARATIVE_EXPORT QDeclarativeInspectorService : public QDeclarativeDebugService +{ + Q_OBJECT + +public: + QDeclarativeInspectorService(); + static QDeclarativeInspectorService *instance(); + + void addView(QDeclarativeView *); + void removeView(QDeclarativeView *); + QList views() const { return m_views; } + + void sendMessage(const QByteArray &message); + +Q_SIGNALS: + void gotMessage(const QByteArray &message); + +protected: + virtual void statusChanged(Status status); + virtual void messageReceived(const QByteArray &); + +private: + void updateStatus(); + + static QDeclarativeInspectorInterface *loadInspectorPlugin(); + + QList m_views; + QDeclarativeInspectorInterface *m_inspectorPlugin; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEOBSERVERSERVICE_H diff --git a/src/declarative/debugger/qjsdebuggeragent.cpp b/src/declarative/debugger/qjsdebuggeragent.cpp new file mode 100644 index 00000000..9935ab11 --- /dev/null +++ b/src/declarative/debugger/qjsdebuggeragent.cpp @@ -0,0 +1,614 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qjsdebuggeragent_p.h" +#include "private/qdeclarativedebughelper_p.h" +#include "private/qjsdebugservice_p.h" + +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QJSDebuggerAgentPrivate +{ +public: + QJSDebuggerAgentPrivate(QJSDebuggerAgent *q) + : q(q), state(NoState), isInitialized(false), coverageEnabled(false) + {} + + void continueExec(); + void recordKnownObjects(const QList &); + QList getLocals(QScriptContext *); + void positionChange(qint64 scriptId, int lineNumber, int columnNumber); + QScriptEngine *engine() { return q->engine(); } + void stopped(); + +public: + QJSDebuggerAgent *q; + JSDebuggerState state; + int stepDepth; + int stepCount; + + QEventLoop loop; + QHash filenames; + JSAgentBreakpoints breakpoints; + // breakpoints by filename (without path) + QHash fileNameToBreakpoints; + QStringList watchExpressions; + QSet knownObjectIds; + bool isInitialized; + bool coverageEnabled; +}; + +namespace { + +class SetupExecEnv +{ +public: + SetupExecEnv(QJSDebuggerAgentPrivate *a) + : agent(a), + previousState(a->state), + hadException(a->engine()->hasUncaughtException()) + { + agent->state = StoppedState; + } + + ~SetupExecEnv() + { + if (!hadException && agent->engine()->hasUncaughtException()) + agent->engine()->clearExceptions(); + agent->state = previousState; + } + +private: + QJSDebuggerAgentPrivate *agent; + JSDebuggerState previousState; + bool hadException; +}; + +} // anonymous namespace + +static JSAgentWatchData fromScriptValue(const QString &expression, + const QScriptValue &value) +{ + static const QString arrayStr = QCoreApplication::translate + ("Debugger::JSAgentWatchData", "[Array of length %1]"); + static const QString undefinedStr = QCoreApplication::translate + ("Debugger::JSAgentWatchData", ""); + + JSAgentWatchData data; + data.exp = expression.toUtf8(); + data.name = data.exp; + data.hasChildren = false; + data.value = value.toString().toUtf8(); + data.objectId = value.objectId(); + if (value.isArray()) { + data.type = "Array"; + data.value = arrayStr.arg(value.property(QLatin1String("length")).toString()).toUtf8(); + data.hasChildren = true; + } else if (value.isBool()) { + data.type = "Bool"; + // data.value = value.toBool() ? "true" : "false"; + } else if (value.isDate()) { + data.type = "Date"; + data.value = value.toDateTime().toString().toUtf8(); + } else if (value.isError()) { + data.type = "Error"; + } else if (value.isFunction()) { + data.type = "Function"; + } else if (value.isUndefined()) { + data.type = undefinedStr.toUtf8(); + } else if (value.isNumber()) { + data.type = "Number"; + } else if (value.isRegExp()) { + data.type = "RegExp"; + } else if (value.isString()) { + data.type = "String"; + } else if (value.isVariant()) { + data.type = "Variant"; + } else if (value.isQObject()) { + const QObject *obj = value.toQObject(); + data.type = "Object"; + data.value += '['; + data.value += obj->metaObject()->className(); + data.value += ']'; + data.hasChildren = true; + } else if (value.isObject()) { + data.type = "Object"; + data.hasChildren = true; + data.value = "[Object]"; + } else if (value.isNull()) { + data.type = ""; + } else { + data.type = ""; + } + return data; +} + +static QList expandObject(const QScriptValue &object) +{ + QList result; + QScriptValueIterator it(object); + while (it.hasNext()) { + it.next(); + if (it.flags() & QScriptValue::SkipInEnumeration) + continue; + if (/*object.isQObject() &&*/ it.value().isFunction()) { + // Cosmetics: skip all functions and slot, there are too many of them, + // and it is not useful information in the debugger. + continue; + } + JSAgentWatchData data = fromScriptValue(it.name(), it.value()); + result.append(data); + } + if (result.isEmpty()) { + JSAgentWatchData data; + data.name = ""; + data.hasChildren = false; + data.value = " "; + data.objectId = 0; + result.append(data); + } + return result; +} + +static QString fileName(const QString &fileUrl) +{ + int lastDelimiterPos = fileUrl.lastIndexOf(QLatin1Char('/')); + return fileUrl.mid(lastDelimiterPos, fileUrl.size() - lastDelimiterPos); +} + +void QJSDebuggerAgentPrivate::recordKnownObjects(const QList& list) +{ + foreach (const JSAgentWatchData &data, list) + knownObjectIds << data.objectId; +} + +QList QJSDebuggerAgentPrivate::getLocals(QScriptContext *ctx) +{ + QList locals; + if (ctx) { + QScriptValue activationObject = ctx->activationObject(); + QScriptValue thisObject = ctx->thisObject(); + locals = expandObject(activationObject); + if (thisObject.isObject() + && thisObject.objectId() != engine()->globalObject().objectId() + && QScriptValueIterator(thisObject).hasNext()) + locals.prepend(fromScriptValue(QLatin1String("this"), thisObject)); + recordKnownObjects(locals); + knownObjectIds << activationObject.objectId(); + } + return locals; +} + +/*! + Constructs a new agent for the given \a engine. The agent will + report debugging-related events (e.g. step completion) to the given + \a backend. +*/ +QJSDebuggerAgent::QJSDebuggerAgent(QScriptEngine *engine, QObject *parent) + : QObject(parent) + , QScriptEngineAgent(engine) + , d(new QJSDebuggerAgentPrivate(this)) +{ + QJSDebuggerAgent::engine()->setAgent(this); +} + +QJSDebuggerAgent::QJSDebuggerAgent(QDeclarativeEngine *engine, QObject *parent) + : QObject(parent) + , QScriptEngineAgent(QDeclarativeDebugHelper::getScriptEngine(engine)) + , d(new QJSDebuggerAgentPrivate(this)) +{ + QJSDebuggerAgent::engine()->setAgent(this); +} + +/*! + Destroys this QJSDebuggerAgent. +*/ +QJSDebuggerAgent::~QJSDebuggerAgent() +{ + engine()->setAgent(0); + delete d; +} + +/*! + Indicates whether the agent got the list of breakpoints. + */ +bool QJSDebuggerAgent::isInitialized() const +{ + return d->isInitialized; +} + +void QJSDebuggerAgent::setCoverageEnabled(bool enabled) +{ + d->isInitialized = true; + d->coverageEnabled = enabled; +} + +void QJSDebuggerAgent::setBreakpoints(const JSAgentBreakpoints &breakpoints) +{ + d->breakpoints = breakpoints; + + d->fileNameToBreakpoints.clear(); + foreach (const JSAgentBreakpointData &bp, breakpoints) + d->fileNameToBreakpoints.insertMulti(fileName(QString::fromUtf8(bp.fileUrl)), bp); + + d->isInitialized = true; +} + +void QJSDebuggerAgent::setWatchExpressions(const QStringList &watchExpressions) +{ + d->watchExpressions = watchExpressions; +} + +void QJSDebuggerAgent::stepOver() +{ + d->stepDepth = 0; + d->state = SteppingOverState; + d->continueExec(); +} + +void QJSDebuggerAgent::stepInto() +{ + d->stepDepth = 0; + d->state = SteppingIntoState; + d->continueExec(); +} + +void QJSDebuggerAgent::stepOut() +{ + d->stepDepth = 0; + d->state = SteppingOutState; + d->continueExec(); +} + +void QJSDebuggerAgent::continueExecution() +{ + d->state = NoState; + d->continueExec(); +} + +JSAgentWatchData QJSDebuggerAgent::executeExpression(const QString &expr) +{ + SetupExecEnv execEnv(d); + + JSAgentWatchData data = fromScriptValue(expr, engine()->evaluate(expr)); + d->knownObjectIds << data.objectId; + return data; +} + +QList QJSDebuggerAgent::expandObjectById(quint64 objectId) +{ + SetupExecEnv execEnv(d); + + QScriptValue v; + if (d->knownObjectIds.contains(objectId)) + v = engine()->objectById(objectId); + + QList result = expandObject(v); + d->recordKnownObjects(result); + return result; +} + +QList QJSDebuggerAgent::locals() +{ + SetupExecEnv execEnv(d); + return d->getLocals(engine()->currentContext()); +} + +QList QJSDebuggerAgent::localsAtFrame(int frameId) +{ + SetupExecEnv execEnv(d); + + int deep = 0; + QScriptContext *ctx = engine()->currentContext(); + while (ctx && deep < frameId) { + ctx = ctx->parentContext(); + deep++; + } + + return d->getLocals(ctx); +} + +QList QJSDebuggerAgent::backtrace() +{ + SetupExecEnv execEnv(d); + + QList backtrace; + + for (QScriptContext *ctx = engine()->currentContext(); ctx; ctx = ctx->parentContext()) { + QScriptContextInfo info(ctx); + + JSAgentStackData frame; + frame.functionName = info.functionName().toUtf8(); + if (frame.functionName.isEmpty()) { + if (ctx->parentContext()) { + switch (info.functionType()) { + case QScriptContextInfo::ScriptFunction: + frame.functionName = ""; + break; + case QScriptContextInfo::NativeFunction: + frame.functionName = ""; + break; + case QScriptContextInfo::QtFunction: + case QScriptContextInfo::QtPropertyFunction: + frame.functionName = ""; + break; + } + } else { + frame.functionName = ""; + } + } + frame.lineNumber = info.lineNumber(); + // if the line number is unknown, fallback to the function line number + if (frame.lineNumber == -1) + frame.lineNumber = info.functionStartLineNumber(); + + frame.fileUrl = info.fileName().toUtf8(); + backtrace.append(frame); + } + + return backtrace; +} + +QList QJSDebuggerAgent::watches() +{ + SetupExecEnv execEnv(d); + + QList watches; + foreach (const QString &expr, d->watchExpressions) + watches << fromScriptValue(expr, engine()->evaluate(expr)); + d->recordKnownObjects(watches); + return watches; +} + +void QJSDebuggerAgent::setProperty(qint64 objectId, + const QString &property, + const QString &value) +{ + SetupExecEnv execEnv(d); + + if (d->knownObjectIds.contains(objectId)) { + QScriptValue object = engine()->objectById(objectId); + if (object.isObject()) { + QScriptValue result = engine()->evaluate(value); + object.setProperty(property, result); + } + } +} + +/*! + \reimp +*/ +void QJSDebuggerAgent::scriptLoad(qint64 id, const QString &program, + const QString &fileName, int baseLineNumber) +{ + d->filenames.insert(id, fileName); + + if (d->coverageEnabled) { + JSAgentCoverageData rd = {"COVERAGE", QJSDebugService::instance()->m_timer.elapsed(), (int)CoverageScriptLoad, + id, program, fileName, baseLineNumber, + 0, 0, QString()}; + QJSDebugService::instance()->processMessage(rd); + } +} + +/*! + \reimp +*/ +void QJSDebuggerAgent::scriptUnload(qint64 id) +{ + d->filenames.remove(id); +} + +/*! + \reimp +*/ +void QJSDebuggerAgent::contextPush() +{ +} + +/*! + \reimp +*/ +void QJSDebuggerAgent::contextPop() +{ +} + +/*! + \reimp +*/ +void QJSDebuggerAgent::functionEntry(qint64 scriptId) +{ + d->stepDepth++; + + if (d->coverageEnabled) { + JSAgentCoverageData rd = {"COVERAGE", QJSDebugService::instance()->m_timer.elapsed(), (int)CoverageFuncEntry, + scriptId, QString(), QString(), 0, 0, 0, QString()}; + QJSDebugService::instance()->processMessage(rd); + QJSDebugService::instance()->m_timer.restart(); + } +} + +/*! + \reimp +*/ +void QJSDebuggerAgent::functionExit(qint64 scriptId, const QScriptValue &returnValue) +{ + d->stepDepth--; + + if (d->coverageEnabled) { + JSAgentCoverageData rd = {"COVERAGE", QJSDebugService::instance()->m_timer.elapsed(), (int)CoverageFuncExit, + scriptId, QString(), QString(), 0, 0, 0, returnValue.toString()}; + QJSDebugService::instance()->processMessage(rd); + } +} + +/*! + \reimp +*/ +void QJSDebuggerAgent::positionChange(qint64 scriptId, int lineNumber, int columnNumber) +{ + d->positionChange(scriptId, lineNumber, columnNumber); + + if (d->coverageEnabled) { + JSAgentCoverageData rd = {"COVERAGE", QJSDebugService::instance()->m_timer.elapsed(), (int)CoveragePosChange, + scriptId, QString(), QString(), 0, lineNumber, columnNumber, QString()}; + QJSDebugService::instance()->processMessage(rd); + } +} + +void QJSDebuggerAgentPrivate::positionChange(qint64 scriptId, int lineNumber, int columnNumber) +{ + Q_UNUSED(columnNumber); + + if (state == StoppedState) + return; //no re-entrency + + // check breakpoints + if (!breakpoints.isEmpty()) { + const QScriptContext *ctx = engine()->currentContext(); + const QScriptContextInfo info(ctx); + + if (info.functionType() == QScriptContextInfo::ScriptFunction) { + QHash::const_iterator it = filenames.constFind(scriptId); + // It is possible that the scripts are loaded before the agent is attached + if (it == filenames.constEnd()) { + it = filenames.insert(scriptId, info.fileName()); + } + + const QString filePath = it.value(); + const JSAgentBreakpoints bps = fileNameToBreakpoints.values(fileName(filePath)).toSet(); + + foreach (const JSAgentBreakpointData &bp, bps) { + if (bp.lineNumber == lineNumber) { + stopped(); + return; + } + } + } + } + + switch (state) { + case NoState: + case StoppedState: + // Do nothing + break; + case SteppingOutState: + if (stepDepth >= 0) + break; + //fallthough + case SteppingOverState: + if (stepDepth > 0) + break; + //fallthough + case SteppingIntoState: + stopped(); + break; + } + +} + +/*! + \reimp +*/ +void QJSDebuggerAgent::exceptionThrow(qint64 scriptId, + const QScriptValue &exception, + bool hasHandler) +{ + Q_UNUSED(scriptId); + Q_UNUSED(exception); + Q_UNUSED(hasHandler); +// qDebug() << Q_FUNC_INFO << exception.toString() << hasHandler; +#if 0 //sometimes, we get exceptions that we should just ignore. + if (!hasHandler && state != StoppedState) + stopped(true, exception); +#endif +} + +/*! + \reimp +*/ +void QJSDebuggerAgent::exceptionCatch(qint64 scriptId, const QScriptValue &exception) +{ + Q_UNUSED(scriptId); + Q_UNUSED(exception); +} + +bool QJSDebuggerAgent::supportsExtension(Extension extension) const +{ + return extension == QScriptEngineAgent::DebuggerInvocationRequest; +} + +QVariant QJSDebuggerAgent::extension(Extension extension, const QVariant &argument) +{ + if (extension == QScriptEngineAgent::DebuggerInvocationRequest) { + d->stopped(); + return QVariant(); + } + return QScriptEngineAgent::extension(extension, argument); +} + +void QJSDebuggerAgentPrivate::stopped() +{ + bool becauseOfException = false; + const QScriptValue &exception = QScriptValue(); + + knownObjectIds.clear(); + state = StoppedState; + + emit q->stopped(becauseOfException, exception.toString()); + + loop.exec(QEventLoop::ExcludeUserInputEvents); +} + +void QJSDebuggerAgentPrivate::continueExec() +{ + loop.quit(); +} + +QT_END_NAMESPACE diff --git a/src/declarative/debugger/qjsdebuggeragent_p.h b/src/declarative/debugger/qjsdebuggeragent_p.h new file mode 100644 index 00000000..a4ffc187 --- /dev/null +++ b/src/declarative/debugger/qjsdebuggeragent_p.h @@ -0,0 +1,229 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QJSDEBUGGERAGENT_P_H +#define QJSDEBUGGERAGENT_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 +#include + +QT_BEGIN_NAMESPACE +class QScriptValue; +class QDeclarativeEngine; +QT_END_NAMESPACE + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QJSDebuggerAgentPrivate; + +enum JSDebuggerState +{ + NoState, + SteppingIntoState, + SteppingOverState, + SteppingOutState, + StoppedState +}; + +enum JSCoverageMessage { + CoverageLocation, + CoverageScriptLoad, + CoveragePosChange, + CoverageFuncEntry, + CoverageFuncExit, + CoverageComplete, + + CoverageMaximumMessage +}; + +struct JSAgentWatchData +{ + QByteArray exp; + QByteArray name; + QByteArray value; + QByteArray type; + bool hasChildren; + quint64 objectId; +}; + +inline QDataStream &operator<<(QDataStream &s, const JSAgentWatchData &data) +{ + return s << data.exp << data.name << data.value + << data.type << data.hasChildren << data.objectId; +} + +inline QDataStream &operator>>(QDataStream &s, JSAgentWatchData &data) +{ + return s >> data.exp >> data.name >> data.value + >> data.type >> data.hasChildren >> data.objectId; +} + +struct JSAgentStackData +{ + QByteArray functionName; + QByteArray fileUrl; + qint32 lineNumber; +}; + +inline QDataStream &operator<<(QDataStream &s, const JSAgentStackData &data) +{ + return s << data.functionName << data.fileUrl << data.lineNumber; +} + +inline QDataStream &operator>>(QDataStream &s, JSAgentStackData &data) +{ + return s >> data.functionName >> data.fileUrl >> data.lineNumber; +} + +struct JSAgentBreakpointData +{ + QByteArray functionName; + QByteArray fileUrl; + qint32 lineNumber; +}; + +typedef QSet JSAgentBreakpoints; + +inline QDataStream &operator<<(QDataStream &s, const JSAgentBreakpointData &data) +{ + return s << data.functionName << data.fileUrl << data.lineNumber; +} + +inline QDataStream &operator>>(QDataStream &s, JSAgentBreakpointData &data) +{ + return s >> data.functionName >> data.fileUrl >> data.lineNumber; +} + +inline bool operator==(const JSAgentBreakpointData &b1, const JSAgentBreakpointData &b2) +{ + return b1.lineNumber == b2.lineNumber && b1.fileUrl == b2.fileUrl; +} + +inline uint qHash(const JSAgentBreakpointData &b) +{ + return b.lineNumber ^ qHash(b.fileUrl); +} + + +class QJSDebuggerAgent : public QObject, public QScriptEngineAgent +{ + Q_OBJECT + +public: + QJSDebuggerAgent(QScriptEngine *engine, QObject *parent = 0); + QJSDebuggerAgent(QDeclarativeEngine *engine, QObject *parent = 0); + ~QJSDebuggerAgent(); + + bool isInitialized() const; + + void setBreakpoints(const JSAgentBreakpoints &); + void setWatchExpressions(const QStringList &); + + void stepOver(); + void stepInto(); + void stepOut(); + void continueExecution(); + void setCoverageEnabled(bool enabled); + + JSAgentWatchData executeExpression(const QString &expr); + QList expandObjectById(quint64 objectId); + QList locals(); + QList localsAtFrame(int frameId); + QList backtrace(); + QList watches(); + void setProperty(qint64 objectId, + const QString &property, + const QString &value); + + // reimplemented + void scriptLoad(qint64 id, const QString &program, + const QString &fileName, int baseLineNumber); + void scriptUnload(qint64 id); + + void contextPush(); + void contextPop(); + + void functionEntry(qint64 scriptId); + void functionExit(qint64 scriptId, + const QScriptValue &returnValue); + + void positionChange(qint64 scriptId, + int lineNumber, int columnNumber); + + void exceptionThrow(qint64 scriptId, + const QScriptValue &exception, + bool hasHandler); + void exceptionCatch(qint64 scriptId, + const QScriptValue &exception); + + bool supportsExtension(Extension extension) const; + QVariant extension(Extension extension, + const QVariant &argument = QVariant()); + +Q_SIGNALS: + void stopped(bool becauseOfException, + const QString &exception); + +private: + friend class QJSDebuggerAgentPrivate; + QJSDebuggerAgentPrivate *d; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QJSDEBUGGERAGENT_P_H diff --git a/src/declarative/debugger/qjsdebugservice.cpp b/src/declarative/debugger/qjsdebugservice.cpp new file mode 100644 index 00000000..686710dc --- /dev/null +++ b/src/declarative/debugger/qjsdebugservice.cpp @@ -0,0 +1,259 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qjsdebugservice_p.h" +#include "private/qjsdebuggeragent_p.h" + +#include +#include +#include +#include + +Q_GLOBAL_STATIC(QJSDebugService, serviceInstance) + +// convert to a QByteArray that can be sent to the debug client +QByteArray JSAgentCoverageData::toByteArray() const +{ + QByteArray data; + //### using QDataStream is relatively expensive + QDataStream ds(&data, QIODevice::WriteOnly); + ds << prefix << time << messageType << scriptId << program << fileName << baseLineNumber + << lineNumber << columnNumber << returnValue; + return data; +} + +QJSDebugService::QJSDebugService(QObject *parent) + : QDeclarativeDebugService(QLatin1String("JSDebugger"), parent) + , m_agent(0), m_deferredSend(true) +{ + m_timer.start(); +} + +QJSDebugService::~QJSDebugService() +{ + delete m_agent; +} + +QJSDebugService *QJSDebugService::instance() +{ + return serviceInstance(); +} + +void QJSDebugService::addEngine(QDeclarativeEngine *engine) +{ + Q_ASSERT(engine); + Q_ASSERT(!m_engines.contains(engine)); + + m_engines.append(engine); + + if (status() == Enabled && !m_engines.isEmpty() && !m_agent) { + m_agent = new QJSDebuggerAgent(engine, engine); + connect(m_agent, SIGNAL(stopped(bool,QString)), + this, SLOT(executionStopped(bool,QString))); + + while (!m_agent->isInitialized()) { + waitForMessage(); + } + } +} + +void QJSDebugService::removeEngine(QDeclarativeEngine *engine) +{ + Q_ASSERT(engine); + Q_ASSERT(m_engines.contains(engine)); + + m_engines.removeAll(engine); +} + +void QJSDebugService::statusChanged(Status status) +{ + if (status == Enabled && !m_engines.isEmpty() && !m_agent) { + // Multiple engines are currently unsupported + QDeclarativeEngine *engine = m_engines.first(); + m_agent = new QJSDebuggerAgent(engine, engine); + + connect(m_agent, SIGNAL(stopped(bool,QString)), + this, SLOT(executionStopped(bool,QString))); + + } else if (status != Enabled && m_agent) { + delete m_agent; + m_agent = 0; + } +} + +void QJSDebugService::messageReceived(const QByteArray &message) +{ + if (!m_agent) { + qWarning() << "QJSDebugService::messageReceived: No QJSDebuggerAgent available"; + return; + } + + QDataStream ds(message); + QByteArray command; + ds >> command; + if (command == "BREAKPOINTS") { + JSAgentBreakpoints breakpoints; + ds >> breakpoints; + m_agent->setBreakpoints(breakpoints); + + //qDebug() << "BREAKPOINTS"; + //foreach (const JSAgentBreakpointData &bp, breakpoints) + // qDebug() << "BREAKPOINT: " << bp.fileUrl << bp.lineNumber; + } else if (command == "WATCH_EXPRESSIONS") { + QStringList watchExpressions; + ds >> watchExpressions; + m_agent->setWatchExpressions(watchExpressions); + } else if (command == "STEPOVER") { + m_agent->stepOver(); + } else if (command == "STEPINTO" || command == "INTERRUPT") { + m_agent->stepInto(); + } else if (command == "STEPOUT") { + m_agent->stepOut(); + } else if (command == "CONTINUE") { + m_agent->continueExecution(); + } else if (command == "EXEC") { + QByteArray id; + QString expr; + ds >> id >> expr; + + JSAgentWatchData data = m_agent->executeExpression(expr); + + QByteArray reply; + QDataStream rs(&reply, QIODevice::WriteOnly); + rs << QByteArray("RESULT") << id << data; + sendMessage(reply); + } else if (command == "EXPAND") { + QByteArray requestId; + quint64 objectId; + ds >> requestId >> objectId; + + QList result = m_agent->expandObjectById(objectId); + + QByteArray reply; + QDataStream rs(&reply, QIODevice::WriteOnly); + rs << QByteArray("EXPANDED") << requestId << result; + sendMessage(reply); + } else if (command == "ACTIVATE_FRAME") { + int frameId; + ds >> frameId; + + QList locals = m_agent->localsAtFrame(frameId); + + QByteArray reply; + QDataStream rs(&reply, QIODevice::WriteOnly); + rs << QByteArray("LOCALS") << frameId << locals; + sendMessage(reply); + } else if (command == "SET_PROPERTY") { + QByteArray id; + qint64 objectId; + QString property; + QString value; + ds >> id >> objectId >> property >> value; + + m_agent->setProperty(objectId, property, value); + + //TODO: feedback + } else if (command == "PING") { + int ping; + ds >> ping; + QByteArray reply; + QDataStream rs(&reply, QIODevice::WriteOnly); + rs << QByteArray("PONG") << ping; + sendMessage(reply); + } else if (command == "COVERAGE") { + bool enabled; + ds >> enabled; + m_agent->setCoverageEnabled(enabled); + if (!enabled) { + sendMessages(); + } + } else { + qDebug() << Q_FUNC_INFO << "Unknown command" << command; + } + + QDeclarativeDebugService::messageReceived(message); +} + +void QJSDebugService::executionStopped(bool becauseOfException, + const QString &exception) +{ + const QList backtrace = m_agent->backtrace(); + const QList watches = m_agent->watches(); + const QList locals = m_agent->locals(); + + QByteArray reply; + QDataStream rs(&reply, QIODevice::WriteOnly); + rs << QByteArray("STOPPED") << backtrace << watches << locals + << becauseOfException << exception; + sendMessage(reply); +} + +/* + Either send the message directly, or queue up + a list of messages to send later (via sendMessages) +*/ +void QJSDebugService::processMessage(const JSAgentCoverageData &message) +{ + if (m_deferredSend) + m_data.append(message); + else + sendMessage(message.toByteArray()); +} + +/* + Send the messages queued up by processMessage +*/ +void QJSDebugService::sendMessages() +{ + if (m_deferredSend) { + //### this is a suboptimal way to send batched messages + for (int i = 0; i < m_data.count(); ++i) + sendMessage(m_data.at(i).toByteArray()); + m_data.clear(); + + //indicate completion + QByteArray data; + QDataStream ds(&data, QIODevice::WriteOnly); + ds << QByteArray("COVERAGE") << (qint64)-1 << (int)CoverageComplete; + sendMessage(data); + } +} + diff --git a/src/declarative/debugger/qjsdebugservice_p.h b/src/declarative/debugger/qjsdebugservice_p.h new file mode 100644 index 00000000..399b0fba --- /dev/null +++ b/src/declarative/debugger/qjsdebugservice_p.h @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QJSDEBUGSERVICE_P_H +#define QJSDEBUGSERVICE_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 +#include + +#include "private/qdeclarativedebugservice_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeEngine; +class QJSDebuggerAgent; + +struct JSAgentCoverageData +{ + QByteArray prefix; + qint64 time; + int messageType; + + qint64 scriptId; + QString program; + QString fileName; + int baseLineNumber; + int lineNumber; + int columnNumber; + QString returnValue; + + QByteArray toByteArray() const; +}; + +class QJSDebugService : public QDeclarativeDebugService +{ + Q_OBJECT + +public: + QJSDebugService(QObject *parent = 0); + ~QJSDebugService(); + + static QJSDebugService *instance(); + + void addEngine(QDeclarativeEngine *); + void removeEngine(QDeclarativeEngine *); + void processMessage(const JSAgentCoverageData &message); + + QElapsedTimer m_timer; + +protected: + void statusChanged(Status status); + void messageReceived(const QByteArray &); + +private Q_SLOTS: + void executionStopped(bool becauseOfException, + const QString &exception); + +private: + void sendMessages(); + QList m_engines; + QPointer m_agent; + bool m_deferredSend; + QList m_data; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QJSDEBUGSERVICE_P_H diff --git a/src/declarative/debugger/qpacketprotocol.cpp b/src/declarative/debugger/qpacketprotocol.cpp new file mode 100644 index 00000000..6732c850 --- /dev/null +++ b/src/declarative/debugger/qpacketprotocol.cpp @@ -0,0 +1,557 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qpacketprotocol_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +#define MAX_PACKET_SIZE 0x7FFFFFFF + +/*! + \class QPacketProtocol + \internal + + \brief The QPacketProtocol class encapsulates communicating discrete packets + across fragmented IO channels, such as TCP sockets. + + QPacketProtocol makes it simple to send arbitrary sized data "packets" across + fragmented transports such as TCP and UDP. + + As transmission boundaries are not respected, sending packets over protocols + like TCP frequently involves "stitching" them back together at the receiver. + QPacketProtocol makes this easier by performing this task for you. Packet + data sent using QPacketProtocol is prepended with a 4-byte size header + allowing the receiving QPacketProtocol to buffer the packet internally until + it has all been received. QPacketProtocol does not perform any sanity + checking on the size or on the data, so this class should only be used in + prototyping or trusted situations where DOS attacks are unlikely. + + QPacketProtocol does not perform any communications itself. Instead it can + operate on any QIODevice that supports the QIODevice::readyRead() signal. A + logical "packet" is encapsulated by the companion QPacket class. The + following example shows two ways to send data using QPacketProtocol. The + transmitted data is equivalent in both. + + \code + QTcpSocket socket; + // ... connect socket ... + + QPacketProtocol protocol(&socket); + + // Send packet the quick way + protocol.send() << "Hello world" << 123; + + // Send packet the longer way + QPacket packet; + packet << "Hello world" << 123; + protocol.send(packet); + \endcode + + Likewise, the following shows how to read data from QPacketProtocol, assuming + that the QPacketProtocol::readyRead() signal has been emitted. + + \code + // ... QPacketProtocol::readyRead() is emitted ... + + int a; + QByteArray b; + + // Receive packet the quick way + protocol.read() >> a >> b; + + // Receive packet the longer way + QPacket packet = protocol.read(); + p >> a >> b; + \endcode + + \ingroup io + \sa QPacket +*/ + +class QPacketProtocolPrivate : public QObject +{ +Q_OBJECT +public: + QPacketProtocolPrivate(QPacketProtocol * parent, QIODevice * _dev) + : QObject(parent), inProgressSize(-1), maxPacketSize(MAX_PACKET_SIZE), + waitingForPacket(false), dev(_dev) + { + Q_ASSERT(4 == sizeof(qint32)); + + QObject::connect(this, SIGNAL(readyRead()), + parent, SIGNAL(readyRead())); + QObject::connect(this, SIGNAL(packetWritten()), + parent, SIGNAL(packetWritten())); + QObject::connect(this, SIGNAL(invalidPacket()), + parent, SIGNAL(invalidPacket())); + QObject::connect(dev, SIGNAL(readyRead()), + this, SLOT(readyToRead())); + QObject::connect(dev, SIGNAL(aboutToClose()), + this, SLOT(aboutToClose())); + QObject::connect(dev, SIGNAL(bytesWritten(qint64)), + this, SLOT(bytesWritten(qint64))); + } + +Q_SIGNALS: + void readyRead(); + void packetWritten(); + void invalidPacket(); + +public Q_SLOTS: + void aboutToClose() + { + inProgress.clear(); + sendingPackets.clear(); + inProgressSize = -1; + } + + void bytesWritten(qint64 bytes) + { + Q_ASSERT(!sendingPackets.isEmpty()); + + while(bytes) { + if(sendingPackets.at(0) > bytes) { + sendingPackets[0] -= bytes; + bytes = 0; + } else { + bytes -= sendingPackets.at(0); + sendingPackets.removeFirst(); + emit packetWritten(); + } + } + } + + void readyToRead() + { + bool gotPackets = false; + while (true) { + // Get size header (if not in progress) + if (-1 == inProgressSize) { + // We need a size header of sizeof(qint32) + if (sizeof(qint32) > (uint)dev->bytesAvailable()) { + if (gotPackets) + emit readyRead(); + return; // no more data available + } + + // Read size header + int read = dev->read((char *)&inProgressSize, sizeof(qint32)); + Q_ASSERT(read == sizeof(qint32)); + Q_UNUSED(read); + + // Check sizing constraints + if (inProgressSize > maxPacketSize) { + QObject::disconnect(dev, SIGNAL(readyRead()), + this, SLOT(readyToRead())); + QObject::disconnect(dev, SIGNAL(aboutToClose()), + this, SLOT(aboutToClose())); + QObject::disconnect(dev, SIGNAL(bytesWritten(qint64)), + this, SLOT(bytesWritten(qint64))); + dev = 0; + emit invalidPacket(); + return; + } + + inProgressSize -= sizeof(qint32); + } else { + inProgress.append(dev->read(inProgressSize - inProgress.size())); + + if (inProgressSize == inProgress.size()) { + // Packet has arrived! + packets.append(inProgress); + inProgressSize = -1; + inProgress.clear(); + + waitingForPacket = false; + gotPackets = true; + } else { + if (gotPackets) + emit readyRead(); + return; // packet in progress is not yet complete + } + } + } + } + +public: + QList sendingPackets; + QList packets; + QByteArray inProgress; + qint32 inProgressSize; + qint32 maxPacketSize; + bool waitingForPacket; + QIODevice * dev; +}; + +/*! + Construct a QPacketProtocol instance that works on \a dev with the + specified \a parent. + */ +QPacketProtocol::QPacketProtocol(QIODevice * dev, QObject * parent) +: QObject(parent), d(new QPacketProtocolPrivate(this, dev)) +{ + Q_ASSERT(dev); +} + +/*! + Destroys the QPacketProtocol instance. + */ +QPacketProtocol::~QPacketProtocol() +{ +} + +/*! + Returns the maximum packet size allowed. By default this is + 2,147,483,647 bytes. + + If a packet claiming to be larger than the maximum packet size is received, + the QPacketProtocol::invalidPacket() signal is emitted. + + \sa QPacketProtocol::setMaximumPacketSize() + */ +qint32 QPacketProtocol::maximumPacketSize() const +{ + return d->maxPacketSize; +} + +/*! + Sets the maximum allowable packet size to \a max. + + \sa QPacketProtocol::maximumPacketSize() + */ +qint32 QPacketProtocol::setMaximumPacketSize(qint32 max) +{ + if(max > (signed)sizeof(qint32)) + d->maxPacketSize = max; + return d->maxPacketSize; +} + +/*! + Returns a streamable object that is transmitted on destruction. For example + + \code + protocol.send() << "Hello world" << 123; + \endcode + + will send a packet containing "Hello world" and 123. To construct more + complex packets, explicitly construct a QPacket instance. + */ +QPacketAutoSend QPacketProtocol::send() +{ + return QPacketAutoSend(this); +} + +/*! + \fn void QPacketProtocol::send(const QPacket & packet) + + Transmit the \a packet. + */ +void QPacketProtocol::send(const QPacket & p) +{ + if(p.b.isEmpty()) + return; // We don't send empty packets + + qint64 sendSize = p.b.size() + sizeof(qint32); + + d->sendingPackets.append(sendSize); + qint32 sendSize32 = sendSize; + qint64 writeBytes = d->dev->write((char *)&sendSize32, sizeof(qint32)); + Q_ASSERT(writeBytes == sizeof(qint32)); + writeBytes = d->dev->write(p.b); + Q_ASSERT(writeBytes == p.b.size()); +} + +/*! + Returns the number of received packets yet to be read. + */ +qint64 QPacketProtocol::packetsAvailable() const +{ + return d->packets.count(); +} + +/*! + Discard any unread packets. + */ +void QPacketProtocol::clear() +{ + d->packets.clear(); +} + +/*! + Return the next unread packet, or an invalid QPacket instance if no packets + are available. This method does NOT block. + */ +QPacket QPacketProtocol::read() +{ + if(0 == d->packets.count()) + return QPacket(); + + QPacket rv(d->packets.at(0)); + d->packets.removeFirst(); + return rv; +} + +/* + Returns the difference between msecs and elapsed. If msecs is -1, + however, -1 is returned. +*/ +static int qt_timeout_value(int msecs, int elapsed) +{ + if (msecs == -1) + return -1; + + int timeout = msecs - elapsed; + return timeout < 0 ? 0 : timeout; +} + +/*! + This function locks until a new packet is available for reading and the + \l{QIODevice::}{readyRead()} signal has been emitted. The function + will timeout after \a msecs milliseconds; the default timeout is + 30000 milliseconds. + + The function returns true if the readyRead() signal is emitted and + there is new data available for reading; otherwise it returns false + (if an error occurred or the operation timed out). + */ + +bool QPacketProtocol::waitForReadyRead(int msecs) +{ + if (!d->packets.isEmpty()) + return true; + + QElapsedTimer stopWatch; + stopWatch.start(); + + d->waitingForPacket = true; + do { + if (!d->dev->waitForReadyRead(msecs)) + return false; + if (!d->waitingForPacket) + return true; + msecs = qt_timeout_value(msecs, stopWatch.elapsed()); + } while (true); +} + +/*! + Return the QIODevice passed to the QPacketProtocol constructor. +*/ +QIODevice * QPacketProtocol::device() +{ + return d->dev; +} + +/*! + \fn void QPacketProtocol::readyRead() + + Emitted whenever a new packet is received. Applications may use + QPacketProtocol::read() to retrieve this packet. + */ + +/*! + \fn void QPacketProtocol::invalidPacket() + + A packet larger than the maximum allowable packet size was received. The + packet will be discarded and, as it indicates corruption in the protocol, no + further packets will be received. + */ + +/*! + \fn void QPacketProtocol::packetWritten() + + Emitted each time a packet is completing written to the device. This signal + may be used for communications flow control. + */ + +/*! + \class QPacket + \internal + + \brief The QPacket class encapsulates an unfragmentable packet of data to be + transmitted by QPacketProtocol. + + The QPacket class works together with QPacketProtocol to make it simple to + send arbitrary sized data "packets" across fragmented transports such as TCP + and UDP. + + QPacket provides a QDataStream interface to an unfragmentable packet. + Applications should construct a QPacket, propagate it with data and then + transmit it over a QPacketProtocol instance. For example: + \code + QPacketProtocol protocol(...); + + QPacket myPacket; + myPacket << "Hello world!" << 123; + protocol.send(myPacket); + \endcode + + As long as both ends of the connection are using the QPacketProtocol class, + the data within this packet will be delivered unfragmented at the other end, + ready for extraction. + + \code + QByteArray greeting; + int count; + + QPacket myPacket = protocol.read(); + + myPacket >> greeting >> count; + \endcode + + Only packets returned from QPacketProtocol::read() may be read from. QPacket + instances constructed by directly by applications are for transmission only + and are considered "write only". Attempting to read data from them will + result in undefined behavior. + + \ingroup io + \sa QPacketProtocol + */ + +/*! + Constructs an empty write-only packet. + */ +QPacket::QPacket() +: QDataStream(), buf(0) +{ + buf = new QBuffer(&b); + buf->open(QIODevice::WriteOnly); + setDevice(buf); + setVersion(QDataStream::Qt_4_7); +} + +/*! + Destroys the QPacket instance. + */ +QPacket::~QPacket() +{ + if(buf) { + delete buf; + buf = 0; + } +} + +/*! + Creates a copy of \a other. The initial stream positions are shared, but the + two packets are otherwise independent. + */ +QPacket::QPacket(const QPacket & other) +: QDataStream(), b(other.b), buf(0) +{ + buf = new QBuffer(&b); + buf->open(other.buf->openMode()); + setDevice(buf); +} + +/*! + \internal + */ +QPacket::QPacket(const QByteArray & ba) +: QDataStream(), b(ba), buf(0) +{ + buf = new QBuffer(&b); + buf->open(QIODevice::ReadOnly); + setDevice(buf); +} + +/*! + Returns true if this packet is empty - that is, contains no data. + */ +bool QPacket::isEmpty() const +{ + return b.isEmpty(); +} + +/*! + Returns raw packet data. + */ +QByteArray QPacket::data() const +{ + return b; +} + +/*! + Clears data in the packet. This is useful for reusing one writable packet. + For example + \code + QPacketProtocol protocol(...); + + QPacket packet; + + packet << "Hello world!" << 123; + protocol.send(packet); + + packet.clear(); + packet << "Goodbyte world!" << 789; + protocol.send(packet); + \endcode + */ +void QPacket::clear() +{ + QBuffer::OpenMode oldMode = buf->openMode(); + buf->close(); + b.clear(); + buf->setBuffer(&b); // reset QBuffer internals with new size of b. + buf->open(oldMode); +} + +/*! + \class QPacketAutoSend + \internal + + \internal + */ +QPacketAutoSend::QPacketAutoSend(QPacketProtocol * _p) +: QPacket(), p(_p) +{ +} + +QPacketAutoSend::~QPacketAutoSend() +{ + if(!b.isEmpty()) + p->send(*this); +} + +QT_END_NAMESPACE + +#include diff --git a/src/declarative/debugger/qpacketprotocol_p.h b/src/declarative/debugger/qpacketprotocol_p.h new file mode 100644 index 00000000..62dedf4d --- /dev/null +++ b/src/declarative/debugger/qpacketprotocol_p.h @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPACKETPROTOCOL_H +#define QPACKETPROTOCOL_H + +#include +#include + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QIODevice; +class QBuffer; +class QPacket; +class QPacketAutoSend; +class QPacketProtocolPrivate; + +class Q_DECLARATIVE_EXPORT QPacketProtocol : public QObject +{ +Q_OBJECT +public: + explicit QPacketProtocol(QIODevice * dev, QObject * parent = 0); + virtual ~QPacketProtocol(); + + qint32 maximumPacketSize() const; + qint32 setMaximumPacketSize(qint32); + + QPacketAutoSend send(); + void send(const QPacket &); + + qint64 packetsAvailable() const; + QPacket read(); + + bool waitForReadyRead(int msecs = 3000); + + void clear(); + + QIODevice * device(); + +Q_SIGNALS: + void readyRead(); + void invalidPacket(); + void packetWritten(); + +private: + QPacketProtocolPrivate * d; +}; + + +class Q_DECLARATIVE_EXPORT QPacket : public QDataStream +{ +public: + QPacket(); + QPacket(const QPacket &); + virtual ~QPacket(); + + void clear(); + bool isEmpty() const; + QByteArray data() const; + +protected: + friend class QPacketProtocol; + QPacket(const QByteArray & ba); + QByteArray b; + QBuffer * buf; +}; + +class Q_DECLARATIVE_PRIVATE_EXPORT QPacketAutoSend : public QPacket +{ +public: + virtual ~QPacketAutoSend(); + +private: + friend class QPacketProtocol; + QPacketAutoSend(QPacketProtocol *); + QPacketProtocol * p; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/declarative/declarative.pro b/src/declarative/declarative.pro new file mode 100644 index 00000000..055e4b93 --- /dev/null +++ b/src/declarative/declarative.pro @@ -0,0 +1,43 @@ +TARGET = QtDeclarative +QPRO_PWD = $$PWD +QT = core gui script network +contains(QT_CONFIG, svg): QT += svg +DEFINES += QT_BUILD_DECLARATIVE_LIB QT_NO_URL_CAST_FROM_STRING +win32-msvc*|win32-icc:QMAKE_LFLAGS += /BASE:0x66000000 +solaris-cc*:QMAKE_CXXFLAGS_RELEASE -= -O2 + +unix|win32-g++*:QMAKE_PKGCONFIG_REQUIRES = QtCore QtGui + +exists("qdeclarative_enable_gcov") { + QMAKE_CXXFLAGS = -fprofile-arcs -ftest-coverage -fno-elide-constructors + LIBS += -lgcov +} + +include(../qbase.pri) + +#INCLUDEPATH -= $$QMAKE_INCDIR_QT/$$TARGET +#DESTDIR=. + +#modules +include(util/util.pri) +include(graphicsitems/graphicsitems.pri) +include(qml/qml.pri) +include(debugger/debugger.pri) + +symbian: { + TARGET.UID3=0x2001E623 + LIBS += -lefsrv -lhal + + contains(QT_CONFIG, freetype) { + DEFINES += QT_NO_FONTCONFIG + INCLUDEPATH += \ + ../3rdparty/freetype/src \ + ../3rdparty/freetype/include + } +} + +linux-g++-maemo:DEFINES += QDECLARATIVEVIEW_NOBACKGROUND + +DEFINES += QT_NO_OPENTYPE +INCLUDEPATH += ../3rdparty/harfbuzz/src + diff --git a/src/declarative/graphicsitems/graphicsitems.pri b/src/declarative/graphicsitems/graphicsitems.pri new file mode 100644 index 00000000..99042740 --- /dev/null +++ b/src/declarative/graphicsitems/graphicsitems.pri @@ -0,0 +1,94 @@ +INCLUDEPATH += $$PWD + +HEADERS += \ + $$PWD/qdeclarativeitemsmodule_p.h \ + $$PWD/qdeclarativeanchors_p.h \ + $$PWD/qdeclarativeanchors_p_p.h \ + $$PWD/qdeclarativeevents_p_p.h \ + $$PWD/qdeclarativeflickable_p.h \ + $$PWD/qdeclarativeflickable_p_p.h \ + $$PWD/qdeclarativeflipable_p.h \ + $$PWD/qdeclarativegridview_p.h \ + $$PWD/qdeclarativeimage_p.h \ + $$PWD/qdeclarativeimagebase_p.h \ + $$PWD/qdeclarativeborderimage_p.h \ + $$PWD/qdeclarativepainteditem_p.h \ + $$PWD/qdeclarativepainteditem_p_p.h \ + $$PWD/qdeclarativeimage_p_p.h \ + $$PWD/qdeclarativeborderimage_p_p.h \ + $$PWD/qdeclarativeimagebase_p_p.h \ + $$PWD/qdeclarativeanimatedimage_p.h \ + $$PWD/qdeclarativeanimatedimage_p_p.h \ + $$PWD/qdeclarativeitem.h \ + $$PWD/qdeclarativeitem_p.h \ + $$PWD/qdeclarativefocuspanel_p.h \ + $$PWD/qdeclarativefocusscope_p.h \ + $$PWD/qdeclarativepositioners_p.h \ + $$PWD/qdeclarativepositioners_p_p.h \ + $$PWD/qdeclarativeloader_p.h \ + $$PWD/qdeclarativeloader_p_p.h \ + $$PWD/qdeclarativemousearea_p.h \ + $$PWD/qdeclarativemousearea_p_p.h \ + $$PWD/qdeclarativepath_p.h \ + $$PWD/qdeclarativepath_p_p.h \ + $$PWD/qdeclarativepathview_p.h \ + $$PWD/qdeclarativepathview_p_p.h \ + $$PWD/qdeclarativerectangle_p.h \ + $$PWD/qdeclarativerectangle_p_p.h \ + $$PWD/qdeclarativerepeater_p.h \ + $$PWD/qdeclarativerepeater_p_p.h \ + $$PWD/qdeclarativescalegrid_p_p.h \ + $$PWD/qdeclarativetranslate_p.h \ + $$PWD/qdeclarativetextinput_p.h \ + $$PWD/qdeclarativetextinput_p_p.h \ + $$PWD/qdeclarativetextedit_p.h \ + $$PWD/qdeclarativetextedit_p_p.h \ + $$PWD/qdeclarativetext_p.h \ + $$PWD/qdeclarativetext_p_p.h \ + $$PWD/qdeclarativevisualitemmodel_p.h \ + $$PWD/qdeclarativelistview_p.h \ + $$PWD/qdeclarativelayoutitem_p.h \ + $$PWD/qdeclarativeitemchangelistener_p.h \ + $$PWD/qdeclarativegraphicswidget_p.h \ + $$PWD/qdeclarativetextlayout_p.h \ + $$PWD/qdeclarativepincharea_p.h \ + $$PWD/qdeclarativepincharea_p_p.h \ + $$PWD/qdeclarativeimplicitsizeitem_p.h \ + $$PWD/qdeclarativeimplicitsizeitem_p_p.h + + +SOURCES += \ + $$PWD/qdeclarativeitemsmodule.cpp \ + $$PWD/qdeclarativeanchors.cpp \ + $$PWD/qdeclarativeevents.cpp \ + $$PWD/qdeclarativeflickable.cpp \ + $$PWD/qdeclarativeflipable.cpp \ + $$PWD/qdeclarativegridview.cpp \ + $$PWD/qdeclarativeimage.cpp \ + $$PWD/qdeclarativeborderimage.cpp \ + $$PWD/qdeclarativeimagebase.cpp \ + $$PWD/qdeclarativeanimatedimage.cpp \ + $$PWD/qdeclarativepainteditem.cpp \ + $$PWD/qdeclarativeitem.cpp \ + $$PWD/qdeclarativefocuspanel.cpp \ + $$PWD/qdeclarativefocusscope.cpp \ + $$PWD/qdeclarativepositioners.cpp \ + $$PWD/qdeclarativeloader.cpp \ + $$PWD/qdeclarativemousearea.cpp \ + $$PWD/qdeclarativepath.cpp \ + $$PWD/qdeclarativepathview.cpp \ + $$PWD/qdeclarativerectangle.cpp \ + $$PWD/qdeclarativerepeater.cpp \ + $$PWD/qdeclarativescalegrid.cpp \ + $$PWD/qdeclarativetranslate.cpp \ + $$PWD/qdeclarativetextinput.cpp \ + $$PWD/qdeclarativetext.cpp \ + $$PWD/qdeclarativetextedit.cpp \ + $$PWD/qdeclarativevisualitemmodel.cpp \ + $$PWD/qdeclarativelistview.cpp \ + $$PWD/qdeclarativelayoutitem.cpp \ + $$PWD/qdeclarativegraphicswidget.cpp \ + $$PWD/qdeclarativetextlayout.cpp \ + $$PWD/qdeclarativepincharea.cpp \ + $$PWD/qdeclarativeimplicitsizeitem.cpp + diff --git a/src/declarative/graphicsitems/qdeclarativeanchors.cpp b/src/declarative/graphicsitems/qdeclarativeanchors.cpp new file mode 100644 index 00000000..f8ca54cb --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativeanchors.cpp @@ -0,0 +1,1165 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativeanchors_p_p.h" + +#include "qdeclarativeitem.h" +#include "private/qdeclarativeitem_p.h" + +#include + +#include + +QT_BEGIN_NAMESPACE + +//TODO: should we cache relationships, so we don't have to check each time (parent-child or sibling)? +//TODO: support non-parent, non-sibling (need to find lowest common ancestor) + +static qreal hcenter(QGraphicsItem *i) +{ + QGraphicsItemPrivate *item = QGraphicsItemPrivate::get(i); + + qreal width = item->width(); + int iw = width; + if (iw % 2) + return (width + 1) / 2; + else + return width / 2; +} + +static qreal vcenter(QGraphicsItem *i) +{ + QGraphicsItemPrivate *item = QGraphicsItemPrivate::get(i); + + qreal height = item->height(); + int ih = height; + if (ih % 2) + return (height + 1) / 2; + else + return height / 2; +} + +//### const item? +//local position +static qreal position(QGraphicsObject *item, QDeclarativeAnchorLine::AnchorLine anchorLine) +{ + qreal ret = 0.0; + QGraphicsItemPrivate *d = QGraphicsItemPrivate::get(item); + switch(anchorLine) { + case QDeclarativeAnchorLine::Left: + ret = item->x(); + break; + case QDeclarativeAnchorLine::Right: + ret = item->x() + d->width(); + break; + case QDeclarativeAnchorLine::Top: + ret = item->y(); + break; + case QDeclarativeAnchorLine::Bottom: + ret = item->y() + d->height(); + break; + case QDeclarativeAnchorLine::HCenter: + ret = item->x() + hcenter(item); + break; + case QDeclarativeAnchorLine::VCenter: + ret = item->y() + vcenter(item); + break; + case QDeclarativeAnchorLine::Baseline: + if (d->isDeclarativeItem) + ret = item->y() + static_cast(item)->baselineOffset(); + break; + default: + break; + } + + return ret; +} + +//position when origin is 0,0 +static qreal adjustedPosition(QGraphicsObject *item, QDeclarativeAnchorLine::AnchorLine anchorLine) +{ + qreal ret = 0.0; + QGraphicsItemPrivate *d = QGraphicsItemPrivate::get(item); + switch(anchorLine) { + case QDeclarativeAnchorLine::Left: + ret = 0.0; + break; + case QDeclarativeAnchorLine::Right: + ret = d->width(); + break; + case QDeclarativeAnchorLine::Top: + ret = 0.0; + break; + case QDeclarativeAnchorLine::Bottom: + ret = d->height(); + break; + case QDeclarativeAnchorLine::HCenter: + ret = hcenter(item); + break; + case QDeclarativeAnchorLine::VCenter: + ret = vcenter(item); + break; + case QDeclarativeAnchorLine::Baseline: + if (d->isDeclarativeItem) + ret = static_cast(item)->baselineOffset(); + break; + default: + break; + } + + return ret; +} + +QDeclarativeAnchors::QDeclarativeAnchors(QObject *parent) + : QObject(*new QDeclarativeAnchorsPrivate(0), parent) +{ + qFatal("QDeclarativeAnchors::QDeclarativeAnchors(QObject*) called"); +} + +QDeclarativeAnchors::QDeclarativeAnchors(QGraphicsObject *item, QObject *parent) + : QObject(*new QDeclarativeAnchorsPrivate(item), parent) +{ +} + +QDeclarativeAnchors::~QDeclarativeAnchors() +{ + Q_D(QDeclarativeAnchors); + d->remDepend(d->fill); + d->remDepend(d->centerIn); + d->remDepend(d->left.item); + d->remDepend(d->right.item); + d->remDepend(d->top.item); + d->remDepend(d->bottom.item); + d->remDepend(d->vCenter.item); + d->remDepend(d->hCenter.item); + d->remDepend(d->baseline.item); +} + +void QDeclarativeAnchorsPrivate::fillChanged() +{ + Q_Q(QDeclarativeAnchors); + if (!fill || !isItemComplete()) + return; + + if (updatingFill < 2) { + ++updatingFill; + + qreal horizontalMargin = q->mirrored() ? rightMargin : leftMargin; + + if (fill == item->parentItem()) { //child-parent + setItemPos(QPointF(horizontalMargin, topMargin)); + } else if (fill->parentItem() == item->parentItem()) { //siblings + setItemPos(QPointF(fill->x()+horizontalMargin, fill->y()+topMargin)); + } + QGraphicsItemPrivate *fillPrivate = QGraphicsItemPrivate::get(fill); + setItemSize(QSizeF(fillPrivate->width()-leftMargin-rightMargin, fillPrivate->height()-topMargin-bottomMargin)); + + --updatingFill; + } else { + // ### Make this certain :) + qmlInfo(item) << QDeclarativeAnchors::tr("Possible anchor loop detected on fill."); + } + +} + +void QDeclarativeAnchorsPrivate::centerInChanged() +{ + Q_Q(QDeclarativeAnchors); + if (!centerIn || fill || !isItemComplete()) + return; + + if (updatingCenterIn < 2) { + ++updatingCenterIn; + + qreal effectiveHCenterOffset = q->mirrored() ? -hCenterOffset : hCenterOffset; + if (centerIn == item->parentItem()) { + QPointF p(hcenter(item->parentItem()) - hcenter(item) + effectiveHCenterOffset, + vcenter(item->parentItem()) - vcenter(item) + vCenterOffset); + setItemPos(p); + + } else if (centerIn->parentItem() == item->parentItem()) { + QPointF p(centerIn->x() + hcenter(centerIn) - hcenter(item) + effectiveHCenterOffset, + centerIn->y() + vcenter(centerIn) - vcenter(item) + vCenterOffset); + setItemPos(p); + } + + --updatingCenterIn; + } else { + // ### Make this certain :) + qmlInfo(item) << QDeclarativeAnchors::tr("Possible anchor loop detected on centerIn."); + } +} + +void QDeclarativeAnchorsPrivate::clearItem(QGraphicsObject *item) +{ + if (!item) + return; + if (fill == item) + fill = 0; + if (centerIn == item) + centerIn = 0; + if (left.item == item) { + left.item = 0; + usedAnchors &= ~QDeclarativeAnchors::LeftAnchor; + } + if (right.item == item) { + right.item = 0; + usedAnchors &= ~QDeclarativeAnchors::RightAnchor; + } + if (top.item == item) { + top.item = 0; + usedAnchors &= ~QDeclarativeAnchors::TopAnchor; + } + if (bottom.item == item) { + bottom.item = 0; + usedAnchors &= ~QDeclarativeAnchors::BottomAnchor; + } + if (vCenter.item == item) { + vCenter.item = 0; + usedAnchors &= ~QDeclarativeAnchors::VCenterAnchor; + } + if (hCenter.item == item) { + hCenter.item = 0; + usedAnchors &= ~QDeclarativeAnchors::HCenterAnchor; + } + if (baseline.item == item) { + baseline.item = 0; + usedAnchors &= ~QDeclarativeAnchors::BaselineAnchor; + } +} + +void QDeclarativeAnchorsPrivate::addDepend(QGraphicsObject *item) +{ + if (!item) + return; + QGraphicsItemPrivate * itemPrivate = QGraphicsItemPrivate::get(item); + if (itemPrivate->isDeclarativeItem) { + QDeclarativeItemPrivate *p = + static_cast(QGraphicsItemPrivate::get(item)); + p->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry); + } else if(itemPrivate->isWidget) { + Q_Q(QDeclarativeAnchors); + QGraphicsWidget *widget = static_cast(item); + QObject::connect(widget, SIGNAL(destroyed(QObject*)), q, SLOT(_q_widgetDestroyed(QObject*))); + QObject::connect(widget, SIGNAL(geometryChanged()), q, SLOT(_q_widgetGeometryChanged())); + } +} + +void QDeclarativeAnchorsPrivate::remDepend(QGraphicsObject *item) +{ + if (!item) + return; + QGraphicsItemPrivate * itemPrivate = QGraphicsItemPrivate::get(item); + if (itemPrivate->isDeclarativeItem) { + QDeclarativeItemPrivate *p = + static_cast(itemPrivate); + p->removeItemChangeListener(this, QDeclarativeItemPrivate::Geometry); + } else if(itemPrivate->isWidget) { + Q_Q(QDeclarativeAnchors); + QGraphicsWidget *widget = static_cast(item); + QObject::disconnect(widget, SIGNAL(destroyed(QObject*)), q, SLOT(_q_widgetDestroyed(QObject*))); + QObject::disconnect(widget, SIGNAL(geometryChanged()), q, SLOT(_q_widgetGeometryChanged())); + } +} + +bool QDeclarativeAnchorsPrivate::isItemComplete() const +{ + return componentComplete; +} + +void QDeclarativeAnchors::classBegin() +{ + Q_D(QDeclarativeAnchors); + d->componentComplete = false; +} + +void QDeclarativeAnchors::componentComplete() +{ + Q_D(QDeclarativeAnchors); + d->componentComplete = true; +} + +bool QDeclarativeAnchors::mirrored() +{ + Q_D(QDeclarativeAnchors); + QGraphicsItemPrivate * itemPrivate = QGraphicsItemPrivate::get(d->item); + return itemPrivate->isDeclarativeItem ? static_cast(itemPrivate)->effectiveLayoutMirror : false; +} + +void QDeclarativeAnchorsPrivate::setItemHeight(qreal v) +{ + updatingMe = true; + QGraphicsItemPrivate::get(item)->setHeight(v); + updatingMe = false; +} + +void QDeclarativeAnchorsPrivate::setItemWidth(qreal v) +{ + updatingMe = true; + QGraphicsItemPrivate::get(item)->setWidth(v); + updatingMe = false; +} + +void QDeclarativeAnchorsPrivate::setItemX(qreal v) +{ + updatingMe = true; + item->setX(v); + updatingMe = false; +} + +void QDeclarativeAnchorsPrivate::setItemY(qreal v) +{ + updatingMe = true; + item->setY(v); + updatingMe = false; +} + +void QDeclarativeAnchorsPrivate::setItemPos(const QPointF &v) +{ + updatingMe = true; + item->setPos(v); + updatingMe = false; +} + +void QDeclarativeAnchorsPrivate::setItemSize(const QSizeF &v) +{ + updatingMe = true; + if(QGraphicsItemPrivate::get(item)->isWidget) + static_cast(item)->resize(v); + else if (QGraphicsItemPrivate::get(item)->isDeclarativeItem) + static_cast(item)->setSize(v); + updatingMe = false; +} + +void QDeclarativeAnchorsPrivate::updateMe() +{ + if (updatingMe) { + updatingMe = false; + return; + } + + fillChanged(); + centerInChanged(); + updateHorizontalAnchors(); + updateVerticalAnchors(); +} + +void QDeclarativeAnchorsPrivate::updateOnComplete() +{ + fillChanged(); + centerInChanged(); + updateHorizontalAnchors(); + updateVerticalAnchors(); +} + +void QDeclarativeAnchorsPrivate::_q_widgetDestroyed(QObject *obj) +{ + clearItem(qobject_cast(obj)); +} + +void QDeclarativeAnchorsPrivate::_q_widgetGeometryChanged() +{ + fillChanged(); + centerInChanged(); + updateHorizontalAnchors(); + updateVerticalAnchors(); +} + +void QDeclarativeAnchorsPrivate::itemGeometryChanged(QDeclarativeItem *, const QRectF &newG, const QRectF &oldG) +{ + fillChanged(); + centerInChanged(); + if (newG.x() != oldG.x() || newG.width() != oldG.width()) + updateHorizontalAnchors(); + if (newG.y() != oldG.y() || newG.height() != oldG.height()) + updateVerticalAnchors(); +} + +QGraphicsObject *QDeclarativeAnchors::fill() const +{ + Q_D(const QDeclarativeAnchors); + return d->fill; +} + +void QDeclarativeAnchors::setFill(QGraphicsObject *f) +{ + Q_D(QDeclarativeAnchors); + if (d->fill == f) + return; + + if (!f) { + d->remDepend(d->fill); + d->fill = f; + emit fillChanged(); + return; + } + if (f != d->item->parentItem() && f->parentItem() != d->item->parentItem()){ + qmlInfo(d->item) << tr("Cannot anchor to an item that isn't a parent or sibling."); + return; + } + d->remDepend(d->fill); + d->fill = f; + d->addDepend(d->fill); + emit fillChanged(); + d->fillChanged(); +} + +void QDeclarativeAnchors::resetFill() +{ + setFill(0); +} + +QGraphicsObject *QDeclarativeAnchors::centerIn() const +{ + Q_D(const QDeclarativeAnchors); + return d->centerIn; +} + +void QDeclarativeAnchors::setCenterIn(QGraphicsObject* c) +{ + Q_D(QDeclarativeAnchors); + if (d->centerIn == c) + return; + + if (!c) { + d->remDepend(d->centerIn); + d->centerIn = c; + emit centerInChanged(); + return; + } + if (c != d->item->parentItem() && c->parentItem() != d->item->parentItem()){ + qmlInfo(d->item) << tr("Cannot anchor to an item that isn't a parent or sibling."); + return; + } + + d->remDepend(d->centerIn); + d->centerIn = c; + d->addDepend(d->centerIn); + emit centerInChanged(); + d->centerInChanged(); +} + +void QDeclarativeAnchors::resetCenterIn() +{ + setCenterIn(0); +} + +bool QDeclarativeAnchorsPrivate::calcStretch(const QDeclarativeAnchorLine &edge1, + const QDeclarativeAnchorLine &edge2, + qreal offset1, + qreal offset2, + QDeclarativeAnchorLine::AnchorLine line, + qreal &stretch) +{ + bool edge1IsParent = (edge1.item == item->parentItem()); + bool edge2IsParent = (edge2.item == item->parentItem()); + bool edge1IsSibling = (edge1.item->parentItem() == item->parentItem()); + bool edge2IsSibling = (edge2.item->parentItem() == item->parentItem()); + + bool invalid = false; + if ((edge2IsParent && edge1IsParent) || (edge2IsSibling && edge1IsSibling)) { + stretch = (position(edge2.item, edge2.anchorLine) + offset2) + - (position(edge1.item, edge1.anchorLine) + offset1); + } else if (edge2IsParent && edge1IsSibling) { + stretch = (position(edge2.item, edge2.anchorLine) + offset2) + - (position(item->parentObject(), line) + + position(edge1.item, edge1.anchorLine) + offset1); + } else if (edge2IsSibling && edge1IsParent) { + stretch = (position(item->parentObject(), line) + position(edge2.item, edge2.anchorLine) + offset2) + - (position(edge1.item, edge1.anchorLine) + offset1); + } else + invalid = true; + + return invalid; +} + +void QDeclarativeAnchorsPrivate::updateVerticalAnchors() +{ + if (fill || centerIn || !isItemComplete()) + return; + + if (updatingVerticalAnchor < 2) { + ++updatingVerticalAnchor; + QGraphicsItemPrivate *itemPrivate = QGraphicsItemPrivate::get(item); + if (usedAnchors & QDeclarativeAnchors::TopAnchor) { + //Handle stretching + bool invalid = true; + qreal height = 0.0; + if (usedAnchors & QDeclarativeAnchors::BottomAnchor) { + invalid = calcStretch(top, bottom, topMargin, -bottomMargin, QDeclarativeAnchorLine::Top, height); + } else if (usedAnchors & QDeclarativeAnchors::VCenterAnchor) { + invalid = calcStretch(top, vCenter, topMargin, vCenterOffset, QDeclarativeAnchorLine::Top, height); + height *= 2; + } + if (!invalid) + setItemHeight(height); + + //Handle top + if (top.item == item->parentItem()) { + setItemY(adjustedPosition(top.item, top.anchorLine) + topMargin); + } else if (top.item->parentItem() == item->parentItem()) { + setItemY(position(top.item, top.anchorLine) + topMargin); + } + } else if (usedAnchors & QDeclarativeAnchors::BottomAnchor) { + //Handle stretching (top + bottom case is handled above) + if (usedAnchors & QDeclarativeAnchors::VCenterAnchor) { + qreal height = 0.0; + bool invalid = calcStretch(vCenter, bottom, vCenterOffset, -bottomMargin, + QDeclarativeAnchorLine::Top, height); + if (!invalid) + setItemHeight(height*2); + } + + //Handle bottom + if (bottom.item == item->parentItem()) { + setItemY(adjustedPosition(bottom.item, bottom.anchorLine) - itemPrivate->height() - bottomMargin); + } else if (bottom.item->parentItem() == item->parentItem()) { + setItemY(position(bottom.item, bottom.anchorLine) - itemPrivate->height() - bottomMargin); + } + } else if (usedAnchors & QDeclarativeAnchors::VCenterAnchor) { + //(stetching handled above) + + //Handle vCenter + if (vCenter.item == item->parentItem()) { + setItemY(adjustedPosition(vCenter.item, vCenter.anchorLine) + - vcenter(item) + vCenterOffset); + } else if (vCenter.item->parentItem() == item->parentItem()) { + setItemY(position(vCenter.item, vCenter.anchorLine) - vcenter(item) + vCenterOffset); + } + } else if (usedAnchors & QDeclarativeAnchors::BaselineAnchor) { + //Handle baseline + if (baseline.item == item->parentItem()) { + if (itemPrivate->isDeclarativeItem) + setItemY(adjustedPosition(baseline.item, baseline.anchorLine) + - static_cast(item)->baselineOffset() + baselineOffset); + } else if (baseline.item->parentItem() == item->parentItem()) { + if (itemPrivate->isDeclarativeItem) + setItemY(position(baseline.item, baseline.anchorLine) + - static_cast(item)->baselineOffset() + baselineOffset); + } + } + --updatingVerticalAnchor; + } else { + // ### Make this certain :) + qmlInfo(item) << QDeclarativeAnchors::tr("Possible anchor loop detected on vertical anchor."); + } +} + +inline QDeclarativeAnchorLine::AnchorLine reverseAnchorLine(QDeclarativeAnchorLine::AnchorLine anchorLine) { + if (anchorLine == QDeclarativeAnchorLine::Left) { + return QDeclarativeAnchorLine::Right; + } else if (anchorLine == QDeclarativeAnchorLine::Right) { + return QDeclarativeAnchorLine::Left; + } else { + return anchorLine; + } +} + +void QDeclarativeAnchorsPrivate::updateHorizontalAnchors() +{ + Q_Q(QDeclarativeAnchors); + if (fill || centerIn || !isItemComplete()) + return; + + if (updatingHorizontalAnchor < 3) { + ++updatingHorizontalAnchor; + qreal effectiveRightMargin, effectiveLeftMargin, effectiveHorizontalCenterOffset; + QDeclarativeAnchorLine effectiveLeft, effectiveRight, effectiveHorizontalCenter; + QDeclarativeAnchors::Anchor effectiveLeftAnchor, effectiveRightAnchor; + if (q->mirrored()) { + effectiveLeftAnchor = QDeclarativeAnchors::RightAnchor; + effectiveRightAnchor = QDeclarativeAnchors::LeftAnchor; + effectiveLeft.item = right.item; + effectiveLeft.anchorLine = reverseAnchorLine(right.anchorLine); + effectiveRight.item = left.item; + effectiveRight.anchorLine = reverseAnchorLine(left.anchorLine); + effectiveHorizontalCenter.item = hCenter.item; + effectiveHorizontalCenter.anchorLine = reverseAnchorLine(hCenter.anchorLine); + effectiveLeftMargin = rightMargin; + effectiveRightMargin = leftMargin; + effectiveHorizontalCenterOffset = -hCenterOffset; + } else { + effectiveLeftAnchor = QDeclarativeAnchors::LeftAnchor; + effectiveRightAnchor = QDeclarativeAnchors::RightAnchor; + effectiveLeft = left; + effectiveRight = right; + effectiveHorizontalCenter = hCenter; + effectiveLeftMargin = leftMargin; + effectiveRightMargin = rightMargin; + effectiveHorizontalCenterOffset = hCenterOffset; + } + + QGraphicsItemPrivate *itemPrivate = QGraphicsItemPrivate::get(item); + if (usedAnchors & effectiveLeftAnchor) { + //Handle stretching + bool invalid = true; + qreal width = 0.0; + if (usedAnchors & effectiveRightAnchor) { + invalid = calcStretch(effectiveLeft, effectiveRight, effectiveLeftMargin, -effectiveRightMargin, QDeclarativeAnchorLine::Left, width); + } else if (usedAnchors & QDeclarativeAnchors::HCenterAnchor) { + invalid = calcStretch(effectiveLeft, effectiveHorizontalCenter, effectiveLeftMargin, effectiveHorizontalCenterOffset, QDeclarativeAnchorLine::Left, width); + width *= 2; + } + if (!invalid) + setItemWidth(width); + + //Handle left + if (effectiveLeft.item == item->parentItem()) { + setItemX(adjustedPosition(effectiveLeft.item, effectiveLeft.anchorLine) + effectiveLeftMargin); + } else if (effectiveLeft.item->parentItem() == item->parentItem()) { + setItemX(position(effectiveLeft.item, effectiveLeft.anchorLine) + effectiveLeftMargin); + } + } else if (usedAnchors & effectiveRightAnchor) { + //Handle stretching (left + right case is handled in updateLeftAnchor) + if (usedAnchors & QDeclarativeAnchors::HCenterAnchor) { + qreal width = 0.0; + bool invalid = calcStretch(effectiveHorizontalCenter, effectiveRight, effectiveHorizontalCenterOffset, -effectiveRightMargin, + QDeclarativeAnchorLine::Left, width); + if (!invalid) + setItemWidth(width*2); + } + + //Handle right + if (effectiveRight.item == item->parentItem()) { + setItemX(adjustedPosition(effectiveRight.item, effectiveRight.anchorLine) - itemPrivate->width() - effectiveRightMargin); + } else if (effectiveRight.item->parentItem() == item->parentItem()) { + setItemX(position(effectiveRight.item, effectiveRight.anchorLine) - itemPrivate->width() - effectiveRightMargin); + } + } else if (usedAnchors & QDeclarativeAnchors::HCenterAnchor) { + //Handle hCenter + if (effectiveHorizontalCenter.item == item->parentItem()) { + setItemX(adjustedPosition(effectiveHorizontalCenter.item, effectiveHorizontalCenter.anchorLine) - hcenter(item) + effectiveHorizontalCenterOffset); + } else if (effectiveHorizontalCenter.item->parentItem() == item->parentItem()) { + setItemX(position(effectiveHorizontalCenter.item, effectiveHorizontalCenter.anchorLine) - hcenter(item) + effectiveHorizontalCenterOffset); + } + } + --updatingHorizontalAnchor; + } else { + // ### Make this certain :) + qmlInfo(item) << QDeclarativeAnchors::tr("Possible anchor loop detected on horizontal anchor."); + } +} + +QDeclarativeAnchorLine QDeclarativeAnchors::top() const +{ + Q_D(const QDeclarativeAnchors); + return d->top; +} + +void QDeclarativeAnchors::setTop(const QDeclarativeAnchorLine &edge) +{ + Q_D(QDeclarativeAnchors); + if (!d->checkVAnchorValid(edge) || d->top == edge) + return; + + d->usedAnchors |= TopAnchor; + + if (!d->checkVValid()) { + d->usedAnchors &= ~TopAnchor; + return; + } + + d->remDepend(d->top.item); + d->top = edge; + d->addDepend(d->top.item); + emit topChanged(); + d->updateVerticalAnchors(); +} + +void QDeclarativeAnchors::resetTop() +{ + Q_D(QDeclarativeAnchors); + d->usedAnchors &= ~TopAnchor; + d->remDepend(d->top.item); + d->top = QDeclarativeAnchorLine(); + emit topChanged(); + d->updateVerticalAnchors(); +} + +QDeclarativeAnchorLine QDeclarativeAnchors::bottom() const +{ + Q_D(const QDeclarativeAnchors); + return d->bottom; +} + +void QDeclarativeAnchors::setBottom(const QDeclarativeAnchorLine &edge) +{ + Q_D(QDeclarativeAnchors); + if (!d->checkVAnchorValid(edge) || d->bottom == edge) + return; + + d->usedAnchors |= BottomAnchor; + + if (!d->checkVValid()) { + d->usedAnchors &= ~BottomAnchor; + return; + } + + d->remDepend(d->bottom.item); + d->bottom = edge; + d->addDepend(d->bottom.item); + emit bottomChanged(); + d->updateVerticalAnchors(); +} + +void QDeclarativeAnchors::resetBottom() +{ + Q_D(QDeclarativeAnchors); + d->usedAnchors &= ~BottomAnchor; + d->remDepend(d->bottom.item); + d->bottom = QDeclarativeAnchorLine(); + emit bottomChanged(); + d->updateVerticalAnchors(); +} + +QDeclarativeAnchorLine QDeclarativeAnchors::verticalCenter() const +{ + Q_D(const QDeclarativeAnchors); + return d->vCenter; +} + +void QDeclarativeAnchors::setVerticalCenter(const QDeclarativeAnchorLine &edge) +{ + Q_D(QDeclarativeAnchors); + if (!d->checkVAnchorValid(edge) || d->vCenter == edge) + return; + + d->usedAnchors |= VCenterAnchor; + + if (!d->checkVValid()) { + d->usedAnchors &= ~VCenterAnchor; + return; + } + + d->remDepend(d->vCenter.item); + d->vCenter = edge; + d->addDepend(d->vCenter.item); + emit verticalCenterChanged(); + d->updateVerticalAnchors(); +} + +void QDeclarativeAnchors::resetVerticalCenter() +{ + Q_D(QDeclarativeAnchors); + d->usedAnchors &= ~VCenterAnchor; + d->remDepend(d->vCenter.item); + d->vCenter = QDeclarativeAnchorLine(); + emit verticalCenterChanged(); + d->updateVerticalAnchors(); +} + +QDeclarativeAnchorLine QDeclarativeAnchors::baseline() const +{ + Q_D(const QDeclarativeAnchors); + return d->baseline; +} + +void QDeclarativeAnchors::setBaseline(const QDeclarativeAnchorLine &edge) +{ + Q_D(QDeclarativeAnchors); + if (!d->checkVAnchorValid(edge) || d->baseline == edge) + return; + + d->usedAnchors |= BaselineAnchor; + + if (!d->checkVValid()) { + d->usedAnchors &= ~BaselineAnchor; + return; + } + + d->remDepend(d->baseline.item); + d->baseline = edge; + d->addDepend(d->baseline.item); + emit baselineChanged(); + d->updateVerticalAnchors(); +} + +void QDeclarativeAnchors::resetBaseline() +{ + Q_D(QDeclarativeAnchors); + d->usedAnchors &= ~BaselineAnchor; + d->remDepend(d->baseline.item); + d->baseline = QDeclarativeAnchorLine(); + emit baselineChanged(); + d->updateVerticalAnchors(); +} + +QDeclarativeAnchorLine QDeclarativeAnchors::left() const +{ + Q_D(const QDeclarativeAnchors); + return d->left; +} + +void QDeclarativeAnchors::setLeft(const QDeclarativeAnchorLine &edge) +{ + Q_D(QDeclarativeAnchors); + if (!d->checkHAnchorValid(edge) || d->left == edge) + return; + + d->usedAnchors |= LeftAnchor; + + if (!d->checkHValid()) { + d->usedAnchors &= ~LeftAnchor; + return; + } + + d->remDepend(d->left.item); + d->left = edge; + d->addDepend(d->left.item); + emit leftChanged(); + d->updateHorizontalAnchors(); +} + +void QDeclarativeAnchors::resetLeft() +{ + Q_D(QDeclarativeAnchors); + d->usedAnchors &= ~LeftAnchor; + d->remDepend(d->left.item); + d->left = QDeclarativeAnchorLine(); + emit leftChanged(); + d->updateHorizontalAnchors(); +} + +QDeclarativeAnchorLine QDeclarativeAnchors::right() const +{ + Q_D(const QDeclarativeAnchors); + return d->right; +} + +void QDeclarativeAnchors::setRight(const QDeclarativeAnchorLine &edge) +{ + Q_D(QDeclarativeAnchors); + if (!d->checkHAnchorValid(edge) || d->right == edge) + return; + + d->usedAnchors |= RightAnchor; + + if (!d->checkHValid()) { + d->usedAnchors &= ~RightAnchor; + return; + } + + d->remDepend(d->right.item); + d->right = edge; + d->addDepend(d->right.item); + emit rightChanged(); + d->updateHorizontalAnchors(); +} + +void QDeclarativeAnchors::resetRight() +{ + Q_D(QDeclarativeAnchors); + d->usedAnchors &= ~RightAnchor; + d->remDepend(d->right.item); + d->right = QDeclarativeAnchorLine(); + emit rightChanged(); + d->updateHorizontalAnchors(); +} + +QDeclarativeAnchorLine QDeclarativeAnchors::horizontalCenter() const +{ + Q_D(const QDeclarativeAnchors); + return d->hCenter; +} + +void QDeclarativeAnchors::setHorizontalCenter(const QDeclarativeAnchorLine &edge) +{ + Q_D(QDeclarativeAnchors); + if (!d->checkHAnchorValid(edge) || d->hCenter == edge) + return; + + d->usedAnchors |= HCenterAnchor; + + if (!d->checkHValid()) { + d->usedAnchors &= ~HCenterAnchor; + return; + } + + d->remDepend(d->hCenter.item); + d->hCenter = edge; + d->addDepend(d->hCenter.item); + emit horizontalCenterChanged(); + d->updateHorizontalAnchors(); +} + +void QDeclarativeAnchors::resetHorizontalCenter() +{ + Q_D(QDeclarativeAnchors); + d->usedAnchors &= ~HCenterAnchor; + d->remDepend(d->hCenter.item); + d->hCenter = QDeclarativeAnchorLine(); + emit horizontalCenterChanged(); + d->updateHorizontalAnchors(); +} + +qreal QDeclarativeAnchors::leftMargin() const +{ + Q_D(const QDeclarativeAnchors); + return d->leftMargin; +} + +void QDeclarativeAnchors::setLeftMargin(qreal offset) +{ + Q_D(QDeclarativeAnchors); + if (d->leftMargin == offset) + return; + d->leftMargin = offset; + if(d->fill) + d->fillChanged(); + else + d->updateHorizontalAnchors(); + emit leftMarginChanged(); +} + +qreal QDeclarativeAnchors::rightMargin() const +{ + Q_D(const QDeclarativeAnchors); + return d->rightMargin; +} + +void QDeclarativeAnchors::setRightMargin(qreal offset) +{ + Q_D(QDeclarativeAnchors); + if (d->rightMargin == offset) + return; + d->rightMargin = offset; + if(d->fill) + d->fillChanged(); + else + d->updateHorizontalAnchors(); + emit rightMarginChanged(); +} + +qreal QDeclarativeAnchors::margins() const +{ + Q_D(const QDeclarativeAnchors); + return d->margins; +} + +void QDeclarativeAnchors::setMargins(qreal offset) +{ + Q_D(QDeclarativeAnchors); + if (d->margins == offset) + return; + //###Is it significantly faster to set them directly so we can call fillChanged only once? + if(!d->rightMargin || d->rightMargin == d->margins) + setRightMargin(offset); + if(!d->leftMargin || d->leftMargin == d->margins) + setLeftMargin(offset); + if(!d->topMargin || d->topMargin == d->margins) + setTopMargin(offset); + if(!d->bottomMargin || d->bottomMargin == d->margins) + setBottomMargin(offset); + d->margins = offset; + emit marginsChanged(); + +} + +qreal QDeclarativeAnchors::horizontalCenterOffset() const +{ + Q_D(const QDeclarativeAnchors); + return d->hCenterOffset; +} + +void QDeclarativeAnchors::setHorizontalCenterOffset(qreal offset) +{ + Q_D(QDeclarativeAnchors); + if (d->hCenterOffset == offset) + return; + d->hCenterOffset = offset; + if(d->centerIn) + d->centerInChanged(); + else + d->updateHorizontalAnchors(); + emit horizontalCenterOffsetChanged(); +} + +qreal QDeclarativeAnchors::topMargin() const +{ + Q_D(const QDeclarativeAnchors); + return d->topMargin; +} + +void QDeclarativeAnchors::setTopMargin(qreal offset) +{ + Q_D(QDeclarativeAnchors); + if (d->topMargin == offset) + return; + d->topMargin = offset; + if(d->fill) + d->fillChanged(); + else + d->updateVerticalAnchors(); + emit topMarginChanged(); +} + +qreal QDeclarativeAnchors::bottomMargin() const +{ + Q_D(const QDeclarativeAnchors); + return d->bottomMargin; +} + +void QDeclarativeAnchors::setBottomMargin(qreal offset) +{ + Q_D(QDeclarativeAnchors); + if (d->bottomMargin == offset) + return; + d->bottomMargin = offset; + if(d->fill) + d->fillChanged(); + else + d->updateVerticalAnchors(); + emit bottomMarginChanged(); +} + +qreal QDeclarativeAnchors::verticalCenterOffset() const +{ + Q_D(const QDeclarativeAnchors); + return d->vCenterOffset; +} + +void QDeclarativeAnchors::setVerticalCenterOffset(qreal offset) +{ + Q_D(QDeclarativeAnchors); + if (d->vCenterOffset == offset) + return; + d->vCenterOffset = offset; + if(d->centerIn) + d->centerInChanged(); + else + d->updateVerticalAnchors(); + emit verticalCenterOffsetChanged(); +} + +qreal QDeclarativeAnchors::baselineOffset() const +{ + Q_D(const QDeclarativeAnchors); + return d->baselineOffset; +} + +void QDeclarativeAnchors::setBaselineOffset(qreal offset) +{ + Q_D(QDeclarativeAnchors); + if (d->baselineOffset == offset) + return; + d->baselineOffset = offset; + d->updateVerticalAnchors(); + emit baselineOffsetChanged(); +} + +QDeclarativeAnchors::Anchors QDeclarativeAnchors::usedAnchors() const +{ + Q_D(const QDeclarativeAnchors); + return d->usedAnchors; +} + +bool QDeclarativeAnchorsPrivate::checkHValid() const +{ + if (usedAnchors & QDeclarativeAnchors::LeftAnchor && + usedAnchors & QDeclarativeAnchors::RightAnchor && + usedAnchors & QDeclarativeAnchors::HCenterAnchor) { + qmlInfo(item) << QDeclarativeAnchors::tr("Cannot specify left, right, and hcenter anchors."); + return false; + } + + return true; +} + +bool QDeclarativeAnchorsPrivate::checkHAnchorValid(QDeclarativeAnchorLine anchor) const +{ + if (!anchor.item) { + qmlInfo(item) << QDeclarativeAnchors::tr("Cannot anchor to a null item."); + return false; + } else if (anchor.anchorLine & QDeclarativeAnchorLine::Vertical_Mask) { + qmlInfo(item) << QDeclarativeAnchors::tr("Cannot anchor a horizontal edge to a vertical edge."); + return false; + } else if (anchor.item != item->parentItem() && anchor.item->parentItem() != item->parentItem()){ + qmlInfo(item) << QDeclarativeAnchors::tr("Cannot anchor to an item that isn't a parent or sibling."); + return false; + } else if (anchor.item == item) { + qmlInfo(item) << QDeclarativeAnchors::tr("Cannot anchor item to self."); + return false; + } + + return true; +} + +bool QDeclarativeAnchorsPrivate::checkVValid() const +{ + if (usedAnchors & QDeclarativeAnchors::TopAnchor && + usedAnchors & QDeclarativeAnchors::BottomAnchor && + usedAnchors & QDeclarativeAnchors::VCenterAnchor) { + qmlInfo(item) << QDeclarativeAnchors::tr("Cannot specify top, bottom, and vcenter anchors."); + return false; + } else if (usedAnchors & QDeclarativeAnchors::BaselineAnchor && + (usedAnchors & QDeclarativeAnchors::TopAnchor || + usedAnchors & QDeclarativeAnchors::BottomAnchor || + usedAnchors & QDeclarativeAnchors::VCenterAnchor)) { + qmlInfo(item) << QDeclarativeAnchors::tr("Baseline anchor cannot be used in conjunction with top, bottom, or vcenter anchors."); + return false; + } + + return true; +} + +bool QDeclarativeAnchorsPrivate::checkVAnchorValid(QDeclarativeAnchorLine anchor) const +{ + if (!anchor.item) { + qmlInfo(item) << QDeclarativeAnchors::tr("Cannot anchor to a null item."); + return false; + } else if (anchor.anchorLine & QDeclarativeAnchorLine::Horizontal_Mask) { + qmlInfo(item) << QDeclarativeAnchors::tr("Cannot anchor a vertical edge to a horizontal edge."); + return false; + } else if (anchor.item != item->parentItem() && anchor.item->parentItem() != item->parentItem()){ + qmlInfo(item) << QDeclarativeAnchors::tr("Cannot anchor to an item that isn't a parent or sibling."); + return false; + } else if (anchor.item == item){ + qmlInfo(item) << QDeclarativeAnchors::tr("Cannot anchor item to self."); + return false; + } + + return true; +} + +QT_END_NAMESPACE + +#include + diff --git a/src/declarative/graphicsitems/qdeclarativeanchors_p.h b/src/declarative/graphicsitems/qdeclarativeanchors_p.h new file mode 100644 index 00000000..6297febd --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativeanchors_p.h @@ -0,0 +1,204 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEANCHORS_H +#define QDECLARATIVEANCHORS_H + +#include "qdeclarativeitem.h" + +#include + +#include + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeAnchorsPrivate; +class QDeclarativeAnchorLine; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeAnchors : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QDeclarativeAnchorLine left READ left WRITE setLeft RESET resetLeft NOTIFY leftChanged) + Q_PROPERTY(QDeclarativeAnchorLine right READ right WRITE setRight RESET resetRight NOTIFY rightChanged) + Q_PROPERTY(QDeclarativeAnchorLine horizontalCenter READ horizontalCenter WRITE setHorizontalCenter RESET resetHorizontalCenter NOTIFY horizontalCenterChanged) + Q_PROPERTY(QDeclarativeAnchorLine top READ top WRITE setTop RESET resetTop NOTIFY topChanged) + Q_PROPERTY(QDeclarativeAnchorLine bottom READ bottom WRITE setBottom RESET resetBottom NOTIFY bottomChanged) + Q_PROPERTY(QDeclarativeAnchorLine verticalCenter READ verticalCenter WRITE setVerticalCenter RESET resetVerticalCenter NOTIFY verticalCenterChanged) + Q_PROPERTY(QDeclarativeAnchorLine baseline READ baseline WRITE setBaseline RESET resetBaseline NOTIFY baselineChanged) + Q_PROPERTY(qreal margins READ margins WRITE setMargins NOTIFY marginsChanged) + Q_PROPERTY(qreal leftMargin READ leftMargin WRITE setLeftMargin NOTIFY leftMarginChanged) + Q_PROPERTY(qreal rightMargin READ rightMargin WRITE setRightMargin NOTIFY rightMarginChanged) + Q_PROPERTY(qreal horizontalCenterOffset READ horizontalCenterOffset WRITE setHorizontalCenterOffset NOTIFY horizontalCenterOffsetChanged) + Q_PROPERTY(qreal topMargin READ topMargin WRITE setTopMargin NOTIFY topMarginChanged) + Q_PROPERTY(qreal bottomMargin READ bottomMargin WRITE setBottomMargin NOTIFY bottomMarginChanged) + Q_PROPERTY(qreal verticalCenterOffset READ verticalCenterOffset WRITE setVerticalCenterOffset NOTIFY verticalCenterOffsetChanged) + Q_PROPERTY(qreal baselineOffset READ baselineOffset WRITE setBaselineOffset NOTIFY baselineOffsetChanged) + Q_PROPERTY(QGraphicsObject *fill READ fill WRITE setFill RESET resetFill NOTIFY fillChanged) + Q_PROPERTY(QGraphicsObject *centerIn READ centerIn WRITE setCenterIn RESET resetCenterIn NOTIFY centerInChanged) + +public: + QDeclarativeAnchors(QObject *parent=0); + QDeclarativeAnchors(QGraphicsObject *item, QObject *parent=0); + virtual ~QDeclarativeAnchors(); + + enum Anchor { + LeftAnchor = 0x01, + RightAnchor = 0x02, + TopAnchor = 0x04, + BottomAnchor = 0x08, + HCenterAnchor = 0x10, + VCenterAnchor = 0x20, + BaselineAnchor = 0x40, + Horizontal_Mask = LeftAnchor | RightAnchor | HCenterAnchor, + Vertical_Mask = TopAnchor | BottomAnchor | VCenterAnchor | BaselineAnchor + }; + Q_DECLARE_FLAGS(Anchors, Anchor) + + QDeclarativeAnchorLine left() const; + void setLeft(const QDeclarativeAnchorLine &edge); + void resetLeft(); + + QDeclarativeAnchorLine right() const; + void setRight(const QDeclarativeAnchorLine &edge); + void resetRight(); + + QDeclarativeAnchorLine horizontalCenter() const; + void setHorizontalCenter(const QDeclarativeAnchorLine &edge); + void resetHorizontalCenter(); + + QDeclarativeAnchorLine top() const; + void setTop(const QDeclarativeAnchorLine &edge); + void resetTop(); + + QDeclarativeAnchorLine bottom() const; + void setBottom(const QDeclarativeAnchorLine &edge); + void resetBottom(); + + QDeclarativeAnchorLine verticalCenter() const; + void setVerticalCenter(const QDeclarativeAnchorLine &edge); + void resetVerticalCenter(); + + QDeclarativeAnchorLine baseline() const; + void setBaseline(const QDeclarativeAnchorLine &edge); + void resetBaseline(); + + qreal leftMargin() const; + void setLeftMargin(qreal); + + qreal rightMargin() const; + void setRightMargin(qreal); + + qreal horizontalCenterOffset() const; + void setHorizontalCenterOffset(qreal); + + qreal topMargin() const; + void setTopMargin(qreal); + + qreal bottomMargin() const; + void setBottomMargin(qreal); + + qreal margins() const; + void setMargins(qreal); + + qreal verticalCenterOffset() const; + void setVerticalCenterOffset(qreal); + + qreal baselineOffset() const; + void setBaselineOffset(qreal); + + QGraphicsObject *fill() const; + void setFill(QGraphicsObject *); + void resetFill(); + + QGraphicsObject *centerIn() const; + void setCenterIn(QGraphicsObject *); + void resetCenterIn(); + + Anchors usedAnchors() const; + + void classBegin(); + void componentComplete(); + + bool mirrored(); + +Q_SIGNALS: + void leftChanged(); + void rightChanged(); + void topChanged(); + void bottomChanged(); + void verticalCenterChanged(); + void horizontalCenterChanged(); + void baselineChanged(); + void fillChanged(); + void centerInChanged(); + void leftMarginChanged(); + void rightMarginChanged(); + void topMarginChanged(); + void bottomMarginChanged(); + void marginsChanged(); + void verticalCenterOffsetChanged(); + void horizontalCenterOffsetChanged(); + void baselineOffsetChanged(); + +private: + friend class QDeclarativeItem; + friend class QDeclarativeItemPrivate; + friend class QDeclarativeGraphicsWidget; + Q_DISABLE_COPY(QDeclarativeAnchors) + Q_DECLARE_PRIVATE(QDeclarativeAnchors) + Q_PRIVATE_SLOT(d_func(), void _q_widgetGeometryChanged()) + Q_PRIVATE_SLOT(d_func(), void _q_widgetDestroyed(QObject *obj)) +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(QDeclarativeAnchors::Anchors) + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeAnchors) + +QT_END_HEADER + +#endif diff --git a/src/declarative/graphicsitems/qdeclarativeanchors_p_p.h b/src/declarative/graphicsitems/qdeclarativeanchors_p_p.h new file mode 100644 index 00000000..f76443c0 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativeanchors_p_p.h @@ -0,0 +1,170 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEANCHORS_P_H +#define QDECLARATIVEANCHORS_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 "private/qdeclarativeanchors_p.h" +#include "private/qdeclarativeitemchangelistener_p.h" +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeAnchorLine +{ +public: + QDeclarativeAnchorLine() : item(0), anchorLine(Invalid) {} + + enum AnchorLine { + Invalid = 0x0, + Left = 0x01, + Right = 0x02, + Top = 0x04, + Bottom = 0x08, + HCenter = 0x10, + VCenter = 0x20, + Baseline = 0x40, + Horizontal_Mask = Left | Right | HCenter, + Vertical_Mask = Top | Bottom | VCenter | Baseline + }; + + QGraphicsObject *item; + AnchorLine anchorLine; +}; + +inline bool operator==(const QDeclarativeAnchorLine& a, const QDeclarativeAnchorLine& b) +{ + return a.item == b.item && a.anchorLine == b.anchorLine; +} + +class QDeclarativeAnchorsPrivate : public QObjectPrivate, public QDeclarativeItemChangeListener +{ + Q_DECLARE_PUBLIC(QDeclarativeAnchors) +public: + QDeclarativeAnchorsPrivate(QGraphicsObject *i) + : componentComplete(true), updatingMe(false), updatingHorizontalAnchor(0), + updatingVerticalAnchor(0), updatingFill(0), updatingCenterIn(0), item(i), usedAnchors(0), fill(0), + centerIn(0), leftMargin(0), rightMargin(0), topMargin(0), bottomMargin(0), + margins(0), vCenterOffset(0), hCenterOffset(0), baselineOffset(0) + { + } + + void clearItem(QGraphicsObject *); + + void addDepend(QGraphicsObject *); + void remDepend(QGraphicsObject *); + bool isItemComplete() const; + + bool componentComplete:1; + bool updatingMe:1; + uint updatingHorizontalAnchor:2; + uint updatingVerticalAnchor:2; + uint updatingFill:2; + uint updatingCenterIn:2; + + void setItemHeight(qreal); + void setItemWidth(qreal); + void setItemX(qreal); + void setItemY(qreal); + void setItemPos(const QPointF &); + void setItemSize(const QSizeF &); + + void updateOnComplete(); + void updateMe(); + + // QDeclarativeItemGeometryListener interface + void itemGeometryChanged(QDeclarativeItem *, const QRectF &, const QRectF &); + void _q_widgetDestroyed(QObject *); + void _q_widgetGeometryChanged(); + QDeclarativeAnchorsPrivate *anchorPrivate() { return this; } + + bool checkHValid() const; + bool checkVValid() const; + bool checkHAnchorValid(QDeclarativeAnchorLine anchor) const; + bool checkVAnchorValid(QDeclarativeAnchorLine anchor) const; + bool calcStretch(const QDeclarativeAnchorLine &edge1, const QDeclarativeAnchorLine &edge2, qreal offset1, qreal offset2, QDeclarativeAnchorLine::AnchorLine line, qreal &stretch); + + bool isMirrored() const; + void updateHorizontalAnchors(); + void updateVerticalAnchors(); + void fillChanged(); + void centerInChanged(); + + QGraphicsObject *item; + QDeclarativeAnchors::Anchors usedAnchors; + + QGraphicsObject *fill; + QGraphicsObject *centerIn; + + QDeclarativeAnchorLine left; + QDeclarativeAnchorLine right; + QDeclarativeAnchorLine top; + QDeclarativeAnchorLine bottom; + QDeclarativeAnchorLine vCenter; + QDeclarativeAnchorLine hCenter; + QDeclarativeAnchorLine baseline; + + qreal leftMargin; + qreal rightMargin; + qreal topMargin; + qreal bottomMargin; + qreal margins; + qreal vCenterOffset; + qreal hCenterOffset; + qreal baselineOffset; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QDeclarativeAnchorLine) + +#endif diff --git a/src/declarative/graphicsitems/qdeclarativeanimatedimage.cpp b/src/declarative/graphicsitems/qdeclarativeanimatedimage.cpp new file mode 100644 index 00000000..49a8dfd2 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativeanimatedimage.cpp @@ -0,0 +1,404 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativeanimatedimage_p.h" +#include "private/qdeclarativeanimatedimage_p_p.h" + +#ifndef QT_NO_MOVIE + +#include +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmlclass AnimatedImage QDeclarativeAnimatedImage + \inherits Image + \since 4.7 + \ingroup basic-visual-elements + + The AnimatedImage element extends the features of the \l Image element, providing + a way to play animations stored as images containing a series of frames, + such as those stored in GIF files. + + Information about the current frame and totla length of the animation can be + obtained using the \l currentFrame and \l frameCount properties. You can + start, pause and stop the animation by changing the values of the \l playing + and \l paused properties. + + The full list of supported formats can be determined with QMovie::supportedFormats(). + + \section1 Example Usage + + \beginfloatleft + \image animatedimageitem.gif + \endfloat + + The following QML shows how to display an animated image and obtain information + about its state, such as the current frame and total number of frames. + The result is an animated image with a simple progress indicator underneath it. + + \clearfloat + \snippet doc/src/snippets/declarative/animatedimage.qml document + + \sa BorderImage, Image +*/ + +/*! + \qmlproperty url AnimatedImage::source + + This property holds the URL that refers to the source image. + + AnimatedImage can handle any image format supported by Qt, loaded from any + URL scheme supported by Qt. + + \sa QDeclarativeImageProvider +*/ + +/*! + \qmlproperty bool AnimatedImage::asynchronous + + Specifies that images on the local filesystem should be loaded + asynchronously in a separate thread. The default value is + false, causing the user interface thread to block while the + image is loaded. Setting \a asynchronous to true is useful where + maintaining a responsive user interface is more desirable + than having images immediately visible. + + Note that this property is only valid for images read from the + local filesystem. Images loaded via a network resource (e.g. HTTP) + are always loaded asynchonously. +*/ + +/*! + \qmlproperty bool AnimatedImage::cache + \since QtQuick 1.1 + + Specifies whether the image should be cached. The default value is + true. Setting \a cache to false is useful when dealing with large images, + to make sure that they aren't cached at the expense of small 'ui element' images. +*/ + +/*! + \qmlproperty bool AnimatedImage::mirror + \since QtQuick 1.1 + + This property holds whether the image should be horizontally inverted + (effectively displaying a mirrored image). + + The default value is false. +*/ + +QDeclarativeAnimatedImage::QDeclarativeAnimatedImage(QDeclarativeItem *parent) + : QDeclarativeImage(*(new QDeclarativeAnimatedImagePrivate), parent) +{ +} + +QDeclarativeAnimatedImage::~QDeclarativeAnimatedImage() +{ + Q_D(QDeclarativeAnimatedImage); + delete d->_movie; +} + +/*! + \qmlproperty bool AnimatedImage::paused + This property holds whether the animated image is paused. + + By default, this property is false. Set it to true when you want to pause + the animation. +*/ +bool QDeclarativeAnimatedImage::isPaused() const +{ + Q_D(const QDeclarativeAnimatedImage); + if(!d->_movie) + return false; + return d->_movie->state()==QMovie::Paused; +} + +void QDeclarativeAnimatedImage::setPaused(bool pause) +{ + Q_D(QDeclarativeAnimatedImage); + if(pause == d->paused) + return; + d->paused = pause; + if(!d->_movie) + return; + d->_movie->setPaused(pause); +} +/*! + \qmlproperty bool AnimatedImage::playing + This property holds whether the animated image is playing. + + By default, this property is true, meaning that the animation + will start playing immediately. +*/ +bool QDeclarativeAnimatedImage::isPlaying() const +{ + Q_D(const QDeclarativeAnimatedImage); + if (!d->_movie) + return false; + return d->_movie->state()!=QMovie::NotRunning; +} + +void QDeclarativeAnimatedImage::setPlaying(bool play) +{ + Q_D(QDeclarativeAnimatedImage); + if(play == d->playing) + return; + d->playing = play; + if (!d->_movie) + return; + if (play) + d->_movie->start(); + else + d->_movie->stop(); +} + +/*! + \qmlproperty int AnimatedImage::currentFrame + \qmlproperty int AnimatedImage::frameCount + + currentFrame is the frame that is currently visible. By monitoring this property + for changes, you can animate other items at the same time as the image. + + frameCount is the number of frames in the animation. For some animation formats, + frameCount is unknown and has a value of zero. +*/ +int QDeclarativeAnimatedImage::currentFrame() const +{ + Q_D(const QDeclarativeAnimatedImage); + if (!d->_movie) + return d->preset_currentframe; + return d->_movie->currentFrameNumber(); +} + +void QDeclarativeAnimatedImage::setCurrentFrame(int frame) +{ + Q_D(QDeclarativeAnimatedImage); + if (!d->_movie) { + d->preset_currentframe = frame; + return; + } + d->_movie->jumpToFrame(frame); +} + +int QDeclarativeAnimatedImage::frameCount() const +{ + Q_D(const QDeclarativeAnimatedImage); + if (!d->_movie) + return 0; + return d->_movie->frameCount(); +} + +void QDeclarativeAnimatedImage::setSource(const QUrl &url) +{ + Q_D(QDeclarativeAnimatedImage); + if (url == d->url) + return; + + delete d->_movie; + d->_movie = 0; + + if (d->reply) { + d->reply->deleteLater(); + d->reply = 0; + } + + d->url = url; + emit sourceChanged(d->url); + + if (isComponentComplete()) + load(); +} + +void QDeclarativeAnimatedImage::load() +{ + Q_D(QDeclarativeAnimatedImage); + + QDeclarativeImageBase::Status oldStatus = d->status; + qreal oldProgress = d->progress; + + if (d->url.isEmpty()) { + delete d->_movie; + d->setPixmap(QPixmap()); + d->progress = 0; + d->status = Null; + if (d->status != oldStatus) + emit statusChanged(d->status); + if (d->progress != oldProgress) + emit progressChanged(d->progress); + } else { +#ifndef QT_NO_LOCALFILE_OPTIMIZED_QML + QString lf = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(d->url); + if (!lf.isEmpty()) { + //### should be unified with movieRequestFinished + d->_movie = new QMovie(lf); + if (!d->_movie->isValid()){ + qmlInfo(this) << "Error Reading Animated Image File " << d->url.toString(); + delete d->_movie; + d->_movie = 0; + d->status = Error; + if (d->status != oldStatus) + emit statusChanged(d->status); + return; + } + connect(d->_movie, SIGNAL(stateChanged(QMovie::MovieState)), + this, SLOT(playingStatusChanged())); + connect(d->_movie, SIGNAL(frameChanged(int)), + this, SLOT(movieUpdate())); + d->_movie->setCacheMode(QMovie::CacheAll); + if(d->playing) + d->_movie->start(); + else + d->_movie->jumpToFrame(0); + if(d->paused) + d->_movie->setPaused(true); + d->setPixmap(d->_movie->currentPixmap()); + d->status = Ready; + d->progress = 1.0; + if (d->status != oldStatus) + emit statusChanged(d->status); + if (d->progress != oldProgress) + emit progressChanged(d->progress); + return; + } +#endif + d->status = Loading; + d->progress = 0; + emit statusChanged(d->status); + emit progressChanged(d->progress); + QNetworkRequest req(d->url); + req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); + d->reply = qmlEngine(this)->networkAccessManager()->get(req); + QObject::connect(d->reply, SIGNAL(finished()), + this, SLOT(movieRequestFinished())); + QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)), + this, SLOT(requestProgress(qint64,qint64))); + } +} + +#define ANIMATEDIMAGE_MAXIMUM_REDIRECT_RECURSION 16 + +void QDeclarativeAnimatedImage::movieRequestFinished() +{ + Q_D(QDeclarativeAnimatedImage); + + d->redirectCount++; + if (d->redirectCount < ANIMATEDIMAGE_MAXIMUM_REDIRECT_RECURSION) { + QVariant redirect = d->reply->attribute(QNetworkRequest::RedirectionTargetAttribute); + if (redirect.isValid()) { + QUrl url = d->reply->url().resolved(redirect.toUrl()); + d->reply->deleteLater(); + d->reply = 0; + setSource(url); + return; + } + } + d->redirectCount=0; + + d->_movie = new QMovie(d->reply); + if (!d->_movie->isValid()){ +#ifndef QT_NO_DEBUG_STREAM + qmlInfo(this) << "Error Reading Animated Image File " << d->url; +#endif + delete d->_movie; + d->_movie = 0; + d->status = Error; + emit statusChanged(d->status); + return; + } + connect(d->_movie, SIGNAL(stateChanged(QMovie::MovieState)), + this, SLOT(playingStatusChanged())); + connect(d->_movie, SIGNAL(frameChanged(int)), + this, SLOT(movieUpdate())); + d->_movie->setCacheMode(QMovie::CacheAll); + if(d->playing) + d->_movie->start(); + if (d->paused || !d->playing) { + d->_movie->jumpToFrame(d->preset_currentframe); + d->preset_currentframe = 0; + } + if(d->paused) + d->_movie->setPaused(true); + d->setPixmap(d->_movie->currentPixmap()); + d->status = Ready; + emit statusChanged(d->status); +} + +void QDeclarativeAnimatedImage::movieUpdate() +{ + Q_D(QDeclarativeAnimatedImage); + d->setPixmap(d->_movie->currentPixmap()); + emit frameChanged(); +} + +void QDeclarativeAnimatedImage::playingStatusChanged() +{ + Q_D(QDeclarativeAnimatedImage); + if((d->_movie->state() != QMovie::NotRunning) != d->playing){ + d->playing = (d->_movie->state() != QMovie::NotRunning); + emit playingChanged(); + } + if((d->_movie->state() == QMovie::Paused) != d->paused){ + d->playing = (d->_movie->state() == QMovie::Paused); + emit pausedChanged(); + } +} + +void QDeclarativeAnimatedImage::componentComplete() +{ + Q_D(QDeclarativeAnimatedImage); + QDeclarativeItem::componentComplete(); // NOT QDeclarativeImage + if (d->url.isValid()) + load(); + if (!d->reply) { + setCurrentFrame(d->preset_currentframe); + d->preset_currentframe = 0; + } +} + +QT_END_NAMESPACE + +#endif // QT_NO_MOVIE diff --git a/src/declarative/graphicsitems/qdeclarativeanimatedimage_p.h b/src/declarative/graphicsitems/qdeclarativeanimatedimage_p.h new file mode 100644 index 00000000..a72ce782 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativeanimatedimage_p.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEANIMATEDIMAGE_H +#define QDECLARATIVEANIMATEDIMAGE_H + +#include "private/qdeclarativeimage_p.h" + +#ifndef QT_NO_MOVIE + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QMovie; +class QDeclarativeAnimatedImagePrivate; + +class Q_AUTOTEST_EXPORT QDeclarativeAnimatedImage : public QDeclarativeImage +{ + Q_OBJECT + + Q_PROPERTY(bool playing READ isPlaying WRITE setPlaying NOTIFY playingChanged) + Q_PROPERTY(bool paused READ isPaused WRITE setPaused NOTIFY pausedChanged) + Q_PROPERTY(int currentFrame READ currentFrame WRITE setCurrentFrame NOTIFY frameChanged) + Q_PROPERTY(int frameCount READ frameCount) + + // read-only for AnimatedImage + Q_PROPERTY(QSize sourceSize READ sourceSize NOTIFY sourceSizeChanged) + +public: + QDeclarativeAnimatedImage(QDeclarativeItem *parent=0); + ~QDeclarativeAnimatedImage(); + + bool isPlaying() const; + void setPlaying(bool play); + + bool isPaused() const; + void setPaused(bool pause); + + int currentFrame() const; + void setCurrentFrame(int frame); + + int frameCount() const; + + // Extends QDeclarativeImage's src property*/ + virtual void setSource(const QUrl&); + +Q_SIGNALS: + void playingChanged(); + void pausedChanged(); + void frameChanged(); + void sourceSizeChanged(); + +private Q_SLOTS: + void movieUpdate(); + void movieRequestFinished(); + void playingStatusChanged(); + +protected: + virtual void load(); + void componentComplete(); + +private: + Q_DISABLE_COPY(QDeclarativeAnimatedImage) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarativeAnimatedImage) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeAnimatedImage) + +QT_END_HEADER + +#endif // QT_NO_MOVIE + +#endif diff --git a/src/declarative/graphicsitems/qdeclarativeanimatedimage_p_p.h b/src/declarative/graphicsitems/qdeclarativeanimatedimage_p_p.h new file mode 100644 index 00000000..36891d1b --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativeanimatedimage_p_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEANIMATEDIMAGE_P_H +#define QDECLARATIVEANIMATEDIMAGE_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 "private/qdeclarativeimage_p_p.h" + +#ifndef QT_NO_MOVIE + +QT_BEGIN_NAMESPACE + +class QMovie; +class QNetworkReply; + +class QDeclarativeAnimatedImagePrivate : public QDeclarativeImagePrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeAnimatedImage) + +public: + QDeclarativeAnimatedImagePrivate() + : playing(true), paused(false), preset_currentframe(0), _movie(0), reply(0), redirectCount(0) + { + } + + bool playing; + bool paused; + int preset_currentframe; + QMovie *_movie; + QNetworkReply *reply; + int redirectCount; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_MOVIE + +#endif // QDECLARATIVEANIMATEDIMAGE_P_H diff --git a/src/declarative/graphicsitems/qdeclarativeborderimage.cpp b/src/declarative/graphicsitems/qdeclarativeborderimage.cpp new file mode 100644 index 00000000..5babbb2b --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativeborderimage.cpp @@ -0,0 +1,623 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativeborderimage_p.h" +#include "private/qdeclarativeborderimage_p_p.h" + +#include +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmlclass BorderImage QDeclarativeBorderImage + \brief The BorderImage element provides an image that can be used as a border. + \inherits Item + \since 4.7 + \ingroup qml-basic-visual-elements + + The BorderImage element is used to create borders out of images by scaling or tiling + parts of each image. + + A BorderImage element breaks a source image, specified using the \l url property, + into 9 regions, as shown below: + + \image declarative-scalegrid.png + + When the image is scaled, regions of the source image are scaled or tiled to + create the displayed border image in the following way: + + \list + \i The corners (regions 1, 3, 7, and 9) are not scaled at all. + \i Regions 2 and 8 are scaled according to + \l{BorderImage::horizontalTileMode}{horizontalTileMode}. + \i Regions 4 and 6 are scaled according to + \l{BorderImage::verticalTileMode}{verticalTileMode}. + \i The middle (region 5) is scaled according to both + \l{BorderImage::horizontalTileMode}{horizontalTileMode} and + \l{BorderImage::verticalTileMode}{verticalTileMode}. + \endlist + + The regions of the image are defined using the \l border property group, which + describes the distance from each edge of the source image to use as a border. + + \section1 Example Usage + + The following examples show the effects of the different modes on an image. + Guide lines are overlaid onto the image to show the different regions of the + image as described above. + + \beginfloatleft + \image qml-borderimage-normal-image.png + \endfloat + + An unscaled image is displayed using an Image element. The \l border property is + used to determine the parts of the image that will lie inside the unscaled corner + areas and the parts that will be stretched horizontally and vertically. + + \snippet doc/src/snippets/declarative/borderimage/normal-image.qml normal image + + \clearfloat + \beginfloatleft + \image qml-borderimage-scaled.png + \endfloat + + A BorderImage element is used to display the image, and it is given a size that is + larger than the original image. Since the \l horizontalTileMode property is set to + \l{BorderImage::horizontalTileMode}{BorderImage.Stretch}, the parts of image in + regions 2 and 8 are stretched horizontally. Since the \l verticalTileMode property + is set to \l{BorderImage::verticalTileMode}{BorderImage.Stretch}, the parts of image + in regions 4 and 6 are stretched vertically. + + \snippet doc/src/snippets/declarative/borderimage/borderimage-scaled.qml scaled border image + + \clearfloat + \beginfloatleft + \image qml-borderimage-tiled.png + \endfloat + + Again, a large BorderImage element is used to display the image. With the + \l horizontalTileMode property set to \l{BorderImage::horizontalTileMode}{BorderImage.Repeat}, + the parts of image in regions 2 and 8 are tiled so that they fill the space at the + top and bottom of the element. Similarly, the \l verticalTileMode property is set to + \l{BorderImage::verticalTileMode}{BorderImage.Repeat}, the parts of image in regions + 4 and 6 are tiled so that they fill the space at the left and right of the element. + + \snippet doc/src/snippets/declarative/borderimage/borderimage-tiled.qml tiled border image + + \clearfloat + In some situations, the width of regions 2 and 8 may not be an exact multiple of the width + of the corresponding regions in the source image. Similarly, the height of regions 4 and 6 + may not be an exact multiple of the height of the corresponding regions. It can be useful + to use \l{BorderImage::horizontalTileMode}{BorderImage.Round} instead of + \l{BorderImage::horizontalTileMode}{BorderImage.Repeat} in cases like these. + + The \l{declarative/imageelements/borderimage}{BorderImage example} shows how a BorderImage + can be used to simulate a shadow effect on a rectangular item. + + \section1 Quality and Performance + + By default, any scaled regions of the image are rendered without smoothing to improve + rendering speed. Setting the \l smooth property improves rendering quality of scaled + regions, but may slow down rendering. + + The source image may not be loaded instantaneously, depending on its original location. + Loading progress can be monitored with the \l progress property. + + \sa Image, AnimatedImage + */ + +/*! + \qmlproperty bool BorderImage::asynchronous + + Specifies that images on the local filesystem should be loaded + asynchronously in a separate thread. The default value is + false, causing the user interface thread to block while the + image is loaded. Setting \a asynchronous to true is useful where + maintaining a responsive user interface is more desirable + than having images immediately visible. + + Note that this property is only valid for images read from the + local filesystem. Images loaded via a network resource (e.g. HTTP) + are always loaded asynchonously. +*/ +QDeclarativeBorderImage::QDeclarativeBorderImage(QDeclarativeItem *parent) + : QDeclarativeImageBase(*(new QDeclarativeBorderImagePrivate), parent) +{ +} + +QDeclarativeBorderImage::~QDeclarativeBorderImage() +{ + Q_D(QDeclarativeBorderImage); + if (d->sciReply) + d->sciReply->deleteLater(); +} +/*! + \qmlproperty enumeration BorderImage::status + + This property describes the status of image loading. It can be one of: + + \list + \o BorderImage.Null - no image has been set + \o BorderImage.Ready - the image has been loaded + \o BorderImage.Loading - the image is currently being loaded + \o BorderImage.Error - an error occurred while loading the image + \endlist + + \sa progress +*/ + +/*! + \qmlproperty real BorderImage::progress + + This property holds the progress of image loading, from 0.0 (nothing loaded) + to 1.0 (finished). + + \sa status +*/ + +/*! + \qmlproperty bool BorderImage::smooth + + Set this property if you want the image to be smoothly filtered when scaled or + transformed. Smooth filtering gives better visual quality, but is slower. If + the image is displayed at its natural size, this property has no visual or + performance effect. + + By default, this property is set to false. + + \note Generally scaling artifacts are only visible if the image is stationary on + the screen. A common pattern when animating an image is to disable smooth + filtering at the beginning of the animation and enable it at the conclusion. +*/ + +/*! + \qmlproperty bool BorderImage::cache + \since QtQuick 1.1 + + Specifies whether the image should be cached. The default value is + true. Setting \a cache to false is useful when dealing with large images, + to make sure that they aren't cached at the expense of small 'ui element' images. +*/ + +/*! + \qmlproperty bool BorderImage::mirror + \since QtQuick 1.1 + + This property holds whether the image should be horizontally inverted + (effectively displaying a mirrored image). + + The default value is false. +*/ + +/*! + \qmlproperty url BorderImage::source + + This property holds the URL that refers to the source image. + + BorderImage can handle any image format supported by Qt, loaded from any + URL scheme supported by Qt. + + This property can also be used to refer to .sci files, which are + written in a QML-specific, text-based format that specifies the + borders, the image file and the tile rules for a given border image. + + The following .sci file sets the borders to 10 on each side for the + image \c picture.png: + + \code + border.left: 10 + border.top: 10 + border.bottom: 10 + border.right: 10 + source: "picture.png" + \endcode + + The URL may be absolute, or relative to the URL of the component. + + \sa QDeclarativeImageProvider +*/ + +/*! + \qmlproperty QSize BorderImage::sourceSize + + This property holds the actual width and height of the loaded image. + + In BorderImage, this property is read-only. + + \sa Image::sourceSize +*/ +void QDeclarativeBorderImage::setSource(const QUrl &url) +{ + Q_D(QDeclarativeBorderImage); + //equality is fairly expensive, so we bypass for simple, common case + if ((d->url.isEmpty() == url.isEmpty()) && url == d->url) + return; + + if (d->sciReply) { + d->sciReply->deleteLater(); + d->sciReply = 0; + } + + d->url = url; + d->sciurl = QUrl(); + emit sourceChanged(d->url); + + if (isComponentComplete()) + load(); +} + +void QDeclarativeBorderImage::setSourceSize(const QSize& size) +{ + Q_UNUSED(size); + qmlInfo(this) << "Setting sourceSize for borderImage not supported"; +} + +void QDeclarativeBorderImage::load() +{ + Q_D(QDeclarativeBorderImage); + if (d->progress != 0.0) { + d->progress = 0.0; + emit progressChanged(d->progress); + } + + if (d->url.isEmpty()) { + d->pix.clear(this); + d->status = Null; + setImplicitWidth(0); + setImplicitHeight(0); + emit statusChanged(d->status); + update(); + } else { + d->status = Loading; + if (d->url.path().endsWith(QLatin1String("sci"))) { +#ifndef QT_NO_LOCALFILE_OPTIMIZED_QML + QString lf = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(d->url); + if (!lf.isEmpty()) { + QFile file(lf); + file.open(QIODevice::ReadOnly); + setGridScaledImage(QDeclarativeGridScaledImage(&file)); + } else +#endif + { + QNetworkRequest req(d->url); + d->sciReply = qmlEngine(this)->networkAccessManager()->get(req); + + static int sciReplyFinished = -1; + static int thisSciRequestFinished = -1; + if (sciReplyFinished == -1) { + sciReplyFinished = + QNetworkReply::staticMetaObject.indexOfSignal("finished()"); + thisSciRequestFinished = + QDeclarativeBorderImage::staticMetaObject.indexOfSlot("sciRequestFinished()"); + } + + QMetaObject::connect(d->sciReply, sciReplyFinished, this, + thisSciRequestFinished, Qt::DirectConnection); + } + } else { + + QDeclarativePixmap::Options options; + if (d->async) + options |= QDeclarativePixmap::Asynchronous; + if (d->cache) + options |= QDeclarativePixmap::Cache; + d->pix.clear(this); + d->pix.load(qmlEngine(this), d->url, options); + + if (d->pix.isLoading()) { + d->pix.connectFinished(this, SLOT(requestFinished())); + d->pix.connectDownloadProgress(this, SLOT(requestProgress(qint64,qint64))); + } else { + QSize impsize = d->pix.implicitSize(); + setImplicitWidth(impsize.width()); + setImplicitHeight(impsize.height()); + + if (d->pix.isReady()) { + d->status = Ready; + } else { + d->status = Error; + qmlInfo(this) << d->pix.error(); + } + + d->progress = 1.0; + emit statusChanged(d->status); + emit progressChanged(d->progress); + requestFinished(); + update(); + } + } + } + + emit statusChanged(d->status); +} + +/*! + \qmlproperty int BorderImage::border.left + \qmlproperty int BorderImage::border.right + \qmlproperty int BorderImage::border.top + \qmlproperty int BorderImage::border.bottom + + The 4 border lines (2 horizontal and 2 vertical) break the image into 9 sections, + as shown below: + + \image declarative-scalegrid.png + + Each border line (left, right, top, and bottom) specifies an offset in pixels + from the respective edge of the source image. By default, each border line has + a value of 0. + + For example, the following definition sets the bottom line 10 pixels up from + the bottom of the image: + + \qml + BorderImage { + border.bottom: 10 + // ... + } + \endqml + + The border lines can also be specified using a + \l {BorderImage::source}{.sci file}. +*/ + +QDeclarativeScaleGrid *QDeclarativeBorderImage::border() +{ + Q_D(QDeclarativeBorderImage); + return d->getScaleGrid(); +} + +/*! + \qmlproperty enumeration BorderImage::horizontalTileMode + \qmlproperty enumeration BorderImage::verticalTileMode + + This property describes how to repeat or stretch the middle parts of the border image. + + \list + \o BorderImage.Stretch - Scales the image to fit to the available area. + \o BorderImage.Repeat - Tile the image until there is no more space. May crop the last image. + \o BorderImage.Round - Like Repeat, but scales the images down to ensure that the last image is not cropped. + \endlist + + The default tile mode for each property is BorderImage.Stretch. +*/ +QDeclarativeBorderImage::TileMode QDeclarativeBorderImage::horizontalTileMode() const +{ + Q_D(const QDeclarativeBorderImage); + return d->horizontalTileMode; +} + +void QDeclarativeBorderImage::setHorizontalTileMode(TileMode t) +{ + Q_D(QDeclarativeBorderImage); + if (t != d->horizontalTileMode) { + d->horizontalTileMode = t; + emit horizontalTileModeChanged(); + update(); + } +} + +QDeclarativeBorderImage::TileMode QDeclarativeBorderImage::verticalTileMode() const +{ + Q_D(const QDeclarativeBorderImage); + return d->verticalTileMode; +} + +void QDeclarativeBorderImage::setVerticalTileMode(TileMode t) +{ + Q_D(QDeclarativeBorderImage); + if (t != d->verticalTileMode) { + d->verticalTileMode = t; + emit verticalTileModeChanged(); + update(); + } +} + +void QDeclarativeBorderImage::setGridScaledImage(const QDeclarativeGridScaledImage& sci) +{ + Q_D(QDeclarativeBorderImage); + if (!sci.isValid()) { + d->status = Error; + emit statusChanged(d->status); + } else { + QDeclarativeScaleGrid *sg = border(); + sg->setTop(sci.gridTop()); + sg->setBottom(sci.gridBottom()); + sg->setLeft(sci.gridLeft()); + sg->setRight(sci.gridRight()); + d->horizontalTileMode = sci.horizontalTileRule(); + d->verticalTileMode = sci.verticalTileRule(); + + d->sciurl = d->url.resolved(QUrl(sci.pixmapUrl())); + + QDeclarativePixmap::Options options; + if (d->async) + options |= QDeclarativePixmap::Asynchronous; + if (d->cache) + options |= QDeclarativePixmap::Cache; + d->pix.clear(this); + d->pix.load(qmlEngine(this), d->sciurl, options); + + if (d->pix.isLoading()) { + static int thisRequestProgress = -1; + static int thisRequestFinished = -1; + if (thisRequestProgress == -1) { + thisRequestProgress = + QDeclarativeBorderImage::staticMetaObject.indexOfSlot("requestProgress(qint64,qint64)"); + thisRequestFinished = + QDeclarativeBorderImage::staticMetaObject.indexOfSlot("requestFinished()"); + } + + d->pix.connectFinished(this, thisRequestFinished); + d->pix.connectDownloadProgress(this, thisRequestProgress); + + } else { + + QSize impsize = d->pix.implicitSize(); + setImplicitWidth(impsize.width()); + setImplicitHeight(impsize.height()); + + if (d->pix.isReady()) { + d->status = Ready; + } else { + d->status = Error; + qmlInfo(this) << d->pix.error(); + } + + d->progress = 1.0; + emit statusChanged(d->status); + emit progressChanged(1.0); + update(); + + } + } +} + +void QDeclarativeBorderImage::requestFinished() +{ + Q_D(QDeclarativeBorderImage); + + QSize impsize = d->pix.implicitSize(); + if (d->pix.isError()) { + d->status = Error; + qmlInfo(this) << d->pix.error(); + } else { + d->status = Ready; + } + + setImplicitWidth(impsize.width()); + setImplicitHeight(impsize.height()); + + if (d->sourcesize.width() != d->pix.width() || d->sourcesize.height() != d->pix.height()) + emit sourceSizeChanged(); + + d->progress = 1.0; + emit statusChanged(d->status); + emit progressChanged(1.0); + update(); +} + +#define BORDERIMAGE_MAX_REDIRECT 16 + +void QDeclarativeBorderImage::sciRequestFinished() +{ + Q_D(QDeclarativeBorderImage); + + d->redirectCount++; + if (d->redirectCount < BORDERIMAGE_MAX_REDIRECT) { + QVariant redirect = d->sciReply->attribute(QNetworkRequest::RedirectionTargetAttribute); + if (redirect.isValid()) { + QUrl url = d->sciReply->url().resolved(redirect.toUrl()); + setSource(url); + return; + } + } + d->redirectCount=0; + + if (d->sciReply->error() != QNetworkReply::NoError) { + d->status = Error; + d->sciReply->deleteLater(); + d->sciReply = 0; + emit statusChanged(d->status); + } else { + QDeclarativeGridScaledImage sci(d->sciReply); + d->sciReply->deleteLater(); + d->sciReply = 0; + setGridScaledImage(sci); + } +} + +void QDeclarativeBorderImage::doUpdate() +{ + update(); +} + +void QDeclarativeBorderImage::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *) +{ + Q_D(QDeclarativeBorderImage); + if (d->pix.isNull() || d->width() <= 0.0 || d->height() <= 0.0) + return; + + bool oldAA = p->testRenderHint(QPainter::Antialiasing); + bool oldSmooth = p->testRenderHint(QPainter::SmoothPixmapTransform); + QTransform oldTransform; + if (d->smooth) + p->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform, d->smooth); + if (d->mirror) { + oldTransform = p->transform(); + QTransform mirror; + mirror.translate(d->width(), 0).scale(-1, 1.0); + p->setWorldTransform(mirror * oldTransform); + } + + const QDeclarativeScaleGrid *border = d->getScaleGrid(); + int left = border->left(); + int right = border->right(); + qreal borderWidth = left + right; + if (borderWidth > 0.0 && d->width() < borderWidth) { + qreal diff = borderWidth - d->width() - 1; + left -= qRound(diff * qreal(left) / borderWidth); + right -= qRound(diff * qreal(right) / borderWidth); + } + int top = border->top(); + int bottom = border->bottom(); + qreal borderHeight = top + bottom; + if (borderHeight > 0.0 && d->height() < borderHeight) { + qreal diff = borderHeight - d->height() - 1; + top -= qRound(diff * qreal(top) / borderHeight); + bottom -= qRound(diff * qreal(bottom) / borderHeight); + } + QMargins margins(left, top, right, bottom); + QTileRules rules((Qt::TileRule)d->horizontalTileMode, (Qt::TileRule)d->verticalTileMode); + qDrawBorderPixmap(p, QRect(0, 0, (int)d->width(), (int)d->height()), margins, d->pix, d->pix.rect(), margins, rules); + if (d->smooth) { + p->setRenderHint(QPainter::Antialiasing, oldAA); + p->setRenderHint(QPainter::SmoothPixmapTransform, oldSmooth); + } + if (d->mirror) + p->setWorldTransform(oldTransform); +} + +QT_END_NAMESPACE diff --git a/src/declarative/graphicsitems/qdeclarativeborderimage_p.h b/src/declarative/graphicsitems/qdeclarativeborderimage_p.h new file mode 100644 index 00000000..3db573c8 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativeborderimage_p.h @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEBORDERIMAGE_H +#define QDECLARATIVEBORDERIMAGE_H + +#include "private/qdeclarativeimagebase_p.h" + +#include + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeScaleGrid; +class QDeclarativeGridScaledImage; +class QDeclarativeBorderImagePrivate; +class Q_AUTOTEST_EXPORT QDeclarativeBorderImage : public QDeclarativeImageBase +{ + Q_OBJECT + Q_ENUMS(TileMode) + + Q_PROPERTY(QDeclarativeScaleGrid *border READ border CONSTANT) + Q_PROPERTY(TileMode horizontalTileMode READ horizontalTileMode WRITE setHorizontalTileMode NOTIFY horizontalTileModeChanged) + Q_PROPERTY(TileMode verticalTileMode READ verticalTileMode WRITE setVerticalTileMode NOTIFY verticalTileModeChanged) + +public: + QDeclarativeBorderImage(QDeclarativeItem *parent=0); + ~QDeclarativeBorderImage(); + + QDeclarativeScaleGrid *border(); + + enum TileMode { Stretch = Qt::StretchTile, Repeat = Qt::RepeatTile, Round = Qt::RoundTile }; + + TileMode horizontalTileMode() const; + void setHorizontalTileMode(TileMode); + + TileMode verticalTileMode() const; + void setVerticalTileMode(TileMode); + + void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *); + void setSource(const QUrl &url); + + void setSourceSize(const QSize&); + +Q_SIGNALS: + void horizontalTileModeChanged(); + void verticalTileModeChanged(); + +protected: + virtual void load(); + +private: + void setGridScaledImage(const QDeclarativeGridScaledImage& sci); + +private Q_SLOTS: + void doUpdate(); + void requestFinished(); + void sciRequestFinished(); + +private: + Q_DISABLE_COPY(QDeclarativeBorderImage) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarativeBorderImage) +}; + +QT_END_NAMESPACE +QML_DECLARE_TYPE(QDeclarativeBorderImage) +QT_END_HEADER + +#endif // QDECLARATIVEBORDERIMAGE_H diff --git a/src/declarative/graphicsitems/qdeclarativeborderimage_p_p.h b/src/declarative/graphicsitems/qdeclarativeborderimage_p_p.h new file mode 100644 index 00000000..872175f4 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativeborderimage_p_p.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEBORDERIMAGE_P_H +#define QDECLARATIVEBORDERIMAGE_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 "private/qdeclarativeimagebase_p_p.h" +#include "private/qdeclarativescalegrid_p_p.h" + +QT_BEGIN_NAMESPACE + +class QNetworkReply; +class QDeclarativeBorderImagePrivate : public QDeclarativeImageBasePrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeBorderImage) + +public: + QDeclarativeBorderImagePrivate() + : border(0), sciReply(0), + horizontalTileMode(QDeclarativeBorderImage::Stretch), + verticalTileMode(QDeclarativeBorderImage::Stretch), + redirectCount(0) + { + } + + ~QDeclarativeBorderImagePrivate() + { + } + + + QDeclarativeScaleGrid *getScaleGrid() + { + Q_Q(QDeclarativeBorderImage); + if (!border) { + border = new QDeclarativeScaleGrid(q); + static int borderChangedSignalIdx = -1; + static int doUpdateSlotIdx = -1; + if (borderChangedSignalIdx < 0) + borderChangedSignalIdx = QDeclarativeScaleGrid::staticMetaObject.indexOfSignal("borderChanged()"); + if (doUpdateSlotIdx < 0) + doUpdateSlotIdx = QDeclarativeBorderImage::staticMetaObject.indexOfSlot("doUpdate()"); + QMetaObject::connect(border, borderChangedSignalIdx, q, doUpdateSlotIdx); + } + return border; + } + + QDeclarativeScaleGrid *border; + QUrl sciurl; + QNetworkReply *sciReply; + QDeclarativeBorderImage::TileMode horizontalTileMode; + QDeclarativeBorderImage::TileMode verticalTileMode; + int redirectCount; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVEBORDERIMAGE_P_H diff --git a/src/declarative/graphicsitems/qdeclarativeevents.cpp b/src/declarative/graphicsitems/qdeclarativeevents.cpp new file mode 100644 index 00000000..59f82f67 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativeevents.cpp @@ -0,0 +1,237 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativeevents_p_p.h" + +QT_BEGIN_NAMESPACE +/*! + \qmlclass KeyEvent QDeclarativeKeyEvent + \since 4.7 + \ingroup qml-event-elements + + \brief The KeyEvent object provides information about a key event. + + For example, the following changes the Item's state property when the Enter + key is pressed: + \qml +Item { + focus: true + Keys.onPressed: { if (event.key == Qt.Key_Enter) state = 'ShowDetails'; } +} + \endqml +*/ + +/*! + \qmlproperty int KeyEvent::key + + This property holds the code of the key that was pressed or released. + + See \l {Qt::Key}{Qt.Key} for the list of keyboard codes. These codes are + independent of the underlying window system. Note that this + function does not distinguish between capital and non-capital + letters, use the text() function (returning the Unicode text the + key generated) for this purpose. + + A value of either 0 or \l {Qt::Key_unknown}{Qt.Key_Unknown} means that the event is not + the result of a known key; for example, it may be the result of + a compose sequence, a keyboard macro, or due to key event + compression. +*/ + +/*! + \qmlproperty string KeyEvent::text + + This property holds the Unicode text that the key generated. + The text returned can be an empty string in cases where modifier keys, + such as Shift, Control, Alt, and Meta, are being pressed or released. + In such cases \c key will contain a valid value +*/ + +/*! + \qmlproperty bool KeyEvent::isAutoRepeat + + This property holds whether this event comes from an auto-repeating key. +*/ + +/*! + \qmlproperty int KeyEvent::count + + This property holds the number of keys involved in this event. If \l KeyEvent::text + is not empty, this is simply the length of the string. +*/ + +/*! + \qmlproperty bool KeyEvent::accepted + + Setting \a accepted to true prevents the key event from being + propagated to the item's parent. + + Generally, if the item acts on the key event then it should be accepted + so that ancestor items do not also respond to the same event. +*/ + +/*! + \qmlproperty int KeyEvent::modifiers + + This property holds the keyboard modifier flags that existed immediately + before the event occurred. + + It contains a bitwise combination of: + \list + \o Qt.NoModifier - No modifier key is pressed. + \o Qt.ShiftModifier - A Shift key on the keyboard is pressed. + \o Qt.ControlModifier - A Ctrl key on the keyboard is pressed. + \o Qt.AltModifier - An Alt key on the keyboard is pressed. + \o Qt.MetaModifier - A Meta key on the keyboard is pressed. + \o Qt.KeypadModifier - A keypad button is pressed. + \endlist + + For example, to react to a Shift key + Enter key combination: + \qml + Item { + focus: true + Keys.onPressed: { + if ((event.key == Qt.Key_Enter) && (event.modifiers & Qt.ShiftModifier)) + doSomething(); + } + } + \endqml +*/ + + +/*! + \qmlclass MouseEvent QDeclarativeMouseEvent + \since 4.7 + \ingroup qml-event-elements + + \brief The MouseEvent object provides information about a mouse event. + + The position of the mouse can be found via the \l x and \l y properties. + The button that caused the event is available via the \l button property. + + \sa MouseArea +*/ + +/*! + \internal + \class QDeclarativeMouseEvent +*/ + +/*! + \qmlproperty int MouseEvent::x + \qmlproperty int MouseEvent::y + + These properties hold the coordinates of the position supplied by the mouse event. +*/ + + +/*! + \qmlproperty bool MouseEvent::accepted + + Setting \a accepted to true prevents the mouse event from being + propagated to items below this item. + + Generally, if the item acts on the mouse event then it should be accepted + so that items lower in the stacking order do not also respond to the same event. +*/ + +/*! + \qmlproperty enumeration MouseEvent::button + + This property holds the button that caused the event. It can be one of: + \list + \o Qt.LeftButton + \o Qt.RightButton + \o Qt.MiddleButton + \endlist +*/ + +/*! + \qmlproperty bool MouseEvent::wasHeld + + This property is true if the mouse button has been held pressed longer the + threshold (800ms). +*/ + +/*! + \qmlproperty int MouseEvent::buttons + + This property holds the mouse buttons pressed when the event was generated. + For mouse move events, this is all buttons that are pressed down. For mouse + press and double click events this includes the button that caused the event. + For mouse release events this excludes the button that caused the event. + + It contains a bitwise combination of: + \list + \o Qt.LeftButton + \o Qt.RightButton + \o Qt.MiddleButton + \endlist +*/ + +/*! + \qmlproperty int MouseEvent::modifiers + + This property holds the keyboard modifier flags that existed immediately + before the event occurred. + + It contains a bitwise combination of: + \list + \o Qt.NoModifier - No modifier key is pressed. + \o Qt.ShiftModifier - A Shift key on the keyboard is pressed. + \o Qt.ControlModifier - A Ctrl key on the keyboard is pressed. + \o Qt.AltModifier - An Alt key on the keyboard is pressed. + \o Qt.MetaModifier - A Meta key on the keyboard is pressed. + \o Qt.KeypadModifier - A keypad button is pressed. + \endlist + + For example, to react to a Shift key + Left mouse button click: + \qml + MouseArea { + onClicked: { + if ((mouse.button == Qt.LeftButton) && (mouse.modifiers & Qt.ShiftModifier)) + doSomething(); + } + } + \endqml +*/ + +QT_END_NAMESPACE diff --git a/src/declarative/graphicsitems/qdeclarativeevents_p_p.h b/src/declarative/graphicsitems/qdeclarativeevents_p_p.h new file mode 100644 index 00000000..b9a2b55f --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativeevents_p_p.h @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEEVENTS_P_H +#define QDECLARATIVEEVENTS_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 + +#include +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeKeyEvent : public QObject +{ + Q_OBJECT + Q_PROPERTY(int key READ key) + Q_PROPERTY(QString text READ text) + Q_PROPERTY(int modifiers READ modifiers) + Q_PROPERTY(bool isAutoRepeat READ isAutoRepeat) + Q_PROPERTY(int count READ count) + Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted) + +public: + QDeclarativeKeyEvent(QEvent::Type type, int key, Qt::KeyboardModifiers modifiers, const QString &text=QString(), bool autorep=false, ushort count=1) + : event(type, key, modifiers, text, autorep, count) { event.setAccepted(false); } + QDeclarativeKeyEvent(const QKeyEvent &ke) + : event(ke) { event.setAccepted(false); } + + int key() const { return event.key(); } + QString text() const { return event.text(); } + int modifiers() const { return event.modifiers(); } + bool isAutoRepeat() const { return event.isAutoRepeat(); } + int count() const { return event.count(); } + + bool isAccepted() { return event.isAccepted(); } + void setAccepted(bool accepted) { event.setAccepted(accepted); } + +private: + QKeyEvent event; +}; + +class QDeclarativeMouseEvent : public QObject +{ + Q_OBJECT + Q_PROPERTY(int x READ x) + Q_PROPERTY(int y READ y) + Q_PROPERTY(int button READ button) + Q_PROPERTY(int buttons READ buttons) + Q_PROPERTY(int modifiers READ modifiers) + Q_PROPERTY(bool wasHeld READ wasHeld) + Q_PROPERTY(bool isClick READ isClick) + Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted) + +public: + QDeclarativeMouseEvent(int x, int y, Qt::MouseButton button, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers + , bool isClick=false, bool wasHeld=false) + : _x(x), _y(y), _button(button), _buttons(buttons), _modifiers(modifiers) + , _wasHeld(wasHeld), _isClick(isClick), _accepted(true) {} + + int x() const { return _x; } + int y() const { return _y; } + int button() const { return _button; } + int buttons() const { return _buttons; } + int modifiers() const { return _modifiers; } + bool wasHeld() const { return _wasHeld; } + bool isClick() const { return _isClick; } + + // only for internal usage + void setX(int x) { _x = x; } + void setY(int y) { _y = y; } + + bool isAccepted() { return _accepted; } + void setAccepted(bool accepted) { _accepted = accepted; } + +private: + int _x; + int _y; + Qt::MouseButton _button; + Qt::MouseButtons _buttons; + Qt::KeyboardModifiers _modifiers; + bool _wasHeld; + bool _isClick; + bool _accepted; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeKeyEvent) +QML_DECLARE_TYPE(QDeclarativeMouseEvent) + +#endif // QDECLARATIVEEVENTS_P_H diff --git a/src/declarative/graphicsitems/qdeclarativeflickable.cpp b/src/declarative/graphicsitems/qdeclarativeflickable.cpp new file mode 100644 index 00000000..229d04bd --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativeflickable.cpp @@ -0,0 +1,1805 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativeflickable_p.h" +#include "private/qdeclarativeflickable_p_p.h" +#include +#include +#include +#include +#include "qplatformdefs.h" + +QT_BEGIN_NAMESPACE + +// The maximum number of pixels a flick can overshoot +#ifndef QML_FLICK_OVERSHOOT +#define QML_FLICK_OVERSHOOT 200 +#endif + +// The number of samples to use in calculating the velocity of a flick +#ifndef QML_FLICK_SAMPLEBUFFER +#define QML_FLICK_SAMPLEBUFFER 3 +#endif + +// The number of samples to discard when calculating the flick velocity. +// Touch panels often produce inaccurate results as the finger is lifted. +#ifndef QML_FLICK_DISCARDSAMPLES +#define QML_FLICK_DISCARDSAMPLES 1 +#endif + +// The default maximum velocity of a flick. +#ifndef QML_FLICK_DEFAULTMAXVELOCITY +#define QML_FLICK_DEFAULTMAXVELOCITY 2500 +#endif + +// The default deceleration of a flick. +#ifndef QML_FLICK_DEFAULTDECELERATION +#define QML_FLICK_DEFAULTDECELERATION 1750 +#endif + +// How much faster to decelerate when overshooting +#ifndef QML_FLICK_OVERSHOOTFRICTION +#define QML_FLICK_OVERSHOOTFRICTION 8 +#endif + +// FlickThreshold determines how far the "mouse" must have moved +// before we perform a flick. +static const int FlickThreshold = 20; + +// RetainGrabVelocity is the maxmimum instantaneous velocity that +// will ensure the Flickable retains the grab on consecutive flicks. +static const int RetainGrabVelocity = 15; + +QDeclarativeFlickableVisibleArea::QDeclarativeFlickableVisibleArea(QDeclarativeFlickable *parent) + : QObject(parent), flickable(parent), m_xPosition(0.), m_widthRatio(0.) + , m_yPosition(0.), m_heightRatio(0.) +{ +} + +qreal QDeclarativeFlickableVisibleArea::widthRatio() const +{ + return m_widthRatio; +} + +qreal QDeclarativeFlickableVisibleArea::xPosition() const +{ + return m_xPosition; +} + +qreal QDeclarativeFlickableVisibleArea::heightRatio() const +{ + return m_heightRatio; +} + +qreal QDeclarativeFlickableVisibleArea::yPosition() const +{ + return m_yPosition; +} + +void QDeclarativeFlickableVisibleArea::updateVisible() +{ + QDeclarativeFlickablePrivate *p = static_cast(QGraphicsItemPrivate::get(flickable)); + + bool changeX = false; + bool changeY = false; + bool changeWidth = false; + bool changeHeight = false; + + // Vertical + const qreal viewheight = flickable->height(); + const qreal maxyextent = -flickable->maxYExtent() + flickable->minYExtent(); + qreal pagePos = (-p->vData.move.value() + flickable->minYExtent()) / (maxyextent + viewheight); + qreal pageSize = viewheight / (maxyextent + viewheight); + + if (pageSize != m_heightRatio) { + m_heightRatio = pageSize; + changeHeight = true; + } + if (pagePos != m_yPosition) { + m_yPosition = pagePos; + changeY = true; + } + + // Horizontal + const qreal viewwidth = flickable->width(); + const qreal maxxextent = -flickable->maxXExtent() + flickable->minXExtent(); + pagePos = (-p->hData.move.value() + flickable->minXExtent()) / (maxxextent + viewwidth); + pageSize = viewwidth / (maxxextent + viewwidth); + + if (pageSize != m_widthRatio) { + m_widthRatio = pageSize; + changeWidth = true; + } + if (pagePos != m_xPosition) { + m_xPosition = pagePos; + changeX = true; + } + + if (changeX) + emit xPositionChanged(m_xPosition); + if (changeY) + emit yPositionChanged(m_yPosition); + if (changeWidth) + emit widthRatioChanged(m_widthRatio); + if (changeHeight) + emit heightRatioChanged(m_heightRatio); +} + + +QDeclarativeFlickablePrivate::QDeclarativeFlickablePrivate() + : contentItem(new QDeclarativeItem) + , hData(this, &QDeclarativeFlickablePrivate::setRoundedViewportX) + , vData(this, &QDeclarativeFlickablePrivate::setRoundedViewportY) + , hMoved(false), vMoved(false) + , stealMouse(false), pressed(false), interactive(true), calcVelocity(false) + , deceleration(QML_FLICK_DEFAULTDECELERATION) + , maxVelocity(QML_FLICK_DEFAULTMAXVELOCITY), reportedVelocitySmoothing(100) + , delayedPressEvent(0), delayedPressTarget(0), pressDelay(0), fixupDuration(400) + , fixupMode(Normal), vTime(0), visibleArea(0) + , flickableDirection(QDeclarativeFlickable::AutoFlickDirection) + , boundsBehavior(QDeclarativeFlickable::DragAndOvershootBounds) +{ +} + +void QDeclarativeFlickablePrivate::init() +{ + Q_Q(QDeclarativeFlickable); + QDeclarative_setParent_noEvent(contentItem, q); + contentItem->setParentItem(q); + static int timelineUpdatedIdx = -1; + static int timelineCompletedIdx = -1; + static int flickableTickedIdx = -1; + static int flickableMovementEndingIdx = -1; + if (timelineUpdatedIdx == -1) { + timelineUpdatedIdx = QDeclarativeTimeLine::staticMetaObject.indexOfSignal("updated()"); + timelineCompletedIdx = QDeclarativeTimeLine::staticMetaObject.indexOfSignal("completed()"); + flickableTickedIdx = QDeclarativeFlickable::staticMetaObject.indexOfSlot("ticked()"); + flickableMovementEndingIdx = QDeclarativeFlickable::staticMetaObject.indexOfSlot("movementEnding()"); + } + QMetaObject::connect(&timeline, timelineUpdatedIdx, + q, flickableTickedIdx, Qt::DirectConnection); + QMetaObject::connect(&timeline, timelineCompletedIdx, + q, flickableMovementEndingIdx, Qt::DirectConnection); + q->setAcceptedMouseButtons(Qt::LeftButton); + q->setFiltersChildEvents(true); + QDeclarativeItemPrivate *viewportPrivate = static_cast(QGraphicsItemPrivate::get(contentItem)); + viewportPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry); + lastPosTime.invalidate(); +} + +/* + Returns the amount to overshoot by given a view size. + Will be up to the lesser of 1/3 of the view size or QML_FLICK_OVERSHOOT +*/ +qreal QDeclarativeFlickablePrivate::overShootDistance(qreal size) +{ + if (maxVelocity <= 0) + return 0.0; + + return qMin(qreal(QML_FLICK_OVERSHOOT), size/3); +} + +void QDeclarativeFlickablePrivate::AxisData::addVelocitySample(qreal v, qreal maxVelocity) +{ + if (v > maxVelocity) + v = maxVelocity; + else if (v < -maxVelocity) + v = -maxVelocity; + velocityBuffer.append(v); + if (velocityBuffer.count() > QML_FLICK_SAMPLEBUFFER) + velocityBuffer.remove(0); +} + +void QDeclarativeFlickablePrivate::AxisData::updateVelocity() +{ + velocity = 0; + if (velocityBuffer.count() > QML_FLICK_DISCARDSAMPLES) { + int count = velocityBuffer.count()-QML_FLICK_DISCARDSAMPLES; + for (int i = 0; i < count; ++i) { + qreal v = velocityBuffer.at(i); + velocity += v; + } + velocity /= count; + } +} + +void QDeclarativeFlickablePrivate::itemGeometryChanged(QDeclarativeItem *item, const QRectF &newGeom, const QRectF &oldGeom) +{ + Q_Q(QDeclarativeFlickable); + if (item == contentItem) { + if (newGeom.x() != oldGeom.x()) + emit q->contentXChanged(); + if (newGeom.y() != oldGeom.y()) + emit q->contentYChanged(); + } +} + +void QDeclarativeFlickablePrivate::flickX(qreal velocity) +{ + Q_Q(QDeclarativeFlickable); + flick(hData, q->minXExtent(), q->maxXExtent(), q->width(), fixupX_callback, velocity); +} + +void QDeclarativeFlickablePrivate::flickY(qreal velocity) +{ + Q_Q(QDeclarativeFlickable); + flick(vData, q->minYExtent(), q->maxYExtent(), q->height(), fixupY_callback, velocity); +} + +void QDeclarativeFlickablePrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal, + QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity) +{ + Q_Q(QDeclarativeFlickable); + qreal maxDistance = -1; + data.fixingUp = false; + // -ve velocity means list is moving up + if (velocity > 0) { + maxDistance = qAbs(minExtent - data.move.value()); + data.flickTarget = minExtent; + } else { + maxDistance = qAbs(maxExtent - data.move.value()); + data.flickTarget = maxExtent; + } + if (maxDistance > 0) { + qreal v = velocity; + if (maxVelocity != -1 && maxVelocity < qAbs(v)) { + if (v < 0) + v = -maxVelocity; + else + v = maxVelocity; + } + timeline.reset(data.move); + if (boundsBehavior == QDeclarativeFlickable::DragAndOvershootBounds) + timeline.accel(data.move, v, deceleration); + else + timeline.accel(data.move, v, deceleration, maxDistance); + timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this)); + if (!hData.flicking && q->xflick()) { + hData.flicking = true; + emit q->flickingChanged(); + emit q->flickingHorizontallyChanged(); + if (!vData.flicking) + emit q->flickStarted(); + } + if (!vData.flicking && q->yflick()) { + vData.flicking = true; + emit q->flickingChanged(); + emit q->flickingVerticallyChanged(); + if (!hData.flicking) + emit q->flickStarted(); + } + } else { + timeline.reset(data.move); + fixup(data, minExtent, maxExtent); + } +} + +void QDeclarativeFlickablePrivate::fixupY_callback(void *data) +{ + ((QDeclarativeFlickablePrivate *)data)->fixupY(); +} + +void QDeclarativeFlickablePrivate::fixupX_callback(void *data) +{ + ((QDeclarativeFlickablePrivate *)data)->fixupX(); +} + +void QDeclarativeFlickablePrivate::fixupX() +{ + Q_Q(QDeclarativeFlickable); + fixup(hData, q->minXExtent(), q->maxXExtent()); +} + +void QDeclarativeFlickablePrivate::fixupY() +{ + Q_Q(QDeclarativeFlickable); + fixup(vData, q->minYExtent(), q->maxYExtent()); +} + +void QDeclarativeFlickablePrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent) +{ + if (data.move.value() > minExtent || maxExtent > minExtent) { + timeline.reset(data.move); + if (data.move.value() != minExtent) { + switch (fixupMode) { + case Immediate: + timeline.set(data.move, minExtent); + break; + case ExtentChanged: + // The target has changed. Don't start from the beginning; just complete the + // second half of the animation using the new extent. + timeline.move(data.move, minExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4); + data.fixingUp = true; + break; + default: { + qreal dist = minExtent - data.move; + timeline.move(data.move, minExtent - dist/2, QEasingCurve(QEasingCurve::InQuad), fixupDuration/4); + timeline.move(data.move, minExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4); + data.fixingUp = true; + } + } + } + } else if (data.move.value() < maxExtent) { + timeline.reset(data.move); + switch (fixupMode) { + case Immediate: + timeline.set(data.move, maxExtent); + break; + case ExtentChanged: + // The target has changed. Don't start from the beginning; just complete the + // second half of the animation using the new extent. + timeline.move(data.move, maxExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4); + data.fixingUp = true; + break; + default: { + qreal dist = maxExtent - data.move; + timeline.move(data.move, maxExtent - dist/2, QEasingCurve(QEasingCurve::InQuad), fixupDuration/4); + timeline.move(data.move, maxExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4); + data.fixingUp = true; + } + } + } + data.inOvershoot = false; + fixupMode = Normal; + vTime = timeline.time(); +} + +void QDeclarativeFlickablePrivate::updateBeginningEnd() +{ + Q_Q(QDeclarativeFlickable); + bool atBoundaryChange = false; + + // Vertical + const int maxyextent = int(-q->maxYExtent()); + const qreal ypos = -vData.move.value(); + bool atBeginning = (ypos <= -q->minYExtent()); + bool atEnd = (maxyextent <= ypos); + + if (atBeginning != vData.atBeginning) { + vData.atBeginning = atBeginning; + atBoundaryChange = true; + } + if (atEnd != vData.atEnd) { + vData.atEnd = atEnd; + atBoundaryChange = true; + } + + // Horizontal + const int maxxextent = int(-q->maxXExtent()); + const qreal xpos = -hData.move.value(); + atBeginning = (xpos <= -q->minXExtent()); + atEnd = (maxxextent <= xpos); + + if (atBeginning != hData.atBeginning) { + hData.atBeginning = atBeginning; + atBoundaryChange = true; + } + if (atEnd != hData.atEnd) { + hData.atEnd = atEnd; + atBoundaryChange = true; + } + + if (atBoundaryChange) + emit q->isAtBoundaryChanged(); + + if (visibleArea) + visibleArea->updateVisible(); +} + +/*! + \qmlclass Flickable QDeclarativeFlickable + \since 4.7 + \ingroup qml-basic-interaction-elements + + \brief The Flickable item provides a surface that can be "flicked". + \inherits Item + + The Flickable item places its children on a surface that can be dragged + and flicked, causing the view onto the child items to scroll. This + behavior forms the basis of Items that are designed to show large numbers + of child items, such as \l ListView and \l GridView. + + In traditional user interfaces, views can be scrolled using standard + controls, such as scroll bars and arrow buttons. In some situations, it + is also possible to drag the view directly by pressing and holding a + mouse button while moving the cursor. In touch-based user interfaces, + this dragging action is often complemented with a flicking action, where + scrolling continues after the user has stopped touching the view. + + Flickable does not automatically clip its contents. If it is not used as + a full-screen item, you should consider setting the \l{Item::}{clip} property + to true. + + \section1 Example Usage + + \div {class="float-right"} + \inlineimage flickable.gif + \enddiv + + The following example shows a small view onto a large image in which the + user can drag or flick the image in order to view different parts of it. + + \snippet doc/src/snippets/declarative/flickable.qml document + + \clearfloat + + Items declared as children of a Flickable are automatically parented to the + Flickable's \l contentItem. This should be taken into account when + operating on the children of the Flickable; it is usually the children of + \c contentItem that are relevant. For example, the bound of Items added + to the Flickable will be available by \c contentItem.childrenRect + + \section1 Limitations + + \note Due to an implementation detail, items placed inside a Flickable cannot anchor to it by + \c id. Use \c parent instead. +*/ + +/*! + \qmlsignal Flickable::onMovementStarted() + + This handler is called when the view begins moving due to user + interaction. +*/ + +/*! + \qmlsignal Flickable::onMovementEnded() + + This handler is called when the view stops moving due to user + interaction. If a flick was generated, this handler will + be triggered once the flick stops. If a flick was not + generated, the handler will be triggered when the + user stops dragging - i.e. a mouse or touch release. +*/ + +/*! + \qmlsignal Flickable::onFlickStarted() + + This handler is called when the view is flicked. A flick + starts from the point that the mouse or touch is released, + while still in motion. +*/ + +/*! + \qmlsignal Flickable::onFlickEnded() + + This handler is called when the view stops moving due to a flick. +*/ + +/*! + \qmlproperty real Flickable::visibleArea.xPosition + \qmlproperty real Flickable::visibleArea.widthRatio + \qmlproperty real Flickable::visibleArea.yPosition + \qmlproperty real Flickable::visibleArea.heightRatio + + These properties describe the position and size of the currently viewed area. + The size is defined as the percentage of the full view currently visible, + scaled to 0.0 - 1.0. The page position is usually in the range 0.0 (beginning) to + 1.0 minus size ratio (end), i.e. \c yPosition is in the range 0.0 to 1.0-\c heightRatio. + However, it is possible for the contents to be dragged outside of the normal + range, resulting in the page positions also being outside the normal range. + + These properties are typically used to draw a scrollbar. For example: + + \snippet doc/src/snippets/declarative/flickableScrollbar.qml 0 + \dots 8 + \snippet doc/src/snippets/declarative/flickableScrollbar.qml 1 + + \sa {declarative/ui-components/scrollbar}{scrollbar example} +*/ + +QDeclarativeFlickable::QDeclarativeFlickable(QDeclarativeItem *parent) + : QDeclarativeItem(*(new QDeclarativeFlickablePrivate), parent) +{ + Q_D(QDeclarativeFlickable); + d->init(); +} + +QDeclarativeFlickable::QDeclarativeFlickable(QDeclarativeFlickablePrivate &dd, QDeclarativeItem *parent) + : QDeclarativeItem(dd, parent) +{ + Q_D(QDeclarativeFlickable); + d->init(); +} + +QDeclarativeFlickable::~QDeclarativeFlickable() +{ +} + +/*! + \qmlproperty real Flickable::contentX + \qmlproperty real Flickable::contentY + + These properties hold the surface coordinate currently at the top-left + corner of the Flickable. For example, if you flick an image up 100 pixels, + \c contentY will be 100. +*/ +qreal QDeclarativeFlickable::contentX() const +{ + Q_D(const QDeclarativeFlickable); + return -d->contentItem->x(); +} + +void QDeclarativeFlickable::setContentX(qreal pos) +{ + Q_D(QDeclarativeFlickable); + d->timeline.reset(d->hData.move); + d->vTime = d->timeline.time(); + movementXEnding(); + if (-pos != d->hData.move.value()) { + d->hData.move.setValue(-pos); + viewportMoved(); + } +} + +qreal QDeclarativeFlickable::contentY() const +{ + Q_D(const QDeclarativeFlickable); + return -d->contentItem->y(); +} + +void QDeclarativeFlickable::setContentY(qreal pos) +{ + Q_D(QDeclarativeFlickable); + d->timeline.reset(d->vData.move); + d->vTime = d->timeline.time(); + movementYEnding(); + if (-pos != d->vData.move.value()) { + d->vData.move.setValue(-pos); + viewportMoved(); + } +} + +/*! + \qmlproperty bool Flickable::interactive + + This property describes whether the user can interact with the Flickable. + A user cannot drag or flick a Flickable that is not interactive. + + By default, this property is true. + + This property is useful for temporarily disabling flicking. This allows + special interaction with Flickable's children; for example, you might want + to freeze a flickable map while scrolling through a pop-up dialog that + is a child of the Flickable. +*/ +bool QDeclarativeFlickable::isInteractive() const +{ + Q_D(const QDeclarativeFlickable); + return d->interactive; +} + +void QDeclarativeFlickable::setInteractive(bool interactive) +{ + Q_D(QDeclarativeFlickable); + if (interactive != d->interactive) { + d->interactive = interactive; + if (!interactive && (d->hData.flicking || d->vData.flicking)) { + d->timeline.clear(); + d->vTime = d->timeline.time(); + d->hData.flicking = false; + d->vData.flicking = false; + emit flickingChanged(); + emit flickingHorizontallyChanged(); + emit flickingVerticallyChanged(); + emit flickEnded(); + } + emit interactiveChanged(); + } +} + +/*! + \qmlproperty real Flickable::horizontalVelocity + \qmlproperty real Flickable::verticalVelocity + + The instantaneous velocity of movement along the x and y axes, in pixels/sec. + + The reported velocity is smoothed to avoid erratic output. +*/ +qreal QDeclarativeFlickable::horizontalVelocity() const +{ + Q_D(const QDeclarativeFlickable); + return d->hData.smoothVelocity.value(); +} + +qreal QDeclarativeFlickable::verticalVelocity() const +{ + Q_D(const QDeclarativeFlickable); + return d->vData.smoothVelocity.value(); +} + +/*! + \qmlproperty bool Flickable::atXBeginning + \qmlproperty bool Flickable::atXEnd + \qmlproperty bool Flickable::atYBeginning + \qmlproperty bool Flickable::atYEnd + + These properties are true if the flickable view is positioned at the beginning, + or end respecively. +*/ +bool QDeclarativeFlickable::isAtXEnd() const +{ + Q_D(const QDeclarativeFlickable); + return d->hData.atEnd; +} + +bool QDeclarativeFlickable::isAtXBeginning() const +{ + Q_D(const QDeclarativeFlickable); + return d->hData.atBeginning; +} + +bool QDeclarativeFlickable::isAtYEnd() const +{ + Q_D(const QDeclarativeFlickable); + return d->vData.atEnd; +} + +bool QDeclarativeFlickable::isAtYBeginning() const +{ + Q_D(const QDeclarativeFlickable); + return d->vData.atBeginning; +} + +void QDeclarativeFlickable::ticked() +{ + viewportMoved(); +} + +/*! + \qmlproperty Item Flickable::contentItem + + The internal item that contains the Items to be moved in the Flickable. + + Items declared as children of a Flickable are automatically parented to the Flickable's contentItem. + + Items created dynamically need to be explicitly parented to the \e contentItem: + \code + Flickable { + id: myFlickable + function addItem(file) { + var component = Qt.createComponent(file) + component.createObject(myFlickable.contentItem); + } + } + \endcode +*/ +QDeclarativeItem *QDeclarativeFlickable::contentItem() +{ + Q_D(QDeclarativeFlickable); + return d->contentItem; +} + +QDeclarativeFlickableVisibleArea *QDeclarativeFlickable::visibleArea() +{ + Q_D(QDeclarativeFlickable); + if (!d->visibleArea) + d->visibleArea = new QDeclarativeFlickableVisibleArea(this); + return d->visibleArea; +} + +/*! + \qmlproperty enumeration Flickable::flickableDirection + + This property determines which directions the view can be flicked. + + \list + \o Flickable.AutoFlickDirection (default) - allows flicking vertically if the + \e contentHeight is not equal to the \e height of the Flickable. + Allows flicking horizontally if the \e contentWidth is not equal + to the \e width of the Flickable. + \o Flickable.HorizontalFlick - allows flicking horizontally. + \o Flickable.VerticalFlick - allows flicking vertically. + \o Flickable.HorizontalAndVerticalFlick - allows flicking in both directions. + \endlist +*/ +QDeclarativeFlickable::FlickableDirection QDeclarativeFlickable::flickableDirection() const +{ + Q_D(const QDeclarativeFlickable); + return d->flickableDirection; +} + +void QDeclarativeFlickable::setFlickableDirection(FlickableDirection direction) +{ + Q_D(QDeclarativeFlickable); + if (direction != d->flickableDirection) { + d->flickableDirection = direction; + emit flickableDirectionChanged(); + } +} + +void QDeclarativeFlickablePrivate::handleMousePressEvent(QGraphicsSceneMouseEvent *event) +{ + Q_Q(QDeclarativeFlickable); + if (interactive && timeline.isActive() + && (qAbs(hData.smoothVelocity.value()) > RetainGrabVelocity || qAbs(vData.smoothVelocity.value()) > RetainGrabVelocity)) + stealMouse = true; // If we've been flicked then steal the click. + else + stealMouse = false; + q->setKeepMouseGrab(stealMouse); + pressed = true; + timeline.clear(); + hData.reset(); + vData.reset(); + hData.dragMinBound = q->minXExtent(); + vData.dragMinBound = q->minYExtent(); + hData.dragMaxBound = q->maxXExtent(); + vData.dragMaxBound = q->maxYExtent(); + fixupMode = Normal; + lastPos = QPoint(); + QDeclarativeItemPrivate::start(lastPosTime); + pressPos = event->pos(); + hData.pressPos = hData.move.value(); + vData.pressPos = vData.move.value(); + hData.flicking = false; + vData.flicking = false; + QDeclarativeItemPrivate::start(pressTime); + QDeclarativeItemPrivate::start(velocityTime); +} + +void QDeclarativeFlickablePrivate::handleMouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + Q_Q(QDeclarativeFlickable); + if (!interactive || !lastPosTime.isValid()) + return; + bool rejectY = false; + bool rejectX = false; + + bool stealY = stealMouse; + bool stealX = stealMouse; + + if (q->yflick()) { + int dy = int(event->pos().y() - pressPos.y()); + if (qAbs(dy) > QApplication::startDragDistance() || QDeclarativeItemPrivate::elapsed(pressTime) > 200) { + if (!vMoved) + vData.dragStartOffset = dy; + qreal newY = dy + vData.pressPos - vData.dragStartOffset; + const qreal minY = vData.dragMinBound; + const qreal maxY = vData.dragMaxBound; + if (newY > minY) + newY = minY + (newY - minY) / 2; + if (newY < maxY && maxY - minY <= 0) + newY = maxY + (newY - maxY) / 2; + if (boundsBehavior == QDeclarativeFlickable::StopAtBounds && (newY > minY || newY < maxY)) { + rejectY = true; + if (newY < maxY) { + newY = maxY; + rejectY = false; + } + if (newY > minY) { + newY = minY; + rejectY = false; + } + } + if (!rejectY && stealMouse) { + vData.move.setValue(qRound(newY)); + vMoved = true; + } + if (qAbs(dy) > QApplication::startDragDistance()) + stealY = true; + } + } + + if (q->xflick()) { + int dx = int(event->pos().x() - pressPos.x()); + if (qAbs(dx) > QApplication::startDragDistance() || QDeclarativeItemPrivate::elapsed(pressTime) > 200) { + if (!hMoved) + hData.dragStartOffset = dx; + qreal newX = dx + hData.pressPos - hData.dragStartOffset; + const qreal minX = hData.dragMinBound; + const qreal maxX = hData.dragMaxBound; + if (newX > minX) + newX = minX + (newX - minX) / 2; + if (newX < maxX && maxX - minX <= 0) + newX = maxX + (newX - maxX) / 2; + if (boundsBehavior == QDeclarativeFlickable::StopAtBounds && (newX > minX || newX < maxX)) { + rejectX = true; + if (newX < maxX) { + newX = maxX; + rejectX = false; + } + if (newX > minX) { + newX = minX; + rejectX = false; + } + } + if (!rejectX && stealMouse) { + hData.move.setValue(qRound(newX)); + hMoved = true; + } + + if (qAbs(dx) > QApplication::startDragDistance()) + stealX = true; + } + } + + stealMouse = stealX || stealY; + if (stealMouse) + q->setKeepMouseGrab(true); + + if (rejectY) { + vData.velocityBuffer.clear(); + vData.velocity = 0; + } + if (rejectX) { + hData.velocityBuffer.clear(); + hData.velocity = 0; + } + + if (hMoved || vMoved) { + q->movementStarting(); + q->viewportMoved(); + } + + if (!lastPos.isNull()) { + qreal elapsed = qreal(QDeclarativeItemPrivate::elapsed(lastPosTime)) / 1000.; + if (elapsed <= 0) + return; + QDeclarativeItemPrivate::restart(lastPosTime); + qreal dy = event->pos().y()-lastPos.y(); + if (q->yflick() && !rejectY) + vData.addVelocitySample(dy/elapsed, maxVelocity); + qreal dx = event->pos().x()-lastPos.x(); + if (q->xflick() && !rejectX) + hData.addVelocitySample(dx/elapsed, maxVelocity); + } + + lastPos = event->pos(); +} + +void QDeclarativeFlickablePrivate::handleMouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + Q_Q(QDeclarativeFlickable); + stealMouse = false; + q->setKeepMouseGrab(false); + pressed = false; + if (!lastPosTime.isValid()) + return; + + // if we drag then pause before release we should not cause a flick. + qint64 elapsed = QDeclarativeItemPrivate::elapsed(lastPosTime); + + vData.updateVelocity(); + hData.updateVelocity(); + vTime = timeline.time(); + + qreal velocity = elapsed < 100 ? vData.velocity : 0; + if (vData.atBeginning || vData.atEnd) + velocity /= 2; + if (q->yflick() && qAbs(velocity) > MinimumFlickVelocity && qAbs(event->pos().y() - pressPos.y()) > FlickThreshold) { + velocityTimeline.reset(vData.smoothVelocity); + vData.smoothVelocity.setValue(-velocity); + flickY(velocity); + } else { + fixupY(); + } + + velocity = elapsed < 100 ? hData.velocity : 0; + if (hData.atBeginning || hData.atEnd) + velocity /= 2; + if (q->xflick() && qAbs(velocity) > MinimumFlickVelocity && qAbs(event->pos().x() - pressPos.x()) > FlickThreshold) { + velocityTimeline.reset(hData.smoothVelocity); + hData.smoothVelocity.setValue(-velocity); + flickX(velocity); + } else { + fixupX(); + } + + if (!timeline.isActive()) + q->movementEnding(); +} + +void QDeclarativeFlickable::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarativeFlickable); + if (d->interactive) { + if (!d->pressed) + d->handleMousePressEvent(event); + event->accept(); + } else { + QDeclarativeItem::mousePressEvent(event); + } +} + +void QDeclarativeFlickable::mouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarativeFlickable); + if (d->interactive) { + d->handleMouseMoveEvent(event); + event->accept(); + } else { + QDeclarativeItem::mouseMoveEvent(event); + } +} + +void QDeclarativeFlickable::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarativeFlickable); + if (d->interactive) { + d->clearDelayedPress(); + d->handleMouseReleaseEvent(event); + event->accept(); + ungrabMouse(); + } else { + QDeclarativeItem::mouseReleaseEvent(event); + } +} + +void QDeclarativeFlickable::wheelEvent(QGraphicsSceneWheelEvent *event) +{ + Q_D(QDeclarativeFlickable); + if (!d->interactive) { + QDeclarativeItem::wheelEvent(event); + } else if (yflick() && event->orientation() == Qt::Vertical) { + bool valid = false; + if (event->delta() > 0 && contentY() > -minYExtent()) { + d->vData.velocity = qMax(event->delta()*2 - d->vData.smoothVelocity.value(), qreal(d->maxVelocity/4)); + valid = true; + } else if (event->delta() < 0 && contentY() < -maxYExtent()) { + d->vData.velocity = qMin(event->delta()*2 - d->vData.smoothVelocity.value(), qreal(-d->maxVelocity/4)); + valid = true; + } + if (valid) { + d->vData.flicking = false; + d->flickY(d->vData.velocity); + if (d->vData.flicking) { + d->vMoved = true; + movementStarting(); + } + event->accept(); + } + } else if (xflick() && event->orientation() == Qt::Horizontal) { + bool valid = false; + if (event->delta() > 0 && contentX() > -minXExtent()) { + d->hData.velocity = qMax(event->delta()*2 - d->hData.smoothVelocity.value(), qreal(d->maxVelocity/4)); + valid = true; + } else if (event->delta() < 0 && contentX() < -maxXExtent()) { + d->hData.velocity = qMin(event->delta()*2 - d->hData.smoothVelocity.value(), qreal(-d->maxVelocity/4)); + valid = true; + } + if (valid) { + d->hData.flicking = false; + d->flickX(d->hData.velocity); + if (d->hData.flicking) { + d->hMoved = true; + movementStarting(); + } + event->accept(); + } + } else { + QDeclarativeItem::wheelEvent(event); + } +} + +bool QDeclarativeFlickablePrivate::isOutermostPressDelay() const +{ + Q_Q(const QDeclarativeFlickable); + QDeclarativeItem *item = q->parentItem(); + while (item) { + QDeclarativeFlickable *flick = qobject_cast(item); + if (flick && flick->pressDelay() > 0 && flick->isInteractive()) + return false; + item = item->parentItem(); + } + + return true; +} + +void QDeclarativeFlickablePrivate::captureDelayedPress(QGraphicsSceneMouseEvent *event) +{ + Q_Q(QDeclarativeFlickable); + if (!q->scene() || pressDelay <= 0) + return; + if (!isOutermostPressDelay()) + return; + delayedPressTarget = q->scene()->mouseGrabberItem(); + delayedPressEvent = new QGraphicsSceneMouseEvent(event->type()); + delayedPressEvent->setAccepted(false); + for (int i = 0x1; i <= 0x10; i <<= 1) { + if (event->buttons() & i) { + Qt::MouseButton button = Qt::MouseButton(i); + delayedPressEvent->setButtonDownPos(button, event->buttonDownPos(button)); + delayedPressEvent->setButtonDownScenePos(button, event->buttonDownScenePos(button)); + delayedPressEvent->setButtonDownScreenPos(button, event->buttonDownScreenPos(button)); + } + } + delayedPressEvent->setButtons(event->buttons()); + delayedPressEvent->setButton(event->button()); + delayedPressEvent->setPos(event->pos()); + delayedPressEvent->setScenePos(event->scenePos()); + delayedPressEvent->setScreenPos(event->screenPos()); + delayedPressEvent->setLastPos(event->lastPos()); + delayedPressEvent->setLastScenePos(event->lastScenePos()); + delayedPressEvent->setLastScreenPos(event->lastScreenPos()); + delayedPressEvent->setModifiers(event->modifiers()); + delayedPressTimer.start(pressDelay, q); +} + +void QDeclarativeFlickablePrivate::clearDelayedPress() +{ + if (delayedPressEvent) { + delayedPressTimer.stop(); + delete delayedPressEvent; + delayedPressEvent = 0; + } +} + +void QDeclarativeFlickablePrivate::setRoundedViewportX(qreal x) +{ + contentItem->setX(qRound(x)); +} + +void QDeclarativeFlickablePrivate::setRoundedViewportY(qreal y) +{ + contentItem->setY(qRound(y)); +} + +void QDeclarativeFlickable::timerEvent(QTimerEvent *event) +{ + Q_D(QDeclarativeFlickable); + if (event->timerId() == d->delayedPressTimer.timerId()) { + d->delayedPressTimer.stop(); + if (d->delayedPressEvent) { + QDeclarativeItem *grabber = scene() ? qobject_cast(scene()->mouseGrabberItem()) : 0; + if (!grabber || grabber != this) { + // We replay the mouse press but the grabber we had might not be interessted by the event (e.g. overlay) + // so we reset the grabber + if (scene()->mouseGrabberItem() == d->delayedPressTarget) + d->delayedPressTarget->ungrabMouse(); + //Use the event handler that will take care of finding the proper item to propagate the event + QApplication::postEvent(scene(), d->delayedPressEvent); + } else { + delete d->delayedPressEvent; + } + d->delayedPressEvent = 0; + } + } +} + +qreal QDeclarativeFlickable::minYExtent() const +{ + return 0.0; +} + +qreal QDeclarativeFlickable::minXExtent() const +{ + return 0.0; +} + +/* returns -ve */ +qreal QDeclarativeFlickable::maxXExtent() const +{ + return width() - vWidth(); +} +/* returns -ve */ +qreal QDeclarativeFlickable::maxYExtent() const +{ + return height() - vHeight(); +} + +void QDeclarativeFlickable::viewportMoved() +{ + Q_D(QDeclarativeFlickable); + + qreal prevX = d->lastFlickablePosition.x(); + qreal prevY = d->lastFlickablePosition.y(); + if (d->pressed || d->calcVelocity) { + int elapsed = QDeclarativeItemPrivate::restart(d->velocityTime); + if (elapsed > 0) { + qreal horizontalVelocity = (prevX - d->hData.move.value()) * 1000 / elapsed; + if (qAbs(horizontalVelocity) > 0) { + d->velocityTimeline.reset(d->hData.smoothVelocity); + d->velocityTimeline.move(d->hData.smoothVelocity, horizontalVelocity, d->reportedVelocitySmoothing); + d->velocityTimeline.move(d->hData.smoothVelocity, 0, d->reportedVelocitySmoothing); + } + qreal verticalVelocity = (prevY - d->vData.move.value()) * 1000 / elapsed; + if (qAbs(verticalVelocity) > 0) { + d->velocityTimeline.reset(d->vData.smoothVelocity); + d->velocityTimeline.move(d->vData.smoothVelocity, verticalVelocity, d->reportedVelocitySmoothing); + d->velocityTimeline.move(d->vData.smoothVelocity, 0, d->reportedVelocitySmoothing); + } + } + } else { + if (d->timeline.time() > d->vTime) { + d->velocityTimeline.clear(); + qreal horizontalVelocity = (prevX - d->hData.move.value()) * 1000 / (d->timeline.time() - d->vTime); + qreal verticalVelocity = (prevY - d->vData.move.value()) * 1000 / (d->timeline.time() - d->vTime); + d->hData.smoothVelocity.setValue(horizontalVelocity); + d->vData.smoothVelocity.setValue(verticalVelocity); + } + } + + if (!d->vData.inOvershoot && !d->vData.fixingUp && d->vData.flicking + && (d->vData.move.value() > minYExtent() || d->vData.move.value() < maxYExtent()) + && qAbs(d->vData.smoothVelocity.value()) > 100) { + // Increase deceleration if we've passed a bound + d->vData.inOvershoot = true; + qreal maxDistance = d->overShootDistance(height()); + d->timeline.reset(d->vData.move); + d->timeline.accel(d->vData.move, -d->vData.smoothVelocity.value(), d->deceleration*QML_FLICK_OVERSHOOTFRICTION, maxDistance); + d->timeline.callback(QDeclarativeTimeLineCallback(&d->vData.move, d->fixupY_callback, d)); + } + if (!d->hData.inOvershoot && !d->hData.fixingUp && d->hData.flicking + && (d->hData.move.value() > minXExtent() || d->hData.move.value() < maxXExtent()) + && qAbs(d->hData.smoothVelocity.value()) > 100) { + // Increase deceleration if we've passed a bound + d->hData.inOvershoot = true; + qreal maxDistance = d->overShootDistance(width()); + d->timeline.reset(d->hData.move); + d->timeline.accel(d->hData.move, -d->hData.smoothVelocity.value(), d->deceleration*QML_FLICK_OVERSHOOTFRICTION, maxDistance); + d->timeline.callback(QDeclarativeTimeLineCallback(&d->hData.move, d->fixupX_callback, d)); + } + + d->lastFlickablePosition = QPointF(d->hData.move.value(), d->vData.move.value()); + + d->vTime = d->timeline.time(); + d->updateBeginningEnd(); +} + +void QDeclarativeFlickable::geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry) +{ + Q_D(QDeclarativeFlickable); + QDeclarativeItem::geometryChanged(newGeometry, oldGeometry); + + bool changed = false; + if (newGeometry.width() != oldGeometry.width()) { + if (xflick()) + changed = true; + if (d->hData.viewSize < 0) { + d->contentItem->setWidth(width()); + emit contentWidthChanged(); + } + // Make sure that we're entirely in view. + if (!d->pressed && !d->hData.moving && !d->vData.moving) { + d->fixupMode = QDeclarativeFlickablePrivate::Immediate; + d->fixupX(); + } + } + if (newGeometry.height() != oldGeometry.height()) { + if (yflick()) + changed = true; + if (d->vData.viewSize < 0) { + d->contentItem->setHeight(height()); + emit contentHeightChanged(); + } + // Make sure that we're entirely in view. + if (!d->pressed && !d->hData.moving && !d->vData.moving) { + d->fixupMode = QDeclarativeFlickablePrivate::Immediate; + d->fixupY(); + } + } + + if (changed) + d->updateBeginningEnd(); +} + +void QDeclarativeFlickable::cancelFlick() +{ + Q_D(QDeclarativeFlickable); + d->timeline.reset(d->hData.move); + d->timeline.reset(d->vData.move); + movementEnding(); +} + +void QDeclarativeFlickablePrivate::data_append(QDeclarativeListProperty *prop, QObject *o) +{ + QGraphicsObject *i = qobject_cast(o); + if (i) { + QGraphicsItemPrivate *d = QGraphicsItemPrivate::get(i); + if (static_cast(d)->componentComplete) { + i->setParentItem(static_cast(prop->data)->contentItem); + } else { + d->setParentItemHelper(static_cast(prop->data)->contentItem, 0, 0); + } + } else { + o->setParent(prop->object); + } +} + +int QDeclarativeFlickablePrivate::data_count(QDeclarativeListProperty *property) +{ + QDeclarativeItem *contentItem= static_cast(property->data)->contentItem; + return contentItem->childItems().count() + contentItem->children().count(); +} + +QObject *QDeclarativeFlickablePrivate::data_at(QDeclarativeListProperty *property, int index) +{ + QDeclarativeItem *contentItem = static_cast(property->data)->contentItem; + + int childItemCount = contentItem->childItems().count(); + + if (index < 0) + return 0; + + if (index < childItemCount) { + return contentItem->childItems().at(index)->toGraphicsObject(); + } else { + return contentItem->children().at(index - childItemCount); + } + + return 0; +} + +void QDeclarativeFlickablePrivate::data_clear(QDeclarativeListProperty *property) +{ + QDeclarativeItem *contentItem = static_cast(property->data)->contentItem; + + const QList graphicsItems = contentItem->childItems(); + for (int i = 0; i < graphicsItems.count(); i++) + contentItem->scene()->removeItem(graphicsItems[i]); + + const QList objects = contentItem->children(); + for (int i = 0; i < objects.count(); i++) + objects[i]->setParent(0); +} + +QDeclarativeListProperty QDeclarativeFlickable::flickableData() +{ + Q_D(QDeclarativeFlickable); + return QDeclarativeListProperty(this, (void *)d, QDeclarativeFlickablePrivate::data_append, + QDeclarativeFlickablePrivate::data_count, + QDeclarativeFlickablePrivate::data_at, + QDeclarativeFlickablePrivate::data_clear); +} + +QDeclarativeListProperty QDeclarativeFlickable::flickableChildren() +{ + Q_D(QDeclarativeFlickable); + return QGraphicsItemPrivate::get(d->contentItem)->childrenList(); +} + +/*! + \qmlproperty enumeration Flickable::boundsBehavior + This property holds whether the surface may be dragged + beyond the Fickable's boundaries, or overshoot the + Flickable's boundaries when flicked. + + This enables the feeling that the edges of the view are soft, + rather than a hard physical boundary. + + The \c boundsBehavior can be one of: + + \list + \o Flickable.StopAtBounds - the contents can not be dragged beyond the boundary + of the flickable, and flicks will not overshoot. + \o Flickable.DragOverBounds - the contents can be dragged beyond the boundary + of the Flickable, but flicks will not overshoot. + \o Flickable.DragAndOvershootBounds (default) - the contents can be dragged + beyond the boundary of the Flickable, and can overshoot the + boundary when flicked. + \endlist +*/ +QDeclarativeFlickable::BoundsBehavior QDeclarativeFlickable::boundsBehavior() const +{ + Q_D(const QDeclarativeFlickable); + return d->boundsBehavior; +} + +void QDeclarativeFlickable::setBoundsBehavior(BoundsBehavior b) +{ + Q_D(QDeclarativeFlickable); + if (b == d->boundsBehavior) + return; + d->boundsBehavior = b; + emit boundsBehaviorChanged(); +} + +/*! + \qmlproperty real Flickable::contentWidth + \qmlproperty real Flickable::contentHeight + + The dimensions of the content (the surface controlled by Flickable). + This should typically be set to the combined size of the items placed in the + Flickable. + + The following snippet shows how these properties are used to display + an image that is larger than the Flickable item itself: + + \snippet doc/src/snippets/declarative/flickable.qml document + + In some cases, the the content dimensions can be automatically set + using the \l {Item::childrenRect.width}{childrenRect.width} + and \l {Item::childrenRect.height}{childrenRect.height} properties. +*/ +qreal QDeclarativeFlickable::contentWidth() const +{ + Q_D(const QDeclarativeFlickable); + return d->hData.viewSize; +} + +void QDeclarativeFlickable::setContentWidth(qreal w) +{ + Q_D(QDeclarativeFlickable); + if (d->hData.viewSize == w) + return; + d->hData.viewSize = w; + if (w < 0) + d->contentItem->setWidth(width()); + else + d->contentItem->setWidth(w); + // Make sure that we're entirely in view. + if (!d->pressed && !d->hData.moving && !d->vData.moving) { + d->fixupMode = QDeclarativeFlickablePrivate::Immediate; + d->fixupX(); + } else if (!d->pressed && d->hData.fixingUp) { + d->fixupMode = QDeclarativeFlickablePrivate::ExtentChanged; + d->fixupX(); + } + emit contentWidthChanged(); + d->updateBeginningEnd(); +} + +qreal QDeclarativeFlickable::contentHeight() const +{ + Q_D(const QDeclarativeFlickable); + return d->vData.viewSize; +} + +void QDeclarativeFlickable::setContentHeight(qreal h) +{ + Q_D(QDeclarativeFlickable); + if (d->vData.viewSize == h) + return; + d->vData.viewSize = h; + if (h < 0) + d->contentItem->setHeight(height()); + else + d->contentItem->setHeight(h); + // Make sure that we're entirely in view. + if (!d->pressed && !d->hData.moving && !d->vData.moving) { + d->fixupMode = QDeclarativeFlickablePrivate::Immediate; + d->fixupY(); + } else if (!d->pressed && d->vData.fixingUp) { + d->fixupMode = QDeclarativeFlickablePrivate::ExtentChanged; + d->fixupY(); + } + emit contentHeightChanged(); + d->updateBeginningEnd(); +} + +/*! + \qmlmethod Flickable::resizeContent(real width, real height, QPointF center) + \preliminary + \since QtQuick 1.1 + + Resizes the content to \a width x \a height about \a center. + + This does not scale the contents of the Flickable - it only resizes the \l contentWidth + and \l contentHeight. + + Resizing the content may result in the content being positioned outside + the bounds of the Flickable. Calling \l returnToBounds() will + move the content back within legal bounds. +*/ +void QDeclarativeFlickable::resizeContent(qreal w, qreal h, QPointF center) +{ + Q_D(QDeclarativeFlickable); + if (w != d->hData.viewSize) { + qreal oldSize = d->hData.viewSize; + d->hData.viewSize = w; + d->contentItem->setWidth(w); + emit contentWidthChanged(); + if (center.x() != 0) { + qreal pos = center.x() * w / oldSize; + setContentX(contentX() + pos - center.x()); + } + } + if (h != d->vData.viewSize) { + qreal oldSize = d->vData.viewSize; + d->vData.viewSize = h; + d->contentItem->setHeight(h); + emit contentHeightChanged(); + if (center.y() != 0) { + qreal pos = center.y() * h / oldSize; + setContentY(contentY() + pos - center.y()); + } + } + d->updateBeginningEnd(); +} + +/*! + \qmlmethod Flickable::returnToBounds() + \preliminary + \since QtQuick 1.1 + + Ensures the content is within legal bounds. + + This may be called to ensure that the content is within legal bounds + after manually positioning the content. +*/ +void QDeclarativeFlickable::returnToBounds() +{ + Q_D(QDeclarativeFlickable); + d->fixupX(); + d->fixupY(); +} + +qreal QDeclarativeFlickable::vWidth() const +{ + Q_D(const QDeclarativeFlickable); + if (d->hData.viewSize < 0) + return width(); + else + return d->hData.viewSize; +} + +qreal QDeclarativeFlickable::vHeight() const +{ + Q_D(const QDeclarativeFlickable); + if (d->vData.viewSize < 0) + return height(); + else + return d->vData.viewSize; +} + +bool QDeclarativeFlickable::xflick() const +{ + Q_D(const QDeclarativeFlickable); + if (d->flickableDirection == QDeclarativeFlickable::AutoFlickDirection) + return vWidth() != width(); + return d->flickableDirection & QDeclarativeFlickable::HorizontalFlick; +} + +bool QDeclarativeFlickable::yflick() const +{ + Q_D(const QDeclarativeFlickable); + if (d->flickableDirection == QDeclarativeFlickable::AutoFlickDirection) + return vHeight() != height(); + return d->flickableDirection & QDeclarativeFlickable::VerticalFlick; +} + +bool QDeclarativeFlickable::sceneEvent(QEvent *event) +{ + bool rv = QDeclarativeItem::sceneEvent(event); + if (event->type() == QEvent::UngrabMouse) { + Q_D(QDeclarativeFlickable); + if (d->pressed) { + // if our mouse grab has been removed (probably by another Flickable), + // fix our state + d->pressed = false; + d->stealMouse = false; + setKeepMouseGrab(false); + } + } + return rv; +} + +bool QDeclarativeFlickable::sendMouseEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarativeFlickable); + QGraphicsSceneMouseEvent mouseEvent(event->type()); + QRectF myRect = mapToScene(QRectF(0, 0, width(), height())).boundingRect(); + + QGraphicsScene *s = scene(); + QDeclarativeItem *grabber = s ? qobject_cast(s->mouseGrabberItem()) : 0; + QGraphicsItem *grabberItem = s ? s->mouseGrabberItem() : 0; + bool disabledItem = grabberItem && !grabberItem->isEnabled(); + bool stealThisEvent = d->stealMouse; + if ((stealThisEvent || myRect.contains(event->scenePos().toPoint())) && (!grabber || !grabber->keepMouseGrab() || disabledItem)) { + mouseEvent.setAccepted(false); + for (int i = 0x1; i <= 0x10; i <<= 1) { + if (event->buttons() & i) { + Qt::MouseButton button = Qt::MouseButton(i); + mouseEvent.setButtonDownPos(button, mapFromScene(event->buttonDownPos(button))); + } + } + mouseEvent.setScenePos(event->scenePos()); + mouseEvent.setLastScenePos(event->lastScenePos()); + mouseEvent.setPos(mapFromScene(event->scenePos())); + mouseEvent.setLastPos(mapFromScene(event->lastScenePos())); + + switch(mouseEvent.type()) { + case QEvent::GraphicsSceneMouseMove: + d->handleMouseMoveEvent(&mouseEvent); + break; + case QEvent::GraphicsSceneMousePress: + if (d->pressed && !event->spontaneous()) // we are already pressed - this is a delayed replay + return false; + + d->handleMousePressEvent(&mouseEvent); + d->captureDelayedPress(event); + stealThisEvent = d->stealMouse; // Update stealThisEvent in case changed by function call above + break; + case QEvent::GraphicsSceneMouseRelease: + if (d->delayedPressEvent) { + // We replay the mouse press but the grabber we had might not be interessted by the event (e.g. overlay) + // so we reset the grabber + if (s->mouseGrabberItem() == d->delayedPressTarget) + d->delayedPressTarget->ungrabMouse(); + //Use the event handler that will take care of finding the proper item to propagate the event + QApplication::sendEvent(scene(), d->delayedPressEvent); + d->clearDelayedPress(); + // We send the release + scene()->sendEvent(s->mouseGrabberItem(), event); + // And the event has been consumed + d->stealMouse = false; + d->pressed = false; + return true; + } + d->handleMouseReleaseEvent(&mouseEvent); + break; + default: + break; + } + grabber = qobject_cast(s->mouseGrabberItem()); + if ((grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) || disabledItem) { + d->clearDelayedPress(); + grabMouse(); + } + + return stealThisEvent || d->delayedPressEvent || disabledItem; + } else if (d->lastPosTime.isValid()) { + d->lastPosTime.invalidate(); + } + if (mouseEvent.type() == QEvent::GraphicsSceneMouseRelease) { + d->clearDelayedPress(); + d->stealMouse = false; + d->pressed = false; + } + + return false; +} + +bool QDeclarativeFlickable::sceneEventFilter(QGraphicsItem *i, QEvent *e) +{ + Q_D(QDeclarativeFlickable); + if (!isVisible() || !d->interactive) + return QDeclarativeItem::sceneEventFilter(i, e); + switch (e->type()) { + case QEvent::GraphicsSceneMousePress: + case QEvent::GraphicsSceneMouseMove: + case QEvent::GraphicsSceneMouseRelease: + return sendMouseEvent(static_cast(e)); + default: + break; + } + + return QDeclarativeItem::sceneEventFilter(i, e); +} + +/*! + \qmlproperty real Flickable::maximumFlickVelocity + This property holds the maximum velocity that the user can flick the view in pixels/second. + + The default value is platform dependent. +*/ +qreal QDeclarativeFlickable::maximumFlickVelocity() const +{ + Q_D(const QDeclarativeFlickable); + return d->maxVelocity; +} + +void QDeclarativeFlickable::setMaximumFlickVelocity(qreal v) +{ + Q_D(QDeclarativeFlickable); + if (v == d->maxVelocity) + return; + d->maxVelocity = v; + emit maximumFlickVelocityChanged(); +} + +/*! + \qmlproperty real Flickable::flickDeceleration + This property holds the rate at which a flick will decelerate. + + The default value is platform dependent. +*/ +qreal QDeclarativeFlickable::flickDeceleration() const +{ + Q_D(const QDeclarativeFlickable); + return d->deceleration; +} + +void QDeclarativeFlickable::setFlickDeceleration(qreal deceleration) +{ + Q_D(QDeclarativeFlickable); + if (deceleration == d->deceleration) + return; + d->deceleration = deceleration; + emit flickDecelerationChanged(); +} + +bool QDeclarativeFlickable::isFlicking() const +{ + Q_D(const QDeclarativeFlickable); + return d->hData.flicking || d->vData.flicking; +} + +/*! + \qmlproperty bool Flickable::flicking + \qmlproperty bool Flickable::flickingHorizontally + \qmlproperty bool Flickable::flickingVertically + + These properties describe whether the view is currently moving horizontally, + vertically or in either direction, due to the user flicking the view. +*/ +bool QDeclarativeFlickable::isFlickingHorizontally() const +{ + Q_D(const QDeclarativeFlickable); + return d->hData.flicking; +} + +bool QDeclarativeFlickable::isFlickingVertically() const +{ + Q_D(const QDeclarativeFlickable); + return d->vData.flicking; +} + +/*! + \qmlproperty int Flickable::pressDelay + + This property holds the time to delay (ms) delivering a press to + children of the Flickable. This can be useful where reacting + to a press before a flicking action has undesirable effects. + + If the flickable is dragged/flicked before the delay times out + the press event will not be delivered. If the button is released + within the timeout, both the press and release will be delivered. + + Note that for nested Flickables with pressDelay set, the pressDelay of + inner Flickables is overridden by the outermost Flickable. +*/ +int QDeclarativeFlickable::pressDelay() const +{ + Q_D(const QDeclarativeFlickable); + return d->pressDelay; +} + +void QDeclarativeFlickable::setPressDelay(int delay) +{ + Q_D(QDeclarativeFlickable); + if (d->pressDelay == delay) + return; + d->pressDelay = delay; + emit pressDelayChanged(); +} + + +bool QDeclarativeFlickable::isMoving() const +{ + Q_D(const QDeclarativeFlickable); + return d->hData.moving || d->vData.moving; +} + +/*! + \qmlproperty bool Flickable::moving + \qmlproperty bool Flickable::movingHorizontally + \qmlproperty bool Flickable::movingVertically + + These properties describe whether the view is currently moving horizontally, + vertically or in either direction, due to the user either dragging or + flicking the view. +*/ +bool QDeclarativeFlickable::isMovingHorizontally() const +{ + Q_D(const QDeclarativeFlickable); + return d->hData.moving; +} + +bool QDeclarativeFlickable::isMovingVertically() const +{ + Q_D(const QDeclarativeFlickable); + return d->vData.moving; +} + +void QDeclarativeFlickable::movementStarting() +{ + Q_D(QDeclarativeFlickable); + if (d->hMoved && !d->hData.moving) { + d->hData.moving = true; + emit movingChanged(); + emit movingHorizontallyChanged(); + if (!d->vData.moving) + emit movementStarted(); + } + else if (d->vMoved && !d->vData.moving) { + d->vData.moving = true; + emit movingChanged(); + emit movingVerticallyChanged(); + if (!d->hData.moving) + emit movementStarted(); + } +} + +void QDeclarativeFlickable::movementEnding() +{ + Q_D(QDeclarativeFlickable); + movementXEnding(); + movementYEnding(); + d->hData.smoothVelocity.setValue(0); + d->vData.smoothVelocity.setValue(0); +} + +void QDeclarativeFlickable::movementXEnding() +{ + Q_D(QDeclarativeFlickable); + if (d->hData.flicking) { + d->hData.flicking = false; + emit flickingChanged(); + emit flickingHorizontallyChanged(); + if (!d->vData.flicking) + emit flickEnded(); + } + if (!d->pressed && !d->stealMouse) { + if (d->hData.moving) { + d->hData.moving = false; + d->hMoved = false; + emit movingChanged(); + emit movingHorizontallyChanged(); + if (!d->vData.moving) + emit movementEnded(); + } + } + d->hData.fixingUp = false; +} + +void QDeclarativeFlickable::movementYEnding() +{ + Q_D(QDeclarativeFlickable); + if (d->vData.flicking) { + d->vData.flicking = false; + emit flickingChanged(); + emit flickingVerticallyChanged(); + if (!d->hData.flicking) + emit flickEnded(); + } + if (!d->pressed && !d->stealMouse) { + if (d->vData.moving) { + d->vData.moving = false; + d->vMoved = false; + emit movingChanged(); + emit movingVerticallyChanged(); + if (!d->hData.moving) + emit movementEnded(); + } + } + d->vData.fixingUp = false; +} + +void QDeclarativeFlickablePrivate::updateVelocity() +{ + Q_Q(QDeclarativeFlickable); + emit q->horizontalVelocityChanged(); + emit q->verticalVelocityChanged(); +} + +QT_END_NAMESPACE diff --git a/src/declarative/graphicsitems/qdeclarativeflickable_p.h b/src/declarative/graphicsitems/qdeclarativeflickable_p.h new file mode 100644 index 00000000..c04246c8 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativeflickable_p.h @@ -0,0 +1,229 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEFLICKABLE_H +#define QDECLARATIVEFLICKABLE_H + +#include "qdeclarativeitem.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeFlickablePrivate; +class QDeclarativeFlickableVisibleArea; +class Q_AUTOTEST_EXPORT QDeclarativeFlickable : public QDeclarativeItem +{ + Q_OBJECT + + Q_PROPERTY(qreal contentWidth READ contentWidth WRITE setContentWidth NOTIFY contentWidthChanged) + Q_PROPERTY(qreal contentHeight READ contentHeight WRITE setContentHeight NOTIFY contentHeightChanged) + Q_PROPERTY(qreal contentX READ contentX WRITE setContentX NOTIFY contentXChanged) + Q_PROPERTY(qreal contentY READ contentY WRITE setContentY NOTIFY contentYChanged) + Q_PROPERTY(QDeclarativeItem *contentItem READ contentItem CONSTANT) + + Q_PROPERTY(qreal horizontalVelocity READ horizontalVelocity NOTIFY horizontalVelocityChanged) + Q_PROPERTY(qreal verticalVelocity READ verticalVelocity NOTIFY verticalVelocityChanged) + + Q_PROPERTY(BoundsBehavior boundsBehavior READ boundsBehavior WRITE setBoundsBehavior NOTIFY boundsBehaviorChanged) + Q_PROPERTY(qreal maximumFlickVelocity READ maximumFlickVelocity WRITE setMaximumFlickVelocity NOTIFY maximumFlickVelocityChanged) + Q_PROPERTY(qreal flickDeceleration READ flickDeceleration WRITE setFlickDeceleration NOTIFY flickDecelerationChanged) + Q_PROPERTY(bool moving READ isMoving NOTIFY movingChanged) + Q_PROPERTY(bool movingHorizontally READ isMovingHorizontally NOTIFY movingHorizontallyChanged) + Q_PROPERTY(bool movingVertically READ isMovingVertically NOTIFY movingVerticallyChanged) + Q_PROPERTY(bool flicking READ isFlicking NOTIFY flickingChanged) + Q_PROPERTY(bool flickingHorizontally READ isFlickingHorizontally NOTIFY flickingHorizontallyChanged) + Q_PROPERTY(bool flickingVertically READ isFlickingVertically NOTIFY flickingVerticallyChanged) + Q_PROPERTY(FlickableDirection flickableDirection READ flickableDirection WRITE setFlickableDirection NOTIFY flickableDirectionChanged) + + Q_PROPERTY(bool interactive READ isInteractive WRITE setInteractive NOTIFY interactiveChanged) + Q_PROPERTY(int pressDelay READ pressDelay WRITE setPressDelay NOTIFY pressDelayChanged) + + Q_PROPERTY(bool atXEnd READ isAtXEnd NOTIFY isAtBoundaryChanged) + Q_PROPERTY(bool atYEnd READ isAtYEnd NOTIFY isAtBoundaryChanged) + Q_PROPERTY(bool atXBeginning READ isAtXBeginning NOTIFY isAtBoundaryChanged) + Q_PROPERTY(bool atYBeginning READ isAtYBeginning NOTIFY isAtBoundaryChanged) + + Q_PROPERTY(QDeclarativeFlickableVisibleArea *visibleArea READ visibleArea CONSTANT) + + Q_PROPERTY(QDeclarativeListProperty flickableData READ flickableData) + Q_PROPERTY(QDeclarativeListProperty flickableChildren READ flickableChildren) + Q_CLASSINFO("DefaultProperty", "flickableData") + + Q_ENUMS(FlickableDirection) + Q_ENUMS(BoundsBehavior) + +public: + QDeclarativeFlickable(QDeclarativeItem *parent=0); + ~QDeclarativeFlickable(); + + QDeclarativeListProperty flickableData(); + QDeclarativeListProperty flickableChildren(); + + enum BoundsBehavior { StopAtBounds, DragOverBounds, DragAndOvershootBounds }; + BoundsBehavior boundsBehavior() const; + void setBoundsBehavior(BoundsBehavior); + + qreal contentWidth() const; + void setContentWidth(qreal); + + qreal contentHeight() const; + void setContentHeight(qreal); + + qreal contentX() const; + virtual void setContentX(qreal pos); + + qreal contentY() const; + virtual void setContentY(qreal pos); + + bool isMoving() const; + bool isMovingHorizontally() const; + bool isMovingVertically() const; + bool isFlicking() const; + bool isFlickingHorizontally() const; + bool isFlickingVertically() const; + + int pressDelay() const; + void setPressDelay(int delay); + + qreal maximumFlickVelocity() const; + void setMaximumFlickVelocity(qreal); + + qreal flickDeceleration() const; + void setFlickDeceleration(qreal); + + bool isInteractive() const; + void setInteractive(bool); + + qreal horizontalVelocity() const; + qreal verticalVelocity() const; + + bool isAtXEnd() const; + bool isAtXBeginning() const; + bool isAtYEnd() const; + bool isAtYBeginning() const; + + QDeclarativeItem *contentItem(); + + enum FlickableDirection { AutoFlickDirection=0x00, HorizontalFlick=0x01, VerticalFlick=0x02, HorizontalAndVerticalFlick=0x03 }; + FlickableDirection flickableDirection() const; + void setFlickableDirection(FlickableDirection); + + Q_INVOKABLE Q_REVISION(1) void resizeContent(qreal w, qreal h, QPointF center); + Q_INVOKABLE Q_REVISION(1) void returnToBounds(); + +Q_SIGNALS: + void contentWidthChanged(); + void contentHeightChanged(); + void contentXChanged(); + void contentYChanged(); + void movingChanged(); + void movingHorizontallyChanged(); + void movingVerticallyChanged(); + void flickingChanged(); + void flickingHorizontallyChanged(); + void flickingVerticallyChanged(); + void horizontalVelocityChanged(); + void verticalVelocityChanged(); + void isAtBoundaryChanged(); + void flickableDirectionChanged(); + void interactiveChanged(); + void boundsBehaviorChanged(); + void maximumFlickVelocityChanged(); + void flickDecelerationChanged(); + void pressDelayChanged(); + void movementStarted(); + void movementEnded(); + void flickStarted(); + void flickEnded(); + +protected: + virtual bool sceneEventFilter(QGraphicsItem *, QEvent *); + void mousePressEvent(QGraphicsSceneMouseEvent *event); + void mouseMoveEvent(QGraphicsSceneMouseEvent *event); + void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); + void wheelEvent(QGraphicsSceneWheelEvent *event); + void timerEvent(QTimerEvent *event); + + QDeclarativeFlickableVisibleArea *visibleArea(); + +protected Q_SLOTS: + virtual void ticked(); + void movementStarting(); + void movementEnding(); + +protected: + void movementXEnding(); + void movementYEnding(); + virtual qreal minXExtent() const; + virtual qreal minYExtent() const; + virtual qreal maxXExtent() const; + virtual qreal maxYExtent() const; + qreal vWidth() const; + qreal vHeight() const; + virtual void viewportMoved(); + virtual void geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry); + bool sceneEvent(QEvent *event); + bool sendMouseEvent(QGraphicsSceneMouseEvent *event); + + bool xflick() const; + bool yflick() const; + void cancelFlick(); + +protected: + QDeclarativeFlickable(QDeclarativeFlickablePrivate &dd, QDeclarativeItem *parent); + +private: + Q_DISABLE_COPY(QDeclarativeFlickable) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarativeFlickable) + friend class QDeclarativeFlickableVisibleArea; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeFlickable) + +QT_END_HEADER + +#endif diff --git a/src/declarative/graphicsitems/qdeclarativeflickable_p_p.h b/src/declarative/graphicsitems/qdeclarativeflickable_p_p.h new file mode 100644 index 00000000..e70767b1 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativeflickable_p_p.h @@ -0,0 +1,244 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEFLICKABLE_P_H +#define QDECLARATIVEFLICKABLE_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 "private/qdeclarativeflickable_p.h" + +#include "private/qdeclarativeitem_p.h" +#include "private/qdeclarativeitemchangelistener_p.h" + +#include +#include +#include + +#include +#include "qplatformdefs.h" + +QT_BEGIN_NAMESPACE + +// Really slow flicks can be annoying. +#ifndef QML_FLICK_MINVELOCITY +#define QML_FLICK_MINVELOCITY 175 +#endif + +const qreal MinimumFlickVelocity = QML_FLICK_MINVELOCITY; + +class QDeclarativeFlickableVisibleArea; +class QDeclarativeFlickablePrivate : public QDeclarativeItemPrivate, public QDeclarativeItemChangeListener +{ + Q_DECLARE_PUBLIC(QDeclarativeFlickable) + +public: + QDeclarativeFlickablePrivate(); + void init(); + + struct Velocity : public QDeclarativeTimeLineValue + { + Velocity(QDeclarativeFlickablePrivate *p) + : parent(p) {} + virtual void setValue(qreal v) { + if (v != value()) { + QDeclarativeTimeLineValue::setValue(v); + parent->updateVelocity(); + } + } + QDeclarativeFlickablePrivate *parent; + }; + + struct AxisData { + AxisData(QDeclarativeFlickablePrivate *fp, void (QDeclarativeFlickablePrivate::*func)(qreal)) + : move(fp, func), viewSize(-1), smoothVelocity(fp), atEnd(false), atBeginning(true) + , fixingUp(false), inOvershoot(false), moving(false), flicking(false) + {} + + void reset() { + velocityBuffer.clear(); + dragStartOffset = 0; + fixingUp = false; + inOvershoot = false; + } + + void addVelocitySample(qreal v, qreal maxVelocity); + void updateVelocity(); + + QDeclarativeTimeLineValueProxy move; + qreal viewSize; + qreal pressPos; + qreal dragStartOffset; + qreal dragMinBound; + qreal dragMaxBound; + qreal velocity; + qreal flickTarget; + QDeclarativeFlickablePrivate::Velocity smoothVelocity; + QPODVector velocityBuffer; + bool atEnd : 1; + bool atBeginning : 1; + bool fixingUp : 1; + bool inOvershoot : 1; + bool moving : 1; + bool flicking : 1; + }; + + void flickX(qreal velocity); + void flickY(qreal velocity); + virtual void flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, + QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity); + + void fixupX(); + void fixupY(); + virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent); + + void updateBeginningEnd(); + + bool isOutermostPressDelay() const; + void captureDelayedPress(QGraphicsSceneMouseEvent *event); + void clearDelayedPress(); + + void setRoundedViewportX(qreal x); + void setRoundedViewportY(qreal y); + + qreal overShootDistance(qreal size); + + void itemGeometryChanged(QDeclarativeItem *, const QRectF &, const QRectF &); + +public: + QDeclarativeItem *contentItem; + + AxisData hData; + AxisData vData; + + QDeclarativeTimeLine timeline; + bool hMoved : 1; + bool vMoved : 1; + bool stealMouse : 1; + bool pressed : 1; + bool interactive : 1; + bool calcVelocity : 1; + QElapsedTimer lastPosTime; + QPointF lastPos; + QPointF pressPos; + QElapsedTimer pressTime; + qreal deceleration; + qreal maxVelocity; + QElapsedTimer velocityTime; + QPointF lastFlickablePosition; + qreal reportedVelocitySmoothing; + QGraphicsSceneMouseEvent *delayedPressEvent; + QGraphicsItem *delayedPressTarget; + QBasicTimer delayedPressTimer; + int pressDelay; + int fixupDuration; + + enum FixupMode { Normal, Immediate, ExtentChanged }; + FixupMode fixupMode; + + static void fixupY_callback(void *); + static void fixupX_callback(void *); + + void updateVelocity(); + int vTime; + QDeclarativeTimeLine velocityTimeline; + QDeclarativeFlickableVisibleArea *visibleArea; + QDeclarativeFlickable::FlickableDirection flickableDirection; + QDeclarativeFlickable::BoundsBehavior boundsBehavior; + + void handleMousePressEvent(QGraphicsSceneMouseEvent *); + void handleMouseMoveEvent(QGraphicsSceneMouseEvent *); + void handleMouseReleaseEvent(QGraphicsSceneMouseEvent *); + + // flickableData property + static void data_append(QDeclarativeListProperty *, QObject *); + static int data_count(QDeclarativeListProperty *); + static QObject *data_at(QDeclarativeListProperty *, int); + static void data_clear(QDeclarativeListProperty *); +}; + +class QDeclarativeFlickableVisibleArea : public QObject +{ + Q_OBJECT + + Q_PROPERTY(qreal xPosition READ xPosition NOTIFY xPositionChanged) + Q_PROPERTY(qreal yPosition READ yPosition NOTIFY yPositionChanged) + Q_PROPERTY(qreal widthRatio READ widthRatio NOTIFY widthRatioChanged) + Q_PROPERTY(qreal heightRatio READ heightRatio NOTIFY heightRatioChanged) + +public: + QDeclarativeFlickableVisibleArea(QDeclarativeFlickable *parent=0); + + qreal xPosition() const; + qreal widthRatio() const; + qreal yPosition() const; + qreal heightRatio() const; + + void updateVisible(); + +signals: + void xPositionChanged(qreal xPosition); + void yPositionChanged(qreal yPosition); + void widthRatioChanged(qreal widthRatio); + void heightRatioChanged(qreal heightRatio); + +private: + QDeclarativeFlickable *flickable; + qreal m_xPosition; + qreal m_widthRatio; + qreal m_yPosition; + qreal m_heightRatio; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeFlickableVisibleArea) + +#endif diff --git a/src/declarative/graphicsitems/qdeclarativeflipable.cpp b/src/declarative/graphicsitems/qdeclarativeflipable.cpp new file mode 100644 index 00000000..11569607 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativeflipable.cpp @@ -0,0 +1,254 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativeflipable_p.h" + +#include "private/qdeclarativeitem_p.h" +#include "private/qdeclarativeguard_p.h" + +#include + +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeFlipablePrivate : public QDeclarativeItemPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeFlipable) +public: + QDeclarativeFlipablePrivate() : current(QDeclarativeFlipable::Front), front(0), back(0) {} + + void updateSceneTransformFromParent(); + void setBackTransform(); + + QDeclarativeFlipable::Side current; + QDeclarativeGuard front; + QDeclarativeGuard back; + + bool wantBackXFlipped; + bool wantBackYFlipped; +}; + +/*! + \qmlclass Flipable QDeclarativeFlipable + \since 4.7 + \ingroup qml-basic-interaction-elements + \brief The Flipable item provides a surface that can be flipped. + \inherits Item + + Flipable is an item that can be visibly "flipped" between its front and + back sides, like a card. It is used together with \l Rotation, \l State + and \l Transition elements to produce a flipping effect. + + The \l front and \l back properties are used to hold the items that are + shown respectively on the front and back sides of the flipable item. + + \section1 Example Usage + + The following example shows a Flipable item that flips whenever it is + clicked, rotating about the y-axis. + + This flipable item has a \c flipped boolean property that is toggled + whenever the MouseArea within the flipable is clicked. When + \c flipped is true, the item changes to the "back" state; in this + state, the \c angle of the \l Rotation item is changed to 180 + degrees to produce the flipping effect. When \c flipped is false, the + item reverts to the default state, in which the \c angle value is 0. + + \snippet doc/src/snippets/declarative/flipable/flipable.qml 0 + + \image flipable.gif + + The \l Transition creates the animation that changes the angle over + four seconds. When the item changes between its "back" and + default states, the NumberAnimation animates the angle between + its old and new values. + + See \l {QML States} for details on state changes and the default + state, and \l {QML Animation and Transitions} for more information on how + animations work within transitions. + + \sa {declarative/ui-components/flipable}{Flipable example} +*/ + +QDeclarativeFlipable::QDeclarativeFlipable(QDeclarativeItem *parent) +: QDeclarativeItem(*(new QDeclarativeFlipablePrivate), parent) +{ +} + +QDeclarativeFlipable::~QDeclarativeFlipable() +{ +} + +/*! + \qmlproperty Item Flipable::front + \qmlproperty Item Flipable::back + + The front and back sides of the flipable. +*/ + +QGraphicsObject *QDeclarativeFlipable::front() +{ + Q_D(const QDeclarativeFlipable); + return d->front; +} + +void QDeclarativeFlipable::setFront(QGraphicsObject *front) +{ + Q_D(QDeclarativeFlipable); + if (d->front) { + qmlInfo(this) << tr("front is a write-once property"); + return; + } + d->front = front; + d->front->setParentItem(this); + if (Back == d->current) + d->front->setOpacity(0.); + emit frontChanged(); +} + +QGraphicsObject *QDeclarativeFlipable::back() +{ + Q_D(const QDeclarativeFlipable); + return d->back; +} + +void QDeclarativeFlipable::setBack(QGraphicsObject *back) +{ + Q_D(QDeclarativeFlipable); + if (d->back) { + qmlInfo(this) << tr("back is a write-once property"); + return; + } + d->back = back; + d->back->setParentItem(this); + if (Front == d->current) + d->back->setOpacity(0.); + connect(back, SIGNAL(widthChanged()), + this, SLOT(retransformBack())); + connect(back, SIGNAL(heightChanged()), + this, SLOT(retransformBack())); + emit backChanged(); +} + +void QDeclarativeFlipable::retransformBack() +{ + Q_D(QDeclarativeFlipable); + if (d->current == QDeclarativeFlipable::Back && d->back) + d->setBackTransform(); +} + +/*! + \qmlproperty enumeration Flipable::side + + The side of the Flipable currently visible. Possible values are \c + Flipable.Front and \c Flipable.Back. +*/ +QDeclarativeFlipable::Side QDeclarativeFlipable::side() const +{ + Q_D(const QDeclarativeFlipable); + if (d->dirtySceneTransform) + const_cast(d)->ensureSceneTransform(); + + return d->current; +} + +// determination on the currently visible side of the flipable +// has to be done on the complete scene transform to give +// correct results. +void QDeclarativeFlipablePrivate::updateSceneTransformFromParent() +{ + Q_Q(QDeclarativeFlipable); + + QDeclarativeItemPrivate::updateSceneTransformFromParent(); + QPointF p1(0, 0); + QPointF p2(1, 0); + QPointF p3(1, 1); + + QPointF scenep1 = sceneTransform.map(p1); + QPointF scenep2 = sceneTransform.map(p2); + QPointF scenep3 = sceneTransform.map(p3); + p1 = q->mapToParent(p1); + p2 = q->mapToParent(p2); + p3 = q->mapToParent(p3); + + qreal cross = (scenep1.x() - scenep2.x()) * (scenep3.y() - scenep2.y()) - + (scenep1.y() - scenep2.y()) * (scenep3.x() - scenep2.x()); + + wantBackYFlipped = p1.x() >= p2.x(); + wantBackXFlipped = p2.y() >= p3.y(); + + QDeclarativeFlipable::Side newSide; + if (cross > 0) { + newSide = QDeclarativeFlipable::Back; + } else { + newSide = QDeclarativeFlipable::Front; + } + + if (newSide != current) { + current = newSide; + if (current == QDeclarativeFlipable::Back && back) + setBackTransform(); + if (front) + front->setOpacity((current==QDeclarativeFlipable::Front)?qreal(1.):qreal(0.)); + if (back) + back->setOpacity((current==QDeclarativeFlipable::Back)?qreal(1.):qreal(0.)); + emit q->sideChanged(); + } +} + +/* Depends on the width/height of the back item, and so needs reevaulating + if those change. +*/ +void QDeclarativeFlipablePrivate::setBackTransform() +{ + QTransform mat; + QGraphicsItemPrivate *dBack = QGraphicsItemPrivate::get(back); + mat.translate(dBack->width()/2,dBack->height()/2); + if (dBack->width() && wantBackYFlipped) + mat.rotate(180, Qt::YAxis); + if (dBack->height() && wantBackXFlipped) + mat.rotate(180, Qt::XAxis); + mat.translate(-dBack->width()/2,-dBack->height()/2); + back->setTransform(mat); +} + +QT_END_NAMESPACE diff --git a/src/declarative/graphicsitems/qdeclarativeflipable_p.h b/src/declarative/graphicsitems/qdeclarativeflipable_p.h new file mode 100644 index 00000000..f74cf8fc --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativeflipable_p.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEFLIPABLE_H +#define QDECLARATIVEFLIPABLE_H + +#include "qdeclarativeitem.h" + +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeFlipablePrivate; +class Q_AUTOTEST_EXPORT QDeclarativeFlipable : public QDeclarativeItem +{ + Q_OBJECT + + Q_ENUMS(Side) + Q_PROPERTY(QGraphicsObject *front READ front WRITE setFront NOTIFY frontChanged) + Q_PROPERTY(QGraphicsObject *back READ back WRITE setBack NOTIFY backChanged) + Q_PROPERTY(Side side READ side NOTIFY sideChanged) + //### flipAxis + //### flipRotation +public: + QDeclarativeFlipable(QDeclarativeItem *parent=0); + ~QDeclarativeFlipable(); + + QGraphicsObject *front(); + void setFront(QGraphicsObject *); + + QGraphicsObject *back(); + void setBack(QGraphicsObject *); + + enum Side { Front, Back }; + Side side() const; + +Q_SIGNALS: + void frontChanged(); + void backChanged(); + void sideChanged(); + +private Q_SLOTS: + void retransformBack(); + +private: + Q_DISABLE_COPY(QDeclarativeFlipable) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarativeFlipable) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeFlipable) + +QT_END_HEADER + +#endif // QDECLARATIVEFLIPABLE_H diff --git a/src/declarative/graphicsitems/qdeclarativefocuspanel.cpp b/src/declarative/graphicsitems/qdeclarativefocuspanel.cpp new file mode 100644 index 00000000..489ee901 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativefocuspanel.cpp @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativefocuspanel_p.h" + +#include "private/qdeclarativeitem_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmlclass FocusPanel QDeclarativeFocusPanel + \since 4.7 + \ingroup qml-basic-interaction-elements + + \brief The FocusPanel item explicitly creates a focus panel. + \inherits Item + + Focus panels assist in keyboard focus handling when building QML + applications. All the details are covered in the + \l {qmlfocus}{keyboard focus documentation}. +*/ + +QDeclarativeFocusPanel::QDeclarativeFocusPanel(QDeclarativeItem *parent) : + QDeclarativeItem(parent) +{ + Q_D(QDeclarativeItem); + d->flags |= QGraphicsItem::ItemIsPanel; +} + +QDeclarativeFocusPanel::~QDeclarativeFocusPanel() +{ +} + +/*! + \qmlproperty bool FocusPanel::active + + Sets whether the item is the active focus panel. +*/ + +bool QDeclarativeFocusPanel::sceneEvent(QEvent *event) +{ + if (event->type() == QEvent::WindowActivate || + event->type() == QEvent::WindowDeactivate) + emit activeChanged(); + return QDeclarativeItem::sceneEvent(event); +} + +QT_END_NAMESPACE diff --git a/src/declarative/graphicsitems/qdeclarativefocuspanel_p.h b/src/declarative/graphicsitems/qdeclarativefocuspanel_p.h new file mode 100644 index 00000000..b7d1e773 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativefocuspanel_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEFOCUSPANEL_H +#define QDECLARATIVEFOCUSPANEL_H + +#include "qdeclarativeitem.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class Q_AUTOTEST_EXPORT QDeclarativeFocusPanel : public QDeclarativeItem +{ + Q_OBJECT + Q_PROPERTY(bool active READ isActive WRITE setActive NOTIFY activeChanged) +public: + QDeclarativeFocusPanel(QDeclarativeItem *parent=0); + virtual ~QDeclarativeFocusPanel(); + +Q_SIGNALS: + void activeChanged(); + +protected: + bool sceneEvent(QEvent *event); + +private: + Q_DISABLE_COPY(QDeclarativeFocusPanel) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarativeItem) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeFocusPanel) + +QT_END_HEADER + +#endif // QDECLARATIVEFOCUSPANEL_H diff --git a/src/declarative/graphicsitems/qdeclarativefocusscope.cpp b/src/declarative/graphicsitems/qdeclarativefocusscope.cpp new file mode 100644 index 00000000..d76a27f7 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativefocusscope.cpp @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativefocusscope_p.h" + +#include "private/qdeclarativeitem_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \qmlclass FocusScope QDeclarativeFocusScope + \since 4.7 + \ingroup qml-basic-interaction-elements + + \brief The FocusScope object explicitly creates a focus scope. + \inherits Item + + Focus scopes assist in keyboard focus handling when building reusable QML + components. All the details are covered in the + \l {qmlfocus}{keyboard focus documentation}. + + \sa {declarative/keyinteraction/focus}{Keyboard focus example} +*/ + +QDeclarativeFocusScope::QDeclarativeFocusScope(QDeclarativeItem *parent) : + QDeclarativeItem(parent) +{ + Q_D(QDeclarativeItem); + d->flags |= QGraphicsItem::ItemIsFocusScope; +} + +QDeclarativeFocusScope::~QDeclarativeFocusScope() +{ +} +QT_END_NAMESPACE diff --git a/src/declarative/graphicsitems/qdeclarativefocusscope_p.h b/src/declarative/graphicsitems/qdeclarativefocusscope_p.h new file mode 100644 index 00000000..5ddcc2ed --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativefocusscope_p.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEFOCUSSCOPE_H +#define QDECLARATIVEFOCUSSCOPE_H + +#include "qdeclarativeitem.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +//### set component root as focusscope +class Q_AUTOTEST_EXPORT QDeclarativeFocusScope : public QDeclarativeItem +{ + Q_OBJECT + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarativeItem) +public: + QDeclarativeFocusScope(QDeclarativeItem *parent=0); + virtual ~QDeclarativeFocusScope(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeFocusScope) + +QT_END_HEADER + +#endif // QDECLARATIVEFOCUSSCOPE_H diff --git a/src/declarative/graphicsitems/qdeclarativegraphicswidget.cpp b/src/declarative/graphicsitems/qdeclarativegraphicswidget.cpp new file mode 100644 index 00000000..43f131df --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativegraphicswidget.cpp @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativegraphicswidget_p.h" +#include "private/qdeclarativeanchors_p.h" +#include "private/qdeclarativeitem_p.h" +#include "private/qdeclarativeanchors_p_p.h" + +QT_BEGIN_NAMESPACE + +class QDeclarativeGraphicsWidgetPrivate : public QObjectPrivate { + Q_DECLARE_PUBLIC(QDeclarativeGraphicsWidget) +public : + QDeclarativeGraphicsWidgetPrivate() : + _anchors(0), _anchorLines(0) + {} + QDeclarativeItemPrivate::AnchorLines *anchorLines() const; + QDeclarativeAnchors *_anchors; + mutable QDeclarativeItemPrivate::AnchorLines *_anchorLines; +}; + +QDeclarativeGraphicsWidget::QDeclarativeGraphicsWidget(QObject *parent) : + QObject(*new QDeclarativeGraphicsWidgetPrivate, parent) +{ +} +QDeclarativeGraphicsWidget::~QDeclarativeGraphicsWidget() +{ + Q_D(QDeclarativeGraphicsWidget); + delete d->_anchorLines; d->_anchorLines = 0; + delete d->_anchors; d->_anchors = 0; +} + +QDeclarativeAnchors *QDeclarativeGraphicsWidget::anchors() +{ + Q_D(QDeclarativeGraphicsWidget); + if (!d->_anchors) + d->_anchors = new QDeclarativeAnchors(static_cast(parent())); + return d->_anchors; +} + +QDeclarativeItemPrivate::AnchorLines *QDeclarativeGraphicsWidgetPrivate::anchorLines() const +{ + Q_Q(const QDeclarativeGraphicsWidget); + if (!_anchorLines) + _anchorLines = new QDeclarativeItemPrivate::AnchorLines(static_cast(q->parent())); + return _anchorLines; +} + +QDeclarativeAnchorLine QDeclarativeGraphicsWidget::left() const +{ + Q_D(const QDeclarativeGraphicsWidget); + return d->anchorLines()->left; +} + +QDeclarativeAnchorLine QDeclarativeGraphicsWidget::right() const +{ + Q_D(const QDeclarativeGraphicsWidget); + return d->anchorLines()->right; +} + +QDeclarativeAnchorLine QDeclarativeGraphicsWidget::horizontalCenter() const +{ + Q_D(const QDeclarativeGraphicsWidget); + return d->anchorLines()->hCenter; +} + +QDeclarativeAnchorLine QDeclarativeGraphicsWidget::top() const +{ + Q_D(const QDeclarativeGraphicsWidget); + return d->anchorLines()->top; +} + +QDeclarativeAnchorLine QDeclarativeGraphicsWidget::bottom() const +{ + Q_D(const QDeclarativeGraphicsWidget); + return d->anchorLines()->bottom; +} + +QDeclarativeAnchorLine QDeclarativeGraphicsWidget::verticalCenter() const +{ + Q_D(const QDeclarativeGraphicsWidget); + return d->anchorLines()->vCenter; +} + +QT_END_NAMESPACE + +#include diff --git a/src/declarative/graphicsitems/qdeclarativegraphicswidget_p.h b/src/declarative/graphicsitems/qdeclarativegraphicswidget_p.h new file mode 100644 index 00000000..93242aca --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativegraphicswidget_p.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEGRAPHICSWIDGET_P_H +#define QDECLARATIVEGRAPHICSWIDGET_P_H + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeAnchorLine; +class QDeclarativeAnchors; +class QGraphicsObject; +class QDeclarativeGraphicsWidgetPrivate; + +// ### TODO can the extension object be the anchor directly? We save one allocation -> awesome. +class QDeclarativeGraphicsWidget : public QObject +{ + Q_OBJECT + Q_PROPERTY(QDeclarativeAnchors * anchors READ anchors DESIGNABLE false CONSTANT FINAL) + Q_PROPERTY(QDeclarativeAnchorLine left READ left CONSTANT FINAL) + Q_PROPERTY(QDeclarativeAnchorLine right READ right CONSTANT FINAL) + Q_PROPERTY(QDeclarativeAnchorLine horizontalCenter READ horizontalCenter CONSTANT FINAL) + Q_PROPERTY(QDeclarativeAnchorLine top READ top CONSTANT FINAL) + Q_PROPERTY(QDeclarativeAnchorLine bottom READ bottom CONSTANT FINAL) + Q_PROPERTY(QDeclarativeAnchorLine verticalCenter READ verticalCenter CONSTANT FINAL) + // ### TODO : QGraphicsWidget don't have a baseline concept yet. + //Q_PROPERTY(QDeclarativeAnchorLine baseline READ baseline CONSTANT FINAL) +public: + QDeclarativeGraphicsWidget(QObject *parent = 0); + ~QDeclarativeGraphicsWidget(); + QDeclarativeAnchors *anchors(); + QDeclarativeAnchorLine left() const; + QDeclarativeAnchorLine right() const; + QDeclarativeAnchorLine horizontalCenter() const; + QDeclarativeAnchorLine top() const; + QDeclarativeAnchorLine bottom() const; + QDeclarativeAnchorLine verticalCenter() const; + Q_DISABLE_COPY(QDeclarativeGraphicsWidget) + Q_DECLARE_PRIVATE(QDeclarativeGraphicsWidget) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEGRAPHICSWIDGET_P_H diff --git a/src/declarative/graphicsitems/qdeclarativegridview.cpp b/src/declarative/graphicsitems/qdeclarativegridview.cpp new file mode 100644 index 00000000..1d9348c7 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativegridview.cpp @@ -0,0 +1,3172 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativegridview_p.h" + +#include "private/qdeclarativevisualitemmodel_p.h" +#include "private/qdeclarativeflickable_p_p.h" + +#include "private/qdeclarativesmoothedanimation_p_p.h" +#include + +#include +#include + +#include +#include +#include "qplatformdefs.h" + +QT_BEGIN_NAMESPACE + +#ifndef QML_FLICK_SNAPONETHRESHOLD +#define QML_FLICK_SNAPONETHRESHOLD 30 +#endif + +//---------------------------------------------------------------------------- + +class FxGridItem +{ +public: + FxGridItem(QDeclarativeItem *i, QDeclarativeGridView *v) : item(i), view(v) { + attached = static_cast(qmlAttachedPropertiesObject(item)); + if (attached) + attached->setView(view); + } + ~FxGridItem() {} + + qreal rowPos() const { + qreal rowPos = 0; + if (view->flow() == QDeclarativeGridView::LeftToRight) { + rowPos = item->y(); + } else { + if (view->effectiveLayoutDirection() == Qt::RightToLeft) + rowPos = -view->cellWidth()-item->x(); + else + rowPos = item->x(); + } + return rowPos; + } + qreal colPos() const { + qreal colPos = 0; + if (view->flow() == QDeclarativeGridView::LeftToRight) { + if (view->effectiveLayoutDirection() == Qt::RightToLeft) { + int colSize = view->cellWidth(); + int columns = view->width()/colSize; + colPos = colSize * (columns-1) - item->x(); + } else { + colPos = item->x(); + } + } else { + colPos = item->y(); + } + + return colPos; + } + + qreal endRowPos() const { + if (view->flow() == QDeclarativeGridView::LeftToRight) { + return item->y() + view->cellHeight() - 1; + } else { + if (view->effectiveLayoutDirection() == Qt::RightToLeft) + return -item->x() - 1; + else + return item->x() + view->cellWidth() - 1; + } + } + void setPosition(qreal col, qreal row) { + if (view->effectiveLayoutDirection() == Qt::RightToLeft) { + if (view->flow() == QDeclarativeGridView::LeftToRight) { + int columns = view->width()/view->cellWidth(); + item->setPos(QPointF((view->cellWidth() * (columns-1) - col), row)); + } else { + item->setPos(QPointF(-view->cellWidth()-row, col)); + } + } else { + if (view->flow() == QDeclarativeGridView::LeftToRight) + item->setPos(QPointF(col, row)); + else + item->setPos(QPointF(row, col)); + } + + } + bool contains(qreal x, qreal y) const { + return (x >= item->x() && x < item->x() + view->cellWidth() && + y >= item->y() && y < item->y() + view->cellHeight()); + } + + QDeclarativeItem *item; + QDeclarativeGridView *view; + QDeclarativeGridViewAttached *attached; + int index; +}; + +//---------------------------------------------------------------------------- + +class QDeclarativeGridViewPrivate : public QDeclarativeFlickablePrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeGridView) + +public: + QDeclarativeGridViewPrivate() + : currentItem(0), layoutDirection(Qt::LeftToRight), flow(QDeclarativeGridView::LeftToRight) + , visibleIndex(0) , currentIndex(-1) + , cellWidth(100), cellHeight(100), columns(1), requestedIndex(-1), itemCount(0) + , highlightRangeStart(0), highlightRangeEnd(0) + , highlightRangeStartValid(false), highlightRangeEndValid(false) + , highlightRange(QDeclarativeGridView::NoHighlightRange) + , highlightComponent(0), highlight(0), trackedItem(0) + , moveReason(Other), buffer(0), highlightXAnimator(0), highlightYAnimator(0) + , highlightMoveDuration(150) + , footerComponent(0), footer(0), headerComponent(0), header(0) + , bufferMode(BufferBefore | BufferAfter), snapMode(QDeclarativeGridView::NoSnap) + , ownModel(false), wrap(false), autoHighlight(true) + , fixCurrentVisibility(false), lazyRelease(false), layoutScheduled(false) + , deferredRelease(false), haveHighlightRange(false), currentIndexCleared(false) {} + + void init(); + void clear(); + FxGridItem *createItem(int modelIndex); + void releaseItem(FxGridItem *item); + void refill(qreal from, qreal to, bool doBuffer=false); + + void updateGrid(); + void scheduleLayout(); + void layout(); + void updateUnrequestedIndexes(); + void updateUnrequestedPositions(); + void updateTrackedItem(); + void createHighlight(); + void updateHighlight(); + void updateCurrent(int modelIndex); + void updateHeader(); + void updateFooter(); + void fixupPosition(); + + FxGridItem *visibleItem(int modelIndex) const { + if (modelIndex >= visibleIndex && modelIndex < visibleIndex + visibleItems.count()) { + for (int i = modelIndex - visibleIndex; i < visibleItems.count(); ++i) { + FxGridItem *item = visibleItems.at(i); + if (item->index == modelIndex) + return item; + } + } + return 0; + } + + bool isRightToLeftTopToBottom() const { + Q_Q(const QDeclarativeGridView); + return flow == QDeclarativeGridView::TopToBottom && q->effectiveLayoutDirection() == Qt::RightToLeft; + } + + void regenerate() { + Q_Q(QDeclarativeGridView); + if (q->isComponentComplete()) { + clear(); + updateGrid(); + setPosition(0); + q->refill(); + updateCurrent(currentIndex); + } + } + + void mirrorChange() { + Q_Q(QDeclarativeGridView); + regenerate(); + } + + qreal position() const { + Q_Q(const QDeclarativeGridView); + return flow == QDeclarativeGridView::LeftToRight ? q->contentY() : q->contentX(); + } + void setPosition(qreal pos) { + Q_Q(QDeclarativeGridView); + if (flow == QDeclarativeGridView::LeftToRight) { + q->QDeclarativeFlickable::setContentY(pos); + q->QDeclarativeFlickable::setContentX(0); + } else { + if (q->effectiveLayoutDirection() == Qt::LeftToRight) + q->QDeclarativeFlickable::setContentX(pos); + else + q->QDeclarativeFlickable::setContentX(-pos-size()); + q->QDeclarativeFlickable::setContentY(0); + } + } + int size() const { + Q_Q(const QDeclarativeGridView); + return flow == QDeclarativeGridView::LeftToRight ? q->height() : q->width(); + } + qreal originPosition() const { + qreal pos = 0; + if (!visibleItems.isEmpty()) + pos = visibleItems.first()->rowPos() - visibleIndex / columns * rowSize(); + return pos; + } + + qreal lastPosition() const { + qreal pos = 0; + if (model && model->count()) + pos = rowPosAt(model->count() - 1) + rowSize(); + return pos; + } + + qreal startPosition() const { + return isRightToLeftTopToBottom() ? -lastPosition()+1 : originPosition(); + } + + qreal endPosition() const { + return isRightToLeftTopToBottom() ? -originPosition()+1 : lastPosition(); + + } + + bool isValid() const { + return model && model->count() && model->isValid(); + } + + int rowSize() const { + return flow == QDeclarativeGridView::LeftToRight ? cellHeight : cellWidth; + } + int colSize() const { + return flow == QDeclarativeGridView::LeftToRight ? cellWidth : cellHeight; + } + + qreal colPosAt(int modelIndex) const { + if (FxGridItem *item = visibleItem(modelIndex)) + return item->colPos(); + if (!visibleItems.isEmpty()) { + if (modelIndex < visibleIndex) { + int count = (visibleIndex - modelIndex) % columns; + int col = visibleItems.first()->colPos() / colSize(); + col = (columns - count + col) % columns; + return col * colSize(); + } else { + int count = columns - 1 - (modelIndex - visibleItems.last()->index - 1) % columns; + return visibleItems.last()->colPos() - count * colSize(); + } + } else { + return (modelIndex % columns) * colSize(); + } + return 0; + } + qreal rowPosAt(int modelIndex) const { + if (FxGridItem *item = visibleItem(modelIndex)) + return item->rowPos(); + if (!visibleItems.isEmpty()) { + if (modelIndex < visibleIndex) { + int firstCol = visibleItems.first()->colPos() / colSize(); + int col = visibleIndex - modelIndex + (columns - firstCol - 1); + int rows = col / columns; + return visibleItems.first()->rowPos() - rows * rowSize(); + } else { + int count = modelIndex - visibleItems.last()->index; + int col = visibleItems.last()->colPos() + count * colSize(); + int rows = col / (columns * colSize()); + return visibleItems.last()->rowPos() + rows * rowSize(); + } + } else { + qreal pos = (modelIndex / columns) * rowSize(); + if (header) + pos += headerSize(); + return pos; + } + return 0; + } + + FxGridItem *firstVisibleItem() const { + const qreal pos = isRightToLeftTopToBottom() ? -position()-size() : position(); + for (int i = 0; i < visibleItems.count(); ++i) { + FxGridItem *item = visibleItems.at(i); + if (item->index != -1 && item->endRowPos() > pos) + return item; + } + return visibleItems.count() ? visibleItems.first() : 0; + } + + int lastVisibleIndex() const { + for (int i = 0; i < visibleItems.count(); ++i) { + FxGridItem *item = visibleItems.at(i); + if (item->index != -1) + return item->index; + } + return -1; + } + + // Map a model index to visibleItems list index. + // These may differ if removed items are still present in the visible list, + // e.g. doing a removal animation + int mapFromModel(int modelIndex) const { + if (modelIndex < visibleIndex || modelIndex >= visibleIndex + visibleItems.count()) + return -1; + for (int i = 0; i < visibleItems.count(); ++i) { + FxGridItem *listItem = visibleItems.at(i); + if (listItem->index == modelIndex) + return i + visibleIndex; + if (listItem->index > modelIndex) + return -1; + } + return -1; // Not in visibleList + } + + qreal snapPosAt(qreal pos) const { + Q_Q(const QDeclarativeGridView); + qreal snapPos = 0; + if (!visibleItems.isEmpty()) { + qreal highlightStart = isRightToLeftTopToBottom() && highlightRangeStartValid ? size()-highlightRangeEnd : highlightRangeStart; + pos += highlightStart; + pos += rowSize()/2; + snapPos = visibleItems.first()->rowPos() - visibleIndex / columns * rowSize(); + snapPos = pos - fmodf(pos - snapPos, qreal(rowSize())); + snapPos -= highlightStart; + qreal maxExtent; + qreal minExtent; + if (isRightToLeftTopToBottom()) { + maxExtent = q->minXExtent(); + minExtent = q->maxXExtent(); + } else { + maxExtent = flow == QDeclarativeGridView::LeftToRight ? -q->maxYExtent() : -q->maxXExtent(); + minExtent = flow == QDeclarativeGridView::LeftToRight ? -q->minYExtent() : -q->minXExtent(); + } + if (snapPos > maxExtent) + snapPos = maxExtent; + if (snapPos < minExtent) + snapPos = minExtent; + } + return snapPos; + } + + FxGridItem *snapItemAt(qreal pos) { + for (int i = 0; i < visibleItems.count(); ++i) { + FxGridItem *item = visibleItems[i]; + if (item->index == -1) + continue; + qreal itemTop = item->rowPos(); + if (itemTop+rowSize()/2 >= pos && itemTop - rowSize()/2 <= pos) + return item; + } + return 0; + } + + int snapIndex() { + int index = currentIndex; + for (int i = 0; i < visibleItems.count(); ++i) { + FxGridItem *item = visibleItems[i]; + if (item->index == -1) + continue; + qreal itemTop = item->rowPos(); + if (itemTop >= highlight->rowPos()-rowSize()/2 && itemTop < highlight->rowPos()+rowSize()/2) { + index = item->index; + if (item->colPos() >= highlight->colPos()-colSize()/2 && item->colPos() < highlight->colPos()+colSize()/2) + return item->index; + } + } + return index; + } + + qreal headerSize() const { + if (!header) + return 0.0; + + return flow == QDeclarativeGridView::LeftToRight + ? header->item->height() + : header->item->width(); + } + + + virtual void itemGeometryChanged(QDeclarativeItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) { + Q_Q(const QDeclarativeGridView); + QDeclarativeFlickablePrivate::itemGeometryChanged(item, newGeometry, oldGeometry); + if (item == q) { + if (newGeometry.height() != oldGeometry.height() + || newGeometry.width() != oldGeometry.width()) { + if (q->isComponentComplete()) { + updateGrid(); + scheduleLayout(); + } + } + } else if ((header && header->item == item) || (footer && footer->item == item)) { + if (header) + updateHeader(); + if (footer) + updateFooter(); + } + } + + void positionViewAtIndex(int index, int mode); + virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent); + virtual void flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, + QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity); + + // for debugging only + void checkVisible() const { + int skip = 0; + for (int i = 0; i < visibleItems.count(); ++i) { + FxGridItem *listItem = visibleItems.at(i); + if (listItem->index == -1) { + ++skip; + } else if (listItem->index != visibleIndex + i - skip) { + for (int j = 0; j < visibleItems.count(); j++) + qDebug() << " index" << j << "item index" << visibleItems.at(j)->index; + qFatal("index %d %d %d", visibleIndex, i, listItem->index); + } + } + } + + QDeclarativeGuard model; + QVariant modelVariant; + QList visibleItems; + QHash unrequestedItems; + FxGridItem *currentItem; + Qt::LayoutDirection layoutDirection; + QDeclarativeGridView::Flow flow; + int visibleIndex; + int currentIndex; + int cellWidth; + int cellHeight; + int columns; + int requestedIndex; + int itemCount; + qreal highlightRangeStart; + qreal highlightRangeEnd; + bool highlightRangeStartValid; + bool highlightRangeEndValid; + QDeclarativeGridView::HighlightRangeMode highlightRange; + QDeclarativeComponent *highlightComponent; + FxGridItem *highlight; + FxGridItem *trackedItem; + enum MovementReason { Other, SetIndex, Mouse }; + MovementReason moveReason; + int buffer; + QSmoothedAnimation *highlightXAnimator; + QSmoothedAnimation *highlightYAnimator; + int highlightMoveDuration; + QDeclarativeComponent *footerComponent; + FxGridItem *footer; + QDeclarativeComponent *headerComponent; + FxGridItem *header; + enum BufferMode { NoBuffer = 0x00, BufferBefore = 0x01, BufferAfter = 0x02 }; + int bufferMode; + QDeclarativeGridView::SnapMode snapMode; + + bool ownModel : 1; + bool wrap : 1; + bool autoHighlight : 1; + bool fixCurrentVisibility : 1; + bool lazyRelease : 1; + bool layoutScheduled : 1; + bool deferredRelease : 1; + bool haveHighlightRange : 1; + bool currentIndexCleared : 1; +}; + +void QDeclarativeGridViewPrivate::init() +{ + Q_Q(QDeclarativeGridView); + QObject::connect(q, SIGNAL(movementEnded()), q, SLOT(animStopped())); + q->setFlag(QGraphicsItem::ItemIsFocusScope); + q->setFlickableDirection(QDeclarativeFlickable::VerticalFlick); + addItemChangeListener(this, Geometry); +} + +void QDeclarativeGridViewPrivate::clear() +{ + for (int i = 0; i < visibleItems.count(); ++i) + releaseItem(visibleItems.at(i)); + visibleItems.clear(); + visibleIndex = 0; + releaseItem(currentItem); + currentItem = 0; + createHighlight(); + trackedItem = 0; + itemCount = 0; +} + +FxGridItem *QDeclarativeGridViewPrivate::createItem(int modelIndex) +{ + Q_Q(QDeclarativeGridView); + // create object + requestedIndex = modelIndex; + FxGridItem *listItem = 0; + if (QDeclarativeItem *item = model->item(modelIndex, false)) { + listItem = new FxGridItem(item, q); + listItem->index = modelIndex; + if (model->completePending()) { + // complete + listItem->item->setZValue(1); + listItem->item->setParentItem(q->contentItem()); + model->completeItem(); + } else { + listItem->item->setParentItem(q->contentItem()); + } + unrequestedItems.remove(listItem->item); + } + requestedIndex = -1; + return listItem; +} + + +void QDeclarativeGridViewPrivate::releaseItem(FxGridItem *item) +{ + Q_Q(QDeclarativeGridView); + if (!item || !model) + return; + if (trackedItem == item) { + QObject::disconnect(trackedItem->item, SIGNAL(yChanged()), q, SLOT(trackedPositionChanged())); + QObject::disconnect(trackedItem->item, SIGNAL(xChanged()), q, SLOT(trackedPositionChanged())); + trackedItem = 0; + } + if (model->release(item->item) == 0) { + // item was not destroyed, and we no longer reference it. + unrequestedItems.insert(item->item, model->indexOf(item->item, q)); + } + delete item; +} + +void QDeclarativeGridViewPrivate::refill(qreal from, qreal to, bool doBuffer) +{ + Q_Q(QDeclarativeGridView); + if (!isValid() || !q->isComponentComplete()) + return; + itemCount = model->count(); + qreal bufferFrom = from - buffer; + qreal bufferTo = to + buffer; + qreal fillFrom = from; + qreal fillTo = to; + if (doBuffer && (bufferMode & BufferAfter)) + fillTo = bufferTo; + if (doBuffer && (bufferMode & BufferBefore)) + fillFrom = bufferFrom; + + bool changed = false; + + int colPos = colPosAt(visibleIndex); + int rowPos = rowPosAt(visibleIndex); + int modelIndex = visibleIndex; + if (visibleItems.count()) { + rowPos = visibleItems.last()->rowPos(); + colPos = visibleItems.last()->colPos() + colSize(); + if (colPos > colSize() * (columns-1)) { + colPos = 0; + rowPos += rowSize(); + } + int i = visibleItems.count() - 1; + while (i > 0 && visibleItems.at(i)->index == -1) + --i; + modelIndex = visibleItems.at(i)->index + 1; + } + + if (visibleItems.count() && (fillFrom > rowPos + rowSize()*2 + || fillTo < rowPosAt(visibleIndex) - rowSize())) { + // We've jumped more than a page. Estimate which items are now + // visible and fill from there. + int count = (fillFrom - (rowPos + rowSize())) / (rowSize()) * columns; + for (int i = 0; i < visibleItems.count(); ++i) + releaseItem(visibleItems.at(i)); + visibleItems.clear(); + modelIndex += count; + if (modelIndex >= model->count()) + modelIndex = model->count() - 1; + else if (modelIndex < 0) + modelIndex = 0; + modelIndex = modelIndex / columns * columns; + visibleIndex = modelIndex; + colPos = colPosAt(visibleIndex); + rowPos = rowPosAt(visibleIndex); + } + + int colNum = colPos / colSize(); + + FxGridItem *item = 0; + + // Item creation and release is staggered in order to avoid + // creating/releasing multiple items in one frame + // while flicking (as much as possible). + while (modelIndex < model->count() && rowPos <= fillTo + rowSize()*(columns - colNum)/(columns+1)) { +// qDebug() << "refill: append item" << modelIndex; + if (!(item = createItem(modelIndex))) + break; + item->setPosition(colPos, rowPos); + visibleItems.append(item); + colPos += colSize(); + colNum++; + if (colPos > colSize() * (columns-1)) { + colPos = 0; + colNum = 0; + rowPos += rowSize(); + } + ++modelIndex; + changed = true; + if (doBuffer) // never buffer more than one item per frame + break; + } + + if (visibleItems.count()) { + rowPos = visibleItems.first()->rowPos(); + colPos = visibleItems.first()->colPos() - colSize(); + if (colPos < 0) { + colPos = colSize() * (columns - 1); + rowPos -= rowSize(); + } + } + colNum = colPos / colSize(); + while (visibleIndex > 0 && rowPos + rowSize() - 1 >= fillFrom - rowSize()*(colNum+1)/(columns+1)){ +// qDebug() << "refill: prepend item" << visibleIndex-1 << "top pos" << rowPos << colPos; + if (!(item = createItem(visibleIndex-1))) + break; + --visibleIndex; + item->setPosition(colPos, rowPos); + visibleItems.prepend(item); + colPos -= colSize(); + colNum--; + if (colPos < 0) { + colPos = colSize() * (columns - 1); + colNum = columns-1; + rowPos -= rowSize(); + } + changed = true; + if (doBuffer) // never buffer more than one item per frame + break; + } + + if (!lazyRelease || !changed || deferredRelease) { // avoid destroying items in the same frame that we create + while (visibleItems.count() > 1 + && (item = visibleItems.first()) + && item->rowPos()+rowSize()-1 < bufferFrom - rowSize()*(item->colPos()/colSize()+1)/(columns+1)) { + if (item->attached->delayRemove()) + break; +// qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endRowPos(); + if (item->index != -1) + visibleIndex++; + visibleItems.removeFirst(); + releaseItem(item); + changed = true; + } + while (visibleItems.count() > 1 + && (item = visibleItems.last()) + && item->rowPos() > bufferTo + rowSize()*(columns - item->colPos()/colSize())/(columns+1)) { + if (item->attached->delayRemove()) + break; +// qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1; + visibleItems.removeLast(); + releaseItem(item); + changed = true; + } + deferredRelease = false; + } else { + deferredRelease = true; + } + if (changed) { + if (header) + updateHeader(); + if (footer) + updateFooter(); + if (flow == QDeclarativeGridView::LeftToRight) + q->setContentHeight(endPosition() - startPosition()); + else + q->setContentWidth(endPosition() - startPosition()); + } else if (!doBuffer && buffer && bufferMode != NoBuffer) { + refill(from, to, true); + } + lazyRelease = false; +} + +void QDeclarativeGridViewPrivate::updateGrid() +{ + Q_Q(QDeclarativeGridView); + + columns = (int)qMax((flow == QDeclarativeGridView::LeftToRight ? q->width() : q->height()) / colSize(), qreal(1.)); + if (isValid()) { + if (flow == QDeclarativeGridView::LeftToRight) + q->setContentHeight(endPosition() - startPosition()); + else + q->setContentWidth(lastPosition() - originPosition()); + } +} + +void QDeclarativeGridViewPrivate::scheduleLayout() +{ + Q_Q(QDeclarativeGridView); + if (!layoutScheduled) { + layoutScheduled = true; + QCoreApplication::postEvent(q, new QEvent(QEvent::User), Qt::HighEventPriority); + } +} + +void QDeclarativeGridViewPrivate::layout() +{ + Q_Q(QDeclarativeGridView); + layoutScheduled = false; + if (!isValid() && !visibleItems.count()) { + clear(); + return; + } + if (visibleItems.count()) { + qreal rowPos = visibleItems.first()->rowPos(); + qreal colPos = visibleItems.first()->colPos(); + int col = visibleIndex % columns; + if (colPos != col * colSize()) { + colPos = col * colSize(); + visibleItems.first()->setPosition(colPos, rowPos); + } + for (int i = 1; i < visibleItems.count(); ++i) { + FxGridItem *item = visibleItems.at(i); + colPos += colSize(); + if (colPos > colSize() * (columns-1)) { + colPos = 0; + rowPos += rowSize(); + } + item->setPosition(colPos, rowPos); + } + } + if (header) + updateHeader(); + if (footer) + updateFooter(); + q->refill(); + updateHighlight(); + moveReason = Other; + if (flow == QDeclarativeGridView::LeftToRight) { + q->setContentHeight(endPosition() - startPosition()); + fixupY(); + } else { + q->setContentWidth(endPosition() - startPosition()); + fixupX(); + } + updateUnrequestedPositions(); +} + +void QDeclarativeGridViewPrivate::updateUnrequestedIndexes() +{ + Q_Q(QDeclarativeGridView); + QHash::iterator it; + for (it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it) + *it = model->indexOf(it.key(), q); +} + +void QDeclarativeGridViewPrivate::updateUnrequestedPositions() +{ + QHash::const_iterator it; + for (it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it) { + QDeclarativeItem *item = it.key(); + if (flow == QDeclarativeGridView::LeftToRight) { + item->setPos(QPointF(colPosAt(*it), rowPosAt(*it))); + } else { + if (isRightToLeftTopToBottom()) + item->setPos(QPointF(-rowPosAt(*it)-item->width(), colPosAt(*it))); + else + item->setPos(QPointF(rowPosAt(*it), colPosAt(*it))); + } + } +} + +void QDeclarativeGridViewPrivate::updateTrackedItem() +{ + Q_Q(QDeclarativeGridView); + FxGridItem *item = currentItem; + if (highlight) + item = highlight; + + if (trackedItem && item != trackedItem) { + QObject::disconnect(trackedItem->item, SIGNAL(yChanged()), q, SLOT(trackedPositionChanged())); + QObject::disconnect(trackedItem->item, SIGNAL(xChanged()), q, SLOT(trackedPositionChanged())); + trackedItem = 0; + } + + if (!trackedItem && item) { + trackedItem = item; + QObject::connect(trackedItem->item, SIGNAL(yChanged()), q, SLOT(trackedPositionChanged())); + QObject::connect(trackedItem->item, SIGNAL(xChanged()), q, SLOT(trackedPositionChanged())); + } + if (trackedItem) + q->trackedPositionChanged(); +} + +void QDeclarativeGridViewPrivate::createHighlight() +{ + Q_Q(QDeclarativeGridView); + bool changed = false; + if (highlight) { + if (trackedItem == highlight) + trackedItem = 0; + if (highlight->item->scene()) + highlight->item->scene()->removeItem(highlight->item); + highlight->item->deleteLater(); + delete highlight; + highlight = 0; + delete highlightXAnimator; + delete highlightYAnimator; + highlightXAnimator = 0; + highlightYAnimator = 0; + changed = true; + } + + if (currentItem) { + QDeclarativeItem *item = 0; + if (highlightComponent) { + QDeclarativeContext *highlightContext = new QDeclarativeContext(qmlContext(q)); + QObject *nobj = highlightComponent->create(highlightContext); + if (nobj) { + QDeclarative_setParent_noEvent(highlightContext, nobj); + item = qobject_cast(nobj); + if (!item) + delete nobj; + } else { + delete highlightContext; + } + } else { + item = new QDeclarativeItem; + QDeclarative_setParent_noEvent(item, q->contentItem()); + item->setParentItem(q->contentItem()); + } + if (item) { + QDeclarative_setParent_noEvent(item, q->contentItem()); + item->setParentItem(q->contentItem()); + highlight = new FxGridItem(item, q); + if (currentItem && autoHighlight) + highlight->setPosition(currentItem->colPos(), currentItem->rowPos()); + highlightXAnimator = new QSmoothedAnimation(q); + highlightXAnimator->target = QDeclarativeProperty(highlight->item, QLatin1String("x")); + highlightXAnimator->userDuration = highlightMoveDuration; + highlightYAnimator = new QSmoothedAnimation(q); + highlightYAnimator->target = QDeclarativeProperty(highlight->item, QLatin1String("y")); + highlightYAnimator->userDuration = highlightMoveDuration; + if (autoHighlight) { + highlightXAnimator->restart(); + highlightYAnimator->restart(); + } + changed = true; + } + } + if (changed) + emit q->highlightItemChanged(); +} + +void QDeclarativeGridViewPrivate::updateHighlight() +{ + if ((!currentItem && highlight) || (currentItem && !highlight)) + createHighlight(); + if (currentItem && autoHighlight && highlight && !hData.moving && !vData.moving) { + // auto-update highlight + highlightXAnimator->to = currentItem->item->x(); + highlightYAnimator->to = currentItem->item->y(); + highlight->item->setWidth(currentItem->item->width()); + highlight->item->setHeight(currentItem->item->height()); + highlightXAnimator->restart(); + highlightYAnimator->restart(); + } + updateTrackedItem(); +} + +void QDeclarativeGridViewPrivate::updateCurrent(int modelIndex) +{ + Q_Q(QDeclarativeGridView); + if (!q->isComponentComplete() || !isValid() || modelIndex < 0 || modelIndex >= model->count()) { + if (currentItem) { + currentItem->attached->setIsCurrentItem(false); + releaseItem(currentItem); + currentItem = 0; + currentIndex = modelIndex; + emit q->currentIndexChanged(); + updateHighlight(); + } else if (currentIndex != modelIndex) { + currentIndex = modelIndex; + emit q->currentIndexChanged(); + } + return; + } + + if (currentItem && currentIndex == modelIndex) { + updateHighlight(); + return; + } + + FxGridItem *oldCurrentItem = currentItem; + currentIndex = modelIndex; + currentItem = createItem(modelIndex); + fixCurrentVisibility = true; + if (oldCurrentItem && (!currentItem || oldCurrentItem->item != currentItem->item)) + oldCurrentItem->attached->setIsCurrentItem(false); + if (currentItem) { + currentItem->setPosition(colPosAt(modelIndex), rowPosAt(modelIndex)); + currentItem->item->setFocus(true); + currentItem->attached->setIsCurrentItem(true); + } + updateHighlight(); + emit q->currentIndexChanged(); + releaseItem(oldCurrentItem); +} + +void QDeclarativeGridViewPrivate::updateFooter() +{ + Q_Q(QDeclarativeGridView); + if (!footer && footerComponent) { + QDeclarativeItem *item = 0; + QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q)); + QObject *nobj = footerComponent->create(context); + if (nobj) { + QDeclarative_setParent_noEvent(context, nobj); + item = qobject_cast(nobj); + if (!item) + delete nobj; + } else { + delete context; + } + if (item) { + QDeclarative_setParent_noEvent(item, q->contentItem()); + item->setParentItem(q->contentItem()); + item->setZValue(1); + QDeclarativeItemPrivate *itemPrivate = static_cast(QGraphicsItemPrivate::get(item)); + itemPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry); + footer = new FxGridItem(item, q); + } + } + if (footer) { + qreal colOffset = 0; + qreal rowOffset; + if (isRightToLeftTopToBottom()) { + rowOffset = footer->item->width()-cellWidth; + } else { + rowOffset = 0; + if (q->effectiveLayoutDirection() == Qt::RightToLeft) + colOffset = footer->item->width()-cellWidth; + } + if (visibleItems.count()) { + qreal endPos = lastPosition(); + if (lastVisibleIndex() == model->count()-1) { + footer->setPosition(colOffset, endPos + rowOffset); + } else { + qreal visiblePos = isRightToLeftTopToBottom() ? -position() : position() + size(); + if (endPos <= visiblePos || footer->endRowPos() < endPos + rowOffset) + footer->setPosition(colOffset, endPos + rowOffset); + } + } else { + qreal endPos = 0; + if (header) { + endPos += flow == QDeclarativeGridView::LeftToRight ? header->item->height() : header->item->width(); + } + footer->setPosition(colOffset, endPos); + } + } +} + +void QDeclarativeGridViewPrivate::updateHeader() +{ + Q_Q(QDeclarativeGridView); + if (!header && headerComponent) { + QDeclarativeItem *item = 0; + QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q)); + QObject *nobj = headerComponent->create(context); + if (nobj) { + QDeclarative_setParent_noEvent(context, nobj); + item = qobject_cast(nobj); + if (!item) + delete nobj; + } else { + delete context; + } + if (item) { + QDeclarative_setParent_noEvent(item, q->contentItem()); + item->setParentItem(q->contentItem()); + item->setZValue(1); + QDeclarativeItemPrivate *itemPrivate = static_cast(QGraphicsItemPrivate::get(item)); + itemPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry); + header = new FxGridItem(item, q); + } + } + if (header) { + qreal colOffset = 0; + qreal rowOffset; + if (isRightToLeftTopToBottom()) { + rowOffset = -cellWidth; + } else { + rowOffset = -headerSize(); + if (q->effectiveLayoutDirection() == Qt::RightToLeft) + colOffset = header->item->width()-cellWidth; + } + if (visibleItems.count()) { + qreal startPos = originPosition(); + if (visibleIndex == 0) { + header->setPosition(colOffset, startPos + rowOffset); + } else { + qreal tempPos = isRightToLeftTopToBottom() ? -position()-size() : position(); + qreal headerPos = isRightToLeftTopToBottom() ? header->rowPos() + cellWidth - headerSize() : header->rowPos(); + if (tempPos <= startPos || headerPos > startPos + rowOffset) + header->setPosition(colOffset, startPos + rowOffset); + } + } else { + header->setPosition(colOffset, 0); + } + } +} + +void QDeclarativeGridViewPrivate::fixupPosition() +{ + moveReason = Other; + if (flow == QDeclarativeGridView::LeftToRight) + fixupY(); + else + fixupX(); +} + +void QDeclarativeGridViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent) +{ + if ((flow == QDeclarativeGridView::TopToBottom && &data == &vData) + || (flow == QDeclarativeGridView::LeftToRight && &data == &hData)) + return; + + fixupMode = moveReason == Mouse ? fixupMode : Immediate; + + qreal highlightStart; + qreal highlightEnd; + qreal viewPos; + if (isRightToLeftTopToBottom()) { + // Handle Right-To-Left exceptions + viewPos = -position()-size(); + highlightStart = highlightRangeStartValid ? size()-highlightRangeEnd : highlightRangeStart; + highlightEnd = highlightRangeEndValid ? size()-highlightRangeStart : highlightRangeEnd; + } else { + viewPos = position(); + highlightStart = highlightRangeStart; + highlightEnd = highlightRangeEnd; + } + + bool strictHighlightRange = haveHighlightRange && highlightRange == QDeclarativeGridView::StrictlyEnforceRange; + + if (snapMode != QDeclarativeGridView::NoSnap) { + qreal tempPosition = isRightToLeftTopToBottom() ? -position()-size() : position(); + if (snapMode == QDeclarativeGridView::SnapOneRow && moveReason == Mouse) { + // if we've been dragged < rowSize()/2 then bias towards the next row + qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset); + qreal bias = 0; + if (data.velocity > 0 && dist > QML_FLICK_SNAPONETHRESHOLD && dist < rowSize()/2) + bias = rowSize()/2; + else if (data.velocity < 0 && dist < -QML_FLICK_SNAPONETHRESHOLD && dist > -rowSize()/2) + bias = -rowSize()/2; + if (isRightToLeftTopToBottom()) + bias = -bias; + tempPosition -= bias; + } + FxGridItem *topItem = snapItemAt(tempPosition+highlightStart); + if (!topItem && strictHighlightRange && currentItem) { + // StrictlyEnforceRange always keeps an item in range + updateHighlight(); + topItem = currentItem; + } + FxGridItem *bottomItem = snapItemAt(tempPosition+highlightEnd); + if (!bottomItem && strictHighlightRange && currentItem) { + // StrictlyEnforceRange always keeps an item in range + updateHighlight(); + bottomItem = currentItem; + } + qreal pos; + bool isInBounds = -position() > maxExtent && -position() <= minExtent; + if (topItem && (isInBounds || strictHighlightRange)) { + if (topItem->index == 0 && header && tempPosition+highlightStart < header->rowPos()+headerSize()/2 && !strictHighlightRange) { + pos = isRightToLeftTopToBottom() ? - header->rowPos() + highlightStart - size() : header->rowPos() - highlightStart; + } else { + if (isRightToLeftTopToBottom()) + pos = qMax(qMin(-topItem->rowPos() + highlightStart - size(), -maxExtent), -minExtent); + else + pos = qMax(qMin(topItem->rowPos() - highlightStart, -maxExtent), -minExtent); + } + } else if (bottomItem && isInBounds) { + if (isRightToLeftTopToBottom()) + pos = qMax(qMin(-bottomItem->rowPos() + highlightEnd - size(), -maxExtent), -minExtent); + else + pos = qMax(qMin(bottomItem->rowPos() - highlightEnd, -maxExtent), -minExtent); + } else { + QDeclarativeFlickablePrivate::fixup(data, minExtent, maxExtent); + return; + } + qreal dist = qAbs(data.move + pos); + if (dist > 0) { + timeline.reset(data.move); + if (fixupMode != Immediate) { + timeline.move(data.move, -pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2); + data.fixingUp = true; + } else { + timeline.set(data.move, -pos); + } + vTime = timeline.time(); + } + } else if (haveHighlightRange && highlightRange == QDeclarativeGridView::StrictlyEnforceRange) { + if (currentItem) { + updateHighlight(); + qreal pos = currentItem->rowPos(); + if (viewPos < pos + rowSize() - highlightEnd) + viewPos = pos + rowSize() - highlightEnd; + if (viewPos > pos - highlightStart) + viewPos = pos - highlightStart; + if (isRightToLeftTopToBottom()) + viewPos = -viewPos-size(); + timeline.reset(data.move); + if (viewPos != position()) { + if (fixupMode != Immediate) { + timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2); + data.fixingUp = true; + } else { + timeline.set(data.move, -viewPos); + } + } + vTime = timeline.time(); + } + } else { + QDeclarativeFlickablePrivate::fixup(data, minExtent, maxExtent); + } + data.inOvershoot = false; + fixupMode = Normal; +} + +void QDeclarativeGridViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, + QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity) +{ + Q_Q(QDeclarativeGridView); + data.fixingUp = false; + moveReason = Mouse; + if ((!haveHighlightRange || highlightRange != QDeclarativeGridView::StrictlyEnforceRange) + && snapMode == QDeclarativeGridView::NoSnap) { + QDeclarativeFlickablePrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity); + return; + } + qreal maxDistance = 0; + qreal dataValue = isRightToLeftTopToBottom() ? -data.move.value()+size() : data.move.value(); + // -ve velocity means list is moving up/left + if (velocity > 0) { + if (data.move.value() < minExtent) { + if (snapMode == QDeclarativeGridView::SnapOneRow) { + // if we've been dragged < averageSize/2 then bias towards the next item + qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset); + qreal bias = dist < rowSize()/2 ? rowSize()/2 : 0; + if (isRightToLeftTopToBottom()) + bias = -bias; + data.flickTarget = -snapPosAt(-dataValue - bias); + maxDistance = qAbs(data.flickTarget - data.move.value()); + velocity = maxVelocity; + } else { + maxDistance = qAbs(minExtent - data.move.value()); + } + } + if (snapMode == QDeclarativeGridView::NoSnap && highlightRange != QDeclarativeGridView::StrictlyEnforceRange) + data.flickTarget = minExtent; + } else { + if (data.move.value() > maxExtent) { + if (snapMode == QDeclarativeGridView::SnapOneRow) { + // if we've been dragged < averageSize/2 then bias towards the next item + qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset); + qreal bias = -dist < rowSize()/2 ? rowSize()/2 : 0; + if (isRightToLeftTopToBottom()) + bias = -bias; + data.flickTarget = -snapPosAt(-dataValue + bias); + maxDistance = qAbs(data.flickTarget - data.move.value()); + velocity = -maxVelocity; + } else { + maxDistance = qAbs(maxExtent - data.move.value()); + } + } + if (snapMode == QDeclarativeGridView::NoSnap && highlightRange != QDeclarativeGridView::StrictlyEnforceRange) + data.flickTarget = maxExtent; + } + + bool overShoot = boundsBehavior == QDeclarativeFlickable::DragAndOvershootBounds; + + if (maxDistance > 0 || overShoot) { + // This mode requires the grid to stop exactly on a row boundary. + qreal v = velocity; + if (maxVelocity != -1 && maxVelocity < qAbs(v)) { + if (v < 0) + v = -maxVelocity; + else + v = maxVelocity; + } + qreal accel = deceleration; + qreal v2 = v * v; + qreal overshootDist = qreal(0.0); + if ((maxDistance > qreal(0.0) && v2 / (2.0f * maxDistance) < accel) || snapMode == QDeclarativeGridView::SnapOneRow) { + // + rowSize()/4 to encourage moving at least one item in the flick direction + qreal dist = v2 / (accel * qreal(2.0)) + rowSize()/4; + dist = qMin(dist, maxDistance); + if (v > 0) + dist = -dist; + if (snapMode != QDeclarativeGridView::SnapOneRow) { + qreal distTemp = isRightToLeftTopToBottom() ? -dist : dist; + data.flickTarget = -snapPosAt(-dataValue + distTemp); + } + data.flickTarget = isRightToLeftTopToBottom() ? -data.flickTarget+size() : data.flickTarget; + if (overShoot) { + if (data.flickTarget >= minExtent) { + overshootDist = overShootDistance(vSize); + data.flickTarget += overshootDist; + } else if (data.flickTarget <= maxExtent) { + overshootDist = overShootDistance(vSize); + data.flickTarget -= overshootDist; + } + } + qreal adjDist = -data.flickTarget + data.move.value(); + if (qAbs(adjDist) > qAbs(dist)) { + // Prevent painfully slow flicking - adjust velocity to suit flickDeceleration + qreal adjv2 = accel * 2.0f * qAbs(adjDist); + if (adjv2 > v2) { + v2 = adjv2; + v = qSqrt(v2); + if (dist > 0) + v = -v; + } + } + dist = adjDist; + accel = v2 / (2.0f * qAbs(dist)); + } else { + data.flickTarget = velocity > 0 ? minExtent : maxExtent; + overshootDist = overShoot ? overShootDistance(vSize) : 0; + } + timeline.reset(data.move); + timeline.accel(data.move, v, accel, maxDistance + overshootDist); + timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this)); + if (!hData.flicking && q->xflick()) { + hData.flicking = true; + emit q->flickingChanged(); + emit q->flickingHorizontallyChanged(); + emit q->flickStarted(); + } + if (!vData.flicking && q->yflick()) { + vData.flicking = true; + emit q->flickingChanged(); + emit q->flickingVerticallyChanged(); + emit q->flickStarted(); + } + } else { + timeline.reset(data.move); + fixup(data, minExtent, maxExtent); + } +} + + +//---------------------------------------------------------------------------- + +/*! + \qmlclass GridView QDeclarativeGridView + \since 4.7 + \ingroup qml-view-elements + + \inherits Flickable + \brief The GridView item provides a grid view of items provided by a model. + + A GridView displays data from models created from built-in QML elements like ListModel + and XmlListModel, or custom model classes defined in C++ that inherit from + QAbstractListModel. + + A GridView has a \l model, which defines the data to be displayed, and + a \l delegate, which defines how the data should be displayed. Items in a + GridView are laid out horizontally or vertically. Grid views are inherently flickable + as GridView inherits from \l Flickable. + + \section1 Example Usage + + The following example shows the definition of a simple list model defined + in a file called \c ContactModel.qml: + + \snippet doc/src/snippets/declarative/gridview/ContactModel.qml 0 + + \div {class="float-right"} + \inlineimage gridview-simple.png + \enddiv + + This model can be referenced as \c ContactModel in other QML files. See \l{QML Modules} + for more information about creating reusable components like this. + + Another component can display this model data in a GridView, as in the following + example, which creates a \c ContactModel component for its model, and a \l Column element + (containing \l Image and \l Text elements) for its delegate. + + \clearfloat + \snippet doc/src/snippets/declarative/gridview/gridview.qml import + \codeline + \snippet doc/src/snippets/declarative/gridview/gridview.qml classdocs simple + + \div {class="float-right"} + \inlineimage gridview-highlight.png + \enddiv + + The view will create a new delegate for each item in the model. Note that the delegate + is able to access the model's \c name and \c portrait data directly. + + An improved grid view is shown below. The delegate is visually improved and is moved + into a separate \c contactDelegate component. + + \clearfloat + \snippet doc/src/snippets/declarative/gridview/gridview.qml classdocs advanced + + The currently selected item is highlighted with a blue \l Rectangle using the \l highlight property, + and \c focus is set to \c true to enable keyboard navigation for the grid view. + The grid view itself is a focus scope (see \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} for more details). + + Delegates are instantiated as needed and may be destroyed at any time. + State should \e never be stored in a delegate. + + GridView attaches a number of properties to the root item of the delegate, for example + \c {GridView.isCurrentItem}. In the following example, the root delegate item can access + this attached property directly as \c GridView.isCurrentItem, while the child + \c contactInfo object must refer to this property as \c wrapper.GridView.isCurrentItem. + + \snippet doc/src/snippets/declarative/gridview/gridview.qml isCurrentItem + + \note Views do not set the \l{Item::}{clip} property automatically. + If the view is not clipped by another item or the screen, it will be necessary + to set this property to true in order to clip the items that are partially or + fully outside the view. + + \sa {declarative/modelviews/gridview}{GridView example} +*/ +QDeclarativeGridView::QDeclarativeGridView(QDeclarativeItem *parent) + : QDeclarativeFlickable(*(new QDeclarativeGridViewPrivate), parent) +{ + Q_D(QDeclarativeGridView); + d->init(); +} + +QDeclarativeGridView::~QDeclarativeGridView() +{ + Q_D(QDeclarativeGridView); + d->clear(); + if (d->ownModel) + delete d->model; + delete d->header; + delete d->footer; +} + +/*! + \qmlattachedproperty bool GridView::isCurrentItem + This attached property is true if this delegate is the current item; otherwise false. + + It is attached to each instance of the delegate. +*/ + +/*! + \qmlattachedproperty GridView GridView::view + This attached property holds the view that manages this delegate instance. + + It is attached to each instance of the delegate. + + \snippet doc/src/snippets/declarative/gridview/gridview.qml isCurrentItem +*/ + +/*! + \qmlattachedproperty bool GridView::delayRemove + This attached property holds whether the delegate may be destroyed. + + It is attached to each instance of the delegate. + + It is sometimes necessary to delay the destruction of an item + until an animation completes. + + The example below ensures that the animation completes before + the item is removed from the grid. + + \snippet doc/src/snippets/declarative/gridview/gridview.qml delayRemove +*/ + +/*! + \qmlattachedsignal GridView::onAdd() + This attached handler is called immediately after an item is added to the view. +*/ + +/*! + \qmlattachedsignal GridView::onRemove() + This attached handler is called immediately before an item is removed from the view. +*/ + + +/*! + \qmlproperty model GridView::model + This property holds the model providing data for the grid. + + The model provides the set of data that is used to create the items + in the view. Models can be created directly in QML using \l ListModel, \l XmlListModel + or \l VisualItemModel, or provided by C++ model classes. If a C++ model class is + used, it must be a subclass of \l QAbstractItemModel or a simple list. + + \sa {qmlmodels}{Data Models} +*/ +QVariant QDeclarativeGridView::model() const +{ + Q_D(const QDeclarativeGridView); + return d->modelVariant; +} + +// For internal use +int QDeclarativeGridView::modelCount() const +{ + Q_D(const QDeclarativeGridView); + return d->model->count(); +} + +void QDeclarativeGridView::setModel(const QVariant &model) +{ + Q_D(QDeclarativeGridView); + if (d->modelVariant == model) + return; + if (d->model) { + disconnect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int))); + disconnect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int))); + disconnect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int))); + disconnect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset())); + disconnect(d->model, SIGNAL(createdItem(int,QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*))); + disconnect(d->model, SIGNAL(destroyingItem(QDeclarativeItem*)), this, SLOT(destroyingItem(QDeclarativeItem*))); + } + d->clear(); + d->modelVariant = model; + QObject *object = qvariant_cast(model); + QDeclarativeVisualModel *vim = 0; + if (object && (vim = qobject_cast(object))) { + if (d->ownModel) { + delete d->model; + d->ownModel = false; + } + d->model = vim; + } else { + if (!d->ownModel) { + d->model = new QDeclarativeVisualDataModel(qmlContext(this), this); + d->ownModel = true; + } + if (QDeclarativeVisualDataModel *dataModel = qobject_cast(d->model)) + dataModel->setModel(model); + } + if (d->model) { + d->bufferMode = QDeclarativeGridViewPrivate::BufferBefore | QDeclarativeGridViewPrivate::BufferAfter; + if (isComponentComplete()) { + refill(); + if ((d->currentIndex >= d->model->count() || d->currentIndex < 0) && !d->currentIndexCleared) { + setCurrentIndex(0); + } else { + d->moveReason = QDeclarativeGridViewPrivate::SetIndex; + d->updateCurrent(d->currentIndex); + if (d->highlight && d->currentItem) { + if (d->autoHighlight) + d->highlight->setPosition(d->currentItem->colPos(), d->currentItem->rowPos()); + d->updateTrackedItem(); + } + d->moveReason = QDeclarativeGridViewPrivate::Other; + } + } + connect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int))); + connect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int))); + connect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int))); + connect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset())); + connect(d->model, SIGNAL(createdItem(int,QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*))); + connect(d->model, SIGNAL(destroyingItem(QDeclarativeItem*)), this, SLOT(destroyingItem(QDeclarativeItem*))); + emit countChanged(); + } + emit modelChanged(); +} + +/*! + \qmlproperty Component GridView::delegate + + The delegate provides a template defining each item instantiated by the view. + The index is exposed as an accessible \c index property. Properties of the + model are also available depending upon the type of \l {qmlmodels}{Data Model}. + + The number of elements in the delegate has a direct effect on the + flicking performance of the view. If at all possible, place functionality + that is not needed for the normal display of the delegate in a \l Loader which + can load additional elements when needed. + + The GridView will layout the items based on the size of the root item + in the delegate. + + \note Delegates are instantiated as needed and may be destroyed at any time. + State should \e never be stored in a delegate. +*/ +QDeclarativeComponent *QDeclarativeGridView::delegate() const +{ + Q_D(const QDeclarativeGridView); + if (d->model) { + if (QDeclarativeVisualDataModel *dataModel = qobject_cast(d->model)) + return dataModel->delegate(); + } + + return 0; +} + +void QDeclarativeGridView::setDelegate(QDeclarativeComponent *delegate) +{ + Q_D(QDeclarativeGridView); + if (delegate == this->delegate()) + return; + + if (!d->ownModel) { + d->model = new QDeclarativeVisualDataModel(qmlContext(this)); + d->ownModel = true; + } + if (QDeclarativeVisualDataModel *dataModel = qobject_cast(d->model)) { + int oldCount = dataModel->count(); + dataModel->setDelegate(delegate); + if (isComponentComplete()) { + for (int i = 0; i < d->visibleItems.count(); ++i) + d->releaseItem(d->visibleItems.at(i)); + d->visibleItems.clear(); + d->releaseItem(d->currentItem); + d->currentItem = 0; + refill(); + d->moveReason = QDeclarativeGridViewPrivate::SetIndex; + d->updateCurrent(d->currentIndex); + if (d->highlight && d->currentItem) { + if (d->autoHighlight) + d->highlight->setPosition(d->currentItem->colPos(), d->currentItem->rowPos()); + d->updateTrackedItem(); + } + d->moveReason = QDeclarativeGridViewPrivate::Other; + } + if (oldCount != dataModel->count()) + emit countChanged(); + emit delegateChanged(); + } +} + +/*! + \qmlproperty int GridView::currentIndex + \qmlproperty Item GridView::currentItem + + The \c currentIndex property holds the index of the current item, and + \c currentItem holds the current item. Setting the currentIndex to -1 + will clear the highlight and set currentItem to null. + + If highlightFollowsCurrentItem is \c true, setting either of these + properties will smoothly scroll the GridView so that the current + item becomes visible. + + Note that the position of the current item + may only be approximate until it becomes visible in the view. +*/ +int QDeclarativeGridView::currentIndex() const +{ + Q_D(const QDeclarativeGridView); + return d->currentIndex; +} + +void QDeclarativeGridView::setCurrentIndex(int index) +{ + Q_D(QDeclarativeGridView); + if (d->requestedIndex >= 0) // currently creating item + return; + d->currentIndexCleared = (index == -1); + if (index == d->currentIndex) + return; + if (isComponentComplete() && d->isValid()) { + if (d->layoutScheduled) + d->layout(); + d->moveReason = QDeclarativeGridViewPrivate::SetIndex; + d->updateCurrent(index); + } else { + d->currentIndex = index; + emit currentIndexChanged(); + } +} + +QDeclarativeItem *QDeclarativeGridView::currentItem() +{ + Q_D(QDeclarativeGridView); + if (!d->currentItem) + return 0; + return d->currentItem->item; +} + +/*! + \qmlproperty Item GridView::highlightItem + + This holds the highlight item created from the \l highlight component. + + The highlightItem is managed by the view unless + \l highlightFollowsCurrentItem is set to false. + + \sa highlight, highlightFollowsCurrentItem +*/ +QDeclarativeItem *QDeclarativeGridView::highlightItem() +{ + Q_D(QDeclarativeGridView); + if (!d->highlight) + return 0; + return d->highlight->item; +} + +/*! + \qmlproperty int GridView::count + This property holds the number of items in the view. +*/ +int QDeclarativeGridView::count() const +{ + Q_D(const QDeclarativeGridView); + if (d->model) + return d->model->count(); + return 0; +} + +/*! + \qmlproperty Component GridView::highlight + This property holds the component to use as the highlight. + + An instance of the highlight component is created for each view. + The geometry of the resulting component instance will be managed by the view + so as to stay with the current item, unless the highlightFollowsCurrentItem property is false. + + \sa highlightItem, highlightFollowsCurrentItem +*/ +QDeclarativeComponent *QDeclarativeGridView::highlight() const +{ + Q_D(const QDeclarativeGridView); + return d->highlightComponent; +} + +void QDeclarativeGridView::setHighlight(QDeclarativeComponent *highlight) +{ + Q_D(QDeclarativeGridView); + if (highlight != d->highlightComponent) { + d->highlightComponent = highlight; + d->updateCurrent(d->currentIndex); + emit highlightChanged(); + } +} + +/*! + \qmlproperty bool GridView::highlightFollowsCurrentItem + This property sets whether the highlight is managed by the view. + + If this property is true (the default value), the highlight is moved smoothly + to follow the current item. Otherwise, the + highlight is not moved by the view, and any movement must be implemented + by the highlight. + + Here is a highlight with its motion defined by a \l {SpringAnimation} item: + + \snippet doc/src/snippets/declarative/gridview/gridview.qml highlightFollowsCurrentItem +*/ +bool QDeclarativeGridView::highlightFollowsCurrentItem() const +{ + Q_D(const QDeclarativeGridView); + return d->autoHighlight; +} + +void QDeclarativeGridView::setHighlightFollowsCurrentItem(bool autoHighlight) +{ + Q_D(QDeclarativeGridView); + if (d->autoHighlight != autoHighlight) { + d->autoHighlight = autoHighlight; + if (autoHighlight) { + d->updateHighlight(); + } else if (d->highlightXAnimator) { + d->highlightXAnimator->stop(); + d->highlightYAnimator->stop(); + } + } +} + +/*! + \qmlproperty int GridView::highlightMoveDuration + This property holds the move animation duration of the highlight delegate. + + highlightFollowsCurrentItem must be true for this property + to have effect. + + The default value for the duration is 150ms. + + \sa highlightFollowsCurrentItem +*/ +int QDeclarativeGridView::highlightMoveDuration() const +{ + Q_D(const QDeclarativeGridView); + return d->highlightMoveDuration; +} + +void QDeclarativeGridView::setHighlightMoveDuration(int duration) +{ + Q_D(QDeclarativeGridView); + if (d->highlightMoveDuration != duration) { + d->highlightMoveDuration = duration; + if (d->highlightYAnimator) { + d->highlightXAnimator->userDuration = d->highlightMoveDuration; + d->highlightYAnimator->userDuration = d->highlightMoveDuration; + } + emit highlightMoveDurationChanged(); + } +} + + +/*! + \qmlproperty real GridView::preferredHighlightBegin + \qmlproperty real GridView::preferredHighlightEnd + \qmlproperty enumeration GridView::highlightRangeMode + + These properties define the preferred range of the highlight (for the current item) + within the view. The \c preferredHighlightBegin value must be less than the + \c preferredHighlightEnd value. + + These properties affect the position of the current item when the view is scrolled. + For example, if the currently selected item should stay in the middle of the + view when it is scrolled, set the \c preferredHighlightBegin and + \c preferredHighlightEnd values to the top and bottom coordinates of where the middle + item would be. If the \c currentItem is changed programmatically, the view will + automatically scroll so that the current item is in the middle of the view. + Furthermore, the behavior of the current item index will occur whether or not a + highlight exists. + + Valid values for \c highlightRangeMode are: + + \list + \o GridView.ApplyRange - the view attempts to maintain the highlight within the range. + However, the highlight can move outside of the range at the ends of the view or due + to mouse interaction. + \o GridView.StrictlyEnforceRange - the highlight never moves outside of the range. + The current item changes if a keyboard or mouse action would cause the highlight to move + outside of the range. + \o GridView.NoHighlightRange - this is the default value. + \endlist +*/ +qreal QDeclarativeGridView::preferredHighlightBegin() const +{ + Q_D(const QDeclarativeGridView); + return d->highlightRangeStart; +} + +void QDeclarativeGridView::setPreferredHighlightBegin(qreal start) +{ + Q_D(QDeclarativeGridView); + d->highlightRangeStartValid = true; + if (d->highlightRangeStart == start) + return; + d->highlightRangeStart = start; + d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; + emit preferredHighlightBeginChanged(); +} + +void QDeclarativeGridView::resetPreferredHighlightBegin() +{ + Q_D(QDeclarativeGridView); + d->highlightRangeStartValid = false; + if (d->highlightRangeStart == 0) + return; + d->highlightRangeStart = 0; + emit preferredHighlightBeginChanged(); +} + +qreal QDeclarativeGridView::preferredHighlightEnd() const +{ + Q_D(const QDeclarativeGridView); + return d->highlightRangeEnd; +} + +void QDeclarativeGridView::setPreferredHighlightEnd(qreal end) +{ + Q_D(QDeclarativeGridView); + d->highlightRangeEndValid = true; + if (d->highlightRangeEnd == end) + return; + d->highlightRangeEnd = end; + d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; + emit preferredHighlightEndChanged(); +} + +void QDeclarativeGridView::resetPreferredHighlightEnd() +{ + Q_D(QDeclarativeGridView); + d->highlightRangeEndValid = false; + if (d->highlightRangeEnd == 0) + return; + d->highlightRangeEnd = 0; + emit preferredHighlightEndChanged(); +} + +QDeclarativeGridView::HighlightRangeMode QDeclarativeGridView::highlightRangeMode() const +{ + Q_D(const QDeclarativeGridView); + return d->highlightRange; +} + +void QDeclarativeGridView::setHighlightRangeMode(HighlightRangeMode mode) +{ + Q_D(QDeclarativeGridView); + if (d->highlightRange == mode) + return; + d->highlightRange = mode; + d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; + emit highlightRangeModeChanged(); +} + +/*! + \qmlproperty enumeration GridView::layoutDirection + This property holds the layout direction of the grid. + + Possible values: + + \list + \o Qt.LeftToRight (default) - Items will be laid out starting in the top, left corner. The flow is + dependent on the \l GridView::flow property. + \o Qt.RightToLeft - Items will be laid out starting in the top, right corner. The flow is dependent + on the \l GridView::flow property. + \endlist + + When using the attached property \l {LayoutMirroring::enabled} for locale layouts, + the layout direction of the grid view will be mirrored. However, the actual property + \c layoutDirection will remain unchanged. You can use the property + \l {LayoutMirroring::enabled} to determine whether the direction has been mirrored. + + \sa {LayoutMirroring}{LayoutMirroring} +*/ + +Qt::LayoutDirection QDeclarativeGridView::layoutDirection() const +{ + Q_D(const QDeclarativeGridView); + return d->layoutDirection; +} + +void QDeclarativeGridView::setLayoutDirection(Qt::LayoutDirection layoutDirection) +{ + Q_D(QDeclarativeGridView); + if (d->layoutDirection != layoutDirection) { + d->layoutDirection = layoutDirection; + d->regenerate(); + emit layoutDirectionChanged(); + } +} + +Qt::LayoutDirection QDeclarativeGridView::effectiveLayoutDirection() const +{ + Q_D(const QDeclarativeGridView); + if (d->effectiveLayoutMirror) + return d->layoutDirection == Qt::RightToLeft ? Qt::LeftToRight : Qt::RightToLeft; + else + return d->layoutDirection; +} + +/*! + \qmlproperty enumeration GridView::flow + This property holds the flow of the grid. + + Possible values: + + \list + \o GridView.LeftToRight (default) - Items are laid out from left to right, and the view scrolls vertically + \o GridView.TopToBottom - Items are laid out from top to bottom, and the view scrolls horizontally + \endlist +*/ +QDeclarativeGridView::Flow QDeclarativeGridView::flow() const +{ + Q_D(const QDeclarativeGridView); + return d->flow; +} + +void QDeclarativeGridView::setFlow(Flow flow) +{ + Q_D(QDeclarativeGridView); + if (d->flow != flow) { + d->flow = flow; + if (d->flow == LeftToRight) { + setContentWidth(-1); + setFlickableDirection(QDeclarativeFlickable::VerticalFlick); + } else { + setContentHeight(-1); + setFlickableDirection(QDeclarativeFlickable::HorizontalFlick); + } + setContentX(0); + setContentY(0); + d->regenerate(); + emit flowChanged(); + } +} + +/*! + \qmlproperty bool GridView::keyNavigationWraps + This property holds whether the grid wraps key navigation + + If this is true, key navigation that would move the current item selection + past one end of the view instead wraps around and moves the selection to + the other end of the view. + + By default, key navigation is not wrapped. +*/ +bool QDeclarativeGridView::isWrapEnabled() const +{ + Q_D(const QDeclarativeGridView); + return d->wrap; +} + +void QDeclarativeGridView::setWrapEnabled(bool wrap) +{ + Q_D(QDeclarativeGridView); + if (d->wrap == wrap) + return; + d->wrap = wrap; + emit keyNavigationWrapsChanged(); +} + +/*! + \qmlproperty int GridView::cacheBuffer + This property determines whether delegates are retained outside the + visible area of the view. + + If non-zero the view will keep as many delegates + instantiated as will fit within the buffer specified. For example, + if in a vertical view the delegate is 20 pixels high and \c cacheBuffer is + set to 40, then up to 2 delegates above and 2 delegates below the visible + area may be retained. + + Note that cacheBuffer is not a pixel buffer - it only maintains additional + instantiated delegates. + + Setting this value can make scrolling the list smoother at the expense + of additional memory usage. It is not a substitute for creating efficient + delegates; the fewer elements in a delegate, the faster a view may be + scrolled. +*/ +int QDeclarativeGridView::cacheBuffer() const +{ + Q_D(const QDeclarativeGridView); + return d->buffer; +} + +void QDeclarativeGridView::setCacheBuffer(int buffer) +{ + Q_D(QDeclarativeGridView); + if (d->buffer != buffer) { + d->buffer = buffer; + if (isComponentComplete()) + refill(); + emit cacheBufferChanged(); + } +} + +/*! + \qmlproperty int GridView::cellWidth + \qmlproperty int GridView::cellHeight + + These properties holds the width and height of each cell in the grid. + + The default cell size is 100x100. +*/ +int QDeclarativeGridView::cellWidth() const +{ + Q_D(const QDeclarativeGridView); + return d->cellWidth; +} + +void QDeclarativeGridView::setCellWidth(int cellWidth) +{ + Q_D(QDeclarativeGridView); + if (cellWidth != d->cellWidth && cellWidth > 0) { + d->cellWidth = qMax(1, cellWidth); + d->updateGrid(); + emit cellWidthChanged(); + d->layout(); + } +} + +int QDeclarativeGridView::cellHeight() const +{ + Q_D(const QDeclarativeGridView); + return d->cellHeight; +} + +void QDeclarativeGridView::setCellHeight(int cellHeight) +{ + Q_D(QDeclarativeGridView); + if (cellHeight != d->cellHeight && cellHeight > 0) { + d->cellHeight = qMax(1, cellHeight); + d->updateGrid(); + emit cellHeightChanged(); + d->layout(); + } +} +/*! + \qmlproperty enumeration GridView::snapMode + + This property determines how the view scrolling will settle following a drag or flick. + The possible values are: + + \list + \o GridView.NoSnap (default) - the view stops anywhere within the visible area. + \o GridView.SnapToRow - the view settles with a row (or column for \c GridView.TopToBottom flow) + aligned with the start of the view. + \o GridView.SnapOneRow - the view will settle no more than one row (or column for \c GridView.TopToBottom flow) + away from the first visible row at the time the mouse button is released. + This mode is particularly useful for moving one page at a time. + \endlist + +*/ +QDeclarativeGridView::SnapMode QDeclarativeGridView::snapMode() const +{ + Q_D(const QDeclarativeGridView); + return d->snapMode; +} + +void QDeclarativeGridView::setSnapMode(SnapMode mode) +{ + Q_D(QDeclarativeGridView); + if (d->snapMode != mode) { + d->snapMode = mode; + emit snapModeChanged(); + } +} + +/*! + \qmlproperty Component GridView::footer + This property holds the component to use as the footer. + + An instance of the footer component is created for each view. The + footer is positioned at the end of the view, after any items. + + \sa header +*/ +QDeclarativeComponent *QDeclarativeGridView::footer() const +{ + Q_D(const QDeclarativeGridView); + return d->footerComponent; +} + +void QDeclarativeGridView::setFooter(QDeclarativeComponent *footer) +{ + Q_D(QDeclarativeGridView); + if (d->footerComponent != footer) { + if (d->footer) { + if (scene()) + scene()->removeItem(d->footer->item); + d->footer->item->deleteLater(); + delete d->footer; + d->footer = 0; + } + d->footerComponent = footer; + if (isComponentComplete()) { + d->updateFooter(); + d->updateGrid(); + d->fixupPosition(); + } + emit footerChanged(); + } +} + +/*! + \qmlproperty Component GridView::header + This property holds the component to use as the header. + + An instance of the header component is created for each view. The + header is positioned at the beginning of the view, before any items. + + \sa footer +*/ +QDeclarativeComponent *QDeclarativeGridView::header() const +{ + Q_D(const QDeclarativeGridView); + return d->headerComponent; +} + +void QDeclarativeGridView::setHeader(QDeclarativeComponent *header) +{ + Q_D(QDeclarativeGridView); + if (d->headerComponent != header) { + if (d->header) { + if (scene()) + scene()->removeItem(d->header->item); + d->header->item->deleteLater(); + delete d->header; + d->header = 0; + } + d->headerComponent = header; + if (isComponentComplete()) { + d->updateHeader(); + d->updateFooter(); + d->updateGrid(); + d->fixupPosition(); + } + emit headerChanged(); + } +} + +void QDeclarativeGridView::setContentX(qreal pos) +{ + Q_D(QDeclarativeGridView); + // Positioning the view manually should override any current movement state + d->moveReason = QDeclarativeGridViewPrivate::Other; + QDeclarativeFlickable::setContentX(pos); +} + +void QDeclarativeGridView::setContentY(qreal pos) +{ + Q_D(QDeclarativeGridView); + // Positioning the view manually should override any current movement state + d->moveReason = QDeclarativeGridViewPrivate::Other; + QDeclarativeFlickable::setContentY(pos); +} + +bool QDeclarativeGridView::event(QEvent *event) +{ + Q_D(QDeclarativeGridView); + if (event->type() == QEvent::User) { + if (d->layoutScheduled) + d->layout(); + return true; + } + + return QDeclarativeFlickable::event(event); +} + +void QDeclarativeGridView::viewportMoved() +{ + Q_D(QDeclarativeGridView); + QDeclarativeFlickable::viewportMoved(); + if (!d->itemCount) + return; + d->lazyRelease = true; + if (d->hData.flicking || d->vData.flicking) { + if (yflick()) { + if (d->vData.velocity > 0) + d->bufferMode = QDeclarativeGridViewPrivate::BufferBefore; + else if (d->vData.velocity < 0) + d->bufferMode = QDeclarativeGridViewPrivate::BufferAfter; + } + + if (xflick()) { + if (d->hData.velocity > 0) + d->bufferMode = QDeclarativeGridViewPrivate::BufferBefore; + else if (d->hData.velocity < 0) + d->bufferMode = QDeclarativeGridViewPrivate::BufferAfter; + } + } + refill(); + if (d->hData.flicking || d->vData.flicking || d->hData.moving || d->vData.moving) + d->moveReason = QDeclarativeGridViewPrivate::Mouse; + if (d->moveReason != QDeclarativeGridViewPrivate::SetIndex) { + if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) { + // reposition highlight + qreal pos = d->highlight->rowPos(); + qreal viewPos; + qreal highlightStart; + qreal highlightEnd; + if (d->isRightToLeftTopToBottom()) { + highlightStart = d->highlightRangeStartValid ? d->size()-d->highlightRangeEnd : d->highlightRangeStart; + highlightEnd = d->highlightRangeEndValid ? d->size()-d->highlightRangeStart : d->highlightRangeEnd; + viewPos = -d->position()-d->size(); + } else { + highlightStart = d->highlightRangeStart; + highlightEnd = d->highlightRangeEnd; + viewPos = d->position(); + } + if (pos > viewPos + highlightEnd - d->rowSize()) + pos = viewPos + highlightEnd - d->rowSize(); + if (pos < viewPos + highlightStart) + pos = viewPos + highlightStart; + + d->highlight->setPosition(d->highlight->colPos(), qRound(pos)); + + // update current index + int idx = d->snapIndex(); + if (idx >= 0 && idx != d->currentIndex) { + d->updateCurrent(idx); + if (d->currentItem && d->currentItem->colPos() != d->highlight->colPos() && d->autoHighlight) { + if (d->flow == LeftToRight) + d->highlightXAnimator->to = d->currentItem->item->x(); + else + d->highlightYAnimator->to = d->currentItem->item->y(); + } + } + } + } +} + +qreal QDeclarativeGridView::minYExtent() const +{ + Q_D(const QDeclarativeGridView); + if (d->flow == QDeclarativeGridView::TopToBottom) + return QDeclarativeFlickable::minYExtent(); + qreal extent = -d->startPosition(); + if (d->header && d->visibleItems.count()) + extent += d->header->item->height(); + if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { + extent += d->highlightRangeStart; + extent = qMax(extent, -(d->rowPosAt(0) + d->rowSize() - d->highlightRangeEnd)); + } + return extent; +} + +qreal QDeclarativeGridView::maxYExtent() const +{ + Q_D(const QDeclarativeGridView); + if (d->flow == QDeclarativeGridView::TopToBottom) + return QDeclarativeFlickable::maxYExtent(); + qreal extent; + if (!d->model || !d->model->count()) { + extent = 0; + } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { + extent = -(d->rowPosAt(d->model->count()-1) - d->highlightRangeStart); + if (d->highlightRangeEnd != d->highlightRangeStart) + extent = qMin(extent, -(d->endPosition() - d->highlightRangeEnd + 1)); + } else { + extent = -(d->endPosition() - height()); + } + if (d->footer) + extent -= d->footer->item->height(); + const qreal minY = minYExtent(); + if (extent > minY) + extent = minY; + return extent; +} + +qreal QDeclarativeGridView::minXExtent() const +{ + Q_D(const QDeclarativeGridView); + if (d->flow == QDeclarativeGridView::LeftToRight) + return QDeclarativeFlickable::minXExtent(); + qreal extent = -d->startPosition(); + qreal highlightStart; + qreal highlightEnd; + qreal endPositionFirstItem = 0; + if (d->isRightToLeftTopToBottom()) { + if (d->model && d->model->count()) + endPositionFirstItem = d->rowPosAt(d->model->count()-1); + highlightStart = d->highlightRangeStartValid + ? d->highlightRangeStart - (d->lastPosition()-endPositionFirstItem) + : d->size() - (d->lastPosition()-endPositionFirstItem); + highlightEnd = d->highlightRangeEndValid ? d->highlightRangeEnd : d->size(); + if (d->footer && d->visibleItems.count()) + extent += d->footer->item->width(); + } else { + endPositionFirstItem = d->rowPosAt(0)+d->rowSize(); + highlightStart = d->highlightRangeStart; + highlightEnd = d->highlightRangeEnd; + if (d->header && d->visibleItems.count()) + extent += d->header->item->width(); + } + if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { + extent += d->isRightToLeftTopToBottom() ? -highlightStart : highlightStart; + extent = qMax(extent, -(endPositionFirstItem - highlightEnd)); + } + return extent; +} + +qreal QDeclarativeGridView::maxXExtent() const +{ + Q_D(const QDeclarativeGridView); + if (d->flow == QDeclarativeGridView::LeftToRight) + return QDeclarativeFlickable::maxXExtent(); + qreal extent; + qreal highlightStart; + qreal highlightEnd; + qreal lastItemPosition = 0; + if (d->isRightToLeftTopToBottom()){ + highlightStart = d->highlightRangeStartValid ? d->highlightRangeEnd : d->size(); + highlightEnd = d->highlightRangeEndValid ? d->highlightRangeStart : d->size(); + lastItemPosition = d->endPosition(); + } else { + highlightStart = d->highlightRangeStart; + highlightEnd = d->highlightRangeEnd; + lastItemPosition = 0; + if (d->model && d->model->count()) + lastItemPosition = d->rowPosAt(d->model->count()-1); + } + if (!d->model || !d->model->count()) { + extent = 0; + } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { + extent = -(lastItemPosition - highlightStart); + if (highlightEnd != highlightStart) + extent = d->isRightToLeftTopToBottom() + ? qMax(extent, -(d->endPosition() - highlightEnd + 1)) + : qMin(extent, -(d->endPosition() - highlightEnd + 1)); + } else { + extent = -(d->endPosition() - width()); + } + if (d->isRightToLeftTopToBottom()) { + if (d->header) + extent -= d->header->item->width(); + } else { + if (d->footer) + extent -= d->footer->item->width(); + } + + const qreal minX = minXExtent(); + if (extent > minX) + extent = minX; + return extent; +} + +void QDeclarativeGridView::keyPressEvent(QKeyEvent *event) +{ + Q_D(QDeclarativeGridView); + keyPressPreHandler(event); + if (event->isAccepted()) + return; + if (d->model && d->model->count() && d->interactive) { + d->moveReason = QDeclarativeGridViewPrivate::SetIndex; + int oldCurrent = currentIndex(); + switch (event->key()) { + case Qt::Key_Up: + moveCurrentIndexUp(); + break; + case Qt::Key_Down: + moveCurrentIndexDown(); + break; + case Qt::Key_Left: + moveCurrentIndexLeft(); + break; + case Qt::Key_Right: + moveCurrentIndexRight(); + break; + default: + break; + } + if (oldCurrent != currentIndex()) { + event->accept(); + return; + } + } + d->moveReason = QDeclarativeGridViewPrivate::Other; + event->ignore(); + QDeclarativeFlickable::keyPressEvent(event); +} + +/*! + \qmlmethod GridView::moveCurrentIndexUp() + + Move the currentIndex up one item in the view. + The current index will wrap if keyNavigationWraps is true and it + is currently at the end. This method has no effect if the \l count is zero. + + \bold Note: methods should only be called after the Component has completed. +*/ +void QDeclarativeGridView::moveCurrentIndexUp() +{ + Q_D(QDeclarativeGridView); + const int count = d->model ? d->model->count() : 0; + if (!count) + return; + if (d->flow == QDeclarativeGridView::LeftToRight) { + if (currentIndex() >= d->columns || d->wrap) { + int index = currentIndex() - d->columns; + setCurrentIndex((index >= 0 && index < count) ? index : count-1); + } + } else { + if (currentIndex() > 0 || d->wrap) { + int index = currentIndex() - 1; + setCurrentIndex((index >= 0 && index < count) ? index : count-1); + } + } +} + +/*! + \qmlmethod GridView::moveCurrentIndexDown() + + Move the currentIndex down one item in the view. + The current index will wrap if keyNavigationWraps is true and it + is currently at the end. This method has no effect if the \l count is zero. + + \bold Note: methods should only be called after the Component has completed. +*/ +void QDeclarativeGridView::moveCurrentIndexDown() +{ + Q_D(QDeclarativeGridView); + const int count = d->model ? d->model->count() : 0; + if (!count) + return; + if (d->flow == QDeclarativeGridView::LeftToRight) { + if (currentIndex() < count - d->columns || d->wrap) { + int index = currentIndex()+d->columns; + setCurrentIndex((index >= 0 && index < count) ? index : 0); + } + } else { + if (currentIndex() < count - 1 || d->wrap) { + int index = currentIndex() + 1; + setCurrentIndex((index >= 0 && index < count) ? index : 0); + } + } +} + +/*! + \qmlmethod GridView::moveCurrentIndexLeft() + + Move the currentIndex left one item in the view. + The current index will wrap if keyNavigationWraps is true and it + is currently at the end. This method has no effect if the \l count is zero. + + \bold Note: methods should only be called after the Component has completed. +*/ +void QDeclarativeGridView::moveCurrentIndexLeft() +{ + Q_D(QDeclarativeGridView); + const int count = d->model ? d->model->count() : 0; + if (!count) + return; + + if (effectiveLayoutDirection() == Qt::LeftToRight) { + if (d->flow == QDeclarativeGridView::LeftToRight) { + if (currentIndex() > 0 || d->wrap) { + int index = currentIndex() - 1; + setCurrentIndex((index >= 0 && index < count) ? index : count-1); + } + } else { + if (currentIndex() >= d->columns || d->wrap) { + int index = currentIndex() - d->columns; + setCurrentIndex((index >= 0 && index < count) ? index : count-1); + } + } + } else { + if (d->flow == QDeclarativeGridView::LeftToRight) { + if (currentIndex() < count - 1 || d->wrap) { + int index = currentIndex() + 1; + setCurrentIndex((index >= 0 && index < count) ? index : 0); + } + } else { + if (currentIndex() < count - d->columns || d->wrap) { + int index = currentIndex() + d->columns; + setCurrentIndex((index >= 0 && index < count) ? index : 0); + } + } + } +} + +/*! + \qmlmethod GridView::moveCurrentIndexRight() + + Move the currentIndex right one item in the view. + The current index will wrap if keyNavigationWraps is true and it + is currently at the end. This method has no effect if the \l count is zero. + + \bold Note: methods should only be called after the Component has completed. +*/ +void QDeclarativeGridView::moveCurrentIndexRight() +{ + Q_D(QDeclarativeGridView); + const int count = d->model ? d->model->count() : 0; + if (!count) + return; + + if (effectiveLayoutDirection() == Qt::LeftToRight) { + if (d->flow == QDeclarativeGridView::LeftToRight) { + if (currentIndex() < count - 1 || d->wrap) { + int index = currentIndex() + 1; + setCurrentIndex((index >= 0 && index < count) ? index : 0); + } + } else { + if (currentIndex() < count - d->columns || d->wrap) { + int index = currentIndex()+d->columns; + setCurrentIndex((index >= 0 && index < count) ? index : 0); + } + } + } else { + if (d->flow == QDeclarativeGridView::LeftToRight) { + if (currentIndex() > 0 || d->wrap) { + int index = currentIndex() - 1; + setCurrentIndex((index >= 0 && index < count) ? index : count-1); + } + } else { + if (currentIndex() >= d->columns || d->wrap) { + int index = currentIndex() - d->columns; + setCurrentIndex((index >= 0 && index < count) ? index : count-1); + } + } + } +} + +void QDeclarativeGridViewPrivate::positionViewAtIndex(int index, int mode) +{ + Q_Q(QDeclarativeGridView); + if (!isValid()) + return; + if (mode < QDeclarativeGridView::Beginning || mode > QDeclarativeGridView::Contain) + return; + + int idx = qMax(qMin(index, model->count()-1), 0); + + if (layoutScheduled) + layout(); + qreal pos = isRightToLeftTopToBottom() ? -position() - size() : position(); + FxGridItem *item = visibleItem(idx); + qreal maxExtent; + if (flow == QDeclarativeGridView::LeftToRight) + maxExtent = -q->maxYExtent(); + else + maxExtent = isRightToLeftTopToBottom() ? q->minXExtent()-size() : -q->maxXExtent(); + + if (!item) { + int itemPos = rowPosAt(idx); + // save the currently visible items in case any of them end up visible again + QList oldVisible = visibleItems; + visibleItems.clear(); + visibleIndex = idx - idx % columns; + if (flow == QDeclarativeGridView::LeftToRight) + maxExtent = -q->maxYExtent(); + else + maxExtent = isRightToLeftTopToBottom() ? q->minXExtent()-size() : -q->maxXExtent(); + setPosition(qMin(qreal(itemPos), maxExtent)); + // now release the reference to all the old visible items. + for (int i = 0; i < oldVisible.count(); ++i) + releaseItem(oldVisible.at(i)); + item = visibleItem(idx); + } + if (item) { + qreal itemPos = item->rowPos(); + switch (mode) { + case QDeclarativeGridView::Beginning: + pos = itemPos; + if (index < 0 && header) { + pos -= flow == QDeclarativeGridView::LeftToRight + ? header->item->height() + : header->item->width(); + } + break; + case QDeclarativeGridView::Center: + pos = itemPos - (size() - rowSize())/2; + break; + case QDeclarativeGridView::End: + pos = itemPos - size() + rowSize(); + if (index >= model->count() && footer) { + pos += flow == QDeclarativeGridView::LeftToRight + ? footer->item->height() + : footer->item->width(); + } + break; + case QDeclarativeGridView::Visible: + if (itemPos > pos + size()) + pos = itemPos - size() + rowSize(); + else if (item->endRowPos() < pos) + pos = itemPos; + break; + case QDeclarativeGridView::Contain: + if (item->endRowPos() > pos + size()) + pos = itemPos - size() + rowSize(); + if (itemPos < pos) + pos = itemPos; + } + + pos = qMin(pos, maxExtent); + qreal minExtent; + if (flow == QDeclarativeGridView::LeftToRight) + minExtent = -q->minYExtent(); + else + minExtent = isRightToLeftTopToBottom() ? q->maxXExtent()-size() : -q->minXExtent(); + pos = qMax(pos, minExtent); + moveReason = QDeclarativeGridViewPrivate::Other; + q->cancelFlick(); + setPosition(pos); + } + fixupPosition(); +} + +/*! + \qmlmethod GridView::positionViewAtIndex(int index, PositionMode mode) + + Positions the view such that the \a index is at the position specified by + \a mode: + + \list + \o GridView.Beginning - position item at the top (or left for \c GridView.TopToBottom flow) of the view. + \o GridView.Center - position item in the center of the view. + \o GridView.End - position item at bottom (or right for horizontal orientation) of the view. + \o GridView.Visible - if any part of the item is visible then take no action, otherwise + bring the item into view. + \o GridView.Contain - ensure the entire item is visible. If the item is larger than + the view the item is positioned at the top (or left for \c GridView.TopToBottom flow) of the view. + \endlist + + If positioning the view at the index would cause empty space to be displayed at + the beginning or end of the view, the view will be positioned at the boundary. + + It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view + at a particular index. This is unreliable since removing items from the start + of the view does not cause all other items to be repositioned. + The correct way to bring an item into view is with \c positionViewAtIndex. + + \bold Note: methods should only be called after the Component has completed. To position + the view at startup, this method should be called by Component.onCompleted. For + example, to position the view at the end: + + \code + Component.onCompleted: positionViewAtIndex(count - 1, GridView.Beginning) + \endcode +*/ +void QDeclarativeGridView::positionViewAtIndex(int index, int mode) +{ + Q_D(QDeclarativeGridView); + if (!d->isValid() || index < 0 || index >= d->model->count()) + return; + d->positionViewAtIndex(index, mode); +} + +/*! + \qmlmethod GridView::positionViewAtBeginning() + \qmlmethod GridView::positionViewAtEnd() + \since QtQuick 1.1 + + Positions the view at the beginning or end, taking into account any header or footer. + + It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view + at a particular index. This is unreliable since removing items from the start + of the list does not cause all other items to be repositioned, and because + the actual start of the view can vary based on the size of the delegates. + + \bold Note: methods should only be called after the Component has completed. To position + the view at startup, this method should be called by Component.onCompleted. For + example, to position the view at the end on startup: + + \code + Component.onCompleted: positionViewAtEnd() + \endcode +*/ +void QDeclarativeGridView::positionViewAtBeginning() +{ + Q_D(QDeclarativeGridView); + if (!d->isValid()) + return; + d->positionViewAtIndex(-1, Beginning); +} + +void QDeclarativeGridView::positionViewAtEnd() +{ + Q_D(QDeclarativeGridView); + if (!d->isValid()) + return; + d->positionViewAtIndex(d->model->count(), End); +} + +/*! + \qmlmethod int GridView::indexAt(int x, int y) + + Returns the index of the visible item containing the point \a x, \a y in content + coordinates. If there is no item at the point specified, or the item is + not visible -1 is returned. + + If the item is outside the visible area, -1 is returned, regardless of + whether an item will exist at that point when scrolled into view. + + \bold Note: methods should only be called after the Component has completed. +*/ +int QDeclarativeGridView::indexAt(qreal x, qreal y) const +{ + Q_D(const QDeclarativeGridView); + for (int i = 0; i < d->visibleItems.count(); ++i) { + const FxGridItem *listItem = d->visibleItems.at(i); + if(listItem->contains(x, y)) + return listItem->index; + } + + return -1; +} + +void QDeclarativeGridView::componentComplete() +{ + Q_D(QDeclarativeGridView); + QDeclarativeFlickable::componentComplete(); + d->updateHeader(); + d->updateFooter(); + d->updateGrid(); + if (d->isValid()) { + refill(); + d->moveReason = QDeclarativeGridViewPrivate::SetIndex; + if (d->currentIndex < 0 && !d->currentIndexCleared) + d->updateCurrent(0); + else + d->updateCurrent(d->currentIndex); + if (d->highlight && d->currentItem) { + if (d->autoHighlight) + d->highlight->setPosition(d->currentItem->colPos(), d->currentItem->rowPos()); + d->updateTrackedItem(); + } + d->moveReason = QDeclarativeGridViewPrivate::Other; + d->fixupPosition(); + } +} + +void QDeclarativeGridView::trackedPositionChanged() +{ + Q_D(QDeclarativeGridView); + if (!d->trackedItem || !d->currentItem) + return; + if (d->moveReason == QDeclarativeGridViewPrivate::SetIndex) { + const qreal trackedPos = d->trackedItem->rowPos(); + qreal viewPos; + qreal highlightStart; + qreal highlightEnd; + if (d->isRightToLeftTopToBottom()) { + viewPos = -d->position()-d->size(); + highlightStart = d->highlightRangeStartValid ? d->size()-d->highlightRangeEnd : d->highlightRangeStart; + highlightEnd = d->highlightRangeEndValid ? d->size()-d->highlightRangeStart : d->highlightRangeEnd; + } else { + viewPos = d->position(); + highlightStart = d->highlightRangeStart; + highlightEnd = d->highlightRangeEnd; + } + qreal pos = viewPos; + if (d->haveHighlightRange) { + if (d->highlightRange == StrictlyEnforceRange) { + if (trackedPos > pos + highlightEnd - d->rowSize()) + pos = trackedPos - highlightEnd + d->rowSize(); + if (trackedPos < pos + highlightStart) + pos = trackedPos - highlightStart; + } else { + if (trackedPos < d->startPosition() + highlightStart) { + pos = d->startPosition(); + } else if (d->trackedItem->endRowPos() > d->endPosition() - d->size() + highlightEnd) { + pos = d->endPosition() - d->size() + 1; + if (pos < d->startPosition()) + pos = d->startPosition(); + } else { + if (trackedPos < viewPos + highlightStart) { + pos = trackedPos - highlightStart; + } else if (trackedPos > viewPos + highlightEnd - d->rowSize()) { + pos = trackedPos - highlightEnd + d->rowSize(); + } + } + } + } else { + if (trackedPos < viewPos && d->currentItem->rowPos() < viewPos) { + pos = qMax(trackedPos, d->currentItem->rowPos()); + } else if (d->trackedItem->endRowPos() >= viewPos + d->size() + && d->currentItem->endRowPos() >= viewPos + d->size()) { + if (d->trackedItem->endRowPos() <= d->currentItem->endRowPos()) { + pos = d->trackedItem->endRowPos() - d->size() + 1; + if (d->rowSize() > d->size()) + pos = trackedPos; + } else { + pos = d->currentItem->endRowPos() - d->size() + 1; + if (d->rowSize() > d->size()) + pos = d->currentItem->rowPos(); + } + } + } + if (viewPos != pos) { + cancelFlick(); + d->calcVelocity = true; + d->setPosition(pos); + d->calcVelocity = false; + } + } +} + +void QDeclarativeGridView::itemsInserted(int modelIndex, int count) +{ + Q_D(QDeclarativeGridView); + if (!isComponentComplete()) + return; + + int index = d->visibleItems.count() ? d->mapFromModel(modelIndex) : 0; + if (index < 0) { + int i = d->visibleItems.count() - 1; + while (i > 0 && d->visibleItems.at(i)->index == -1) + --i; + if (d->visibleItems.at(i)->index + 1 == modelIndex) { + // Special case of appending an item to the model. + index = d->visibleIndex + d->visibleItems.count(); + } else { + if (modelIndex <= d->visibleIndex) { + // Insert before visible items + d->visibleIndex += count; + for (int i = 0; i < d->visibleItems.count(); ++i) { + FxGridItem *listItem = d->visibleItems.at(i); + if (listItem->index != -1 && listItem->index >= modelIndex) + listItem->index += count; + } + } + if (d->currentIndex >= modelIndex) { + // adjust current item index + d->currentIndex += count; + if (d->currentItem) + d->currentItem->index = d->currentIndex; + emit currentIndexChanged(); + } + d->scheduleLayout(); + d->itemCount += count; + emit countChanged(); + return; + } + } + + int insertCount = count; + if (index < d->visibleIndex && d->visibleItems.count()) { + insertCount -= d->visibleIndex - index; + index = d->visibleIndex; + modelIndex = d->visibleIndex; + } + + qreal tempPos = d->isRightToLeftTopToBottom() ? -d->position()-d->size()+d->width()+1 : d->position(); + int to = d->buffer+tempPos+d->size()-1; + int colPos = 0; + int rowPos = 0; + if (d->visibleItems.count()) { + index -= d->visibleIndex; + if (index < d->visibleItems.count()) { + colPos = d->visibleItems.at(index)->colPos(); + rowPos = d->visibleItems.at(index)->rowPos(); + } else { + // appending items to visible list + colPos = d->visibleItems.at(index-1)->colPos() + d->colSize(); + rowPos = d->visibleItems.at(index-1)->rowPos(); + if (colPos > d->colSize() * (d->columns-1)) { + colPos = 0; + rowPos += d->rowSize(); + } + } + } else if (d->itemCount == 0 && d->header) { + rowPos = d->headerSize(); + } + + // Update the indexes of the following visible items. + for (int i = 0; i < d->visibleItems.count(); ++i) { + FxGridItem *listItem = d->visibleItems.at(i); + if (listItem->index != -1 && listItem->index >= modelIndex) + listItem->index += count; + } + + bool addedVisible = false; + QList added; + int i = 0; + while (i < insertCount && rowPos <= to + d->rowSize()*(d->columns - (colPos/d->colSize()))/qreal(d->columns)) { + if (!addedVisible) { + d->scheduleLayout(); + addedVisible = true; + } + FxGridItem *item = d->createItem(modelIndex + i); + if (!item) { + // broken or no delegate + d->clear(); + return; + } + d->visibleItems.insert(index, item); + item->setPosition(colPos, rowPos); + added.append(item); + colPos += d->colSize(); + if (colPos > d->colSize() * (d->columns-1)) { + colPos = 0; + rowPos += d->rowSize(); + } + ++index; + ++i; + } + if (i < insertCount) { + // We didn't insert all our new items, which means anything + // beyond the current index is not visible - remove it. + while (d->visibleItems.count() > index) { + d->releaseItem(d->visibleItems.takeLast()); + } + } + + // update visibleIndex + d->visibleIndex = 0; + for (QList::Iterator it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) { + if ((*it)->index != -1) { + d->visibleIndex = (*it)->index; + break; + } + } + + if (d->itemCount && d->currentIndex >= modelIndex) { + // adjust current item index + d->currentIndex += count; + if (d->currentItem) { + d->currentItem->index = d->currentIndex; + d->currentItem->setPosition(d->colPosAt(d->currentIndex), d->rowPosAt(d->currentIndex)); + } + emit currentIndexChanged(); + } else if (d->itemCount == 0 && (!d->currentIndex || (d->currentIndex < 0 && !d->currentIndexCleared))) { + setCurrentIndex(0); + } + + // everything is in order now - emit add() signal + for (int j = 0; j < added.count(); ++j) + added.at(j)->attached->emitAdd(); + + d->itemCount += count; + emit countChanged(); +} + +void QDeclarativeGridView::itemsRemoved(int modelIndex, int count) +{ + Q_D(QDeclarativeGridView); + if (!isComponentComplete()) + return; + + d->itemCount -= count; + bool currentRemoved = d->currentIndex >= modelIndex && d->currentIndex < modelIndex + count; + bool removedVisible = false; + // Remove the items from the visible list, skipping anything already marked for removal + QList::Iterator it = d->visibleItems.begin(); + while (it != d->visibleItems.end()) { + FxGridItem *item = *it; + if (item->index == -1 || item->index < modelIndex) { + // already removed, or before removed items + if (item->index < modelIndex && !removedVisible) { + d->scheduleLayout(); + removedVisible = true; + } + ++it; + } else if (item->index >= modelIndex + count) { + // after removed items + item->index -= count; + ++it; + } else { + // removed item + if (!removedVisible) { + d->scheduleLayout(); + removedVisible = true; + } + item->attached->emitRemove(); + if (item->attached->delayRemove()) { + item->index = -1; + connect(item->attached, SIGNAL(delayRemoveChanged()), this, SLOT(destroyRemoved()), Qt::QueuedConnection); + ++it; + } else { + it = d->visibleItems.erase(it); + d->releaseItem(item); + } + } + } + + // If we removed items before visible items a layout may be + // required to ensure item 0 is in the first column. + if (!removedVisible && modelIndex < d->visibleIndex) + d->scheduleLayout(); + + // fix current + if (d->currentIndex >= modelIndex + count) { + d->currentIndex -= count; + if (d->currentItem) + d->currentItem->index -= count; + emit currentIndexChanged(); + } else if (currentRemoved) { + // current item has been removed. + d->releaseItem(d->currentItem); + d->currentItem = 0; + d->currentIndex = -1; + if (d->itemCount) + d->updateCurrent(qMin(modelIndex, d->itemCount-1)); + else + emit currentIndexChanged(); + } + + // update visibleIndex + d->visibleIndex = 0; + for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) { + if ((*it)->index != -1) { + d->visibleIndex = (*it)->index; + break; + } + } + + if (removedVisible && d->visibleItems.isEmpty()) { + d->timeline.clear(); + if (d->itemCount == 0) { + d->setPosition(0); + d->updateHeader(); + d->updateFooter(); + update(); + } + } + + emit countChanged(); +} + +void QDeclarativeGridView::destroyRemoved() +{ + Q_D(QDeclarativeGridView); + for (QList::Iterator it = d->visibleItems.begin(); + it != d->visibleItems.end();) { + FxGridItem *listItem = *it; + if (listItem->index == -1 && listItem->attached->delayRemove() == false) { + d->releaseItem(listItem); + it = d->visibleItems.erase(it); + } else { + ++it; + } + } + + // Correct the positioning of the items + d->layout(); +} + +void QDeclarativeGridView::itemsMoved(int from, int to, int count) +{ + Q_D(QDeclarativeGridView); + if (!isComponentComplete()) + return; + QHash moved; + + FxGridItem *firstItem = d->firstVisibleItem(); + + QList::Iterator it = d->visibleItems.begin(); + while (it != d->visibleItems.end()) { + FxGridItem *item = *it; + if (item->index >= from && item->index < from + count) { + // take the items that are moving + item->index += (to-from); + moved.insert(item->index, item); + it = d->visibleItems.erase(it); + } else { + if (item->index > from && item->index != -1) { + // move everything after the moved items. + item->index -= count; + if (item->index < d->visibleIndex) + d->visibleIndex = item->index; + } + ++it; + } + } + + int remaining = count; + int endIndex = d->visibleIndex; + it = d->visibleItems.begin(); + while (it != d->visibleItems.end()) { + FxGridItem *item = *it; + if (remaining && item->index >= to && item->index < to + count) { + // place items in the target position, reusing any existing items + FxGridItem *movedItem = moved.take(item->index); + if (!movedItem) + movedItem = d->createItem(item->index); + if (!movedItem) { + // broken or no delegate + d->clear(); + return; + } + it = d->visibleItems.insert(it, movedItem); + if (it == d->visibleItems.begin() && firstItem) + movedItem->setPosition(firstItem->colPos(), firstItem->rowPos()); + ++it; + --remaining; + } else { + if (item->index != -1) { + if (item->index >= to) { + // update everything after the moved items. + item->index += count; + } + endIndex = item->index; + } + ++it; + } + } + + // If we have moved items to the end of the visible items + // then add any existing moved items that we have + while (FxGridItem *item = moved.take(endIndex+1)) { + d->visibleItems.append(item); + ++endIndex; + } + + // update visibleIndex + for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) { + if ((*it)->index != -1) { + d->visibleIndex = (*it)->index; + break; + } + } + + // Fix current index + if (d->currentIndex >= 0 && d->currentItem) { + int oldCurrent = d->currentIndex; + d->currentIndex = d->model->indexOf(d->currentItem->item, this); + if (oldCurrent != d->currentIndex) { + d->currentItem->index = d->currentIndex; + emit currentIndexChanged(); + } + } + + // Whatever moved items remain are no longer visible items. + while (moved.count()) { + int idx = moved.begin().key(); + FxGridItem *item = moved.take(idx); + if (d->currentItem && item->item == d->currentItem->item) + item->setPosition(d->colPosAt(idx), d->rowPosAt(idx)); + d->releaseItem(item); + } + + d->layout(); +} + +void QDeclarativeGridView::modelReset() +{ + Q_D(QDeclarativeGridView); + d->clear(); + refill(); + d->moveReason = QDeclarativeGridViewPrivate::SetIndex; + d->updateCurrent(d->currentIndex); + if (d->highlight && d->currentItem) { + if (d->autoHighlight) + d->highlight->setPosition(d->currentItem->colPos(), d->currentItem->rowPos()); + d->updateTrackedItem(); + } + d->moveReason = QDeclarativeGridViewPrivate::Other; + + emit countChanged(); +} + +void QDeclarativeGridView::createdItem(int index, QDeclarativeItem *item) +{ + Q_D(QDeclarativeGridView); + if (d->requestedIndex != index) { + item->setParentItem(this); + d->unrequestedItems.insert(item, index); + if (d->flow == QDeclarativeGridView::LeftToRight) { + item->setPos(QPointF(d->colPosAt(index), d->rowPosAt(index))); + } else { + item->setPos(QPointF(d->rowPosAt(index), d->colPosAt(index))); + } + } +} + +void QDeclarativeGridView::destroyingItem(QDeclarativeItem *item) +{ + Q_D(QDeclarativeGridView); + d->unrequestedItems.remove(item); +} + +void QDeclarativeGridView::animStopped() +{ + Q_D(QDeclarativeGridView); + d->bufferMode = QDeclarativeGridViewPrivate::NoBuffer; + if (d->haveHighlightRange && d->highlightRange == QDeclarativeGridView::StrictlyEnforceRange) + d->updateHighlight(); +} + +void QDeclarativeGridView::refill() +{ + Q_D(QDeclarativeGridView); + if (d->isRightToLeftTopToBottom()) + d->refill(-d->position()-d->size()+1, -d->position()); + else + d->refill(d->position(), d->position()+d->size()-1); +} + + +QDeclarativeGridViewAttached *QDeclarativeGridView::qmlAttachedProperties(QObject *obj) +{ + return new QDeclarativeGridViewAttached(obj); +} + +QT_END_NAMESPACE diff --git a/src/declarative/graphicsitems/qdeclarativegridview_p.h b/src/declarative/graphicsitems/qdeclarativegridview_p.h new file mode 100644 index 00000000..d7d7fdfc --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativegridview_p.h @@ -0,0 +1,286 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEGRIDVIEW_H +#define QDECLARATIVEGRIDVIEW_H + +#include "private/qdeclarativeflickable_p.h" +#include "private/qdeclarativeguard_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QDeclarativeVisualModel; +class QDeclarativeGridViewAttached; +class QDeclarativeGridViewPrivate; +class Q_AUTOTEST_EXPORT QDeclarativeGridView : public QDeclarativeFlickable +{ + Q_OBJECT + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarativeGridView) + + Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged) + Q_PROPERTY(QDeclarativeComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged) + Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) + Q_PROPERTY(QDeclarativeItem *currentItem READ currentItem NOTIFY currentIndexChanged) + Q_PROPERTY(int count READ count NOTIFY countChanged) + + Q_PROPERTY(QDeclarativeComponent *highlight READ highlight WRITE setHighlight NOTIFY highlightChanged) + Q_PROPERTY(QDeclarativeItem *highlightItem READ highlightItem NOTIFY highlightItemChanged) + Q_PROPERTY(bool highlightFollowsCurrentItem READ highlightFollowsCurrentItem WRITE setHighlightFollowsCurrentItem) + Q_PROPERTY(int highlightMoveDuration READ highlightMoveDuration WRITE setHighlightMoveDuration NOTIFY highlightMoveDurationChanged) + + Q_PROPERTY(qreal preferredHighlightBegin READ preferredHighlightBegin WRITE setPreferredHighlightBegin NOTIFY preferredHighlightBeginChanged RESET resetPreferredHighlightBegin) + Q_PROPERTY(qreal preferredHighlightEnd READ preferredHighlightEnd WRITE setPreferredHighlightEnd NOTIFY preferredHighlightEndChanged RESET resetPreferredHighlightEnd) + Q_PROPERTY(HighlightRangeMode highlightRangeMode READ highlightRangeMode WRITE setHighlightRangeMode NOTIFY highlightRangeModeChanged) + + Q_PROPERTY(Flow flow READ flow WRITE setFlow NOTIFY flowChanged) + Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection NOTIFY layoutDirectionChanged REVISION 1) + Q_PROPERTY(bool keyNavigationWraps READ isWrapEnabled WRITE setWrapEnabled NOTIFY keyNavigationWrapsChanged) + Q_PROPERTY(int cacheBuffer READ cacheBuffer WRITE setCacheBuffer NOTIFY cacheBufferChanged) + Q_PROPERTY(int cellWidth READ cellWidth WRITE setCellWidth NOTIFY cellWidthChanged) + Q_PROPERTY(int cellHeight READ cellHeight WRITE setCellHeight NOTIFY cellHeightChanged) + + Q_PROPERTY(SnapMode snapMode READ snapMode WRITE setSnapMode NOTIFY snapModeChanged) + + Q_PROPERTY(QDeclarativeComponent *header READ header WRITE setHeader NOTIFY headerChanged) + Q_PROPERTY(QDeclarativeComponent *footer READ footer WRITE setFooter NOTIFY footerChanged) + + Q_ENUMS(HighlightRangeMode) + Q_ENUMS(SnapMode) + Q_ENUMS(Flow) + Q_ENUMS(PositionMode) + Q_CLASSINFO("DefaultProperty", "data") + +public: + QDeclarativeGridView(QDeclarativeItem *parent=0); + ~QDeclarativeGridView(); + + QVariant model() const; + int modelCount() const; + void setModel(const QVariant &); + + QDeclarativeComponent *delegate() const; + void setDelegate(QDeclarativeComponent *); + + int currentIndex() const; + void setCurrentIndex(int idx); + + QDeclarativeItem *currentItem(); + QDeclarativeItem *highlightItem(); + int count() const; + + QDeclarativeComponent *highlight() const; + void setHighlight(QDeclarativeComponent *highlight); + + bool highlightFollowsCurrentItem() const; + void setHighlightFollowsCurrentItem(bool); + + int highlightMoveDuration() const; + void setHighlightMoveDuration(int); + + enum HighlightRangeMode { NoHighlightRange, ApplyRange, StrictlyEnforceRange }; + HighlightRangeMode highlightRangeMode() const; + void setHighlightRangeMode(HighlightRangeMode mode); + + qreal preferredHighlightBegin() const; + void setPreferredHighlightBegin(qreal); + void resetPreferredHighlightBegin(); + + qreal preferredHighlightEnd() const; + void setPreferredHighlightEnd(qreal); + void resetPreferredHighlightEnd(); + + Qt::LayoutDirection layoutDirection() const; + void setLayoutDirection(Qt::LayoutDirection); + Qt::LayoutDirection effectiveLayoutDirection() const; + + enum Flow { LeftToRight, TopToBottom }; + Flow flow() const; + void setFlow(Flow); + + bool isWrapEnabled() const; + void setWrapEnabled(bool); + + int cacheBuffer() const; + void setCacheBuffer(int); + + int cellWidth() const; + void setCellWidth(int); + + int cellHeight() const; + void setCellHeight(int); + + enum SnapMode { NoSnap, SnapToRow, SnapOneRow }; + SnapMode snapMode() const; + void setSnapMode(SnapMode mode); + + QDeclarativeComponent *footer() const; + void setFooter(QDeclarativeComponent *); + + QDeclarativeComponent *header() const; + void setHeader(QDeclarativeComponent *); + + virtual void setContentX(qreal pos); + virtual void setContentY(qreal pos); + + enum PositionMode { Beginning, Center, End, Visible, Contain }; + + Q_INVOKABLE void positionViewAtIndex(int index, int mode); + Q_INVOKABLE int indexAt(qreal x, qreal y) const; + Q_INVOKABLE Q_REVISION(1) void positionViewAtBeginning(); + Q_INVOKABLE Q_REVISION(1) void positionViewAtEnd(); + + static QDeclarativeGridViewAttached *qmlAttachedProperties(QObject *); + +public Q_SLOTS: + void moveCurrentIndexUp(); + void moveCurrentIndexDown(); + void moveCurrentIndexLeft(); + void moveCurrentIndexRight(); + +Q_SIGNALS: + void countChanged(); + void currentIndexChanged(); + void cellWidthChanged(); + void cellHeightChanged(); + void highlightChanged(); + void highlightItemChanged(); + void preferredHighlightBeginChanged(); + void preferredHighlightEndChanged(); + void highlightRangeModeChanged(); + void highlightMoveDurationChanged(); + void modelChanged(); + void delegateChanged(); + void flowChanged(); + Q_REVISION(1) void layoutDirectionChanged(); + void keyNavigationWrapsChanged(); + void cacheBufferChanged(); + void snapModeChanged(); + void headerChanged(); + void footerChanged(); + +protected: + virtual bool event(QEvent *event); + virtual void viewportMoved(); + virtual qreal minYExtent() const; + virtual qreal maxYExtent() const; + virtual qreal minXExtent() const; + virtual qreal maxXExtent() const; + virtual void keyPressEvent(QKeyEvent *); + virtual void componentComplete(); + +private Q_SLOTS: + void trackedPositionChanged(); + void itemsInserted(int index, int count); + void itemsRemoved(int index, int count); + void itemsMoved(int from, int to, int count); + void modelReset(); + void destroyRemoved(); + void createdItem(int index, QDeclarativeItem *item); + void destroyingItem(QDeclarativeItem *item); + void animStopped(); + +private: + void refill(); +}; + +class QDeclarativeGridViewAttached : public QObject +{ + Q_OBJECT +public: + QDeclarativeGridViewAttached(QObject *parent) + : QObject(parent), m_view(0), m_isCurrent(false), m_delayRemove(false) {} + ~QDeclarativeGridViewAttached() {} + + Q_PROPERTY(QDeclarativeGridView *view READ view NOTIFY viewChanged) + QDeclarativeGridView *view() { return m_view; } + void setView(QDeclarativeGridView *view) { + if (view != m_view) { + m_view = view; + emit viewChanged(); + } + } + + Q_PROPERTY(bool isCurrentItem READ isCurrentItem NOTIFY currentItemChanged) + bool isCurrentItem() const { return m_isCurrent; } + void setIsCurrentItem(bool c) { + if (m_isCurrent != c) { + m_isCurrent = c; + emit currentItemChanged(); + } + } + + Q_PROPERTY(bool delayRemove READ delayRemove WRITE setDelayRemove NOTIFY delayRemoveChanged) + bool delayRemove() const { return m_delayRemove; } + void setDelayRemove(bool delay) { + if (m_delayRemove != delay) { + m_delayRemove = delay; + emit delayRemoveChanged(); + } + } + + void emitAdd() { emit add(); } + void emitRemove() { emit remove(); } + +Q_SIGNALS: + void currentItemChanged(); + void delayRemoveChanged(); + void add(); + void remove(); + void viewChanged(); + +public: + QDeclarativeGuard m_view; + bool m_isCurrent : 1; + bool m_delayRemove : 1; +}; + + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeGridView) +QML_DECLARE_TYPEINFO(QDeclarativeGridView, QML_HAS_ATTACHED_PROPERTIES) + +QT_END_HEADER + +#endif diff --git a/src/declarative/graphicsitems/qdeclarativeimage.cpp b/src/declarative/graphicsitems/qdeclarativeimage.cpp new file mode 100644 index 00000000..862d4771 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativeimage.cpp @@ -0,0 +1,584 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativeimage_p.h" +#include "private/qdeclarativeimage_p_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + + +/*! + \qmlclass Image QDeclarativeImage + \since 4.7 + \ingroup qml-basic-visual-elements + \brief The Image element displays an image in a declarative user interface + \inherits Item + + The Image element is used to display images in a declarative user interface. + + The source of the image is specified as a URL using the \l source property. + Images can be supplied in any of the standard image formats supported by Qt, + including bitmap formats such as PNG and JPEG, and vector graphics formats + such as SVG. If you need to display animated images, use the \l AnimatedImage + element. + + If the \l{Item::width}{width} and \l{Item::height}{height} properties are not + specified, the Image element automatically uses the size of the loaded image. + By default, specifying the width and height of the element causes the image + to be scaled to that size. This behavior can be changed by setting the + \l fillMode property, allowing the image to be stretched and tiled instead. + + \section1 Example Usage + + The following example shows the simplest usage of the Image element. + + \snippet doc/src/snippets/declarative/image.qml document + + \beginfloatleft + \image declarative-qtlogo.png + \endfloat + + \clearfloat + + \section1 Performance + + By default, locally available images are loaded immediately, and the user interface + is blocked until loading is complete. If a large image is to be loaded, it may be + preferable to load the image in a low priority thread, by enabling the \l asynchronous + property. + + If the image is obtained from a network rather than a local resource, it is + automatically loaded asynchronously, and the \l progress and \l status properties + are updated as appropriate. + + Images are cached and shared internally, so if several Image elements have the same \l source, + only one copy of the image will be loaded. + + \bold Note: Images are often the greatest user of memory in QML user interfaces. It is recommended + that images which do not form part of the user interface have their + size bounded via the \l sourceSize property. This is especially important for content + that is loaded from external sources or provided by the user. + + \sa {declarative/imageelements/image}{Image example}, QDeclarativeImageProvider +*/ + +QDeclarativeImage::QDeclarativeImage(QDeclarativeItem *parent) + : QDeclarativeImageBase(*(new QDeclarativeImagePrivate), parent) +{ +} + +QDeclarativeImage::QDeclarativeImage(QDeclarativeImagePrivate &dd, QDeclarativeItem *parent) + : QDeclarativeImageBase(dd, parent) +{ +} + +QDeclarativeImage::~QDeclarativeImage() +{ +} + +QPixmap QDeclarativeImage::pixmap() const +{ + Q_D(const QDeclarativeImage); + return d->pix.pixmap(); +} + +void QDeclarativeImage::setPixmap(const QPixmap &pix) +{ + Q_D(QDeclarativeImage); + if (!d->url.isEmpty()) + return; + d->setPixmap(pix); +} + +void QDeclarativeImagePrivate::setPixmap(const QPixmap &pixmap) +{ + Q_Q(QDeclarativeImage); + pix.setPixmap(pixmap); + + q->pixmapChange(); + status = pix.isNull() ? QDeclarativeImageBase::Null : QDeclarativeImageBase::Ready; + + q->update(); +} + +/*! + \qmlproperty enumeration Image::fillMode + + Set this property to define what happens when the source image has a different size + than the item. + + \list + \o Image.Stretch - the image is scaled to fit + \o Image.PreserveAspectFit - the image is scaled uniformly to fit without cropping + \o Image.PreserveAspectCrop - the image is scaled uniformly to fill, cropping if necessary + \o Image.Tile - the image is duplicated horizontally and vertically + \o Image.TileVertically - the image is stretched horizontally and tiled vertically + \o Image.TileHorizontally - the image is stretched vertically and tiled horizontally + \endlist + + \table + + \row + \o \image declarative-qtlogo-stretch.png + \o Stretch (default) + \qml + Image { + width: 130; height: 100 + smooth: true + source: "qtlogo.png" + } + \endqml + + \row + \o \image declarative-qtlogo-preserveaspectfit.png + \o PreserveAspectFit + \qml + Image { + width: 130; height: 100 + fillMode: Image.PreserveAspectFit + smooth: true + source: "qtlogo.png" + } + \endqml + + \row + \o \image declarative-qtlogo-preserveaspectcrop.png + \o PreserveAspectCrop + \qml + Image { + width: 130; height: 100 + fillMode: Image.PreserveAspectCrop + smooth: true + source: "qtlogo.png" + clip: true + } + \endqml + + \row + \o \image declarative-qtlogo-tile.png + \o Tile + \qml + Image { + width: 120; height: 120 + fillMode: Image.Tile + source: "qtlogo.png" + } + \endqml + + \row + \o \image declarative-qtlogo-tilevertically.png + \o TileVertically + \qml + Image { + width: 120; height: 120 + fillMode: Image.TileVertically + smooth: true + source: "qtlogo.png" + } + \endqml + + \row + \o \image declarative-qtlogo-tilehorizontally.png + \o TileHorizontally + \qml + Image { + width: 120; height: 120 + fillMode: Image.TileHorizontally + smooth: true + source: "qtlogo.png" + } + \endqml + + \endtable + + Note that \c clip is \c false by default which means that the element might + paint outside its bounding rectangle even if the fillMode is set to \c PreserveAspectCrop. + + \sa {declarative/imageelements/image}{Image example} +*/ +QDeclarativeImage::FillMode QDeclarativeImage::fillMode() const +{ + Q_D(const QDeclarativeImage); + return d->fillMode; +} + +void QDeclarativeImage::setFillMode(FillMode mode) +{ + Q_D(QDeclarativeImage); + if (d->fillMode == mode) + return; + d->fillMode = mode; + update(); + updatePaintedGeometry(); + emit fillModeChanged(); +} + +/*! + + \qmlproperty real Image::paintedWidth + \qmlproperty real Image::paintedHeight + + These properties hold the size of the image that is actually painted. + In most cases it is the same as \c width and \c height, but when using a + \c fillMode \c PreserveAspectFit or \c fillMode \c PreserveAspectCrop + \c paintedWidth or \c paintedHeight can be smaller or larger than + \c width and \c height of the Image element. +*/ +qreal QDeclarativeImage::paintedWidth() const +{ + Q_D(const QDeclarativeImage); + return d->paintedWidth; +} + +qreal QDeclarativeImage::paintedHeight() const +{ + Q_D(const QDeclarativeImage); + return d->paintedHeight; +} + +/*! + \qmlproperty enumeration Image::status + + This property holds the status of image loading. It can be one of: + \list + \o Image.Null - no image has been set + \o Image.Ready - the image has been loaded + \o Image.Loading - the image is currently being loaded + \o Image.Error - an error occurred while loading the image + \endlist + + Use this status to provide an update or respond to the status change in some way. + For example, you could: + + \list + \o Trigger a state change: + \qml + State { name: 'loaded'; when: image.status == Image.Ready } + \endqml + + \o Implement an \c onStatusChanged signal handler: + \qml + Image { + id: image + onStatusChanged: if (image.status == Image.Ready) console.log('Loaded') + } + \endqml + + \o Bind to the status value: + \qml + Text { text: image.status == Image.Ready ? 'Loaded' : 'Not loaded' } + \endqml + \endlist + + \sa progress +*/ + +/*! + \qmlproperty real Image::progress + + This property holds the progress of image loading, from 0.0 (nothing loaded) + to 1.0 (finished). + + \sa status +*/ + +/*! + \qmlproperty bool Image::smooth + + Set this property if you want the image to be smoothly filtered when scaled or + transformed. Smooth filtering gives better visual quality, but is slower. If + the image is displayed at its natural size, this property has no visual or + performance effect. + + \note Generally scaling artifacts are only visible if the image is stationary on + the screen. A common pattern when animating an image is to disable smooth + filtering at the beginning of the animation and reenable it at the conclusion. +*/ + +/*! + \qmlproperty QSize Image::sourceSize + + This property holds the actual width and height of the loaded image. + + Unlike the \l {Item::}{width} and \l {Item::}{height} properties, which scale + the painting of the image, this property sets the actual number of pixels + stored for the loaded image so that large images do not use more + memory than necessary. For example, this ensures the image in memory is no + larger than 1024x1024 pixels, regardless of the Image's \l {Item::}{width} and + \l {Item::}{height} values: + + \code + Rectangle { + width: ... + height: ... + + Image { + anchors.fill: parent + source: "reallyBigImage.jpg" + sourceSize.width: 1024 + sourceSize.height: 1024 + } + } + \endcode + + If the image's actual size is larger than the sourceSize, the image is scaled down. + If only one dimension of the size is set to greater than 0, the + other dimension is set in proportion to preserve the source image's aspect ratio. + (The \l fillMode is independent of this.) + + If the source is an instrinsically scalable image (eg. SVG), this property + determines the size of the loaded image regardless of intrinsic size. + Avoid changing this property dynamically; rendering an SVG is \e slow compared + to an image. + + If the source is a non-scalable image (eg. JPEG), the loaded image will + be no greater than this property specifies. For some formats (currently only JPEG), + the whole image will never actually be loaded into memory. + + Since QtQuick 1.1 the sourceSize can be cleared to the natural size of the image + by setting sourceSize to \c undefined. + + \note \e {Changing this property dynamically causes the image source to be reloaded, + potentially even from the network, if it is not in the disk cache.} +*/ + +void QDeclarativeImage::updatePaintedGeometry() +{ + Q_D(QDeclarativeImage); + + if (d->fillMode == PreserveAspectFit) { + if (!d->pix.width() || !d->pix.height()) { + setImplicitWidth(0); + setImplicitHeight(0); + return; + } + qreal w = widthValid() ? width() : d->pix.width(); + qreal widthScale = w / qreal(d->pix.width()); + qreal h = heightValid() ? height() : d->pix.height(); + qreal heightScale = h / qreal(d->pix.height()); + if (widthScale <= heightScale) { + d->paintedWidth = w; + d->paintedHeight = widthScale * qreal(d->pix.height()); + } else if(heightScale < widthScale) { + d->paintedWidth = heightScale * qreal(d->pix.width()); + d->paintedHeight = h; + } + if (widthValid() && !heightValid()) { + setImplicitHeight(d->paintedHeight); + } else { + setImplicitHeight(d->pix.height()); + } + if (heightValid() && !widthValid()) { + setImplicitWidth(d->paintedWidth); + } else { + setImplicitWidth(d->pix.width()); + } + } else if (d->fillMode == PreserveAspectCrop) { + if (!d->pix.width() || !d->pix.height()) + return; + qreal widthScale = width() / qreal(d->pix.width()); + qreal heightScale = height() / qreal(d->pix.height()); + if (widthScale < heightScale) { + widthScale = heightScale; + } else if(heightScale < widthScale) { + heightScale = widthScale; + } + + d->paintedHeight = heightScale * qreal(d->pix.height()); + d->paintedWidth = widthScale * qreal(d->pix.width()); + } else { + d->paintedWidth = width(); + d->paintedHeight = height(); + } + emit paintedGeometryChanged(); +} + +void QDeclarativeImage::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + QDeclarativeImageBase::geometryChanged(newGeometry, oldGeometry); + updatePaintedGeometry(); +} + +QRectF QDeclarativeImage::boundingRect() const +{ + Q_D(const QDeclarativeImage); + return QRectF(0, 0, qMax(d->mWidth, d->paintedWidth), qMax(d->mHeight, d->paintedHeight)); +} + +/*! + \qmlproperty url Image::source + + Image can handle any image format supported by Qt, loaded from any URL scheme supported by Qt. + + The URL may be absolute, or relative to the URL of the component. + + \sa QDeclarativeImageProvider +*/ + +/*! + \qmlproperty bool Image::asynchronous + + Specifies that images on the local filesystem should be loaded + asynchronously in a separate thread. The default value is + false, causing the user interface thread to block while the + image is loaded. Setting \a asynchronous to true is useful where + maintaining a responsive user interface is more desirable + than having images immediately visible. + + Note that this property is only valid for images read from the + local filesystem. Images loaded via a network resource (e.g. HTTP) + are always loaded asynchonously. +*/ + +/*! + \qmlproperty bool Image::cache + \since QtQuick 1.1 + + Specifies whether the image should be cached. The default value is + true. Setting \a cache to false is useful when dealing with large images, + to make sure that they aren't cached at the expense of small 'ui element' images. +*/ + +/*! + \qmlproperty bool Image::mirror + \since QtQuick 1.1 + + This property holds whether the image should be horizontally inverted + (effectively displaying a mirrored image). + + The default value is false. +*/ + + +void QDeclarativeImage::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *) +{ + Q_D(QDeclarativeImage); + if (d->pix.pixmap().isNull() ) + return; + + int drawWidth = width(); + int drawHeight = height(); + bool doClip = false; + QTransform transform; + qreal widthScale = width() / qreal(d->pix.width()); + qreal heightScale = height() / qreal(d->pix.height()); + + if (width() != d->pix.width() || height() != d->pix.height()) { + if (d->fillMode >= Tile) { + if (d->fillMode == TileVertically) { + transform.scale(widthScale, 1.0); + drawWidth = d->pix.width(); + } else if (d->fillMode == TileHorizontally) { + transform.scale(1.0, heightScale); + drawHeight = d->pix.height(); + } + } else { + if (d->fillMode == PreserveAspectFit) { + if (widthScale <= heightScale) { + heightScale = widthScale; + transform.translate(0, (height() - heightScale * d->pix.height()) / 2); + } else if(heightScale < widthScale) { + widthScale = heightScale; + transform.translate((width() - widthScale * d->pix.width()) / 2, 0); + } + } else if (d->fillMode == PreserveAspectCrop) { + if (widthScale < heightScale) { + widthScale = heightScale; + transform.translate((width() - widthScale * d->pix.width()) / 2, 0); + } else if(heightScale < widthScale) { + heightScale = widthScale; + transform.translate(0, (height() - heightScale * d->pix.height()) / 2); + } + } + transform.scale(widthScale, heightScale); + drawWidth = d->pix.width(); + drawHeight = d->pix.height(); + doClip = clip(); + } + } + + QTransform oldTransform; + bool oldAA = p->testRenderHint(QPainter::Antialiasing); + bool oldSmooth = p->testRenderHint(QPainter::SmoothPixmapTransform); + if (d->smooth) + p->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform, d->smooth); + if (doClip) { + p->save(); + p->setClipRect(QRectF(0, 0, d->mWidth, d->mHeight), Qt::IntersectClip); + } + if (d->mirror) + transform.translate(drawWidth, 0).scale(-1.0, 1.0); + if (!transform.isIdentity()) { + oldTransform = p->transform(); + p->setWorldTransform(transform * oldTransform); + } + + if (d->fillMode >= Tile) + p->drawTiledPixmap(QRectF(0, 0, drawWidth, drawHeight), d->pix); + else + p->drawPixmap(QRectF(0, 0, drawWidth, drawHeight), d->pix, QRectF(0, 0, drawWidth, drawHeight)); + + if (d->smooth) { + p->setRenderHint(QPainter::Antialiasing, oldAA); + p->setRenderHint(QPainter::SmoothPixmapTransform, oldSmooth); + } + if (doClip) + p->restore(); + if (!transform.isIdentity()) + p->setWorldTransform(oldTransform); +} + +void QDeclarativeImage::pixmapChange() +{ + Q_D(QDeclarativeImage); + // PreserveAspectFit calculates the implicit size differently so we + // don't call our superclass pixmapChange(), since that would + // result in the implicit size being set incorrectly, then updated + // in updatePaintedGeometry() + if (d->fillMode != PreserveAspectFit) + QDeclarativeImageBase::pixmapChange(); + updatePaintedGeometry(); +} + +QT_END_NAMESPACE diff --git a/src/declarative/graphicsitems/qdeclarativeimage_p.h b/src/declarative/graphicsitems/qdeclarativeimage_p.h new file mode 100644 index 00000000..5b1daa04 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativeimage_p.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEIMAGE_H +#define QDECLARATIVEIMAGE_H + +#include "private/qdeclarativeimagebase_p.h" + +#include + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeImagePrivate; +class Q_AUTOTEST_EXPORT QDeclarativeImage : public QDeclarativeImageBase +{ + Q_OBJECT + Q_ENUMS(FillMode) + + Q_PROPERTY(FillMode fillMode READ fillMode WRITE setFillMode NOTIFY fillModeChanged) + Q_PROPERTY(qreal paintedWidth READ paintedWidth NOTIFY paintedGeometryChanged) + Q_PROPERTY(qreal paintedHeight READ paintedHeight NOTIFY paintedGeometryChanged) + +public: + QDeclarativeImage(QDeclarativeItem *parent=0); + ~QDeclarativeImage(); + + enum FillMode { Stretch, PreserveAspectFit, PreserveAspectCrop, Tile, TileVertically, TileHorizontally }; + FillMode fillMode() const; + void setFillMode(FillMode); + + QPixmap pixmap() const; + void setPixmap(const QPixmap &); + + qreal paintedWidth() const; + qreal paintedHeight() const; + + void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *); + QRectF boundingRect() const; + +Q_SIGNALS: + void fillModeChanged(); + void paintedGeometryChanged(); + +protected: + QDeclarativeImage(QDeclarativeImagePrivate &dd, QDeclarativeItem *parent); + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); + void pixmapChange(); + void updatePaintedGeometry(); + +private: + Q_DISABLE_COPY(QDeclarativeImage) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarativeImage) +}; + +QT_END_NAMESPACE +QML_DECLARE_TYPE(QDeclarativeImage) +QT_END_HEADER + +#endif // QDECLARATIVEIMAGE_H diff --git a/src/declarative/graphicsitems/qdeclarativeimage_p_p.h b/src/declarative/graphicsitems/qdeclarativeimage_p_p.h new file mode 100644 index 00000000..26f1680e --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativeimage_p_p.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEIMAGE_P_H +#define QDECLARATIVEIMAGE_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 "private/qdeclarativeitem_p.h" +#include "private/qdeclarativeimagebase_p_p.h" + +QT_BEGIN_NAMESPACE + +class QDeclarativeImagePrivate : public QDeclarativeImageBasePrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeImage) + +public: + QDeclarativeImagePrivate() + : fillMode(QDeclarativeImage::Stretch), paintedWidth(0), paintedHeight(0) + { + } + + QDeclarativeImage::FillMode fillMode; + qreal paintedWidth; + qreal paintedHeight; + void setPixmap(const QPixmap &pix); +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVEIMAGE_P_H diff --git a/src/declarative/graphicsitems/qdeclarativeimagebase.cpp b/src/declarative/graphicsitems/qdeclarativeimagebase.cpp new file mode 100644 index 00000000..cd1d3f68 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativeimagebase.cpp @@ -0,0 +1,284 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativeimagebase_p.h" +#include "private/qdeclarativeimagebase_p_p.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QDeclarativeImageBase::QDeclarativeImageBase(QDeclarativeItem *parent) + : QDeclarativeImplicitSizeItem(*(new QDeclarativeImageBasePrivate), parent) +{ +} + +QDeclarativeImageBase::QDeclarativeImageBase(QDeclarativeImageBasePrivate &dd, QDeclarativeItem *parent) + : QDeclarativeImplicitSizeItem(dd, parent) +{ +} + +QDeclarativeImageBase::~QDeclarativeImageBase() +{ +} + +QDeclarativeImageBase::Status QDeclarativeImageBase::status() const +{ + Q_D(const QDeclarativeImageBase); + return d->status; +} + + +qreal QDeclarativeImageBase::progress() const +{ + Q_D(const QDeclarativeImageBase); + return d->progress; +} + + +bool QDeclarativeImageBase::asynchronous() const +{ + Q_D(const QDeclarativeImageBase); + return d->async; +} + +void QDeclarativeImageBase::setAsynchronous(bool async) +{ + Q_D(QDeclarativeImageBase); + if (d->async != async) { + d->async = async; + emit asynchronousChanged(); + } +} + +QUrl QDeclarativeImageBase::source() const +{ + Q_D(const QDeclarativeImageBase); + return d->url; +} + +void QDeclarativeImageBase::setSource(const QUrl &url) +{ + Q_D(QDeclarativeImageBase); + //equality is fairly expensive, so we bypass for simple, common case + if ((d->url.isEmpty() == url.isEmpty()) && url == d->url) + return; + + d->url = url; + emit sourceChanged(d->url); + + if (isComponentComplete()) + load(); +} + +void QDeclarativeImageBase::setSourceSize(const QSize& size) +{ + Q_D(QDeclarativeImageBase); + if (d->sourcesize == size) + return; + + d->sourcesize = size; + d->explicitSourceSize = true; + emit sourceSizeChanged(); + if (isComponentComplete()) + load(); +} + +QSize QDeclarativeImageBase::sourceSize() const +{ + Q_D(const QDeclarativeImageBase); + + int width = d->sourcesize.width(); + int height = d->sourcesize.height(); + return QSize(width != -1 ? width : d->pix.width(), height != -1 ? height : d->pix.height()); +} + +void QDeclarativeImageBase::resetSourceSize() +{ + Q_D(QDeclarativeImageBase); + if (!d->explicitSourceSize) + return; + d->explicitSourceSize = false; + d->sourcesize = QSize(); + emit sourceSizeChanged(); + if (isComponentComplete()) + load(); +} + +bool QDeclarativeImageBase::cache() const +{ + Q_D(const QDeclarativeImageBase); + return d->cache; +} + +void QDeclarativeImageBase::setCache(bool cache) +{ + Q_D(QDeclarativeImageBase); + if (d->cache == cache) + return; + + d->cache = cache; + emit cacheChanged(); + if (isComponentComplete()) + load(); +} + +void QDeclarativeImageBase::setMirror(bool mirror) +{ + Q_D(QDeclarativeImageBase); + if (mirror == d->mirror) + return; + + d->mirror = mirror; + + if (isComponentComplete()) + update(); + + emit mirrorChanged(); +} + +bool QDeclarativeImageBase::mirror() const +{ + Q_D(const QDeclarativeImageBase); + return d->mirror; +} + +void QDeclarativeImageBase::load() +{ + Q_D(QDeclarativeImageBase); + + if (d->url.isEmpty()) { + d->pix.clear(this); + d->status = Null; + d->progress = 0.0; + pixmapChange(); + emit progressChanged(d->progress); + emit statusChanged(d->status); + update(); + } else { + QDeclarativePixmap::Options options; + if (d->async) + options |= QDeclarativePixmap::Asynchronous; + if (d->cache) + options |= QDeclarativePixmap::Cache; + d->pix.clear(this); + d->pix.load(qmlEngine(this), d->url, d->explicitSourceSize ? sourceSize() : QSize(), options); + + if (d->pix.isLoading()) { + d->progress = 0.0; + d->status = Loading; + emit progressChanged(d->progress); + emit statusChanged(d->status); + + static int thisRequestProgress = -1; + static int thisRequestFinished = -1; + if (thisRequestProgress == -1) { + thisRequestProgress = + QDeclarativeImageBase::staticMetaObject.indexOfSlot("requestProgress(qint64,qint64)"); + thisRequestFinished = + QDeclarativeImageBase::staticMetaObject.indexOfSlot("requestFinished()"); + } + + d->pix.connectFinished(this, thisRequestFinished); + d->pix.connectDownloadProgress(this, thisRequestProgress); + + } else { + requestFinished(); + } + } +} + +void QDeclarativeImageBase::requestFinished() +{ + Q_D(QDeclarativeImageBase); + + QDeclarativeImageBase::Status oldStatus = d->status; + qreal oldProgress = d->progress; + + if (d->pix.isError()) { + d->status = Error; + qmlInfo(this) << d->pix.error(); + } else { + d->status = Ready; + } + + d->progress = 1.0; + + pixmapChange(); + + if (d->sourcesize.width() != d->pix.width() || d->sourcesize.height() != d->pix.height()) + emit sourceSizeChanged(); + + if (d->status != oldStatus) + emit statusChanged(d->status); + if (d->progress != oldProgress) + emit progressChanged(d->progress); + + update(); +} + +void QDeclarativeImageBase::requestProgress(qint64 received, qint64 total) +{ + Q_D(QDeclarativeImageBase); + if (d->status == Loading && total > 0) { + d->progress = qreal(received)/total; + emit progressChanged(d->progress); + } +} + +void QDeclarativeImageBase::componentComplete() +{ + Q_D(QDeclarativeImageBase); + QDeclarativeItem::componentComplete(); + if (d->url.isValid()) + load(); +} + +void QDeclarativeImageBase::pixmapChange() +{ + Q_D(QDeclarativeImageBase); + setImplicitWidth(d->pix.width()); + setImplicitHeight(d->pix.height()); +} + +QT_END_NAMESPACE diff --git a/src/declarative/graphicsitems/qdeclarativeimagebase_p.h b/src/declarative/graphicsitems/qdeclarativeimagebase_p.h new file mode 100644 index 00000000..6dd47302 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativeimagebase_p.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEIMAGEBASE_H +#define QDECLARATIVEIMAGEBASE_H + +#include "qdeclarativeimplicitsizeitem_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDeclarativeImageBasePrivate; +class Q_AUTOTEST_EXPORT QDeclarativeImageBase : public QDeclarativeImplicitSizeItem +{ + Q_OBJECT + Q_ENUMS(Status) + + Q_PROPERTY(Status status READ status NOTIFY statusChanged) + Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) + Q_PROPERTY(qreal progress READ progress NOTIFY progressChanged) + Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged) + Q_PROPERTY(bool cache READ cache WRITE setCache NOTIFY cacheChanged REVISION 1) + Q_PROPERTY(QSize sourceSize READ sourceSize WRITE setSourceSize RESET resetSourceSize NOTIFY sourceSizeChanged) + Q_PROPERTY(bool mirror READ mirror WRITE setMirror NOTIFY mirrorChanged REVISION 1) + +public: + QDeclarativeImageBase(QDeclarativeItem *parent=0); + ~QDeclarativeImageBase(); + enum Status { Null, Ready, Loading, Error }; + Status status() const; + qreal progress() const; + + QUrl source() const; + virtual void setSource(const QUrl &url); + + bool asynchronous() const; + void setAsynchronous(bool); + + bool cache() const; + void setCache(bool); + + virtual void setSourceSize(const QSize&); + QSize sourceSize() const; + void resetSourceSize(); + + virtual void setMirror(bool mirror); + bool mirror() const; + +Q_SIGNALS: + void sourceChanged(const QUrl &); + void sourceSizeChanged(); + void statusChanged(QDeclarativeImageBase::Status); + void progressChanged(qreal progress); + void asynchronousChanged(); + Q_REVISION(1) void cacheChanged(); + Q_REVISION(1) void mirrorChanged(); + +protected: + virtual void load(); + virtual void componentComplete(); + virtual void pixmapChange(); + QDeclarativeImageBase(QDeclarativeImageBasePrivate &dd, QDeclarativeItem *parent); + +private Q_SLOTS: + virtual void requestFinished(); + void requestProgress(qint64,qint64); + +private: + Q_DISABLE_COPY(QDeclarativeImageBase) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarativeImageBase) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEIMAGEBASE_H diff --git a/src/declarative/graphicsitems/qdeclarativeimagebase_p_p.h b/src/declarative/graphicsitems/qdeclarativeimagebase_p_p.h new file mode 100644 index 00000000..1bd5899e --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativeimagebase_p_p.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEIMAGEBASE_P_H +#define QDECLARATIVEIMAGEBASE_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 "private/qdeclarativeimplicitsizeitem_p_p.h" +#include "private/qdeclarativepixmapcache_p.h" + +#include + +QT_BEGIN_NAMESPACE + +class QNetworkReply; +class QDeclarativeImageBasePrivate : public QDeclarativeImplicitSizeItemPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeImageBase) + +public: + QDeclarativeImageBasePrivate() + : status(QDeclarativeImageBase::Null), + progress(0.0), + explicitSourceSize(false), + async(false), + cache(true), + mirror(false) + { + QGraphicsItemPrivate::flags = QGraphicsItemPrivate::flags & ~QGraphicsItem::ItemHasNoContents; + } + + QDeclarativePixmap pix; + QDeclarativeImageBase::Status status; + QUrl url; + qreal progress; + QSize sourcesize; + bool explicitSourceSize : 1; + bool async : 1; + bool cache : 1; + bool mirror: 1; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/declarative/graphicsitems/qdeclarativeimplicitsizeitem.cpp b/src/declarative/graphicsitems/qdeclarativeimplicitsizeitem.cpp new file mode 100644 index 00000000..3666e18e --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativeimplicitsizeitem.cpp @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativeimplicitsizeitem_p.h" +#include "private/qdeclarativeimplicitsizeitem_p_p.h" + +QT_BEGIN_NAMESPACE + +void QDeclarativeImplicitSizeItemPrivate::implicitWidthChanged() +{ + Q_Q(QDeclarativeImplicitSizeItem); + emit q->implicitWidthChanged(); +} + +void QDeclarativeImplicitSizeItemPrivate::implicitHeightChanged() +{ + Q_Q(QDeclarativeImplicitSizeItem); + emit q->implicitHeightChanged(); +} + +QDeclarativeImplicitSizeItem::QDeclarativeImplicitSizeItem(QDeclarativeItem *parent) + : QDeclarativeItem(*(new QDeclarativeImplicitSizeItemPrivate), parent) +{ +} + +QDeclarativeImplicitSizeItem::QDeclarativeImplicitSizeItem(QDeclarativeImplicitSizeItemPrivate &dd, QDeclarativeItem *parent) + : QDeclarativeItem(dd, parent) +{ +} + + +void QDeclarativeImplicitSizePaintedItemPrivate::implicitWidthChanged() +{ + Q_Q(QDeclarativeImplicitSizePaintedItem); + emit q->implicitWidthChanged(); +} + +void QDeclarativeImplicitSizePaintedItemPrivate::implicitHeightChanged() +{ + Q_Q(QDeclarativeImplicitSizePaintedItem); + emit q->implicitHeightChanged(); +} + +QDeclarativeImplicitSizePaintedItem::QDeclarativeImplicitSizePaintedItem(QDeclarativeItem *parent) + : QDeclarativePaintedItem(*(new QDeclarativeImplicitSizePaintedItemPrivate), parent) +{ +} + +QDeclarativeImplicitSizePaintedItem::QDeclarativeImplicitSizePaintedItem(QDeclarativeImplicitSizePaintedItemPrivate &dd, QDeclarativeItem *parent) + : QDeclarativePaintedItem(dd, parent) +{ +} + +QT_END_NAMESPACE diff --git a/src/declarative/graphicsitems/qdeclarativeimplicitsizeitem_p.h b/src/declarative/graphicsitems/qdeclarativeimplicitsizeitem_p.h new file mode 100644 index 00000000..58371342 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativeimplicitsizeitem_p.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEIMPLICITSIZEITEM_H +#define QDECLARATIVEIMPLICITSIZEITEM_H + +#include "qdeclarativepainteditem_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDeclarativeImplicitSizeItemPrivate; +class Q_AUTOTEST_EXPORT QDeclarativeImplicitSizeItem : public QDeclarativeItem +{ + Q_OBJECT + Q_PROPERTY(qreal implicitWidth READ implicitWidth NOTIFY implicitWidthChanged REVISION 1) + Q_PROPERTY(qreal implicitHeight READ implicitHeight NOTIFY implicitHeightChanged REVISION 1) + +public: + QDeclarativeImplicitSizeItem(QDeclarativeItem *parent = 0); + +protected: + QDeclarativeImplicitSizeItem(QDeclarativeImplicitSizeItemPrivate &dd, QDeclarativeItem *parent); + +Q_SIGNALS: + Q_REVISION(1) void implicitWidthChanged(); + Q_REVISION(1) void implicitHeightChanged(); + +private: + Q_DISABLE_COPY(QDeclarativeImplicitSizeItem) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarativeImplicitSizeItem) +}; + +class QDeclarativeImplicitSizePaintedItemPrivate; +class Q_AUTOTEST_EXPORT QDeclarativeImplicitSizePaintedItem : public QDeclarativePaintedItem +{ + Q_OBJECT + Q_PROPERTY(qreal implicitWidth READ implicitWidth NOTIFY implicitWidthChanged REVISION 1) + Q_PROPERTY(qreal implicitHeight READ implicitHeight NOTIFY implicitHeightChanged REVISION 1) + +public: + QDeclarativeImplicitSizePaintedItem(QDeclarativeItem *parent = 0); + +protected: + QDeclarativeImplicitSizePaintedItem(QDeclarativeImplicitSizePaintedItemPrivate &dd, QDeclarativeItem *parent); + virtual void drawContents(QPainter *, const QRect &) {}; + +Q_SIGNALS: + Q_REVISION(1) void implicitWidthChanged(); + Q_REVISION(1) void implicitHeightChanged(); + +private: + Q_DISABLE_COPY(QDeclarativeImplicitSizePaintedItem) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarativeImplicitSizePaintedItem) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEIMPLICITSIZEITEM_H diff --git a/src/declarative/graphicsitems/qdeclarativeimplicitsizeitem_p_p.h b/src/declarative/graphicsitems/qdeclarativeimplicitsizeitem_p_p.h new file mode 100644 index 00000000..2eb284b8 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativeimplicitsizeitem_p_p.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEIMPLICITSIZEITEM_P_H +#define QDECLARATIVEIMPLICITSIZEITEM_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 "private/qdeclarativeitem_p.h" +#include "private/qdeclarativepainteditem_p_p.h" + +QT_BEGIN_NAMESPACE + +class QDeclarativeImplicitSizeItemPrivate : public QDeclarativeItemPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeImplicitSizeItem) + +public: + QDeclarativeImplicitSizeItemPrivate() + { + } + + virtual void implicitWidthChanged(); + virtual void implicitHeightChanged(); +}; + + +class QDeclarativeImplicitSizePaintedItemPrivate : public QDeclarativePaintedItemPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeImplicitSizePaintedItem) + +public: + QDeclarativeImplicitSizePaintedItemPrivate() + { + } + + virtual void implicitWidthChanged(); + virtual void implicitHeightChanged(); +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVEIMPLICITSIZEITEM_P_H diff --git a/src/declarative/graphicsitems/qdeclarativeitem.cpp b/src/declarative/graphicsitems/qdeclarativeitem.cpp new file mode 100644 index 00000000..fa6e8e11 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativeitem.cpp @@ -0,0 +1,3759 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativeitem.h" + +#include "private/qdeclarativeevents_p_p.h" +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmlclass Transform QGraphicsTransform + \ingroup qml-transform-elements + \since 4.7 + \brief The Transform elements provide a way of building advanced transformations on Items. + + The Transform element is a base type which cannot be instantiated directly. + The following concrete Transform types are available: + + \list + \o \l Rotation + \o \l Scale + \o \l Translate + \endlist + + The Transform elements let you create and control advanced transformations that can be configured + independently using specialized properties. + + You can assign any number of Transform elements to an \l Item. Each Transform is applied in order, + one at a time. +*/ + +/*! + \qmlclass Translate QDeclarativeTranslate + \ingroup qml-transform-elements + \since 4.7 + \brief The Translate object provides a way to move an Item without changing its x or y properties. + + The Translate object provides independent control over position in addition to the Item's x and y properties. + + The following example moves the Y axis of the \l Rectangle elements while still allowing the \l Row element + to lay the items out as if they had not been transformed: + \qml + import QtQuick 1.0 + + Row { + Rectangle { + width: 100; height: 100 + color: "blue" + transform: Translate { y: 20 } + } + Rectangle { + width: 100; height: 100 + color: "red" + transform: Translate { y: -20 } + } + } + \endqml + + \image translate.png +*/ + +/*! + \qmlproperty real Translate::x + + The translation along the X axis. +*/ + +/*! + \qmlproperty real Translate::y + + The translation along the Y axis. +*/ + +/*! + \qmlclass Scale QGraphicsScale + \ingroup qml-transform-elements + \since 4.7 + \brief The Scale element provides a way to scale an Item. + + The Scale element gives more control over scaling than using \l Item's \l{Item::scale}{scale} property. Specifically, + it allows a different scale for the x and y axes, and allows the scale to be relative to an + arbitrary point. + + The following example scales the X axis of the Rectangle, relative to its interior point 25, 25: + \qml + Rectangle { + width: 100; height: 100 + color: "blue" + transform: Scale { origin.x: 25; origin.y: 25; xScale: 3} + } + \endqml + + \sa Rotation, Translate +*/ + +/*! + \qmlproperty real Scale::origin.x + \qmlproperty real Scale::origin.y + + The point that the item is scaled from (i.e., the point that stays fixed relative to the parent as + the rest of the item grows). By default the origin is 0, 0. +*/ + +/*! + \qmlproperty real Scale::xScale + + The scaling factor for the X axis. +*/ + +/*! + \qmlproperty real Scale::yScale + + The scaling factor for the Y axis. +*/ + +/*! + \qmlclass Rotation QGraphicsRotation + \ingroup qml-transform-elements + \since 4.7 + \brief The Rotation object provides a way to rotate an Item. + + The Rotation object gives more control over rotation than using \l Item's \l{Item::rotation}{rotation} property. + Specifically, it allows (z axis) rotation to be relative to an arbitrary point. + + The following example rotates a Rectangle around its interior point 25, 25: + \qml + Rectangle { + width: 100; height: 100 + color: "blue" + transform: Rotation { origin.x: 25; origin.y: 25; angle: 45} + } + \endqml + + Rotation also provides a way to specify 3D-like rotations for Items. For these types of + rotations you must specify the axis to rotate around in addition to the origin point. + + The following example shows various 3D-like rotations applied to an \l Image. + \snippet doc/src/snippets/declarative/rotation.qml 0 + + \image axisrotation.png + + \sa {declarative/ui-components/dialcontrol}{Dial Control example}, {declarative/toys/clocks}{Clocks example} +*/ + +/*! + \qmlproperty real Rotation::origin.x + \qmlproperty real Rotation::origin.y + + The origin point of the rotation (i.e., the point that stays fixed relative to the parent as + the rest of the item rotates). By default the origin is 0, 0. +*/ + +/*! + \qmlproperty real Rotation::axis.x + \qmlproperty real Rotation::axis.y + \qmlproperty real Rotation::axis.z + + The axis to rotate around. For simple (2D) rotation around a point, you do not need to specify an axis, + as the default axis is the z axis (\c{ axis { x: 0; y: 0; z: 1 } }). + + For a typical 3D-like rotation you will usually specify both the origin and the axis. + + \image 3d-rotation-axis.png +*/ + +/*! + \qmlproperty real Rotation::angle + + The angle to rotate, in degrees clockwise. +*/ + +QDeclarativeContents::QDeclarativeContents(QDeclarativeItem *item) : m_item(item), m_x(0), m_y(0), m_width(0), m_height(0) +{ + //### optimize + connect(this, SIGNAL(rectChanged(QRectF)), m_item, SIGNAL(childrenRectChanged(QRectF))); +} + +QDeclarativeContents::~QDeclarativeContents() +{ + QList children = m_item->childItems(); + for (int i = 0; i < children.count(); ++i) { + QDeclarativeItem *child = qobject_cast(children.at(i)); + if(!child)//### Should this be ignoring non-QDeclarativeItem graphicsobjects? + continue; + QDeclarativeItemPrivate::get(child)->removeItemChangeListener(this, QDeclarativeItemPrivate::Geometry | QDeclarativeItemPrivate::Destroyed); + } +} + +QRectF QDeclarativeContents::rectF() const +{ + return QRectF(m_x, m_y, m_width, m_height); +} + +void QDeclarativeContents::calcHeight(QDeclarativeItem *changed) +{ + qreal oldy = m_y; + qreal oldheight = m_height; + + if (changed) { + qreal top = oldy; + qreal bottom = oldy + oldheight; + qreal y = changed->y(); + if (y + changed->height() > bottom) + bottom = y + changed->height(); + if (y < top) + top = y; + m_y = top; + m_height = bottom - top; + } else { + qreal top = FLT_MAX; + qreal bottom = 0; + QList children = m_item->childItems(); + for (int i = 0; i < children.count(); ++i) { + QDeclarativeItem *child = qobject_cast(children.at(i)); + if(!child)//### Should this be ignoring non-QDeclarativeItem graphicsobjects? + continue; + qreal y = child->y(); + if (y + child->height() > bottom) + bottom = y + child->height(); + if (y < top) + top = y; + } + if (!children.isEmpty()) + m_y = top; + m_height = qMax(bottom - top, qreal(0.0)); + } + + if (m_height != oldheight || m_y != oldy) + emit rectChanged(rectF()); +} + +void QDeclarativeContents::calcWidth(QDeclarativeItem *changed) +{ + qreal oldx = m_x; + qreal oldwidth = m_width; + + if (changed) { + qreal left = oldx; + qreal right = oldx + oldwidth; + qreal x = changed->x(); + if (x + changed->width() > right) + right = x + changed->width(); + if (x < left) + left = x; + m_x = left; + m_width = right - left; + } else { + qreal left = FLT_MAX; + qreal right = 0; + QList children = m_item->childItems(); + for (int i = 0; i < children.count(); ++i) { + QDeclarativeItem *child = qobject_cast(children.at(i)); + if(!child)//### Should this be ignoring non-QDeclarativeItem graphicsobjects? + continue; + qreal x = child->x(); + if (x + child->width() > right) + right = x + child->width(); + if (x < left) + left = x; + } + if (!children.isEmpty()) + m_x = left; + m_width = qMax(right - left, qreal(0.0)); + } + + if (m_width != oldwidth || m_x != oldx) + emit rectChanged(rectF()); +} + +void QDeclarativeContents::complete() +{ + QList children = m_item->childItems(); + for (int i = 0; i < children.count(); ++i) { + QDeclarativeItem *child = qobject_cast(children.at(i)); + if(!child)//### Should this be ignoring non-QDeclarativeItem graphicsobjects? + continue; + QDeclarativeItemPrivate::get(child)->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry | QDeclarativeItemPrivate::Destroyed); + //###what about changes to visibility? + } + + calcGeometry(); +} + +void QDeclarativeContents::itemGeometryChanged(QDeclarativeItem *changed, const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_UNUSED(changed) + //### we can only pass changed if the left edge has moved left, or the right edge has moved right + if (newGeometry.width() != oldGeometry.width() || newGeometry.x() != oldGeometry.x()) + calcWidth(/*changed*/); + if (newGeometry.height() != oldGeometry.height() || newGeometry.y() != oldGeometry.y()) + calcHeight(/*changed*/); +} + +void QDeclarativeContents::itemDestroyed(QDeclarativeItem *item) +{ + if (item) + QDeclarativeItemPrivate::get(item)->removeItemChangeListener(this, QDeclarativeItemPrivate::Geometry | QDeclarativeItemPrivate::Destroyed); + calcGeometry(); +} + +void QDeclarativeContents::childRemoved(QDeclarativeItem *item) +{ + if (item) + QDeclarativeItemPrivate::get(item)->removeItemChangeListener(this, QDeclarativeItemPrivate::Geometry | QDeclarativeItemPrivate::Destroyed); + calcGeometry(); +} + +void QDeclarativeContents::childAdded(QDeclarativeItem *item) +{ + if (item) + QDeclarativeItemPrivate::get(item)->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry | QDeclarativeItemPrivate::Destroyed); + calcWidth(item); + calcHeight(item); +} + +QDeclarativeItemKeyFilter::QDeclarativeItemKeyFilter(QDeclarativeItem *item) +: m_processPost(false), m_next(0) +{ + QDeclarativeItemPrivate *p = + item?static_cast(QGraphicsItemPrivate::get(item)):0; + if (p) { + m_next = p->keyHandler; + p->keyHandler = this; + } +} + +QDeclarativeItemKeyFilter::~QDeclarativeItemKeyFilter() +{ +} + +void QDeclarativeItemKeyFilter::keyPressed(QKeyEvent *event, bool post) +{ + if (m_next) m_next->keyPressed(event, post); +} + +void QDeclarativeItemKeyFilter::keyReleased(QKeyEvent *event, bool post) +{ + if (m_next) m_next->keyReleased(event, post); +} + +void QDeclarativeItemKeyFilter::inputMethodEvent(QInputMethodEvent *event, bool post) +{ + if (m_next) m_next->inputMethodEvent(event, post); +} + +QVariant QDeclarativeItemKeyFilter::inputMethodQuery(Qt::InputMethodQuery query) const +{ + if (m_next) return m_next->inputMethodQuery(query); + return QVariant(); +} + +void QDeclarativeItemKeyFilter::componentComplete() +{ + if (m_next) m_next->componentComplete(); +} + + +/*! + \qmlclass KeyNavigation QDeclarativeKeyNavigationAttached + \ingroup qml-basic-interaction-elements + \since 4.7 + \brief The KeyNavigation attached property supports key navigation by arrow keys. + + Key-based user interfaces commonly allow the use of arrow keys to navigate between + focusable items. The KeyNavigation attached property enables this behavior by providing a + convenient way to specify the item that should gain focus when an arrow or tab key is pressed. + + The following example provides key navigation for a 2x2 grid of items: + + \snippet doc/src/snippets/declarative/keynavigation.qml 0 + + The top-left item initially receives focus by setting \l {Item::}{focus} to + \c true. When an arrow key is pressed, the focus will move to the + appropriate item, as defined by the value that has been set for + the KeyNavigation \l left, \l right, \l up or \l down properties. + + Note that if a KeyNavigation attached property receives the key press and release + events for a requested arrow or tab key, the event is accepted and does not + propagate any further. + + By default, KeyNavigation receives key events after the item to which it is attached. + If the item accepts the key event, the KeyNavigation attached property will not + receive an event for that key. Setting the \l priority property to + \c KeyNavigation.BeforeItem allows the event to be used for key navigation + before the item, rather than after. + + If item to which the focus is switching is not enabled or visible, an attempt will + be made to skip this item and focus on the next. This is possible if there are + a chain of items with the same KeyNavigation handler. If multiple items in a row are not enabled + or visible, they will also be skipped. + + \sa {Keys}{Keys attached property} +*/ + +/*! + \qmlproperty Item KeyNavigation::left + \qmlproperty Item KeyNavigation::right + \qmlproperty Item KeyNavigation::up + \qmlproperty Item KeyNavigation::down + \qmlproperty Item KeyNavigation::tab + \qmlproperty Item KeyNavigation::backtab + + These properties hold the item to assign focus to + when the left, right, up or down cursor keys, or the + tab key are pressed. +*/ + +/*! + \qmlproperty Item KeyNavigation::tab + \qmlproperty Item KeyNavigation::backtab + + These properties hold the item to assign focus to + when the Tab key or Shift+Tab key combination (Backtab) are pressed. +*/ + +QDeclarativeKeyNavigationAttached::QDeclarativeKeyNavigationAttached(QObject *parent) +: QObject(*(new QDeclarativeKeyNavigationAttachedPrivate), parent), + QDeclarativeItemKeyFilter(qobject_cast(parent)) +{ + m_processPost = true; +} + +QDeclarativeKeyNavigationAttached * +QDeclarativeKeyNavigationAttached::qmlAttachedProperties(QObject *obj) +{ + return new QDeclarativeKeyNavigationAttached(obj); +} + +QDeclarativeItem *QDeclarativeKeyNavigationAttached::left() const +{ + Q_D(const QDeclarativeKeyNavigationAttached); + return d->left; +} + +void QDeclarativeKeyNavigationAttached::setLeft(QDeclarativeItem *i) +{ + Q_D(QDeclarativeKeyNavigationAttached); + if (d->left == i) + return; + d->left = i; + emit leftChanged(); +} + +QDeclarativeItem *QDeclarativeKeyNavigationAttached::right() const +{ + Q_D(const QDeclarativeKeyNavigationAttached); + return d->right; +} + +void QDeclarativeKeyNavigationAttached::setRight(QDeclarativeItem *i) +{ + Q_D(QDeclarativeKeyNavigationAttached); + if (d->right == i) + return; + d->right = i; + emit rightChanged(); +} + +QDeclarativeItem *QDeclarativeKeyNavigationAttached::up() const +{ + Q_D(const QDeclarativeKeyNavigationAttached); + return d->up; +} + +void QDeclarativeKeyNavigationAttached::setUp(QDeclarativeItem *i) +{ + Q_D(QDeclarativeKeyNavigationAttached); + if (d->up == i) + return; + d->up = i; + emit upChanged(); +} + +QDeclarativeItem *QDeclarativeKeyNavigationAttached::down() const +{ + Q_D(const QDeclarativeKeyNavigationAttached); + return d->down; +} + +void QDeclarativeKeyNavigationAttached::setDown(QDeclarativeItem *i) +{ + Q_D(QDeclarativeKeyNavigationAttached); + if (d->down == i) + return; + d->down = i; + emit downChanged(); +} + +QDeclarativeItem *QDeclarativeKeyNavigationAttached::tab() const +{ + Q_D(const QDeclarativeKeyNavigationAttached); + return d->tab; +} + +void QDeclarativeKeyNavigationAttached::setTab(QDeclarativeItem *i) +{ + Q_D(QDeclarativeKeyNavigationAttached); + if (d->tab == i) + return; + d->tab = i; + emit tabChanged(); +} + +QDeclarativeItem *QDeclarativeKeyNavigationAttached::backtab() const +{ + Q_D(const QDeclarativeKeyNavigationAttached); + return d->backtab; +} + +void QDeclarativeKeyNavigationAttached::setBacktab(QDeclarativeItem *i) +{ + Q_D(QDeclarativeKeyNavigationAttached); + if (d->backtab == i) + return; + d->backtab = i; + emit backtabChanged(); +} + +/*! + \qmlproperty enumeration KeyNavigation::priority + + This property determines whether the keys are processed before + or after the attached item's own key handling. + + \list + \o KeyNavigation.BeforeItem - process the key events before normal + item key processing. If the event is used for key navigation, it will be accepted and will not + be passed on to the item. + \o KeyNavigation.AfterItem (default) - process the key events after normal item key + handling. If the item accepts the key event it will not be + handled by the KeyNavigation attached property handler. + \endlist +*/ +QDeclarativeKeyNavigationAttached::Priority QDeclarativeKeyNavigationAttached::priority() const +{ + return m_processPost ? AfterItem : BeforeItem; +} + +void QDeclarativeKeyNavigationAttached::setPriority(Priority order) +{ + bool processPost = order == AfterItem; + if (processPost != m_processPost) { + m_processPost = processPost; + emit priorityChanged(); + } +} + +void QDeclarativeKeyNavigationAttached::keyPressed(QKeyEvent *event, bool post) +{ + Q_D(QDeclarativeKeyNavigationAttached); + event->ignore(); + + if (post != m_processPost) { + QDeclarativeItemKeyFilter::keyPressed(event, post); + return; + } + + bool mirror = false; + switch(event->key()) { + case Qt::Key_Left: { + if (QDeclarativeItem *parentItem = qobject_cast(parent())) + mirror = QDeclarativeItemPrivate::get(parentItem)->effectiveLayoutMirror; + QDeclarativeItem* leftItem = mirror ? d->right : d->left; + if (leftItem) { + setFocusNavigation(leftItem, mirror ? "right" : "left"); + event->accept(); + } + break; + } + case Qt::Key_Right: { + if (QDeclarativeItem *parentItem = qobject_cast(parent())) + mirror = QDeclarativeItemPrivate::get(parentItem)->effectiveLayoutMirror; + QDeclarativeItem* rightItem = mirror ? d->left : d->right; + if (rightItem) { + setFocusNavigation(rightItem, mirror ? "left" : "right"); + event->accept(); + } + break; + } + case Qt::Key_Up: + if (d->up) { + setFocusNavigation(d->up, "up"); + event->accept(); + } + break; + case Qt::Key_Down: + if (d->down) { + setFocusNavigation(d->down, "down"); + event->accept(); + } + break; + case Qt::Key_Tab: + if (d->tab) { + setFocusNavigation(d->tab, "tab"); + event->accept(); + } + break; + case Qt::Key_Backtab: + if (d->backtab) { + setFocusNavigation(d->backtab, "backtab"); + event->accept(); + } + break; + default: + break; + } + + if (!event->isAccepted()) QDeclarativeItemKeyFilter::keyPressed(event, post); +} + +void QDeclarativeKeyNavigationAttached::keyReleased(QKeyEvent *event, bool post) +{ + Q_D(QDeclarativeKeyNavigationAttached); + event->ignore(); + + if (post != m_processPost) { + QDeclarativeItemKeyFilter::keyReleased(event, post); + return; + } + + bool mirror = false; + switch(event->key()) { + case Qt::Key_Left: + if (QDeclarativeItem *parentItem = qobject_cast(parent())) + mirror = QDeclarativeItemPrivate::get(parentItem)->effectiveLayoutMirror; + if (mirror ? d->right : d->left) + event->accept(); + break; + case Qt::Key_Right: + if (QDeclarativeItem *parentItem = qobject_cast(parent())) + mirror = QDeclarativeItemPrivate::get(parentItem)->effectiveLayoutMirror; + if (mirror ? d->left : d->right) + event->accept(); + break; + case Qt::Key_Up: + if (d->up) { + event->accept(); + } + break; + case Qt::Key_Down: + if (d->down) { + event->accept(); + } + break; + case Qt::Key_Tab: + if (d->tab) { + event->accept(); + } + break; + case Qt::Key_Backtab: + if (d->backtab) { + event->accept(); + } + break; + default: + break; + } + + if (!event->isAccepted()) QDeclarativeItemKeyFilter::keyReleased(event, post); +} + +void QDeclarativeKeyNavigationAttached::setFocusNavigation(QDeclarativeItem *currentItem, const char *dir) +{ + QDeclarativeItem *initialItem = currentItem; + bool isNextItem = false; + do { + isNextItem = false; + if (currentItem->isVisible() && currentItem->isEnabled()) { + currentItem->setFocus(true); + } else { + QObject *attached = + qmlAttachedPropertiesObject(currentItem, false); + if (attached) { + QDeclarativeItem *tempItem = qvariant_cast(attached->property(dir)); + if (tempItem) { + currentItem = tempItem; + isNextItem = true; + } + } + } + } + while (currentItem != initialItem && isNextItem); +} + +/*! + \qmlclass LayoutMirroring QDeclarativeLayoutMirroringAttached + \since QtQuick 1.1 + \ingroup qml-utility-elements + \brief The LayoutMirroring attached property is used to mirror layout behavior. + + The LayoutMirroring attached property is used to horizontally mirror \l {anchor-layout}{Item anchors}, + \l{Using QML Positioner and Repeater Items}{positioner} elements (such as \l Row and \l Grid) + and views (such as \l GridView and horizontal \l ListView). Mirroring is a visual change: left + anchors become right anchors, and positioner elements like \l Grid and \l Row reverse the + horizontal layout of child items. + + Mirroring is enabled for an item by setting the \l enabled property to true. By default, this + only affects the item itself; setting the \l childrenInherit property to true propagates the mirroring + behavior to all child elements as well. If the \c LayoutMirroring attached property has not been defined + for an item, mirroring is not enabled. + + The following example shows mirroring in action. The \l Row below is specified as being anchored + to the left of its parent. However, since mirroring has been enabled, the anchor is horizontally + reversed and it is now anchored to the right. Also, since items in a \l Row are positioned + from left to right by default, they are now positioned from right to left instead, as demonstrated + by the numbering and opacity of the items: + + \snippet doc/src/snippets/declarative/layoutmirroring.qml 0 + + \image layoutmirroring.png + + Layout mirroring is useful when it is necessary to support both left-to-right and right-to-left + layout versions of an application to target different language areas. The \l childrenInherit + property allows layout mirroring to be applied without manually setting layout configurations + for every item in an application. Keep in mind, however, that mirroring does not affect any + positioning that is defined by the \l Item \l {Item::}{x} coordinate value, so even with + mirroring enabled, it will often be necessary to apply some layout fixes to support the + desired layout direction. Also, it may be necessary to disable the mirroring of individual + child items (by setting \l {enabled}{LayoutMirroring.enabled} to false for such items) if + mirroring is not the desired behavior, or if the child item already implements mirroring in + some custom way. + + See \l {QML Right-to-left User Interfaces} for further details on using \c LayoutMirroring and + other related features to implement right-to-left support for an application. +*/ + +/*! + \qmlproperty bool LayoutMirroring::enabled + + This property holds whether the item's layout is mirrored horizontally. Setting this to true + horizontally reverses \l {anchor-layout}{anchor} settings such that left anchors become right, + and right anchors become left. For \l{Using QML Positioner and Repeater Items}{positioner} elements + (such as \l Row and \l Grid) and view elements (such as \l {GridView}{GridView} and \l {ListView}{ListView}) + this also mirrors the horizontal layout direction of the item. + + The default value is false. +*/ + +/*! + \qmlproperty bool LayoutMirroring::childrenInherit + + This property holds whether the \l {enabled}{LayoutMirroring.enabled} value for this item + is inherited by its children. + + The default value is false. +*/ + +QDeclarativeLayoutMirroringAttached::QDeclarativeLayoutMirroringAttached(QObject *parent) : QObject(parent), itemPrivate(0) +{ + if (QDeclarativeItem *item = qobject_cast(parent)) { + itemPrivate = QDeclarativeItemPrivate::get(item); + itemPrivate->attachedLayoutDirection = this; + } else + qmlInfo(parent) << tr("LayoutDirection attached property only works with Items"); +} + +QDeclarativeLayoutMirroringAttached * QDeclarativeLayoutMirroringAttached::qmlAttachedProperties(QObject *object) +{ + return new QDeclarativeLayoutMirroringAttached(object); +} + +bool QDeclarativeLayoutMirroringAttached::enabled() const +{ + return itemPrivate ? itemPrivate->effectiveLayoutMirror : false; +} + +void QDeclarativeLayoutMirroringAttached::setEnabled(bool enabled) +{ + if (!itemPrivate) + return; + + itemPrivate->isMirrorImplicit = false; + if (enabled != itemPrivate->effectiveLayoutMirror) { + itemPrivate->setLayoutMirror(enabled); + if (itemPrivate->inheritMirrorFromItem) + itemPrivate->resolveLayoutMirror(); + } +} + +void QDeclarativeLayoutMirroringAttached::resetEnabled() +{ + if (itemPrivate && !itemPrivate->isMirrorImplicit) { + itemPrivate->isMirrorImplicit = true; + itemPrivate->resolveLayoutMirror(); + } +} + +bool QDeclarativeLayoutMirroringAttached::childrenInherit() const +{ + return itemPrivate ? itemPrivate->inheritMirrorFromItem : false; +} + +void QDeclarativeLayoutMirroringAttached::setChildrenInherit(bool childrenInherit) { + if (itemPrivate && childrenInherit != itemPrivate->inheritMirrorFromItem) { + itemPrivate->inheritMirrorFromItem = childrenInherit; + itemPrivate->resolveLayoutMirror(); + childrenInheritChanged(); + } +} + +void QDeclarativeItemPrivate::resolveLayoutMirror() +{ + Q_Q(QDeclarativeItem); + if (QDeclarativeItem *parentItem = q->parentItem()) { + QDeclarativeItemPrivate *parentPrivate = QDeclarativeItemPrivate::get(parentItem); + setImplicitLayoutMirror(parentPrivate->inheritedLayoutMirror, parentPrivate->inheritMirrorFromParent); + } else { + setImplicitLayoutMirror(isMirrorImplicit ? false : effectiveLayoutMirror, inheritMirrorFromItem); + } +} + +void QDeclarativeItemPrivate::setImplicitLayoutMirror(bool mirror, bool inherit) +{ + inherit = inherit || inheritMirrorFromItem; + if (!isMirrorImplicit && inheritMirrorFromItem) + mirror = effectiveLayoutMirror; + if (mirror == inheritedLayoutMirror && inherit == inheritMirrorFromParent) + return; + + inheritMirrorFromParent = inherit; + inheritedLayoutMirror = inheritMirrorFromParent ? mirror : false; + + if (isMirrorImplicit) + setLayoutMirror(inherit ? inheritedLayoutMirror : false); + for (int i = 0; i < children.count(); ++i) { + if (QDeclarativeItem *child = qobject_cast(children.at(i))) { + QDeclarativeItemPrivate *childPrivate = QDeclarativeItemPrivate::get(child); + childPrivate->setImplicitLayoutMirror(inheritedLayoutMirror, inheritMirrorFromParent); + } + } +} + +void QDeclarativeItemPrivate::setLayoutMirror(bool mirror) +{ + if (mirror != effectiveLayoutMirror) { + effectiveLayoutMirror = mirror; + if (_anchors) { + _anchors->d_func()->fillChanged(); + _anchors->d_func()->centerInChanged(); + _anchors->d_func()->updateHorizontalAnchors(); + } + mirrorChange(); + if (attachedLayoutDirection) { + emit attachedLayoutDirection->enabledChanged(); + } + } +} + +/*! + \qmlclass Keys QDeclarativeKeysAttached + \ingroup qml-basic-interaction-elements + \since 4.7 + \brief The Keys attached property provides key handling to Items. + + All visual primitives support key handling via the Keys + attached property. Keys can be handled via the onPressed + and onReleased signal properties. + + The signal properties have a \l KeyEvent parameter, named + \e event which contains details of the event. If a key is + handled \e event.accepted should be set to true to prevent the + event from propagating up the item hierarchy. + + \section1 Example Usage + + The following example shows how the general onPressed handler can + be used to test for a certain key; in this case, the left cursor + key: + + \snippet doc/src/snippets/declarative/keys/keys-pressed.qml key item + + Some keys may alternatively be handled via specific signal properties, + for example \e onSelectPressed. These handlers automatically set + \e event.accepted to true. + + \snippet doc/src/snippets/declarative/keys/keys-handler.qml key item + + See \l{Qt::Key}{Qt.Key} for the list of keyboard codes. + + \section1 Key Handling Priorities + + The Keys attached property can be configured to handle key events + before or after the item it is attached to. This makes it possible + to intercept events in order to override an item's default behavior, + or act as a fallback for keys not handled by the item. + + If \l priority is Keys.BeforeItem (default) the order of key event processing is: + + \list 1 + \o Items specified in \c forwardTo + \o specific key handlers, e.g. onReturnPressed + \o onKeyPress, onKeyRelease handlers + \o Item specific key handling, e.g. TextInput key handling + \o parent item + \endlist + + If priority is Keys.AfterItem the order of key event processing is: + + \list 1 + \o Item specific key handling, e.g. TextInput key handling + \o Items specified in \c forwardTo + \o specific key handlers, e.g. onReturnPressed + \o onKeyPress, onKeyRelease handlers + \o parent item + \endlist + + If the event is accepted during any of the above steps, key + propagation stops. + + \sa KeyEvent, {KeyNavigation}{KeyNavigation attached property} +*/ + +/*! + \qmlproperty bool Keys::enabled + + This flags enables key handling if true (default); otherwise + no key handlers will be called. +*/ + +/*! + \qmlproperty enumeration Keys::priority + + This property determines whether the keys are processed before + or after the attached item's own key handling. + + \list + \o Keys.BeforeItem (default) - process the key events before normal + item key processing. If the event is accepted it will not + be passed on to the item. + \o Keys.AfterItem - process the key events after normal item key + handling. If the item accepts the key event it will not be + handled by the Keys attached property handler. + \endlist +*/ + +/*! + \qmlproperty list Keys::forwardTo + + This property provides a way to forward key presses, key releases, and keyboard input + coming from input methods to other items. This can be useful when you want + one item to handle some keys (e.g. the up and down arrow keys), and another item to + handle other keys (e.g. the left and right arrow keys). Once an item that has been + forwarded keys accepts the event it is no longer forwarded to items later in the + list. + + This example forwards key events to two lists: + \qml + Item { + ListView { + id: list1 + // ... + } + ListView { + id: list2 + // ... + } + Keys.forwardTo: [list1, list2] + focus: true + } + \endqml +*/ + +/*! + \qmlsignal Keys::onPressed(KeyEvent event) + + This handler is called when a key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onReleased(KeyEvent event) + + This handler is called when a key has been released. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onDigit0Pressed(KeyEvent event) + + This handler is called when the digit '0' has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onDigit1Pressed(KeyEvent event) + + This handler is called when the digit '1' has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onDigit2Pressed(KeyEvent event) + + This handler is called when the digit '2' has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onDigit3Pressed(KeyEvent event) + + This handler is called when the digit '3' has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onDigit4Pressed(KeyEvent event) + + This handler is called when the digit '4' has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onDigit5Pressed(KeyEvent event) + + This handler is called when the digit '5' has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onDigit6Pressed(KeyEvent event) + + This handler is called when the digit '6' has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onDigit7Pressed(KeyEvent event) + + This handler is called when the digit '7' has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onDigit8Pressed(KeyEvent event) + + This handler is called when the digit '8' has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onDigit9Pressed(KeyEvent event) + + This handler is called when the digit '9' has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onLeftPressed(KeyEvent event) + + This handler is called when the Left arrow has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onRightPressed(KeyEvent event) + + This handler is called when the Right arrow has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onUpPressed(KeyEvent event) + + This handler is called when the Up arrow has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onDownPressed(KeyEvent event) + + This handler is called when the Down arrow has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onTabPressed(KeyEvent event) + + This handler is called when the Tab key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onBacktabPressed(KeyEvent event) + + This handler is called when the Shift+Tab key combination (Backtab) has + been pressed. The \a event parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onAsteriskPressed(KeyEvent event) + + This handler is called when the Asterisk '*' has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onEscapePressed(KeyEvent event) + + This handler is called when the Escape key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onReturnPressed(KeyEvent event) + + This handler is called when the Return key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onEnterPressed(KeyEvent event) + + This handler is called when the Enter key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onDeletePressed(KeyEvent event) + + This handler is called when the Delete key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onSpacePressed(KeyEvent event) + + This handler is called when the Space key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onBackPressed(KeyEvent event) + + This handler is called when the Back key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onCancelPressed(KeyEvent event) + + This handler is called when the Cancel key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onSelectPressed(KeyEvent event) + + This handler is called when the Select key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onYesPressed(KeyEvent event) + + This handler is called when the Yes key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onNoPressed(KeyEvent event) + + This handler is called when the No key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onContext1Pressed(KeyEvent event) + + This handler is called when the Context1 key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onContext2Pressed(KeyEvent event) + + This handler is called when the Context2 key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onContext3Pressed(KeyEvent event) + + This handler is called when the Context3 key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onContext4Pressed(KeyEvent event) + + This handler is called when the Context4 key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onCallPressed(KeyEvent event) + + This handler is called when the Call key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onHangupPressed(KeyEvent event) + + This handler is called when the Hangup key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onFlipPressed(KeyEvent event) + + This handler is called when the Flip key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onMenuPressed(KeyEvent event) + + This handler is called when the Menu key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onVolumeUpPressed(KeyEvent event) + + This handler is called when the VolumeUp key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onVolumeDownPressed(KeyEvent event) + + This handler is called when the VolumeDown key has been pressed. The \a event + parameter provides information about the event. +*/ + +const QDeclarativeKeysAttached::SigMap QDeclarativeKeysAttached::sigMap[] = { + { Qt::Key_Left, "leftPressed" }, + { Qt::Key_Right, "rightPressed" }, + { Qt::Key_Up, "upPressed" }, + { Qt::Key_Down, "downPressed" }, + { Qt::Key_Tab, "tabPressed" }, + { Qt::Key_Backtab, "backtabPressed" }, + { Qt::Key_Asterisk, "asteriskPressed" }, + { Qt::Key_NumberSign, "numberSignPressed" }, + { Qt::Key_Escape, "escapePressed" }, + { Qt::Key_Return, "returnPressed" }, + { Qt::Key_Enter, "enterPressed" }, + { Qt::Key_Delete, "deletePressed" }, + { Qt::Key_Space, "spacePressed" }, + { Qt::Key_Back, "backPressed" }, + { Qt::Key_Cancel, "cancelPressed" }, + { Qt::Key_Select, "selectPressed" }, + { Qt::Key_Yes, "yesPressed" }, + { Qt::Key_No, "noPressed" }, + { Qt::Key_Context1, "context1Pressed" }, + { Qt::Key_Context2, "context2Pressed" }, + { Qt::Key_Context3, "context3Pressed" }, + { Qt::Key_Context4, "context4Pressed" }, + { Qt::Key_Call, "callPressed" }, + { Qt::Key_Hangup, "hangupPressed" }, + { Qt::Key_Flip, "flipPressed" }, + { Qt::Key_Menu, "menuPressed" }, + { Qt::Key_VolumeUp, "volumeUpPressed" }, + { Qt::Key_VolumeDown, "volumeDownPressed" }, + { 0, 0 } +}; + +bool QDeclarativeKeysAttachedPrivate::isConnected(const char *signalName) +{ + return isSignalConnected(signalIndex(signalName)); +} + +QDeclarativeKeysAttached::QDeclarativeKeysAttached(QObject *parent) +: QObject(*(new QDeclarativeKeysAttachedPrivate), parent), + QDeclarativeItemKeyFilter(qobject_cast(parent)) +{ + Q_D(QDeclarativeKeysAttached); + m_processPost = false; + d->item = qobject_cast(parent); +} + +QDeclarativeKeysAttached::~QDeclarativeKeysAttached() +{ +} + +QDeclarativeKeysAttached::Priority QDeclarativeKeysAttached::priority() const +{ + return m_processPost ? AfterItem : BeforeItem; +} + +void QDeclarativeKeysAttached::setPriority(Priority order) +{ + bool processPost = order == AfterItem; + if (processPost != m_processPost) { + m_processPost = processPost; + emit priorityChanged(); + } +} + +void QDeclarativeKeysAttached::componentComplete() +{ + Q_D(QDeclarativeKeysAttached); + if (d->item) { + for (int ii = 0; ii < d->targets.count(); ++ii) { + QGraphicsItem *targetItem = d->finalFocusProxy(d->targets.at(ii)); + if (targetItem && (targetItem->flags() & QGraphicsItem::ItemAcceptsInputMethod)) { + d->item->setFlag(QGraphicsItem::ItemAcceptsInputMethod); + break; + } + } + } +} + +void QDeclarativeKeysAttached::keyPressed(QKeyEvent *event, bool post) +{ + Q_D(QDeclarativeKeysAttached); + if (post != m_processPost || !d->enabled || d->inPress) { + event->ignore(); + QDeclarativeItemKeyFilter::keyPressed(event, post); + return; + } + + // first process forwards + if (d->item && d->item->scene()) { + d->inPress = true; + for (int ii = 0; ii < d->targets.count(); ++ii) { + QGraphicsItem *i = d->finalFocusProxy(d->targets.at(ii)); + if (i && i->isVisible()) { + d->item->scene()->sendEvent(i, event); + if (event->isAccepted()) { + d->inPress = false; + return; + } + } + } + d->inPress = false; + } + + QDeclarativeKeyEvent ke(*event); + QByteArray keySignal = keyToSignal(event->key()); + if (!keySignal.isEmpty()) { + keySignal += "(QDeclarativeKeyEvent*)"; + if (d->isConnected(keySignal)) { + // If we specifically handle a key then default to accepted + ke.setAccepted(true); + int idx = QDeclarativeKeysAttached::staticMetaObject.indexOfSignal(keySignal); + metaObject()->method(idx).invoke(this, Qt::DirectConnection, Q_ARG(QDeclarativeKeyEvent*, &ke)); + } + } + if (!ke.isAccepted()) + emit pressed(&ke); + event->setAccepted(ke.isAccepted()); + + if (!event->isAccepted()) QDeclarativeItemKeyFilter::keyPressed(event, post); +} + +void QDeclarativeKeysAttached::keyReleased(QKeyEvent *event, bool post) +{ + Q_D(QDeclarativeKeysAttached); + if (post != m_processPost || !d->enabled || d->inRelease) { + event->ignore(); + QDeclarativeItemKeyFilter::keyReleased(event, post); + return; + } + + if (d->item && d->item->scene()) { + d->inRelease = true; + for (int ii = 0; ii < d->targets.count(); ++ii) { + QGraphicsItem *i = d->finalFocusProxy(d->targets.at(ii)); + if (i && i->isVisible()) { + d->item->scene()->sendEvent(i, event); + if (event->isAccepted()) { + d->inRelease = false; + return; + } + } + } + d->inRelease = false; + } + + QDeclarativeKeyEvent ke(*event); + emit released(&ke); + event->setAccepted(ke.isAccepted()); + + if (!event->isAccepted()) QDeclarativeItemKeyFilter::keyReleased(event, post); +} + +void QDeclarativeKeysAttached::inputMethodEvent(QInputMethodEvent *event, bool post) +{ + Q_D(QDeclarativeKeysAttached); + if (post == m_processPost && d->item && !d->inIM && d->item->scene()) { + d->inIM = true; + for (int ii = 0; ii < d->targets.count(); ++ii) { + QGraphicsItem *i = d->finalFocusProxy(d->targets.at(ii)); + if (i && i->isVisible() && (i->flags() & QGraphicsItem::ItemAcceptsInputMethod)) { + d->item->scene()->sendEvent(i, event); + if (event->isAccepted()) { + d->imeItem = i; + d->inIM = false; + return; + } + } + } + d->inIM = false; + } + if (!event->isAccepted()) QDeclarativeItemKeyFilter::inputMethodEvent(event, post); +} + +class QDeclarativeItemAccessor : public QGraphicsItem +{ +public: + QVariant doInputMethodQuery(Qt::InputMethodQuery query) const { + return QGraphicsItem::inputMethodQuery(query); + } +}; + +QVariant QDeclarativeKeysAttached::inputMethodQuery(Qt::InputMethodQuery query) const +{ + Q_D(const QDeclarativeKeysAttached); + if (d->item) { + for (int ii = 0; ii < d->targets.count(); ++ii) { + QGraphicsItem *i = d->finalFocusProxy(d->targets.at(ii)); + if (i && i->isVisible() && (i->flags() & QGraphicsItem::ItemAcceptsInputMethod) && i == d->imeItem) { //### how robust is i == d->imeItem check? + QVariant v = static_cast(i)->doInputMethodQuery(query); + if (v.userType() == QVariant::RectF) + v = d->item->mapRectFromItem(i, v.toRectF()); //### cost? + return v; + } + } + } + return QDeclarativeItemKeyFilter::inputMethodQuery(query); +} + +QDeclarativeKeysAttached *QDeclarativeKeysAttached::qmlAttachedProperties(QObject *obj) +{ + return new QDeclarativeKeysAttached(obj); +} + +/*! + \class QDeclarativeItem + \since 4.7 + \brief The QDeclarativeItem class provides the most basic of all visual items in QML. + + All visual items in Qt Declarative inherit from QDeclarativeItem. Although QDeclarativeItem + has no visual appearance, it defines all the properties that are + common across visual items - such as the x and y position, the + width and height, \l {anchor-layout}{anchoring} and key handling. + + You can subclass QDeclarativeItem to provide your own custom visual item that inherits + these features. Note that, because it does not draw anything, QDeclarativeItem sets the + QGraphicsItem::ItemHasNoContents flag. If you subclass QDeclarativeItem to create a visual + item, you will need to unset this flag. + +*/ + +/*! + \qmlclass Item QDeclarativeItem + \ingroup qml-basic-visual-elements + \since 4.7 + \brief The Item is the most basic of all visual items in QML. + + All visual items in Qt Declarative inherit from Item. Although Item + has no visual appearance, it defines all the properties that are + common across visual items - such as the x and y position, the + width and height, \l {anchor-layout}{anchoring} and key handling. + + Item is also useful for grouping items together. + + \qml + Item { + Image { + source: "tile.png" + } + Image { + x: 80 + width: 100 + height: 100 + source: "tile.png" + } + Image { + x: 190 + width: 100 + height: 100 + fillMode: Image.Tile + source: "tile.png" + } + } + \endqml + + + \section1 Key Handling + + Key handling is available to all Item-based visual elements via the \l {Keys}{Keys} + attached property. The \e Keys attached property provides basic handlers such + as \l {Keys::onPressed}{onPressed} and \l {Keys::onReleased}{onReleased}, + as well as handlers for specific keys, such as + \l {Keys::onCancelPressed}{onCancelPressed}. The example below + assigns \l {qmlfocus}{focus} to the item and handles + the Left key via the general \e onPressed handler and the Select key via the + onSelectPressed handler: + + \qml + Item { + focus: true + Keys.onPressed: { + if (event.key == Qt.Key_Left) { + console.log("move left"); + event.accepted = true; + } + } + Keys.onSelectPressed: console.log("Selected"); + } + \endqml + + See the \l {Keys}{Keys} attached property for detailed documentation. + + \section1 Layout Mirroring + + Item layouts can be mirrored using the \l {LayoutMirroring}{LayoutMirroring} attached property. + +*/ + +/*! + \fn void QDeclarativeItem::childrenRectChanged(const QRectF &) + \internal +*/ + +/*! + \fn void QDeclarativeItem::baselineOffsetChanged(qreal) + \internal +*/ + +/*! + \fn void QDeclarativeItem::stateChanged(const QString &state) + \internal +*/ + +/*! + \fn void QDeclarativeItem::parentChanged(QDeclarativeItem *) + \internal +*/ + +/*! + \fn void QDeclarativeItem::smoothChanged(bool) + \internal +*/ + +/*! + \fn void QDeclarativeItem::clipChanged(bool) + \internal +*/ + +/*! \fn void QDeclarativeItem::transformOriginChanged(TransformOrigin) + \internal +*/ + +/*! + \fn void QDeclarativeItem::focusChanged(bool) + \internal +*/ + +/*! + \fn void QDeclarativeItem::activeFocusChanged(bool) + \internal +*/ + +// ### Must fix +struct RegisterAnchorLineAtStartup { + RegisterAnchorLineAtStartup() { + qRegisterMetaType("QDeclarativeAnchorLine"); + } +}; +static RegisterAnchorLineAtStartup registerAnchorLineAtStartup; + + +/*! + \fn QDeclarativeItem::QDeclarativeItem(QDeclarativeItem *parent) + + Constructs a QDeclarativeItem with the given \a parent. +*/ +QDeclarativeItem::QDeclarativeItem(QDeclarativeItem* parent) + : QGraphicsObject(*(new QDeclarativeItemPrivate), parent, 0) +{ + Q_D(QDeclarativeItem); + d->init(parent); +} + +/*! \internal +*/ +QDeclarativeItem::QDeclarativeItem(QDeclarativeItemPrivate &dd, QDeclarativeItem *parent) + : QGraphicsObject(dd, parent, 0) +{ + Q_D(QDeclarativeItem); + d->init(parent); +} + +/*! + Destroys the QDeclarativeItem. +*/ +QDeclarativeItem::~QDeclarativeItem() +{ + Q_D(QDeclarativeItem); + for (int ii = 0; ii < d->changeListeners.count(); ++ii) { + QDeclarativeAnchorsPrivate *anchor = d->changeListeners.at(ii).listener->anchorPrivate(); + if (anchor) + anchor->clearItem(this); + } + if (!d->parent || (parentItem() && !parentItem()->QGraphicsItem::d_ptr->inDestructor)) { + for (int ii = 0; ii < d->changeListeners.count(); ++ii) { + QDeclarativeAnchorsPrivate *anchor = d->changeListeners.at(ii).listener->anchorPrivate(); + if (anchor && anchor->item && anchor->item->parentItem() != this) //child will be deleted anyway + anchor->updateOnComplete(); + } + } + for(int ii = 0; ii < d->changeListeners.count(); ++ii) { + const QDeclarativeItemPrivate::ChangeListener &change = d->changeListeners.at(ii); + if (change.types & QDeclarativeItemPrivate::Destroyed) + change.listener->itemDestroyed(this); + } + d->changeListeners.clear(); + delete d->_anchorLines; d->_anchorLines = 0; + delete d->_anchors; d->_anchors = 0; + delete d->_stateGroup; d->_stateGroup = 0; + delete d->_contents; d->_contents = 0; +} + +/*! + \qmlproperty enumeration Item::transformOrigin + This property holds the origin point around which scale and rotation transform. + + Nine transform origins are available, as shown in the image below. + + \image declarative-transformorigin.png + + This example rotates an image around its bottom-right corner. + \qml + Image { + source: "myimage.png" + transformOrigin: Item.BottomRight + rotation: 45 + } + \endqml + + The default transform origin is \c Item.Center. + + To set an arbitrary transform origin point use the \l Scale or \l Rotation + transform elements. +*/ + +/*! + \qmlproperty Item Item::parent + This property holds the parent of the item. +*/ + +/*! + \property QDeclarativeItem::parent + This property holds the parent of the item. +*/ +void QDeclarativeItem::setParentItem(QDeclarativeItem *parent) +{ + QGraphicsObject::setParentItem(parent); +} + +/*! + Returns the QDeclarativeItem parent of this item. +*/ +QDeclarativeItem *QDeclarativeItem::parentItem() const +{ + return qobject_cast(QGraphicsObject::parentItem()); +} + +/*! + \qmlproperty real Item::childrenRect.x + \qmlproperty real Item::childrenRect.y + \qmlproperty real Item::childrenRect.width + \qmlproperty real Item::childrenRect.height + + The childrenRect properties allow an item access to the geometry of its + children. This property is useful if you have an item that needs to be + sized to fit its children. +*/ + + +/*! + \qmlproperty list Item::children + \qmlproperty list Item::resources + + The children property contains the list of visual children of this item. + The resources property contains non-visual resources that you want to + reference by name. + + Generally you can rely on Item's default property to handle all this for + you, but it can come in handy in some cases. + + \qml + Item { + children: [ + Text {}, + Rectangle {} + ] + resources: [ + Component { + id: myComponent + Text {} + } + ] + } + \endqml +*/ + +/*! + Returns true if construction of the QML component is complete; otherwise + returns false. + + It is often desirable to delay some processing until the component is + completed. + + \sa componentComplete() +*/ +bool QDeclarativeItem::isComponentComplete() const +{ + Q_D(const QDeclarativeItem); + return d->componentComplete; +} + +void QDeclarativeItemPrivate::data_append(QDeclarativeListProperty *prop, QObject *o) +{ + if (!o) + return; + + QDeclarativeItem *that = static_cast(prop->object); + + // This test is measurably (albeit only slightly) faster than qobject_cast<>() + const QMetaObject *mo = o->metaObject(); + while (mo && mo != &QGraphicsObject::staticMetaObject) mo = mo->d.superdata; + + if (mo) { + QGraphicsObject *graphicsObject = static_cast(o); + QDeclarativeItemPrivate *contentItemPrivate = static_cast(QGraphicsItemPrivate::get(graphicsObject)); + if (contentItemPrivate->componentComplete) { + graphicsObject->setParentItem(that); + } else { + contentItemPrivate->setParentItemHelper(that, /*newParentVariant=*/0, /*thisPointerVariant=*/0); + } + } else { + o->setParent(that); + } +} + +static inline int children_count_helper(QDeclarativeListProperty *prop) +{ + QGraphicsItemPrivate *d = QGraphicsItemPrivate::get(static_cast(prop->object)); + return d->children.count(); +} + +static inline QObject *children_at_helper(QDeclarativeListProperty *prop, int index) +{ + QGraphicsItemPrivate *d = QGraphicsItemPrivate::get(static_cast(prop->object)); + if (index >= 0 && index < d->children.count()) + return d->children.at(index)->toGraphicsObject(); + else + return 0; +} + +static inline void children_clear_helper(QDeclarativeListProperty *prop) +{ + QDeclarativeItemPrivate *d = static_cast(QGraphicsItemPrivate::get(static_cast(prop->object))); + int childCount = d->children.count(); + if (d->componentComplete) { + for (int index = 0 ;index < childCount; index++) + d->children.at(0)->setParentItem(0); + } else { + for (int index = 0 ;index < childCount; index++) + QGraphicsItemPrivate::get(d->children.at(0))->setParentItemHelper(0, /*newParentVariant=*/0, /*thisPointerVariant=*/0); + } +} + +int QDeclarativeItemPrivate::data_count(QDeclarativeListProperty *prop) +{ + return resources_count(prop) + children_count_helper(prop); +} + +QObject *QDeclarativeItemPrivate::data_at(QDeclarativeListProperty *prop, int i) +{ + int resourcesCount = resources_count(prop); + if (i < resourcesCount) + return resources_at(prop, i); + const int j = i - resourcesCount; + if (j < children_count_helper(prop)) + return children_at_helper(prop, j); + return 0; +} + +void QDeclarativeItemPrivate::data_clear(QDeclarativeListProperty *prop) +{ + resources_clear(prop); + children_clear_helper(prop); +} + +QObject *QDeclarativeItemPrivate::resources_at(QDeclarativeListProperty *prop, int index) +{ + const QObjectList children = prop->object->children(); + if (index < children.count()) + return children.at(index); + else + return 0; +} + +void QDeclarativeItemPrivate::resources_append(QDeclarativeListProperty *prop, QObject *o) +{ + o->setParent(prop->object); +} + +int QDeclarativeItemPrivate::resources_count(QDeclarativeListProperty *prop) +{ + return prop->object->children().count(); +} + +void QDeclarativeItemPrivate::resources_clear(QDeclarativeListProperty *prop) +{ + const QObjectList children = prop->object->children(); + for (int index = 0; index < children.count(); index++) + children.at(index)->setParent(0); +} + +int QDeclarativeItemPrivate::transform_count(QDeclarativeListProperty *list) +{ + QGraphicsObject *object = qobject_cast(list->object); + if (object) { + QGraphicsItemPrivate *d = QGraphicsItemPrivate::get(object); + return d->transformData ? d->transformData->graphicsTransforms.size() : 0; + } else { + return 0; + } +} + +void QDeclarativeItemPrivate::transform_append(QDeclarativeListProperty *list, QGraphicsTransform *item) +{ + QGraphicsObject *object = qobject_cast(list->object); + if (object && item) // QGraphicsItem applies the list in the wrong order, so we prepend. + QGraphicsItemPrivate::get(object)->prependGraphicsTransform(item); +} + +QGraphicsTransform *QDeclarativeItemPrivate::transform_at(QDeclarativeListProperty *list, int idx) +{ + QGraphicsObject *object = qobject_cast(list->object); + if (object) { + QGraphicsItemPrivate *d = QGraphicsItemPrivate::get(object); + if (!d->transformData) + return 0; + return d->transformData->graphicsTransforms.at(idx); + } else { + return 0; + } +} + +void QDeclarativeItemPrivate::transform_clear(QDeclarativeListProperty *list) +{ + QGraphicsObject *object = qobject_cast(list->object); + if (object) { + QGraphicsItemPrivate *d = QGraphicsItemPrivate::get(object); + if (!d->transformData) + return; + object->setTransformations(QList()); + } +} + +void QDeclarativeItemPrivate::parentProperty(QObject *o, void *rv, QDeclarativeNotifierEndpoint *e) +{ + QDeclarativeItem *item = static_cast(o); + if (e) + e->connect(&item->d_func()->parentNotifier); + *((QDeclarativeItem **)rv) = item->parentItem(); +} + +/*! + \qmlproperty list Item::data + \default + + The data property allows you to freely mix visual children and resources + in an item. If you assign a visual item to the data list it becomes + a child and if you assign any other object type, it is added as a resource. + + So you can write: + \qml + Item { + Text {} + Rectangle {} + Timer {} + } + \endqml + + instead of: + \qml + Item { + children: [ + Text {}, + Rectangle {} + ] + resources: [ + Timer {} + ] + } + \endqml + + data is a behind-the-scenes property: you should never need to explicitly + specify it. + */ + +QDeclarativeListProperty QDeclarativeItemPrivate::data() +{ + return QDeclarativeListProperty(q_func(), 0, QDeclarativeItemPrivate::data_append, + QDeclarativeItemPrivate::data_count, + QDeclarativeItemPrivate::data_at, + QDeclarativeItemPrivate::data_clear + ); +} + +/*! + \property QDeclarativeItem::childrenRect + \brief The geometry of an item's children. + + This property holds the (collective) position and size of the item's children. +*/ +QRectF QDeclarativeItem::childrenRect() +{ + Q_D(QDeclarativeItem); + if (!d->_contents) { + d->_contents = new QDeclarativeContents(this); + if (d->componentComplete) + d->_contents->complete(); + } + return d->_contents->rectF(); +} + +bool QDeclarativeItem::clip() const +{ + return flags() & ItemClipsChildrenToShape; +} + +void QDeclarativeItem::setClip(bool c) +{ + if (clip() == c) + return; + setFlag(ItemClipsChildrenToShape, c); + emit clipChanged(c); +} + +/*! + \qmlproperty real Item::x + \qmlproperty real Item::y + \qmlproperty real Item::width + \qmlproperty real Item::height + + Defines the item's position and size relative to its parent. + + \qml + Item { x: 100; y: 100; width: 100; height: 100 } + \endqml + */ + +/*! + \qmlproperty real Item::z + + Sets the stacking order of sibling items. By default the stacking order is 0. + + Items with a higher stacking value are drawn on top of siblings with a + lower stacking order. Items with the same stacking value are drawn + bottom up in the order they appear. Items with a negative stacking + value are drawn under their parent's content. + + The following example shows the various effects of stacking order. + + \table + \row + \o \image declarative-item_stacking1.png + \o Same \c z - later children above earlier children: + \qml + Item { + Rectangle { + color: "red" + width: 100; height: 100 + } + Rectangle { + color: "blue" + x: 50; y: 50; width: 100; height: 100 + } + } + \endqml + \row + \o \image declarative-item_stacking2.png + \o Higher \c z on top: + \qml + Item { + Rectangle { + z: 1 + color: "red" + width: 100; height: 100 + } + Rectangle { + color: "blue" + x: 50; y: 50; width: 100; height: 100 + } + } + \endqml + \row + \o \image declarative-item_stacking3.png + \o Same \c z - children above parents: + \qml + Item { + Rectangle { + color: "red" + width: 100; height: 100 + Rectangle { + color: "blue" + x: 50; y: 50; width: 100; height: 100 + } + } + } + \endqml + \row + \o \image declarative-item_stacking4.png + \o Lower \c z below: + \qml + Item { + Rectangle { + color: "red" + width: 100; height: 100 + Rectangle { + z: -1 + color: "blue" + x: 50; y: 50; width: 100; height: 100 + } + } + } + \endqml + \endtable + */ + +/*! + \qmlproperty bool Item::visible + + This property holds whether the item is visible. By default this is true. + + Setting this property directly affects the \c visible value of child + items. When set to \c false, the \c visible values of all child items also + become \c false. When set to \c true, the \c visible values of child items + are returned to \c true, unless they have explicitly been set to \c false. + + (Because of this flow-on behavior, using the \c visible property may not + have the intended effect if a property binding should only respond to + explicit property changes. In such cases it may be better to use the + \l opacity property instead.) + + Setting this property to \c false automatically causes \l focus to be set + to \c false, and this item will longer receive mouse and keyboard events. + (In contrast, setting the \l opacity to 0 does not affect the \l focus + property and the receiving of key events.) + + \note This property's value is only affected by changes to this property or + the parent's \c visible property. It does not change, for example, if this + item moves off-screen, or if the \l opacity changes to 0. +*/ + + +/*! + This function is called to handle this item's changes in + geometry from \a oldGeometry to \a newGeometry. If the two + geometries are the same, it doesn't do anything. + */ +void QDeclarativeItem::geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry) +{ + Q_D(QDeclarativeItem); + + if (d->_anchors) + d->_anchors->d_func()->updateMe(); + + if (transformOrigin() != QDeclarativeItem::TopLeft + && (newGeometry.width() != oldGeometry.width() || newGeometry.height() != oldGeometry.height())) { + if (d->transformData) { + QPointF origin = d->computeTransformOrigin(); + if (transformOriginPoint() != origin) + setTransformOriginPoint(origin); + } else { + d->transformOriginDirty = true; + } + } + + for(int ii = 0; ii < d->changeListeners.count(); ++ii) { + const QDeclarativeItemPrivate::ChangeListener &change = d->changeListeners.at(ii); + if (change.types & QDeclarativeItemPrivate::Geometry) + change.listener->itemGeometryChanged(this, newGeometry, oldGeometry); + } + + if (newGeometry.width() != oldGeometry.width()) + emit widthChanged(); + if (newGeometry.height() != oldGeometry.height()) + emit heightChanged(); +} + +void QDeclarativeItemPrivate::removeItemChangeListener(QDeclarativeItemChangeListener *listener, ChangeTypes types) +{ + ChangeListener change(listener, types); + changeListeners.removeOne(change); +} + +/*! \internal */ +void QDeclarativeItem::keyPressEvent(QKeyEvent *event) +{ + Q_D(QDeclarativeItem); + keyPressPreHandler(event); + if (event->isAccepted()) + return; + if (d->keyHandler) + d->keyHandler->keyPressed(event, true); + else + event->ignore(); +} + +/*! \internal */ +void QDeclarativeItem::keyReleaseEvent(QKeyEvent *event) +{ + Q_D(QDeclarativeItem); + keyReleasePreHandler(event); + if (event->isAccepted()) + return; + if (d->keyHandler) + d->keyHandler->keyReleased(event, true); + else + event->ignore(); +} + +/*! \internal */ +void QDeclarativeItem::inputMethodEvent(QInputMethodEvent *event) +{ + Q_D(QDeclarativeItem); + inputMethodPreHandler(event); + if (event->isAccepted()) + return; + if (d->keyHandler) + d->keyHandler->inputMethodEvent(event, true); + else + event->ignore(); +} + +/*! \internal */ +QVariant QDeclarativeItem::inputMethodQuery(Qt::InputMethodQuery query) const +{ + Q_D(const QDeclarativeItem); + QVariant v; + if (d->keyHandler) + v = d->keyHandler->inputMethodQuery(query); + + if (!v.isValid()) + v = QGraphicsObject::inputMethodQuery(query); + + return v; +} + +/*! + \internal + */ +void QDeclarativeItem::keyPressPreHandler(QKeyEvent *event) +{ + Q_D(QDeclarativeItem); + if (d->keyHandler && !d->doneEventPreHandler) + d->keyHandler->keyPressed(event, false); + else + event->ignore(); + d->doneEventPreHandler = true; +} + +/*! + \internal + */ +void QDeclarativeItem::keyReleasePreHandler(QKeyEvent *event) +{ + Q_D(QDeclarativeItem); + if (d->keyHandler && !d->doneEventPreHandler) + d->keyHandler->keyReleased(event, false); + else + event->ignore(); + d->doneEventPreHandler = true; +} + +/*! + \internal + */ +void QDeclarativeItem::inputMethodPreHandler(QInputMethodEvent *event) +{ + Q_D(QDeclarativeItem); + if (d->keyHandler && !d->doneEventPreHandler) + d->keyHandler->inputMethodEvent(event, false); + else + event->ignore(); + d->doneEventPreHandler = true; +} + +/*! + \internal +*/ +QDeclarativeAnchorLine QDeclarativeItemPrivate::left() const +{ + return anchorLines()->left; +} + +/*! + \internal +*/ +QDeclarativeAnchorLine QDeclarativeItemPrivate::right() const +{ + return anchorLines()->right; +} + +/*! + \internal +*/ +QDeclarativeAnchorLine QDeclarativeItemPrivate::horizontalCenter() const +{ + return anchorLines()->hCenter; +} + +/*! + \internal +*/ +QDeclarativeAnchorLine QDeclarativeItemPrivate::top() const +{ + return anchorLines()->top; +} + +/*! + \internal +*/ +QDeclarativeAnchorLine QDeclarativeItemPrivate::bottom() const +{ + return anchorLines()->bottom; +} + +/*! + \internal +*/ +QDeclarativeAnchorLine QDeclarativeItemPrivate::verticalCenter() const +{ + return anchorLines()->vCenter; +} + + +/*! + \internal +*/ +QDeclarativeAnchorLine QDeclarativeItemPrivate::baseline() const +{ + return anchorLines()->baseline; +} + +/*! + \qmlproperty AnchorLine Item::anchors.top + \qmlproperty AnchorLine Item::anchors.bottom + \qmlproperty AnchorLine Item::anchors.left + \qmlproperty AnchorLine Item::anchors.right + \qmlproperty AnchorLine Item::anchors.horizontalCenter + \qmlproperty AnchorLine Item::anchors.verticalCenter + \qmlproperty AnchorLine Item::anchors.baseline + + \qmlproperty Item Item::anchors.fill + \qmlproperty Item Item::anchors.centerIn + + \qmlproperty real Item::anchors.margins + \qmlproperty real Item::anchors.topMargin + \qmlproperty real Item::anchors.bottomMargin + \qmlproperty real Item::anchors.leftMargin + \qmlproperty real Item::anchors.rightMargin + \qmlproperty real Item::anchors.horizontalCenterOffset + \qmlproperty real Item::anchors.verticalCenterOffset + \qmlproperty real Item::anchors.baselineOffset + + \qmlproperty bool Item::anchors.mirrored + + Anchors provide a way to position an item by specifying its + relationship with other items. + + Margins apply to top, bottom, left, right, and fill anchors. + The \c anchors.margins property can be used to set all of the various margins at once, to the same value. + Note that margins are anchor-specific and are not applied if an item does not + use anchors. + + Offsets apply for horizontal center, vertical center, and baseline anchors. + + \table + \row + \o \image declarative-anchors_example.png + \o Text anchored to Image, horizontally centered and vertically below, with a margin. + \qml + Item { + Image { + id: pic + // ... + } + Text { + id: label + anchors.horizontalCenter: pic.horizontalCenter + anchors.top: pic.bottom + anchors.topMargin: 5 + // ... + } + } + \endqml + \row + \o \image declarative-anchors_example2.png + \o + Left of Text anchored to right of Image, with a margin. The y + property of both defaults to 0. + + \qml + Item { + Image { + id: pic + // ... + } + Text { + id: label + anchors.left: pic.right + anchors.leftMargin: 5 + // ... + } + } + \endqml + \endtable + + \c anchors.fill provides a convenient way for one item to have the + same geometry as another item, and is equivalent to connecting all + four directional anchors. + + To clear an anchor value, set it to \c undefined. + + \c anchors.mirrored returns true it the layout has been \l {LayoutMirroring}{mirrored}. + + \note You can only anchor an item to siblings or a parent. + + For more information see \l {anchor-layout}{Anchor Layouts}. +*/ + +/*! + \property QDeclarativeItem::baselineOffset + \brief The position of the item's baseline in local coordinates. + + The baseline of a \l Text item is the imaginary line on which the text + sits. Controls containing text usually set their baseline to the + baseline of their text. + + For non-text items, a default baseline offset of 0 is used. +*/ +qreal QDeclarativeItem::baselineOffset() const +{ + Q_D(const QDeclarativeItem); + if (!d->baselineOffset.isValid()) { + return 0.0; + } else + return d->baselineOffset; +} + +void QDeclarativeItem::setBaselineOffset(qreal offset) +{ + Q_D(QDeclarativeItem); + if (offset == d->baselineOffset) + return; + + d->baselineOffset = offset; + + for(int ii = 0; ii < d->changeListeners.count(); ++ii) { + const QDeclarativeItemPrivate::ChangeListener &change = d->changeListeners.at(ii); + if (change.types & QDeclarativeItemPrivate::Geometry) { + QDeclarativeAnchorsPrivate *anchor = change.listener->anchorPrivate(); + if (anchor) + anchor->updateVerticalAnchors(); + } + } + emit baselineOffsetChanged(offset); +} + +/*! + \qmlproperty real Item::rotation + This property holds the rotation of the item in degrees clockwise. + + This specifies how many degrees to rotate the item around its transformOrigin. + The default rotation is 0 degrees (i.e. not rotated at all). + + \table + \row + \o \image declarative-rotation.png + \o + \qml + Rectangle { + color: "blue" + width: 100; height: 100 + Rectangle { + color: "red" + x: 25; y: 25; width: 50; height: 50 + rotation: 30 + } + } + \endqml + \endtable + + \sa transform, Rotation +*/ + +/*! + \qmlproperty real Item::scale + This property holds the scale of the item. + + A scale of less than 1 means the item will be displayed smaller than + normal, and a scale of greater than 1 means the item will be + displayed larger than normal. A negative scale means the item will + be mirrored. + + By default, items are displayed at a scale of 1 (i.e. at their + normal size). + + Scaling is from the item's transformOrigin. + + \table + \row + \o \image declarative-scale.png + \o + \qml + Rectangle { + color: "blue" + width: 100; height: 100 + Rectangle { + color: "green" + width: 25; height: 25 + } + Rectangle { + color: "red" + x: 25; y: 25; width: 50; height: 50 + scale: 1.4 + } + } + \endqml + \endtable + + \sa transform, Scale +*/ + +/*! + \qmlproperty real Item::opacity + + This property holds the opacity of the item. Opacity is specified as a + number between 0 (fully transparent) and 1 (fully opaque). The default is 1. + + When this property is set, the specified opacity is also applied + individually to child items. In almost all cases this is what you want, + but in some cases it may produce undesired results. For example in the + second set of rectangles below, the red rectangle has specified an opacity + of 0.5, which affects the opacity of its blue child rectangle even though + the child has not specified an opacity. + + \table + \row + \o \image declarative-item_opacity1.png + \o + \qml + Item { + Rectangle { + color: "red" + width: 100; height: 100 + Rectangle { + color: "blue" + x: 50; y: 50; width: 100; height: 100 + } + } + } + \endqml + \row + \o \image declarative-item_opacity2.png + \o + \qml + Item { + Rectangle { + opacity: 0.5 + color: "red" + width: 100; height: 100 + Rectangle { + color: "blue" + x: 50; y: 50; width: 100; height: 100 + } + } + } + \endqml + \endtable + + If an item's opacity is set to 0, the item will no longer receive mouse + events, but will continue to receive key events and will retain the keyboard + \l focus if it has been set. (In contrast, setting the \l visible property + to \c false stops both mouse and keyboard events, and also removes focus + from the item.) +*/ + +/*! + Returns a value indicating whether mouse input should + remain with this item exclusively. + + \sa setKeepMouseGrab() + */ +bool QDeclarativeItem::keepMouseGrab() const +{ + Q_D(const QDeclarativeItem); + return d->keepMouse; +} + +/*! + The flag indicating whether the mouse should remain + with this item is set to \a keep. + + This is useful for items that wish to grab and keep mouse + interaction following a predefined gesture. For example, + an item that is interested in horizontal mouse movement + may set keepMouseGrab to true once a threshold has been + exceeded. Once keepMouseGrab has been set to true, filtering + items will not react to mouse events. + + If the item does not indicate that it wishes to retain mouse grab, + a filtering item may steal the grab. For example, Flickable may attempt + to steal a mouse grab if it detects that the user has begun to + move the viewport. + + \sa keepMouseGrab() + */ +void QDeclarativeItem::setKeepMouseGrab(bool keep) +{ + Q_D(QDeclarativeItem); + d->keepMouse = keep; +} + +/*! + \qmlmethod object Item::mapFromItem(Item item, real x, real y) + + Maps the point (\a x, \a y), which is in \a item's coordinate system, to + this item's coordinate system, and returns an object with \c x and \c y + properties matching the mapped cooordinate. + + If \a item is a \c null value, this maps the point from the coordinate + system of the root QML view. +*/ + +/*! + Maps the point (\a x, \a y), which is in \a item's coordinate system, to + this item's coordinate system, and returns a script value with \c x and \c y + properties matching the mapped cooordinate. + + If \a item is a \c null value, this maps the point from the coordinate + system of the root QML view. + + \sa Item::mapFromItem() +*/ +QScriptValue QDeclarativeItem::mapFromItem(const QScriptValue &item, qreal x, qreal y) const +{ + QScriptValue sv = QDeclarativeEnginePrivate::getScriptEngine(qmlEngine(this))->newObject(); + QDeclarativeItem *itemObj = qobject_cast(item.toQObject()); + if (!itemObj && !item.isNull()) { + qmlInfo(this) << "mapFromItem() given argument \"" << item.toString() << "\" which is neither null nor an Item"; + return 0; + } + + // If QGraphicsItem::mapFromItem() is called with 0, behaves the same as mapFromScene() + QPointF p = qobject_cast(this)->mapFromItem(itemObj, x, y); + sv.setProperty(QLatin1String("x"), p.x()); + sv.setProperty(QLatin1String("y"), p.y()); + return sv; +} + +/*! + \qmlmethod object Item::mapToItem(Item item, real x, real y) + + Maps the point (\a x, \a y), which is in this item's coordinate system, to + \a item's coordinate system, and returns an object with \c x and \c y + properties matching the mapped cooordinate. + + If \a item is a \c null value, this maps \a x and \a y to the coordinate + system of the root QML view. +*/ + +/*! + Maps the point (\a x, \a y), which is in this item's coordinate system, to + \a item's coordinate system, and returns a script value with \c x and \c y + properties matching the mapped cooordinate. + + If \a item is a \c null value, this maps \a x and \a y to the coordinate + system of the root QML view. + + \sa Item::mapToItem() +*/ +QScriptValue QDeclarativeItem::mapToItem(const QScriptValue &item, qreal x, qreal y) const +{ + QScriptValue sv = QDeclarativeEnginePrivate::getScriptEngine(qmlEngine(this))->newObject(); + QDeclarativeItem *itemObj = qobject_cast(item.toQObject()); + if (!itemObj && !item.isNull()) { + qmlInfo(this) << "mapToItem() given argument \"" << item.toString() << "\" which is neither null nor an Item"; + return 0; + } + + // If QGraphicsItem::mapToItem() is called with 0, behaves the same as mapToScene() + QPointF p = qobject_cast(this)->mapToItem(itemObj, x, y); + sv.setProperty(QLatin1String("x"), p.x()); + sv.setProperty(QLatin1String("y"), p.y()); + return sv; +} + +/*! + \qmlmethod Item::forceActiveFocus() + + Forces active focus on the item. + + This method sets focus on the item and makes sure that all the focus scopes + higher in the object hierarchy are also given the focus. +*/ + +/*! + Forces active focus on the item. + + This method sets focus on the item and makes sure that all the focus scopes + higher in the object hierarchy are also given the focus. +*/ +void QDeclarativeItem::forceActiveFocus() +{ + setFocus(true); + QGraphicsItem *parent = parentItem(); + while (parent) { + if (parent->flags() & QGraphicsItem::ItemIsFocusScope) + parent->setFocus(Qt::OtherFocusReason); + parent = parent->parentItem(); + } +} + + +/*! + \qmlmethod Item::childAt(real x, real y) + + Returns the visible child item at point (\a x, \a y), which is in this + item's coordinate system, or \c null if there is no such item. +*/ + +/*! + Returns the visible child item at point (\a x, \a y), which is in this + item's coordinate system, or 0 if there is no such item. +*/ +QDeclarativeItem *QDeclarativeItem::childAt(qreal x, qreal y) const +{ + const QList children = childItems(); + for (int i = children.count()-1; i >= 0; --i) { + if (QDeclarativeItem *child = qobject_cast(children.at(i))) { + if (child->isVisible() && child->x() <= x + && child->x() + child->width() >= x + && child->y() <= y + && child->y() + child->height() >= y) + return child; + } + } + return 0; +} + +void QDeclarativeItemPrivate::focusChanged(bool flag) +{ + Q_Q(QDeclarativeItem); + if (!(flags & QGraphicsItem::ItemIsFocusScope) && parent) + emit q->activeFocusChanged(flag); //see also QDeclarativeItemPrivate::subFocusItemChange() + emit q->focusChanged(flag); +} + +QDeclarativeListProperty QDeclarativeItemPrivate::resources() +{ + return QDeclarativeListProperty(q_func(), 0, QDeclarativeItemPrivate::resources_append, + QDeclarativeItemPrivate::resources_count, + QDeclarativeItemPrivate::resources_at, + QDeclarativeItemPrivate::resources_clear + ); +} + +/*! + \qmlproperty list Item::states + This property holds a list of states defined by the item. + + \qml + Item { + states: [ + State { + // ... + }, + State { + // ... + } + // ... + ] + } + \endqml + + \sa {qmlstate}{States} +*/ + +QDeclarativeListProperty QDeclarativeItemPrivate::states() +{ + return _states()->statesProperty(); +} + +/*! + \qmlproperty list Item::transitions + This property holds a list of transitions defined by the item. + + \qml + Item { + transitions: [ + Transition { + // ... + }, + Transition { + // ... + } + // ... + ] + } + \endqml + + \sa {QML Animation and Transitions}{Transitions} +*/ + + +QDeclarativeListProperty QDeclarativeItemPrivate::transitions() +{ + return _states()->transitionsProperty(); +} + +/* + \qmlproperty list Item::filter + This property holds a list of graphical filters to be applied to the item. + + \l {Filter}{Filters} include things like \l {Blur}{blurring} + the item, or giving it a \l Reflection. Some + filters may not be available on all canvases; if a filter is not + available on a certain canvas, it will simply not be applied for + that canvas (but the QML will still be considered valid). + + \qml + Item { + filter: [ + Blur { + // ... + }, + Reflection { + // ... + } + // ... + ] + } + \endqml +*/ + +/*! + \qmlproperty bool Item::clip + This property holds whether clipping is enabled. The default clip value is \c false. + + If clipping is enabled, an item will clip its own painting, as well + as the painting of its children, to its bounding rectangle. + + Non-rectangular clipping regions are not supported for performance reasons. +*/ + +/*! + \property QDeclarativeItem::clip + This property holds whether clipping is enabled. The default clip value is \c false. + + If clipping is enabled, an item will clip its own painting, as well + as the painting of its children, to its bounding rectangle. If you set + clipping during an item's paint operation, remember to re-set it to + prevent clipping the rest of your scene. + + Non-rectangular clipping regions are not supported for performance reasons. +*/ + +/*! + \qmlproperty string Item::state + + This property holds the name of the current state of the item. + + This property is often used in scripts to change between states. For + example: + + \js + function toggle() { + if (button.state == 'On') + button.state = 'Off'; + else + button.state = 'On'; + } + \endjs + + If the item is in its base state (i.e. no explicit state has been + set), \c state will be a blank string. Likewise, you can return an + item to its base state by setting its current state to \c ''. + + \sa {qmlstates}{States} +*/ + +QString QDeclarativeItemPrivate::state() const +{ + if (!_stateGroup) + return QString(); + else + return _stateGroup->state(); +} + +void QDeclarativeItemPrivate::setState(const QString &state) +{ + _states()->setState(state); +} + +/*! + \qmlproperty list Item::transform + This property holds the list of transformations to apply. + + For more information see \l Transform. +*/ + +/*! \internal */ +QDeclarativeListProperty QDeclarativeItem::transform() +{ + Q_D(QDeclarativeItem); + return QDeclarativeListProperty(this, 0, d->transform_append, d->transform_count, + d->transform_at, d->transform_clear); +} + +/*! + \internal + + classBegin() is called when the item is constructed, but its + properties have not yet been set. + + \sa componentComplete(), isComponentComplete() +*/ +void QDeclarativeItem::classBegin() +{ + Q_D(QDeclarativeItem); + d->componentComplete = false; + if (d->_stateGroup) + d->_stateGroup->classBegin(); + if (d->_anchors) + d->_anchors->classBegin(); +} + +/*! + \internal + + componentComplete() is called when all items in the component + have been constructed. It is often desirable to delay some + processing until the component is complete an all bindings in the + component have been resolved. +*/ +void QDeclarativeItem::componentComplete() +{ + Q_D(QDeclarativeItem); + d->componentComplete = true; + if (d->_stateGroup) + d->_stateGroup->componentComplete(); + if (d->_anchors) { + d->_anchors->componentComplete(); + d->_anchors->d_func()->updateOnComplete(); + } + if (d->keyHandler) + d->keyHandler->componentComplete(); + if (d->_contents) + d->_contents->complete(); +} + +QDeclarativeStateGroup *QDeclarativeItemPrivate::_states() +{ + Q_Q(QDeclarativeItem); + if (!_stateGroup) { + _stateGroup = new QDeclarativeStateGroup; + if (!componentComplete) + _stateGroup->classBegin(); + QObject::connect(_stateGroup, SIGNAL(stateChanged(QString)), + q, SIGNAL(stateChanged(QString))); + } + + return _stateGroup; +} + +QDeclarativeItemPrivate::AnchorLines::AnchorLines(QGraphicsObject *q) +{ + left.item = q; + left.anchorLine = QDeclarativeAnchorLine::Left; + right.item = q; + right.anchorLine = QDeclarativeAnchorLine::Right; + hCenter.item = q; + hCenter.anchorLine = QDeclarativeAnchorLine::HCenter; + top.item = q; + top.anchorLine = QDeclarativeAnchorLine::Top; + bottom.item = q; + bottom.anchorLine = QDeclarativeAnchorLine::Bottom; + vCenter.item = q; + vCenter.anchorLine = QDeclarativeAnchorLine::VCenter; + baseline.item = q; + baseline.anchorLine = QDeclarativeAnchorLine::Baseline; +} + +QPointF QDeclarativeItemPrivate::computeTransformOrigin() const +{ + Q_Q(const QDeclarativeItem); + + QRectF br = q->boundingRect(); + + switch(origin) { + default: + case QDeclarativeItem::TopLeft: + return QPointF(0, 0); + case QDeclarativeItem::Top: + return QPointF(br.width() / 2., 0); + case QDeclarativeItem::TopRight: + return QPointF(br.width(), 0); + case QDeclarativeItem::Left: + return QPointF(0, br.height() / 2.); + case QDeclarativeItem::Center: + return QPointF(br.width() / 2., br.height() / 2.); + case QDeclarativeItem::Right: + return QPointF(br.width(), br.height() / 2.); + case QDeclarativeItem::BottomLeft: + return QPointF(0, br.height()); + case QDeclarativeItem::Bottom: + return QPointF(br.width() / 2., br.height()); + case QDeclarativeItem::BottomRight: + return QPointF(br.width(), br.height()); + } +} + +/*! \internal */ +bool QDeclarativeItem::sceneEvent(QEvent *event) +{ + Q_D(QDeclarativeItem); + if (event->type() == QEvent::KeyPress) { + QKeyEvent *k = static_cast(event); + if ((k->key() == Qt::Key_Tab || k->key() == Qt::Key_Backtab) && + !(k->modifiers() & (Qt::ControlModifier | Qt::AltModifier))) { + keyPressEvent(static_cast(event)); + if (!event->isAccepted()) + return QGraphicsItem::sceneEvent(event); + else + return true; + } else { + return QGraphicsItem::sceneEvent(event); + } + } else { + bool rv = QGraphicsItem::sceneEvent(event); + + if (event->type() == QEvent::FocusIn || + event->type() == QEvent::FocusOut) { + d->focusChanged(hasActiveFocus()); + } + return rv; + } +} + +/*! + \internal + + Note that unlike QGraphicsItems, QDeclarativeItem::itemChange() is \e not called + during initial widget polishing. Items wishing to optimize start-up construction + should instead consider using componentComplete(). +*/ +QVariant QDeclarativeItem::itemChange(GraphicsItemChange change, + const QVariant &value) +{ + Q_D(QDeclarativeItem); + switch (change) { + case ItemParentHasChanged: + d->resolveLayoutMirror(); + emit parentChanged(parentItem()); + d->parentNotifier.notify(); + break; + case ItemVisibleHasChanged: { + for(int ii = 0; ii < d->changeListeners.count(); ++ii) { + const QDeclarativeItemPrivate::ChangeListener &change = d->changeListeners.at(ii); + if (change.types & QDeclarativeItemPrivate::Visibility) { + change.listener->itemVisibilityChanged(this); + } + } + } + break; + case ItemOpacityHasChanged: { + for(int ii = 0; ii < d->changeListeners.count(); ++ii) { + const QDeclarativeItemPrivate::ChangeListener &change = d->changeListeners.at(ii); + if (change.types & QDeclarativeItemPrivate::Opacity) { + change.listener->itemOpacityChanged(this); + } + } + } + break; + case ItemChildAddedChange: + if (d->_contents && d->componentComplete) + d->_contents->childAdded(qobject_cast( + value.value())); + break; + case ItemChildRemovedChange: + if (d->_contents && d->componentComplete) + d->_contents->childRemoved(qobject_cast( + value.value())); + break; + default: + break; + } + + return QGraphicsItem::itemChange(change, value); +} + +/*! \internal */ +QRectF QDeclarativeItem::boundingRect() const +{ + Q_D(const QDeclarativeItem); + return QRectF(0, 0, d->mWidth, d->mHeight); +} + +/*! + \enum QDeclarativeItem::TransformOrigin + + Controls the point about which simple transforms like scale apply. + + \value TopLeft The top-left corner of the item. + \value Top The center point of the top of the item. + \value TopRight The top-right corner of the item. + \value Left The left most point of the vertical middle. + \value Center The center of the item. + \value Right The right most point of the vertical middle. + \value BottomLeft The bottom-left corner of the item. + \value Bottom The center point of the bottom of the item. + \value BottomRight The bottom-right corner of the item. +*/ + +/*! + Returns the current transform origin. +*/ +QDeclarativeItem::TransformOrigin QDeclarativeItem::transformOrigin() const +{ + Q_D(const QDeclarativeItem); + return d->origin; +} + +/*! + Set the transform \a origin. +*/ +void QDeclarativeItem::setTransformOrigin(TransformOrigin origin) +{ + Q_D(QDeclarativeItem); + if (origin != d->origin) { + d->origin = origin; + if (d->transformData) + QGraphicsItem::setTransformOriginPoint(d->computeTransformOrigin()); + else + d->transformOriginDirty = true; + emit transformOriginChanged(d->origin); + } +} + +void QDeclarativeItemPrivate::transformChanged() +{ + Q_Q(QDeclarativeItem); + if (transformOriginDirty) { + q->QGraphicsItem::setTransformOriginPoint(computeTransformOrigin()); + transformOriginDirty = false; + } +} + +/*! + \property QDeclarativeItem::smooth + \brief whether the item is smoothly transformed. + + This property is provided purely for the purpose of optimization. Turning + smooth transforms off is faster, but looks worse; turning smooth + transformations on is slower, but looks better. + + By default smooth transformations are off. +*/ + +/*! + Returns true if the item should be drawn with antialiasing and + smooth pixmap filtering, false otherwise. + + The default is false. + + \sa setSmooth() +*/ +bool QDeclarativeItem::smooth() const +{ + Q_D(const QDeclarativeItem); + return d->smooth; +} + +/*! + Sets whether the item should be drawn with antialiasing and + smooth pixmap filtering to \a smooth. + + \sa smooth() +*/ +void QDeclarativeItem::setSmooth(bool smooth) +{ + Q_D(QDeclarativeItem); + if (d->smooth == smooth) + return; + d->smooth = smooth; + emit smoothChanged(smooth); + update(); +} + +/*! + \property QDeclarativeItem::anchors + \internal +*/ + +/*! + \property QDeclarativeItem::left + \internal +*/ + +/*! + \property QDeclarativeItem::right + \internal +*/ + +/*! + \property QDeclarativeItem::horizontalCenter + \internal +*/ + +/*! + \property QDeclarativeItem::top + \internal +*/ + +/*! + \property QDeclarativeItem::bottom + \internal +*/ + +/*! + \property QDeclarativeItem::verticalCenter + \internal +*/ + +/*! + \property QDeclarativeItem::focus + \internal +*/ + +/*! + \property QDeclarativeItem::transform + \internal +*/ + +/*! + \property QDeclarativeItem::transformOrigin + \internal +*/ + +/*! + \property QDeclarativeItem::activeFocus + \internal +*/ + +/*! + \property QDeclarativeItem::baseline + \internal +*/ + +/*! + \property QDeclarativeItem::data + \internal +*/ + +/*! + \property QDeclarativeItem::resources + \internal +*/ + +/*! + \property QDeclarativeItem::state + \internal +*/ + +/*! + \property QDeclarativeItem::states + \internal +*/ + +/*! + \property QDeclarativeItem::transformOriginPoint + \internal +*/ + +/*! + \property QDeclarativeItem::transitions + \internal +*/ + +/*! + \internal + Return the width of the item +*/ +qreal QDeclarativeItem::width() const +{ + Q_D(const QDeclarativeItem); + return d->width(); +} + +/*! + \internal + Set the width of the item +*/ +void QDeclarativeItem::setWidth(qreal w) +{ + Q_D(QDeclarativeItem); + d->setWidth(w); +} + +/*! + \internal + Reset the width of the item +*/ +void QDeclarativeItem::resetWidth() +{ + Q_D(QDeclarativeItem); + d->resetWidth(); +} + +/*! + \internal + Return the width of the item +*/ +qreal QDeclarativeItemPrivate::width() const +{ + return mWidth; +} + +/*! + \internal +*/ +void QDeclarativeItemPrivate::setWidth(qreal w) +{ + Q_Q(QDeclarativeItem); + if (qIsNaN(w)) + return; + + widthValid = true; + if (mWidth == w) + return; + + qreal oldWidth = mWidth; + + q->prepareGeometryChange(); + mWidth = w; + + q->geometryChanged(QRectF(q->x(), q->y(), width(), height()), + QRectF(q->x(), q->y(), oldWidth, height())); +} + +/*! + \internal +*/ +void QDeclarativeItemPrivate::resetWidth() +{ + Q_Q(QDeclarativeItem); + widthValid = false; + q->setImplicitWidth(q->implicitWidth()); +} + +void QDeclarativeItemPrivate::implicitWidthChanged() +{ + Q_Q(QDeclarativeItem); + emit q->implicitWidthChanged(); +} + +qreal QDeclarativeItemPrivate::implicitWidth() const +{ + return mImplicitWidth; +} + +/*! + Returns the width of the item that is implied by other properties that determine the content. +*/ +qreal QDeclarativeItem::implicitWidth() const +{ + Q_D(const QDeclarativeItem); + return d->implicitWidth(); +} + +/*! + Sets the implied width of the item to \a w. + This is the width implied by other properties that determine the content. +*/ +void QDeclarativeItem::setImplicitWidth(qreal w) +{ + Q_D(QDeclarativeItem); + bool changed = w != d->mImplicitWidth; + d->mImplicitWidth = w; + if (d->mWidth == w || widthValid()) { + if (changed) + d->implicitWidthChanged(); + return; + } + + qreal oldWidth = d->mWidth; + + prepareGeometryChange(); + d->mWidth = w; + + geometryChanged(QRectF(x(), y(), width(), height()), + QRectF(x(), y(), oldWidth, height())); + + if (changed) + d->implicitWidthChanged(); +} + +/*! + Returns whether the width property has been set explicitly. +*/ +bool QDeclarativeItem::widthValid() const +{ + Q_D(const QDeclarativeItem); + return d->widthValid; +} + +/*! + \internal + Return the height of the item +*/ +qreal QDeclarativeItem::height() const +{ + Q_D(const QDeclarativeItem); + return d->height(); +} + +/*! + \internal + Set the height of the item +*/ +void QDeclarativeItem::setHeight(qreal h) +{ + Q_D(QDeclarativeItem); + d->setHeight(h); +} + +/*! + \internal + Reset the height of the item +*/ +void QDeclarativeItem::resetHeight() +{ + Q_D(QDeclarativeItem); + d->resetHeight(); +} + +/*! + \internal +*/ +qreal QDeclarativeItemPrivate::height() const +{ + return mHeight; +} + +/*! + \internal +*/ +void QDeclarativeItemPrivate::setHeight(qreal h) +{ + Q_Q(QDeclarativeItem); + if (qIsNaN(h)) + return; + + heightValid = true; + if (mHeight == h) + return; + + qreal oldHeight = mHeight; + + q->prepareGeometryChange(); + mHeight = h; + + q->geometryChanged(QRectF(q->x(), q->y(), width(), height()), + QRectF(q->x(), q->y(), width(), oldHeight)); +} + +/*! + \internal +*/ +void QDeclarativeItemPrivate::resetHeight() +{ + Q_Q(QDeclarativeItem); + heightValid = false; + q->setImplicitHeight(q->implicitHeight()); +} + +void QDeclarativeItemPrivate::implicitHeightChanged() +{ + Q_Q(QDeclarativeItem); + emit q->implicitHeightChanged(); +} + +qreal QDeclarativeItemPrivate::implicitHeight() const +{ + return mImplicitHeight; +} + +/*! + Returns the height of the item that is implied by other properties that determine the content. +*/ +qreal QDeclarativeItem::implicitHeight() const +{ + Q_D(const QDeclarativeItem); + return d->implicitHeight(); +} + +/*! + \qmlproperty real Item::implicitWidth + \qmlproperty real Item::implicitHeight + \since QtQuick 1.1 + + Defines the natural width or height of the Item if no \l width or \l height is specified. + + The default implicit size for most items is 0x0, however some elements have an inherent + implicit size which cannot be overridden, e.g. Image, Text. + + Setting the implicit size is useful for defining components that have a preferred size + based on their content, for example: + + \qml + // Label.qml + import QtQuick 1.1 + + Item { + property alias icon: image.source + property alias label: text.text + implicitWidth: text.implicitWidth + image.implicitWidth + implicitHeight: Math.max(text.implicitHeight, image.implicitHeight) + Image { id: image } + Text { + id: text + wrapMode: Text.Wrap + anchors.left: image.right; anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + } + } + \endqml + + \bold Note: using implicitWidth of Text or TextEdit and setting the width explicitly + incurs a performance penalty as the text must be laid out twice. +*/ + + +/*! + Sets the implied height of the item to \a h. + This is the height implied by other properties that determine the content. +*/ +void QDeclarativeItem::setImplicitHeight(qreal h) +{ + Q_D(QDeclarativeItem); + bool changed = h != d->mImplicitHeight; + d->mImplicitHeight = h; + if (d->mHeight == h || heightValid()) { + if (changed) + d->implicitHeightChanged(); + return; + } + + qreal oldHeight = d->mHeight; + + prepareGeometryChange(); + d->mHeight = h; + + geometryChanged(QRectF(x(), y(), width(), height()), + QRectF(x(), y(), width(), oldHeight)); + + if (changed) + d->implicitHeightChanged(); +} + +/*! + Returns whether the height property has been set explicitly. +*/ +bool QDeclarativeItem::heightValid() const +{ + Q_D(const QDeclarativeItem); + return d->heightValid; +} + +/*! \internal */ +void QDeclarativeItem::setSize(const QSizeF &size) +{ + Q_D(QDeclarativeItem); + d->heightValid = true; + d->widthValid = true; + + if (d->height() == size.height() && d->width() == size.width()) + return; + + qreal oldHeight = d->height(); + qreal oldWidth = d->width(); + + prepareGeometryChange(); + d->setHeight(size.height()); + d->setWidth(size.width()); + + geometryChanged(QRectF(x(), y(), width(), height()), + QRectF(x(), y(), oldWidth, oldHeight)); +} + +/*! + \qmlproperty bool Item::activeFocus + + This property indicates whether the item has active focus. + + An item with active focus will receive keyboard input, + or is a FocusScope ancestor of the item that will receive keyboard input. + + Usually, activeFocus is gained by setting focus on an item and its enclosing + FocusScopes. In the following example \c input will have activeFocus. + \qml + Rectangle { + FocusScope { + focus: true + TextInput { + id: input + focus: true + } + } + } + \endqml + + \sa focus, {qmlfocus}{Keyboard Focus} +*/ + +/*! \internal */ +bool QDeclarativeItem::hasActiveFocus() const +{ + Q_D(const QDeclarativeItem); + return (focusItem() && focusItem()->isVisible()) && (focusItem() == this || + (d->flags & QGraphicsItem::ItemIsFocusScope && focusItem() != 0)); +} + +/*! + \qmlproperty bool Item::focus + This property indicates whether the item has focus within the enclosing focus scope. If true, this item + will gain active focus when the enclosing focus scope gains active focus. + In the following example, \c input will be given active focus when \c scope gains active focus. + \qml + Rectangle { + FocusScope { + id: scope + TextInput { + id: input + focus: true + } + } + } + \endqml + + For the purposes of this property, the scene as a whole is assumed to act like a focus scope. + On a practical level, that means the following QML will give active focus to \c input on startup. + + \qml + Rectangle { + TextInput { + id: input + focus: true + } + } + \endqml + + \sa activeFocus, {qmlfocus}{Keyboard Focus} +*/ + +/*! \internal */ +bool QDeclarativeItem::hasFocus() const +{ + Q_D(const QDeclarativeItem); + QGraphicsItem *p = d->parent; + while (p) { + if (p->flags() & QGraphicsItem::ItemIsFocusScope) { + return p->focusScopeItem() == this; + } + p = p->parentItem(); + } + + return hasActiveFocus(); +} + +/*! \internal */ +void QDeclarativeItem::setFocus(bool focus) +{ + if (focus) + QGraphicsItem::setFocus(Qt::OtherFocusReason); + else + QGraphicsItem::clearFocus(); +} + +/*! + \internal +*/ +void QDeclarativeItem::paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *) +{ +} + +/*! + \internal +*/ +bool QDeclarativeItem::event(QEvent *ev) +{ + Q_D(QDeclarativeItem); + switch (ev->type()) { + case QEvent::KeyPress: + case QEvent::KeyRelease: + case QEvent::InputMethod: + d->doneEventPreHandler = false; + break; + default: + break; + } + + return QGraphicsObject::event(ev); +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug debug, QDeclarativeItem *item) +{ + if (!item) { + debug << "QDeclarativeItem(0)"; + return debug; + } + + debug << item->metaObject()->className() << "(this =" << ((void*)item) + << ", parent =" << ((void*)item->parentItem()) + << ", geometry =" << QRectF(item->pos(), QSizeF(item->width(), item->height())) + << ", z =" << item->zValue() << ')'; + return debug; +} +#endif + +qint64 QDeclarativeItemPrivate::consistentTime = -1; +void QDeclarativeItemPrivate::setConsistentTime(qint64 t) +{ + consistentTime = t; +} + +class QElapsedTimerConsistentTimeHack +{ +public: + void start() { + t1 = QDeclarativeItemPrivate::consistentTime; + t2 = 0; + } + qint64 elapsed() { + return QDeclarativeItemPrivate::consistentTime - t1; + } + qint64 restart() { + qint64 val = QDeclarativeItemPrivate::consistentTime - t1; + t1 = QDeclarativeItemPrivate::consistentTime; + t2 = 0; + return val; + } + +private: + qint64 t1; + qint64 t2; +}; + +void QDeclarativeItemPrivate::start(QElapsedTimer &t) +{ + if (QDeclarativeItemPrivate::consistentTime == -1) + t.start(); + else + ((QElapsedTimerConsistentTimeHack*)&t)->start(); +} + +qint64 QDeclarativeItemPrivate::elapsed(QElapsedTimer &t) +{ + if (QDeclarativeItemPrivate::consistentTime == -1) + return t.elapsed(); + else + return ((QElapsedTimerConsistentTimeHack*)&t)->elapsed(); +} + +qint64 QDeclarativeItemPrivate::restart(QElapsedTimer &t) +{ + if (QDeclarativeItemPrivate::consistentTime == -1) + return t.restart(); + else + return ((QElapsedTimerConsistentTimeHack*)&t)->restart(); +} + +QT_END_NAMESPACE + +#include diff --git a/src/declarative/graphicsitems/qdeclarativeitem.h b/src/declarative/graphicsitems/qdeclarativeitem.h new file mode 100644 index 00000000..ce8c9453 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativeitem.h @@ -0,0 +1,234 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEITEM_H +#define QDECLARATIVEITEM_H + +#include +#include + +#include +#include +#include +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeState; +class QDeclarativeAnchorLine; +class QDeclarativeTransition; +class QDeclarativeKeyEvent; +class QDeclarativeAnchors; +class QDeclarativeItemPrivate; +class Q_DECLARATIVE_EXPORT QDeclarativeItem : public QGraphicsObject, public QDeclarativeParserStatus +{ + Q_OBJECT + Q_INTERFACES(QDeclarativeParserStatus) + + Q_PROPERTY(QDeclarativeItem * parent READ parentItem WRITE setParentItem NOTIFY parentChanged DESIGNABLE false FINAL) + Q_PRIVATE_PROPERTY(QDeclarativeItem::d_func(), QDeclarativeListProperty data READ data DESIGNABLE false) + Q_PRIVATE_PROPERTY(QDeclarativeItem::d_func(), QDeclarativeListProperty resources READ resources DESIGNABLE false) + Q_PRIVATE_PROPERTY(QDeclarativeItem::d_func(), QDeclarativeListProperty states READ states DESIGNABLE false) + Q_PRIVATE_PROPERTY(QDeclarativeItem::d_func(), QDeclarativeListProperty transitions READ transitions DESIGNABLE false) + Q_PRIVATE_PROPERTY(QDeclarativeItem::d_func(), QString state READ state WRITE setState NOTIFY stateChanged) + Q_PROPERTY(QRectF childrenRect READ childrenRect NOTIFY childrenRectChanged DESIGNABLE false FINAL) + Q_PRIVATE_PROPERTY(QDeclarativeItem::d_func(), QDeclarativeAnchors * anchors READ anchors DESIGNABLE false CONSTANT FINAL) + Q_PRIVATE_PROPERTY(QDeclarativeItem::d_func(), QDeclarativeAnchorLine left READ left CONSTANT FINAL) + Q_PRIVATE_PROPERTY(QDeclarativeItem::d_func(), QDeclarativeAnchorLine right READ right CONSTANT FINAL) + Q_PRIVATE_PROPERTY(QDeclarativeItem::d_func(), QDeclarativeAnchorLine horizontalCenter READ horizontalCenter CONSTANT FINAL) + Q_PRIVATE_PROPERTY(QDeclarativeItem::d_func(), QDeclarativeAnchorLine top READ top CONSTANT FINAL) + Q_PRIVATE_PROPERTY(QDeclarativeItem::d_func(), QDeclarativeAnchorLine bottom READ bottom CONSTANT FINAL) + Q_PRIVATE_PROPERTY(QDeclarativeItem::d_func(), QDeclarativeAnchorLine verticalCenter READ verticalCenter CONSTANT FINAL) + Q_PRIVATE_PROPERTY(QDeclarativeItem::d_func(), QDeclarativeAnchorLine baseline READ baseline CONSTANT FINAL) + Q_PROPERTY(qreal baselineOffset READ baselineOffset WRITE setBaselineOffset NOTIFY baselineOffsetChanged) + Q_PROPERTY(bool clip READ clip WRITE setClip NOTIFY clipChanged) // ### move to QGI/QGO, NOTIFY + Q_PROPERTY(bool focus READ hasFocus WRITE setFocus NOTIFY focusChanged FINAL) + Q_PROPERTY(bool activeFocus READ hasActiveFocus NOTIFY activeFocusChanged) + Q_PROPERTY(QDeclarativeListProperty transform READ transform DESIGNABLE false FINAL) + Q_PROPERTY(TransformOrigin transformOrigin READ transformOrigin WRITE setTransformOrigin NOTIFY transformOriginChanged) + Q_PROPERTY(QPointF transformOriginPoint READ transformOriginPoint) // transformOriginPoint is read-only for Item + Q_PROPERTY(bool smooth READ smooth WRITE setSmooth NOTIFY smoothChanged) + Q_PROPERTY(qreal implicitWidth READ implicitWidth WRITE setImplicitWidth NOTIFY implicitWidthChanged REVISION 1) + Q_PROPERTY(qreal implicitHeight READ implicitHeight WRITE setImplicitHeight NOTIFY implicitHeightChanged REVISION 1) + + Q_ENUMS(TransformOrigin) + Q_CLASSINFO("DefaultProperty", "data") + +public: + enum TransformOrigin { + TopLeft, Top, TopRight, + Left, Center, Right, + BottomLeft, Bottom, BottomRight + }; + + QDeclarativeItem(QDeclarativeItem *parent = 0); + virtual ~QDeclarativeItem(); + + QDeclarativeItem *parentItem() const; + void setParentItem(QDeclarativeItem *parent); + + QRectF childrenRect(); + + bool clip() const; + void setClip(bool); + + qreal baselineOffset() const; + void setBaselineOffset(qreal); + + QDeclarativeListProperty transform(); + + qreal width() const; + void setWidth(qreal); + void resetWidth(); + qreal implicitWidth() const; + + qreal height() const; + void setHeight(qreal); + void resetHeight(); + qreal implicitHeight() const; + + void setSize(const QSizeF &size); + + TransformOrigin transformOrigin() const; + void setTransformOrigin(TransformOrigin); + + bool smooth() const; + void setSmooth(bool); + + QRectF boundingRect() const; + virtual void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *); + + bool hasActiveFocus() const; + bool hasFocus() const; + void setFocus(bool); + + bool keepMouseGrab() const; + void setKeepMouseGrab(bool); + + Q_INVOKABLE QScriptValue mapFromItem(const QScriptValue &item, qreal x, qreal y) const; + Q_INVOKABLE QScriptValue mapToItem(const QScriptValue &item, qreal x, qreal y) const; + Q_INVOKABLE void forceActiveFocus(); + Q_INVOKABLE QDeclarativeItem *childAt(qreal x, qreal y) const; + +Q_SIGNALS: + void childrenRectChanged(const QRectF &); + void baselineOffsetChanged(qreal); + void stateChanged(const QString &); + void focusChanged(bool); + void activeFocusChanged(bool); + void parentChanged(QDeclarativeItem *); + void transformOriginChanged(TransformOrigin); + void smoothChanged(bool); + void clipChanged(bool); + Q_REVISION(1) void implicitWidthChanged(); + Q_REVISION(1) void implicitHeightChanged(); + +protected: + bool isComponentComplete() const; + virtual bool sceneEvent(QEvent *); + virtual bool event(QEvent *); + virtual QVariant itemChange(GraphicsItemChange, const QVariant &); + + void setImplicitWidth(qreal); + bool widthValid() const; // ### better name? + void setImplicitHeight(qreal); + bool heightValid() const; // ### better name? + + virtual void classBegin(); + virtual void componentComplete(); + virtual void keyPressEvent(QKeyEvent *event); + virtual void keyReleaseEvent(QKeyEvent *event); + virtual void inputMethodEvent(QInputMethodEvent *); + virtual QVariant inputMethodQuery(Qt::InputMethodQuery query) const; + void keyPressPreHandler(QKeyEvent *); + void keyReleasePreHandler(QKeyEvent *); + void inputMethodPreHandler(QInputMethodEvent *); + + virtual void geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry); + +protected: + QDeclarativeItem(QDeclarativeItemPrivate &dd, QDeclarativeItem *parent = 0); + +private: + Q_DISABLE_COPY(QDeclarativeItem) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarativeItem) +}; + +template + T qobject_cast(QGraphicsObject *o) +{ + QObject *obj = o; + return qobject_cast(obj); +} + +// ### move to QGO +template +T qobject_cast(QGraphicsItem *item) +{ + if (!item) return 0; + QObject *o = item->toGraphicsObject(); + return qobject_cast(o); +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug Q_DECLARATIVE_EXPORT operator<<(QDebug debug, QDeclarativeItem *item); +#endif + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeItem) +QML_DECLARE_TYPE(QGraphicsObject) +QML_DECLARE_TYPE(QGraphicsTransform) +QML_DECLARE_TYPE(QGraphicsScale) +QML_DECLARE_TYPE(QGraphicsRotation) +QML_DECLARE_TYPE(QGraphicsWidget) +QML_DECLARE_TYPE(QAction) + +QT_END_HEADER + +#endif // QDECLARATIVEITEM_H diff --git a/src/declarative/graphicsitems/qdeclarativeitem_p.h b/src/declarative/graphicsitems/qdeclarativeitem_p.h new file mode 100644 index 00000000..934df7df --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativeitem_p.h @@ -0,0 +1,624 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEITEM_P_H +#define QDECLARATIVEITEM_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 "qdeclarativeitem.h" + +#include "private/qdeclarativeanchors_p.h" +#include "private/qdeclarativeanchors_p_p.h" +#include "private/qdeclarativeitemchangelistener_p.h" +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QNetworkReply; +class QDeclarativeItemKeyFilter; +class QDeclarativeLayoutMirroringAttached; + +//### merge into private? +class QDeclarativeContents : public QObject, public QDeclarativeItemChangeListener +{ + Q_OBJECT +public: + QDeclarativeContents(QDeclarativeItem *item); + ~QDeclarativeContents(); + + QRectF rectF() const; + + void childRemoved(QDeclarativeItem *item); + void childAdded(QDeclarativeItem *item); + + void calcGeometry() { calcWidth(); calcHeight(); } + void complete(); + +Q_SIGNALS: + void rectChanged(QRectF); + +protected: + void itemGeometryChanged(QDeclarativeItem *item, const QRectF &newGeometry, const QRectF &oldGeometry); + void itemDestroyed(QDeclarativeItem *item); + //void itemVisibilityChanged(QDeclarativeItem *item) + +private: + void calcHeight(QDeclarativeItem *changed = 0); + void calcWidth(QDeclarativeItem *changed = 0); + + QDeclarativeItem *m_item; + qreal m_x; + qreal m_y; + qreal m_width; + qreal m_height; +}; + +class Q_DECLARATIVE_EXPORT QDeclarativeItemPrivate : public QGraphicsItemPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeItem) + +public: + QDeclarativeItemPrivate() + : _anchors(0), _contents(0), + baselineOffset(0), + _anchorLines(0), + _stateGroup(0), origin(QDeclarativeItem::Center), + widthValid(false), heightValid(false), + componentComplete(true), keepMouse(false), + smooth(false), transformOriginDirty(true), doneEventPreHandler(false), + inheritedLayoutMirror(false), effectiveLayoutMirror(false), isMirrorImplicit(true), + inheritMirrorFromParent(false), inheritMirrorFromItem(false), keyHandler(0), + mWidth(0), mHeight(0), mImplicitWidth(0), mImplicitHeight(0), attachedLayoutDirection(0), hadSubFocusItem(false) + { + QGraphicsItemPrivate::acceptedMouseButtons = 0; + isDeclarativeItem = 1; + QGraphicsItemPrivate::flags = QGraphicsItem::GraphicsItemFlags( + QGraphicsItem::ItemHasNoContents + | QGraphicsItem::ItemIsFocusable + | QGraphicsItem::ItemNegativeZStacksBehindParent); + } + + void init(QDeclarativeItem *parent) + { + Q_Q(QDeclarativeItem); + if (parent) { + QDeclarative_setParent_noEvent(q, parent); + q->setParentItem(parent); + QDeclarativeItemPrivate *parentPrivate = QDeclarativeItemPrivate::get(parent); + setImplicitLayoutMirror(parentPrivate->inheritedLayoutMirror, parentPrivate->inheritMirrorFromParent); + } + baselineOffset.invalidate(); + mouseSetsFocus = false; + } + + bool isMirrored() const { + return effectiveLayoutMirror; + } + + // Private Properties + qreal width() const; + void setWidth(qreal); + void resetWidth(); + + qreal height() const; + void setHeight(qreal); + void resetHeight(); + + virtual qreal implicitWidth() const; + virtual qreal implicitHeight() const; + virtual void implicitWidthChanged(); + virtual void implicitHeightChanged(); + + void resolveLayoutMirror(); + void setImplicitLayoutMirror(bool mirror, bool inherit); + void setLayoutMirror(bool mirror); + + QDeclarativeListProperty data(); + QDeclarativeListProperty resources(); + + QDeclarativeListProperty states(); + QDeclarativeListProperty transitions(); + + QString state() const; + void setState(const QString &); + + QDeclarativeAnchorLine left() const; + QDeclarativeAnchorLine right() const; + QDeclarativeAnchorLine horizontalCenter() const; + QDeclarativeAnchorLine top() const; + QDeclarativeAnchorLine bottom() const; + QDeclarativeAnchorLine verticalCenter() const; + QDeclarativeAnchorLine baseline() const; + + // data property + static void data_append(QDeclarativeListProperty *, QObject *); + static int data_count(QDeclarativeListProperty *); + static QObject *data_at(QDeclarativeListProperty *, int); + static void data_clear(QDeclarativeListProperty *); + + // resources property + static QObject *resources_at(QDeclarativeListProperty *, int); + static void resources_append(QDeclarativeListProperty *, QObject *); + static int resources_count(QDeclarativeListProperty *); + static void resources_clear(QDeclarativeListProperty *); + + // transform property + static int transform_count(QDeclarativeListProperty *list); + static void transform_append(QDeclarativeListProperty *list, QGraphicsTransform *); + static QGraphicsTransform *transform_at(QDeclarativeListProperty *list, int); + static void transform_clear(QDeclarativeListProperty *list); + + static QDeclarativeItemPrivate* get(QDeclarativeItem *item) + { + return item->d_func(); + } + + // Accelerated property accessors + QDeclarativeNotifier parentNotifier; + static void parentProperty(QObject *o, void *rv, QDeclarativeNotifierEndpoint *e); + + QDeclarativeAnchors *anchors() { + if (!_anchors) { + Q_Q(QDeclarativeItem); + _anchors = new QDeclarativeAnchors(q); + if (!componentComplete) + _anchors->classBegin(); + } + return _anchors; + } + QDeclarativeAnchors *_anchors; + QDeclarativeContents *_contents; + + QDeclarativeNullableValue baselineOffset; + + struct AnchorLines { + AnchorLines(QGraphicsObject *); + QDeclarativeAnchorLine left; + QDeclarativeAnchorLine right; + QDeclarativeAnchorLine hCenter; + QDeclarativeAnchorLine top; + QDeclarativeAnchorLine bottom; + QDeclarativeAnchorLine vCenter; + QDeclarativeAnchorLine baseline; + }; + mutable AnchorLines *_anchorLines; + AnchorLines *anchorLines() const { + Q_Q(const QDeclarativeItem); + if (!_anchorLines) _anchorLines = + new AnchorLines(const_cast(q)); + return _anchorLines; + } + + enum ChangeType { + Geometry = 0x01, + SiblingOrder = 0x02, + Visibility = 0x04, + Opacity = 0x08, + Destroyed = 0x10 + }; + + Q_DECLARE_FLAGS(ChangeTypes, ChangeType) + + struct ChangeListener { + ChangeListener(QDeclarativeItemChangeListener *l, QDeclarativeItemPrivate::ChangeTypes t) : listener(l), types(t) {} + QDeclarativeItemChangeListener *listener; + QDeclarativeItemPrivate::ChangeTypes types; + bool operator==(const ChangeListener &other) const { return listener == other.listener && types == other.types; } + }; + + void addItemChangeListener(QDeclarativeItemChangeListener *listener, ChangeTypes types) { + changeListeners.append(ChangeListener(listener, types)); + } + void removeItemChangeListener(QDeclarativeItemChangeListener *, ChangeTypes types); + QPODVector changeListeners; + + QDeclarativeStateGroup *_states(); + QDeclarativeStateGroup *_stateGroup; + + QDeclarativeItem::TransformOrigin origin:5; + bool widthValid:1; + bool heightValid:1; + bool componentComplete:1; + bool keepMouse:1; + bool smooth:1; + bool transformOriginDirty : 1; + bool doneEventPreHandler : 1; + bool inheritedLayoutMirror:1; + bool effectiveLayoutMirror:1; + bool isMirrorImplicit:1; + bool inheritMirrorFromParent:1; + bool inheritMirrorFromItem:1; + + QDeclarativeItemKeyFilter *keyHandler; + + qreal mWidth; + qreal mHeight; + qreal mImplicitWidth; + qreal mImplicitHeight; + + QDeclarativeLayoutMirroringAttached* attachedLayoutDirection; + + bool hadSubFocusItem; + + QPointF computeTransformOrigin() const; + + virtual void setPosHelper(const QPointF &pos) + { + Q_Q(QDeclarativeItem); + QRectF oldGeometry(this->pos.x(), this->pos.y(), mWidth, mHeight); + QGraphicsItemPrivate::setPosHelper(pos); + q->geometryChanged(QRectF(this->pos.x(), this->pos.y(), mWidth, mHeight), oldGeometry); + } + + // Reimplemented from QGraphicsItemPrivate + virtual void subFocusItemChange() + { + bool hasSubFocusItem = subFocusItem != 0; + if (((flags & QGraphicsItem::ItemIsFocusScope) || !parent) && hasSubFocusItem != hadSubFocusItem) + emit q_func()->activeFocusChanged(hasSubFocusItem); + //see also QDeclarativeItemPrivate::focusChanged + hadSubFocusItem = hasSubFocusItem; + } + + // Reimplemented from QGraphicsItemPrivate + virtual void focusScopeItemChange(bool isSubFocusItem) + { + emit q_func()->focusChanged(isSubFocusItem); + } + + + // Reimplemented from QGraphicsItemPrivate + virtual void siblingOrderChange() + { + Q_Q(QDeclarativeItem); + for(int ii = 0; ii < changeListeners.count(); ++ii) { + const QDeclarativeItemPrivate::ChangeListener &change = changeListeners.at(ii); + if (change.types & QDeclarativeItemPrivate::SiblingOrder) { + change.listener->itemSiblingOrderChanged(q); + } + } + } + + // Reimplemented from QGraphicsItemPrivate + virtual void transformChanged(); + + virtual void focusChanged(bool); + + virtual void mirrorChange() {}; + + static qint64 consistentTime; + static void setConsistentTime(qint64 t); + static void start(QElapsedTimer &); + static qint64 elapsed(QElapsedTimer &); + static qint64 restart(QElapsedTimer &); +}; + +/* + Key filters can be installed on a QDeclarativeItem, but not removed. Currently they + are only used by attached objects (which are only destroyed on Item + destruction), so this isn't a problem. If in future this becomes any form + of public API, they will have to support removal too. +*/ +class QDeclarativeItemKeyFilter +{ +public: + QDeclarativeItemKeyFilter(QDeclarativeItem * = 0); + virtual ~QDeclarativeItemKeyFilter(); + + virtual void keyPressed(QKeyEvent *event, bool post); + virtual void keyReleased(QKeyEvent *event, bool post); + virtual void inputMethodEvent(QInputMethodEvent *event, bool post); + virtual QVariant inputMethodQuery(Qt::InputMethodQuery query) const; + virtual void componentComplete(); + + bool m_processPost; + +private: + QDeclarativeItemKeyFilter *m_next; +}; + +class QDeclarativeKeyNavigationAttachedPrivate : public QObjectPrivate +{ +public: + QDeclarativeKeyNavigationAttachedPrivate() + : QObjectPrivate(), left(0), right(0), up(0), down(0), tab(0), backtab(0) {} + + QDeclarativeItem *left; + QDeclarativeItem *right; + QDeclarativeItem *up; + QDeclarativeItem *down; + QDeclarativeItem *tab; + QDeclarativeItem *backtab; +}; + +class QDeclarativeKeyNavigationAttached : public QObject, public QDeclarativeItemKeyFilter +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeKeyNavigationAttached) + + Q_PROPERTY(QDeclarativeItem *left READ left WRITE setLeft NOTIFY leftChanged) + Q_PROPERTY(QDeclarativeItem *right READ right WRITE setRight NOTIFY rightChanged) + Q_PROPERTY(QDeclarativeItem *up READ up WRITE setUp NOTIFY upChanged) + Q_PROPERTY(QDeclarativeItem *down READ down WRITE setDown NOTIFY downChanged) + Q_PROPERTY(QDeclarativeItem *tab READ tab WRITE setTab NOTIFY tabChanged) + Q_PROPERTY(QDeclarativeItem *backtab READ backtab WRITE setBacktab NOTIFY backtabChanged) + Q_PROPERTY(Priority priority READ priority WRITE setPriority NOTIFY priorityChanged) + + Q_ENUMS(Priority) + +public: + QDeclarativeKeyNavigationAttached(QObject * = 0); + + QDeclarativeItem *left() const; + void setLeft(QDeclarativeItem *); + QDeclarativeItem *right() const; + void setRight(QDeclarativeItem *); + QDeclarativeItem *up() const; + void setUp(QDeclarativeItem *); + QDeclarativeItem *down() const; + void setDown(QDeclarativeItem *); + QDeclarativeItem *tab() const; + void setTab(QDeclarativeItem *); + QDeclarativeItem *backtab() const; + void setBacktab(QDeclarativeItem *); + + enum Priority { BeforeItem, AfterItem }; + Priority priority() const; + void setPriority(Priority); + + static QDeclarativeKeyNavigationAttached *qmlAttachedProperties(QObject *); + +Q_SIGNALS: + void leftChanged(); + void rightChanged(); + void upChanged(); + void downChanged(); + void tabChanged(); + void backtabChanged(); + void priorityChanged(); + +private: + virtual void keyPressed(QKeyEvent *event, bool post); + virtual void keyReleased(QKeyEvent *event, bool post); + void setFocusNavigation(QDeclarativeItem *currentItem, const char *dir); +}; + +class QDeclarativeLayoutMirroringAttached : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool enabled READ enabled WRITE setEnabled RESET resetEnabled NOTIFY enabledChanged) + Q_PROPERTY(bool childrenInherit READ childrenInherit WRITE setChildrenInherit NOTIFY childrenInheritChanged) + +public: + explicit QDeclarativeLayoutMirroringAttached(QObject *parent = 0); + + bool enabled() const; + void setEnabled(bool); + void resetEnabled(); + + bool childrenInherit() const; + void setChildrenInherit(bool); + + static QDeclarativeLayoutMirroringAttached *qmlAttachedProperties(QObject *); +Q_SIGNALS: + void enabledChanged(); + void childrenInheritChanged(); +private: + friend class QDeclarativeItemPrivate; + QDeclarativeItemPrivate *itemPrivate; +}; + +class QDeclarativeKeysAttachedPrivate : public QObjectPrivate +{ +public: + QDeclarativeKeysAttachedPrivate() + : QObjectPrivate(), inPress(false), inRelease(false) + , inIM(false), enabled(true), imeItem(0), item(0) + {} + + bool isConnected(const char *signalName); + + QGraphicsItem *finalFocusProxy(QGraphicsItem *item) const + { + QGraphicsItem *fp; + while ((fp = item->focusProxy())) + item = fp; + return item; + } + + //loop detection + bool inPress:1; + bool inRelease:1; + bool inIM:1; + + bool enabled : 1; + + QGraphicsItem *imeItem; + QList targets; + QDeclarativeItem *item; +}; + +class QDeclarativeKeysAttached : public QObject, public QDeclarativeItemKeyFilter +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeKeysAttached) + + Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) + Q_PROPERTY(QDeclarativeListProperty forwardTo READ forwardTo) + Q_PROPERTY(Priority priority READ priority WRITE setPriority NOTIFY priorityChanged) + + Q_ENUMS(Priority) + +public: + QDeclarativeKeysAttached(QObject *parent=0); + ~QDeclarativeKeysAttached(); + + bool enabled() const { Q_D(const QDeclarativeKeysAttached); return d->enabled; } + void setEnabled(bool enabled) { + Q_D(QDeclarativeKeysAttached); + if (enabled != d->enabled) { + d->enabled = enabled; + emit enabledChanged(); + } + } + + enum Priority { BeforeItem, AfterItem}; + Priority priority() const; + void setPriority(Priority); + + QDeclarativeListProperty forwardTo() { + Q_D(QDeclarativeKeysAttached); + return QDeclarativeListProperty(this, d->targets); + } + + virtual void componentComplete(); + + static QDeclarativeKeysAttached *qmlAttachedProperties(QObject *); + +Q_SIGNALS: + void enabledChanged(); + void priorityChanged(); + void pressed(QDeclarativeKeyEvent *event); + void released(QDeclarativeKeyEvent *event); + void digit0Pressed(QDeclarativeKeyEvent *event); + void digit1Pressed(QDeclarativeKeyEvent *event); + void digit2Pressed(QDeclarativeKeyEvent *event); + void digit3Pressed(QDeclarativeKeyEvent *event); + void digit4Pressed(QDeclarativeKeyEvent *event); + void digit5Pressed(QDeclarativeKeyEvent *event); + void digit6Pressed(QDeclarativeKeyEvent *event); + void digit7Pressed(QDeclarativeKeyEvent *event); + void digit8Pressed(QDeclarativeKeyEvent *event); + void digit9Pressed(QDeclarativeKeyEvent *event); + + void leftPressed(QDeclarativeKeyEvent *event); + void rightPressed(QDeclarativeKeyEvent *event); + void upPressed(QDeclarativeKeyEvent *event); + void downPressed(QDeclarativeKeyEvent *event); + void tabPressed(QDeclarativeKeyEvent *event); + void backtabPressed(QDeclarativeKeyEvent *event); + + void asteriskPressed(QDeclarativeKeyEvent *event); + void numberSignPressed(QDeclarativeKeyEvent *event); + void escapePressed(QDeclarativeKeyEvent *event); + void returnPressed(QDeclarativeKeyEvent *event); + void enterPressed(QDeclarativeKeyEvent *event); + void deletePressed(QDeclarativeKeyEvent *event); + void spacePressed(QDeclarativeKeyEvent *event); + void backPressed(QDeclarativeKeyEvent *event); + void cancelPressed(QDeclarativeKeyEvent *event); + void selectPressed(QDeclarativeKeyEvent *event); + void yesPressed(QDeclarativeKeyEvent *event); + void noPressed(QDeclarativeKeyEvent *event); + void context1Pressed(QDeclarativeKeyEvent *event); + void context2Pressed(QDeclarativeKeyEvent *event); + void context3Pressed(QDeclarativeKeyEvent *event); + void context4Pressed(QDeclarativeKeyEvent *event); + void callPressed(QDeclarativeKeyEvent *event); + void hangupPressed(QDeclarativeKeyEvent *event); + void flipPressed(QDeclarativeKeyEvent *event); + void menuPressed(QDeclarativeKeyEvent *event); + void volumeUpPressed(QDeclarativeKeyEvent *event); + void volumeDownPressed(QDeclarativeKeyEvent *event); + +private: + virtual void keyPressed(QKeyEvent *event, bool post); + virtual void keyReleased(QKeyEvent *event, bool post); + virtual void inputMethodEvent(QInputMethodEvent *, bool post); + virtual QVariant inputMethodQuery(Qt::InputMethodQuery query) const; + + const QByteArray keyToSignal(int key) { + QByteArray keySignal; + if (key >= Qt::Key_0 && key <= Qt::Key_9) { + keySignal = "digit0Pressed"; + keySignal[5] = '0' + (key - Qt::Key_0); + } else { + int i = 0; + while (sigMap[i].key && sigMap[i].key != key) + ++i; + keySignal = sigMap[i].sig; + } + return keySignal; + } + + struct SigMap { + int key; + const char *sig; + }; + + static const SigMap sigMap[]; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QDeclarativeItemPrivate::ChangeTypes); + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeKeysAttached) +QML_DECLARE_TYPEINFO(QDeclarativeKeysAttached, QML_HAS_ATTACHED_PROPERTIES) +QML_DECLARE_TYPE(QDeclarativeKeyNavigationAttached) +QML_DECLARE_TYPEINFO(QDeclarativeKeyNavigationAttached, QML_HAS_ATTACHED_PROPERTIES) +QML_DECLARE_TYPE(QDeclarativeLayoutMirroringAttached) +QML_DECLARE_TYPEINFO(QDeclarativeLayoutMirroringAttached, QML_HAS_ATTACHED_PROPERTIES) + +#endif // QDECLARATIVEITEM_P_H diff --git a/src/declarative/graphicsitems/qdeclarativeitemchangelistener_p.h b/src/declarative/graphicsitems/qdeclarativeitemchangelistener_p.h new file mode 100644 index 00000000..af0f21f4 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativeitemchangelistener_p.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEITEMCHANGELISTENER +#define QDECLARATIVEITEMCHANGELISTENER + +// +// 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 + +QT_BEGIN_NAMESPACE + +class QRectF; +class QDeclarativeItem; +class QDeclarativeAnchorsPrivate; +class QDeclarativeItemChangeListener +{ +public: + virtual void itemGeometryChanged(QDeclarativeItem *, const QRectF &, const QRectF &) {} + virtual void itemSiblingOrderChanged(QDeclarativeItem *) {} + virtual void itemVisibilityChanged(QDeclarativeItem *) {} + virtual void itemOpacityChanged(QDeclarativeItem *) {} + virtual void itemDestroyed(QDeclarativeItem *) {} + virtual QDeclarativeAnchorsPrivate *anchorPrivate() { return 0; } +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVEITEMCHANGELISTENER diff --git a/src/declarative/graphicsitems/qdeclarativeitemsmodule.cpp b/src/declarative/graphicsitems/qdeclarativeitemsmodule.cpp new file mode 100644 index 00000000..e9af7e7d --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativeitemsmodule.cpp @@ -0,0 +1,261 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativeitemsmodule_p.h" + +#include +#include +#include + +#include "private/qdeclarativeevents_p_p.h" +#include "private/qdeclarativescalegrid_p_p.h" +#include "private/qdeclarativeanimatedimage_p.h" +#include "private/qdeclarativeborderimage_p.h" +#include "private/qdeclarativepositioners_p.h" +#include "private/qdeclarativemousearea_p.h" +#include "private/qdeclarativeflickable_p.h" +#include "private/qdeclarativeflickable_p_p.h" +#include "private/qdeclarativeflipable_p.h" +#include "private/qdeclarativefocuspanel_p.h" +#include "private/qdeclarativefocusscope_p.h" +#include "private/qdeclarativegridview_p.h" +#include "private/qdeclarativeimage_p.h" +#include "private/qdeclarativeitem_p.h" +#include "private/qdeclarativelayoutitem_p.h" +#include "private/qdeclarativelistview_p.h" +#include "private/qdeclarativeloader_p.h" +#include "private/qdeclarativemousearea_p.h" +#include "private/qdeclarativepath_p.h" +#include "private/qdeclarativepathview_p.h" +#include "private/qdeclarativerectangle_p.h" +#include "private/qdeclarativerepeater_p.h" +#include "private/qdeclarativetranslate_p.h" +#include "private/qdeclarativetext_p.h" +#include "private/qdeclarativetextedit_p.h" +#include "private/qdeclarativetextinput_p.h" +#include "private/qdeclarativevisualitemmodel_p.h" +#include "private/qdeclarativegraphicswidget_p.h" +#ifdef QT_WEBKIT_LIB +#include "private/qdeclarativewebview_p.h" +#include "private/qdeclarativewebview_p_p.h" +#endif +#include "private/qdeclarativeanchors_p.h" +#include "private/qdeclarativepincharea_p.h" + +static QDeclarativePrivate::AutoParentResult qgraphicsobject_autoParent(QObject *obj, QObject *parent) +{ + QGraphicsObject* gobj = qobject_cast(obj); + if (!gobj) + return QDeclarativePrivate::IncompatibleObject; + + QGraphicsObject* gparent = qobject_cast(parent); + if (!gparent) + return QDeclarativePrivate::IncompatibleParent; + + gobj->setParentItem(gparent); + return QDeclarativePrivate::Parented; +} + +void QDeclarativeItemModule::defineModule() +{ + QDeclarativePrivate::RegisterAutoParent autoparent = { 0, &qgraphicsobject_autoParent }; + QDeclarativePrivate::qmlregister(QDeclarativePrivate::AutoParentRegistration, &autoparent); +#ifdef QT_NO_MOVIE + qmlRegisterTypeNotAvailable("QtQuick",1,0,"AnimatedImage", + qApp->translate("QDeclarativeAnimatedImage","Qt was built without support for QMovie")); +#else + qmlRegisterType("QtQuick",1,0,"AnimatedImage"); +#endif + qmlRegisterType("QtQuick",1,0,"BorderImage"); + qmlRegisterType("QtQuick",1,0,"Column"); + qmlRegisterType("QtQuick",1,0,"Drag"); + qmlRegisterType("QtQuick",1,0,"Flickable"); + qmlRegisterType("QtQuick",1,0,"Flipable"); + qmlRegisterType("QtQuick",1,0,"Flow"); + qmlRegisterType("QtQuick",1,0,"FocusPanel"); + qmlRegisterType("QtQuick",1,0,"FocusScope"); + qmlRegisterType("QtQuick",1,0,"Gradient"); + qmlRegisterType("QtQuick",1,0,"GradientStop"); + qmlRegisterType("QtQuick",1,0,"Grid"); + qmlRegisterType("QtQuick",1,0,"GridView"); + qmlRegisterType("QtQuick",1,0,"Image"); + qmlRegisterType("QtQuick",1,0,"Item"); + qmlRegisterType("QtQuick",1,0,"LayoutItem"); + qmlRegisterType("QtQuick",1,0,"ListView"); + qmlRegisterType("QtQuick",1,0,"Loader"); + qmlRegisterType("QtQuick",1,0,"MouseArea"); + qmlRegisterType("QtQuick",1,0,"Path"); + qmlRegisterType("QtQuick",1,0,"PathAttribute"); + qmlRegisterType("QtQuick",1,0,"PathCubic"); + qmlRegisterType("QtQuick",1,0,"PathLine"); + qmlRegisterType("QtQuick",1,0,"PathPercent"); + qmlRegisterType("QtQuick",1,0,"PathQuad"); + qmlRegisterType("QtQuick",1,0,"PathView"); +#ifndef QT_NO_VALIDATOR + qmlRegisterType("QtQuick",1,0,"IntValidator"); + qmlRegisterType("QtQuick",1,0,"DoubleValidator"); + qmlRegisterType("QtQuick",1,0,"RegExpValidator"); +#endif + qmlRegisterType("QtQuick",1,0,"Rectangle"); + qmlRegisterType("QtQuick",1,0,"Repeater"); + qmlRegisterType("QtQuick",1,0,"Rotation"); + qmlRegisterType("QtQuick",1,0,"Row"); + qmlRegisterType("QtQuick",1,0,"Translate"); + qmlRegisterType("QtQuick",1,0,"Scale"); + qmlRegisterType("QtQuick",1,0,"Text"); + qmlRegisterType("QtQuick",1,0,"TextEdit"); +#ifndef QT_NO_LINEEDIT + qmlRegisterType("QtQuick",1,0,"TextInput"); +#endif + qmlRegisterType("QtQuick",1,0,"ViewSection"); + qmlRegisterType("QtQuick",1,0,"VisualDataModel"); + qmlRegisterType("QtQuick",1,0,"VisualItemModel"); + + qmlRegisterType(); + qmlRegisterType(); + qmlRegisterType(); + qmlRegisterType(); + qmlRegisterType("QtQuick",1,0,"QGraphicsWidget"); + qmlRegisterExtendedType("QtQuick",1,0,"QGraphicsWidget"); + qmlRegisterType(); + qmlRegisterType(); + qmlRegisterType(); + qmlRegisterType(); +#ifndef QT_NO_VALIDATOR + qmlRegisterType(); +#endif + qmlRegisterType(); +#ifndef QT_NO_ACTION + qmlRegisterType(); +#endif + qmlRegisterType(); + qmlRegisterType(); +#ifndef QT_NO_GRAPHICSEFFECT + qmlRegisterType(); +#endif + + qmlRegisterUncreatableType("QtQuick",1,0,"KeyNavigation",QDeclarativeKeyNavigationAttached::tr("KeyNavigation is only available via attached properties")); + qmlRegisterUncreatableType("QtQuick",1,0,"Keys",QDeclarativeKeysAttached::tr("Keys is only available via attached properties")); + + // QtQuick 1.1 items + qmlRegisterType("QtQuick",1,1,"PinchArea"); + qmlRegisterType("QtQuick",1,1,"Pinch"); + qmlRegisterType(); + qmlRegisterType("QtQuick",1,1,"Item"); + qmlRegisterType("QtQuick",1,1,"MouseArea"); + qmlRegisterType("QtQuick",1,1,"Flickable"); + qmlRegisterType("QtQuick",1,1,"ListView"); + qmlRegisterType("QtQuick",1,1,"GridView"); + qmlRegisterType("QtQuick",1,1,"Row"); + qmlRegisterType("QtQuick",1,1,"Grid"); + qmlRegisterType("QtQuick",1,1,"Flow"); + qmlRegisterType("QtQuick",1,1,"Repeater"); + qmlRegisterType("QtQuick",1,1,"Text"); + qmlRegisterType("QtQuick",1,1,"TextEdit"); +#ifndef QT_NO_LINEEDIT + qmlRegisterType("QtQuick",1,1,"TextInput"); +#endif + qmlRegisterRevision("QtQuick",1,1); + qmlRegisterRevision("QtQuick",1,0); + qmlRegisterRevision("QtQuick",1,1); + qmlRegisterRevision("QtQuick",1,0); + qmlRegisterRevision("QtQuick",1,1); + qmlRegisterUncreatableType("QtQuick",1,1,"LayoutMirroring", QDeclarativeLayoutMirroringAttached::tr("LayoutMirroring is only available via attached properties")); + +#ifndef QT_NO_IMPORT_QT47_QML +#ifdef QT_NO_MOVIE + qmlRegisterTypeNotAvailable("Qt",4,7,"AnimatedImage", + qApp->translate("QDeclarativeAnimatedImage","Qt was built without support for QMovie")); +#else + qmlRegisterType("Qt",4,7,"AnimatedImage"); +#endif + qmlRegisterType("Qt",4,7,"BorderImage"); + qmlRegisterType("Qt",4,7,"Column"); + qmlRegisterType("Qt",4,7,"Drag"); + qmlRegisterType("Qt",4,7,"Flickable"); + qmlRegisterType("Qt",4,7,"Flipable"); + qmlRegisterType("Qt",4,7,"Flow"); + qmlRegisterType("Qt",4,7,"FocusPanel"); + qmlRegisterType("Qt",4,7,"FocusScope"); + qmlRegisterType("Qt",4,7,"Gradient"); + qmlRegisterType("Qt",4,7,"GradientStop"); + qmlRegisterType("Qt",4,7,"Grid"); + qmlRegisterType("Qt",4,7,"GridView"); + qmlRegisterType("Qt",4,7,"Image"); + qmlRegisterType("Qt",4,7,"Item"); + qmlRegisterType("Qt",4,7,"LayoutItem"); + qmlRegisterType("Qt",4,7,"ListView"); + qmlRegisterType("Qt",4,7,"Loader"); + qmlRegisterType("Qt",4,7,"MouseArea"); + qmlRegisterType("Qt",4,7,"Path"); + qmlRegisterType("Qt",4,7,"PathAttribute"); + qmlRegisterType("Qt",4,7,"PathCubic"); + qmlRegisterType("Qt",4,7,"PathLine"); + qmlRegisterType("Qt",4,7,"PathPercent"); + qmlRegisterType("Qt",4,7,"PathQuad"); + qmlRegisterType("Qt",4,7,"PathView"); +#ifndef QT_NO_VALIDATOR + qmlRegisterType("Qt",4,7,"IntValidator"); + qmlRegisterType("Qt",4,7,"DoubleValidator"); + qmlRegisterType("Qt",4,7,"RegExpValidator"); +#endif + qmlRegisterType("Qt",4,7,"Rectangle"); + qmlRegisterType("Qt",4,7,"Repeater"); + qmlRegisterType("Qt",4,7,"Rotation"); + qmlRegisterType("Qt",4,7,"Row"); + qmlRegisterType("Qt",4,7,"Translate"); + qmlRegisterType("Qt",4,7,"Scale"); + qmlRegisterType("Qt",4,7,"Text"); + qmlRegisterType("Qt",4,7,"TextEdit"); +#ifndef QT_NO_LINEEDIT + qmlRegisterType("Qt",4,7,"TextInput"); +#endif + qmlRegisterType("Qt",4,7,"ViewSection"); + qmlRegisterType("Qt",4,7,"VisualDataModel"); + qmlRegisterType("Qt",4,7,"VisualItemModel"); + + qmlRegisterType("Qt",4,7,"QGraphicsWidget"); + qmlRegisterExtendedType("Qt",4,7,"QGraphicsWidget"); + + qmlRegisterUncreatableType("Qt",4,7,"KeyNavigation",QDeclarativeKeyNavigationAttached::tr("KeyNavigation is only available via attached properties")); + qmlRegisterUncreatableType("Qt",4,7,"Keys",QDeclarativeKeysAttached::tr("Keys is only available via attached properties")); +#endif +} diff --git a/src/declarative/graphicsitems/qdeclarativeitemsmodule_p.h b/src/declarative/graphicsitems/qdeclarativeitemsmodule_p.h new file mode 100644 index 00000000..9c399d08 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativeitemsmodule_p.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEITEMMODULE_H +#define QDECLARATIVEITEMMODULE_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeItemModule +{ +public: + static void defineModule(); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEITEMMODULE_H diff --git a/src/declarative/graphicsitems/qdeclarativelayoutitem.cpp b/src/declarative/graphicsitems/qdeclarativelayoutitem.cpp new file mode 100644 index 00000000..cd0d18df --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativelayoutitem.cpp @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativelayoutitem_p.h" + +#include + +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmlclass LayoutItem QDeclarativeLayoutItem + \ingroup qml-utility-elements + \since 4.7 + \brief The LayoutItem element allows declarative UI elements to be placed inside Qt's Graphics View layouts. + + LayoutItem is a variant of \l Item with additional size hint properties. These properties provide the size hints + necessary for items to work in conjunction with Qt \l{Graphics View Framework}{Graphics View} layout classes + such as QGraphicsLinearLayout and QGraphicsGridLayout. The Qt layout mechanisms will resize the LayoutItem as appropriate, + taking its size hints into account, and you can propagate this to the other elements in your UI via anchors and bindings. + + This is a QGraphicsLayoutItem subclass, and its properties merely expose the QGraphicsLayoutItem functionality to QML. + + The \l{declarative/cppextensions/qgraphicslayouts/layoutitem}{LayoutItem example} + demonstrates how a LayoutItem can be used within a \l{Graphics View Framework}{Graphics View} + scene. +*/ + +/*! + \qmlproperty QSizeF LayoutItem::maximumSize + + The maximumSize property can be set to specify the maximum desired size of this LayoutItem +*/ + +/*! + \qmlproperty QSizeF LayoutItem::minimumSize + + The minimumSize property can be set to specify the minimum desired size of this LayoutItem +*/ + +/*! + \qmlproperty QSizeF LayoutItem::preferredSize + + The preferredSize property can be set to specify the preferred size of this LayoutItem +*/ + +QDeclarativeLayoutItem::QDeclarativeLayoutItem(QDeclarativeItem* parent) + : QDeclarativeItem(parent), m_maximumSize(INT_MAX,INT_MAX), m_minimumSize(0,0), m_preferredSize(0,0) +{ + setGraphicsItem(this); +} + +void QDeclarativeLayoutItem::setGeometry(const QRectF & rect) +{ + setX(rect.x()); + setY(rect.y()); + setWidth(rect.width()); + setHeight(rect.height()); +} + +QSizeF QDeclarativeLayoutItem::sizeHint(Qt::SizeHint w, const QSizeF &constraint) const +{ + Q_UNUSED(constraint); + if(w == Qt::MinimumSize){ + return m_minimumSize; + }else if(w == Qt::MaximumSize){ + return m_maximumSize; + }else{ + return m_preferredSize; + } +} + +QT_END_NAMESPACE diff --git a/src/declarative/graphicsitems/qdeclarativelayoutitem_p.h b/src/declarative/graphicsitems/qdeclarativelayoutitem_p.h new file mode 100644 index 00000000..3982bbf7 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativelayoutitem_p.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEGRAPHICSLAYOUTITEM_H +#define QDECLARATIVEGRAPHICSLAYOUTITEM_H +#include "qdeclarativeitem.h" + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeLayoutItem : public QDeclarativeItem, public QGraphicsLayoutItem +{ + Q_OBJECT + Q_INTERFACES(QGraphicsLayoutItem) + Q_PROPERTY(QSizeF maximumSize READ maximumSize WRITE setMaximumSize NOTIFY maximumSizeChanged) + Q_PROPERTY(QSizeF minimumSize READ minimumSize WRITE setMinimumSize NOTIFY minimumSizeChanged) + Q_PROPERTY(QSizeF preferredSize READ preferredSize WRITE setPreferredSize NOTIFY preferredSizeChanged) +public: + QDeclarativeLayoutItem(QDeclarativeItem* parent=0); + + QSizeF maximumSize() const { return m_maximumSize; } + void setMaximumSize(const QSizeF &s) { if(s==m_maximumSize) return; m_maximumSize = s; emit maximumSizeChanged(); } + + QSizeF minimumSize() const { return m_minimumSize; } + void setMinimumSize(const QSizeF &s) { if(s==m_minimumSize) return; m_minimumSize = s; emit minimumSizeChanged(); } + + QSizeF preferredSize() const { return m_preferredSize; } + void setPreferredSize(const QSizeF &s) { if(s==m_preferredSize) return; m_preferredSize = s; emit preferredSizeChanged(); } + + virtual void setGeometry(const QRectF & rect); +protected: + virtual QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const; + +Q_SIGNALS: + void maximumSizeChanged(); + void minimumSizeChanged(); + void preferredSizeChanged(); + +private: + QSizeF m_maximumSize; + QSizeF m_minimumSize; + QSizeF m_preferredSize; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeLayoutItem) + +QT_END_HEADER +#endif diff --git a/src/declarative/graphicsitems/qdeclarativelistview.cpp b/src/declarative/graphicsitems/qdeclarativelistview.cpp new file mode 100644 index 00000000..331dc35f --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativelistview.cpp @@ -0,0 +1,3651 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativelistview_p.h" + +#include "private/qdeclarativeflickable_p_p.h" +#include "private/qdeclarativevisualitemmodel_p.h" + +#include "private/qdeclarativesmoothedanimation_p_p.h" +#include +#include +#include +#include + +#include +#include +#include +#include "qplatformdefs.h" + +QT_BEGIN_NAMESPACE + +#ifndef QML_FLICK_SNAPONETHRESHOLD +#define QML_FLICK_SNAPONETHRESHOLD 30 +#endif + +void QDeclarativeViewSection::setProperty(const QString &property) +{ + if (property != m_property) { + m_property = property; + emit propertyChanged(); + } +} + +void QDeclarativeViewSection::setCriteria(QDeclarativeViewSection::SectionCriteria criteria) +{ + if (criteria != m_criteria) { + m_criteria = criteria; + emit criteriaChanged(); + } +} + +void QDeclarativeViewSection::setDelegate(QDeclarativeComponent *delegate) +{ + if (delegate != m_delegate) { + m_delegate = delegate; + emit delegateChanged(); + } +} + +QString QDeclarativeViewSection::sectionString(const QString &value) +{ + if (m_criteria == FirstCharacter) + return value.isEmpty() ? QString() : value.at(0); + else + return value; +} + +//---------------------------------------------------------------------------- + +class FxListItem +{ +public: + FxListItem(QDeclarativeItem *i, QDeclarativeListView *v) : item(i), section(0), view(v) { + attached = static_cast(qmlAttachedPropertiesObject(item)); + if (attached) + attached->setView(view); + } + ~FxListItem() {} + qreal position() const { + if (section) { + if (view->orientation() == QDeclarativeListView::Vertical) + return section->y(); + else + return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -section->width()-section->x() : section->x()); + } else { + return itemPosition(); + } + } + + qreal itemPosition() const { + if (view->orientation() == QDeclarativeListView::Vertical) + return item->y(); + else + return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -item->width()-item->x() : item->x()); + } + qreal size() const { + if (section) + return (view->orientation() == QDeclarativeListView::Vertical ? item->height()+section->height() : item->width()+section->width()); + else + return (view->orientation() == QDeclarativeListView::Vertical ? item->height() : item->width()); + } + qreal itemSize() const { + return (view->orientation() == QDeclarativeListView::Vertical ? item->height() : item->width()); + } + qreal sectionSize() const { + if (section) + return (view->orientation() == QDeclarativeListView::Vertical ? section->height() : section->width()); + return 0.0; + } + qreal endPosition() const { + if (view->orientation() == QDeclarativeListView::Vertical) { + return item->y() + (item->height() >= 1.0 ? item->height() : 1) - 1; + } else { + return (view->effectiveLayoutDirection() == Qt::RightToLeft + ? -item->width()-item->x() + (item->width() >= 1.0 ? item->width() : 1) + : item->x() + (item->width() >= 1.0 ? item->width() : 1)) - 1; + } + } + void setPosition(qreal pos) { + if (view->orientation() == QDeclarativeListView::Vertical) { + if (section) { + section->setY(pos); + pos += section->height(); + } + item->setY(pos); + } else { + if (view->effectiveLayoutDirection() == Qt::RightToLeft) { + if (section) { + section->setX(-section->width()-pos); + pos += section->width(); + } + item->setX(-item->width()-pos); + } else { + if (section) { + section->setX(pos); + pos += section->width(); + } + item->setX(pos); + } + } + } + void setSize(qreal size) { + if (view->orientation() == QDeclarativeListView::Vertical) + item->setHeight(size); + else + item->setWidth(size); + } + bool contains(qreal x, qreal y) const { + return (x >= item->x() && x < item->x() + item->width() && + y >= item->y() && y < item->y() + item->height()); + } + + QDeclarativeItem *item; + QDeclarativeItem *section; + QDeclarativeListView *view; + QDeclarativeListViewAttached *attached; + int index; +}; + +//---------------------------------------------------------------------------- + +class QDeclarativeListViewPrivate : public QDeclarativeFlickablePrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeListView) + +public: + QDeclarativeListViewPrivate() + : currentItem(0), orient(QDeclarativeListView::Vertical), layoutDirection(Qt::LeftToRight) + , visiblePos(0), visibleIndex(0) + , averageSize(100.0), currentIndex(-1), requestedIndex(-1) + , itemCount(0), highlightRangeStart(0), highlightRangeEnd(0) + , highlightRangeStartValid(false), highlightRangeEndValid(false) + , highlightComponent(0), highlight(0), trackedItem(0) + , moveReason(Other), buffer(0), highlightPosAnimator(0), highlightSizeAnimator(0) + , sectionCriteria(0), spacing(0.0) + , highlightMoveSpeed(400), highlightMoveDuration(-1) + , highlightResizeSpeed(400), highlightResizeDuration(-1), highlightRange(QDeclarativeListView::NoHighlightRange) + , snapMode(QDeclarativeListView::NoSnap), overshootDist(0.0) + , footerComponent(0), footer(0), headerComponent(0), header(0) + , bufferMode(BufferBefore | BufferAfter) + , ownModel(false), wrap(false), autoHighlight(true), haveHighlightRange(false) + , correctFlick(false), inFlickCorrection(false), lazyRelease(false) + , deferredRelease(false), layoutScheduled(false), currentIndexCleared(false) + , inViewportMoved(false) + , minExtentDirty(true), maxExtentDirty(true) + {} + + void init(); + void clear(); + FxListItem *createItem(int modelIndex); + void releaseItem(FxListItem *item); + + FxListItem *visibleItem(int modelIndex) const { + if (modelIndex >= visibleIndex && modelIndex < visibleIndex + visibleItems.count()) { + for (int i = modelIndex - visibleIndex; i < visibleItems.count(); ++i) { + FxListItem *item = visibleItems.at(i); + if (item->index == modelIndex) + return item; + } + } + return 0; + } + + FxListItem *firstVisibleItem() const { + const qreal pos = isRightToLeft() ? -position()-size() : position(); + for (int i = 0; i < visibleItems.count(); ++i) { + FxListItem *item = visibleItems.at(i); + if (item->index != -1 && item->endPosition() > pos) + return item; + } + return visibleItems.count() ? visibleItems.first() : 0; + } + + // Returns the item before modelIndex, if created. + // May return an item marked for removal. + FxListItem *itemBefore(int modelIndex) const { + if (modelIndex < visibleIndex) + return 0; + int idx = 1; + int lastIndex = -1; + while (idx < visibleItems.count()) { + FxListItem *item = visibleItems.at(idx); + if (item->index != -1) + lastIndex = item->index; + if (item->index == modelIndex) + return visibleItems.at(idx-1); + ++idx; + } + if (lastIndex == modelIndex-1) + return visibleItems.last(); + return 0; + } + + void regenerate() { + Q_Q(QDeclarativeListView); + if (q->isComponentComplete()) { + if (header) { + if (q->scene()) + q->scene()->removeItem(header->item); + header->item->deleteLater(); + delete header; + header = 0; + } + if (footer) { + if (q->scene()) + q->scene()->removeItem(footer->item); + footer->item->deleteLater(); + delete footer; + footer = 0; + } + updateHeader(); + updateFooter(); + clear(); + setPosition(0); + q->refill(); + updateCurrent(currentIndex); + } + } + + void mirrorChange() { + Q_Q(QDeclarativeListView); + regenerate(); + } + + bool isRightToLeft() const { + Q_Q(const QDeclarativeListView); + return orient == QDeclarativeListView::Horizontal && q->effectiveLayoutDirection() == Qt::RightToLeft; + } + + qreal position() const { + Q_Q(const QDeclarativeListView); + return orient == QDeclarativeListView::Vertical ? q->contentY() : q->contentX(); + } + + void setPosition(qreal pos) { + Q_Q(QDeclarativeListView); + if (orient == QDeclarativeListView::Vertical) { + q->QDeclarativeFlickable::setContentY(pos); + } else { + if (isRightToLeft()) + q->QDeclarativeFlickable::setContentX(-pos-size()); + else + q->QDeclarativeFlickable::setContentX(pos); + } + } + qreal size() const { + Q_Q(const QDeclarativeListView); + return orient == QDeclarativeListView::Vertical ? q->height() : q->width(); + } + + qreal originPosition() const { + qreal pos = 0; + if (!visibleItems.isEmpty()) { + pos = (*visibleItems.constBegin())->position(); + if (visibleIndex > 0) + pos -= visibleIndex * (averageSize + spacing); + } + return pos; + } + + qreal lastPosition() const { + qreal pos = 0; + if (!visibleItems.isEmpty()) { + int invisibleCount = visibleItems.count() - visibleIndex; + for (int i = visibleItems.count()-1; i >= 0; --i) { + if (visibleItems.at(i)->index != -1) { + invisibleCount = model->count() - visibleItems.at(i)->index - 1; + break; + } + } + pos = (*(--visibleItems.constEnd()))->endPosition() + invisibleCount * (averageSize + spacing); + } else if (model && model->count()) { + pos = model->count() * averageSize + (model->count()-1) * spacing; + } + return pos; + } + + qreal startPosition() const { + return isRightToLeft() ? -lastPosition()-1 : originPosition(); + } + + qreal endPosition() const { + return isRightToLeft() ? -originPosition()-1 : lastPosition(); + } + + qreal positionAt(int modelIndex) const { + if (FxListItem *item = visibleItem(modelIndex)) + return item->position(); + if (!visibleItems.isEmpty()) { + if (modelIndex < visibleIndex) { + int count = visibleIndex - modelIndex; + qreal cs = 0; + if (modelIndex == currentIndex && currentItem) { + cs = currentItem->size() + spacing; + --count; + } + return (*visibleItems.constBegin())->position() - count * (averageSize + spacing) - cs; + } else { + int idx = visibleItems.count() - 1; + while (idx >= 0 && visibleItems.at(idx)->index == -1) + --idx; + if (idx < 0) + idx = visibleIndex; + else + idx = visibleItems.at(idx)->index; + int count = modelIndex - idx - 1; + + return (*(--visibleItems.constEnd()))->endPosition() + spacing + count * (averageSize + spacing) + 1; + } + } + return 0; + } + + qreal endPositionAt(int modelIndex) const { + if (FxListItem *item = visibleItem(modelIndex)) + return item->endPosition(); + if (!visibleItems.isEmpty()) { + if (modelIndex < visibleIndex) { + int count = visibleIndex - modelIndex; + return (*visibleItems.constBegin())->position() - (count - 1) * (averageSize + spacing) - spacing - 1; + } else { + int idx = visibleItems.count() - 1; + while (idx >= 0 && visibleItems.at(idx)->index == -1) + --idx; + if (idx < 0) + idx = visibleIndex; + else + idx = visibleItems.at(idx)->index; + int count = modelIndex - idx - 1; + return (*(--visibleItems.constEnd()))->endPosition() + count * (averageSize + spacing); + } + } + return 0; + } + + QString sectionAt(int modelIndex) { + if (FxListItem *item = visibleItem(modelIndex)) + return item->attached->section(); + + QString section; + if (sectionCriteria) { + QString propValue = model->stringValue(modelIndex, sectionCriteria->property()); + section = sectionCriteria->sectionString(propValue); + } + + return section; + } + + bool isValid() const { + return model && model->count() && model->isValid(); + } + + qreal snapPosAt(qreal pos) { + if (FxListItem *snapItem = snapItemAt(pos)) + return snapItem->position(); + if (visibleItems.count()) { + qreal firstPos = visibleItems.first()->position(); + qreal endPos = visibleItems.last()->position(); + if (pos < firstPos) { + return firstPos - qRound((firstPos - pos) / averageSize) * averageSize; + } else if (pos > endPos) + return endPos + qRound((pos - endPos) / averageSize) * averageSize; + } + return qRound((pos - originPosition()) / averageSize) * averageSize + originPosition(); + } + + FxListItem *snapItemAt(qreal pos) { + FxListItem *snapItem = 0; + for (int i = 0; i < visibleItems.count(); ++i) { + FxListItem *item = visibleItems[i]; + if (item->index == -1) + continue; + qreal itemTop = item->position(); + if (highlight && itemTop >= pos && item->endPosition() <= pos + highlight->size() - 1) + return item; + if (itemTop+item->size()/2 >= pos && itemTop-item->size()/2 < pos) + snapItem = item; + } + return snapItem; + } + + int lastVisibleIndex() const { + int lastIndex = -1; + for (int i = visibleItems.count()-1; i >= 0; --i) { + FxListItem *listItem = visibleItems.at(i); + if (listItem->index != -1) { + lastIndex = listItem->index; + break; + } + } + return lastIndex; + } + + // map a model index to visibleItems index. + int mapFromModel(int modelIndex) const { + if (modelIndex < visibleIndex || modelIndex >= visibleIndex + visibleItems.count()) + return -1; + for (int i = 0; i < visibleItems.count(); ++i) { + FxListItem *listItem = visibleItems.at(i); + if (listItem->index == modelIndex) + return i; + if (listItem->index > modelIndex) + return -1; + } + return -1; // Not in visibleList + } + + void updateViewport() { + Q_Q(QDeclarativeListView); + if (orient == QDeclarativeListView::Vertical) { + q->setContentHeight(endPosition() - startPosition() + 1); + } else { + q->setContentWidth(endPosition() - startPosition() + 1); + } + } + + void itemGeometryChanged(QDeclarativeItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) { + Q_Q(QDeclarativeListView); + QDeclarativeFlickablePrivate::itemGeometryChanged(item, newGeometry, oldGeometry); + if (!q->isComponentComplete()) + return; + if (item != contentItem && (!highlight || item != highlight->item)) { + if ((orient == QDeclarativeListView::Vertical && newGeometry.height() != oldGeometry.height()) + || (orient == QDeclarativeListView::Horizontal && newGeometry.width() != oldGeometry.width())) { + scheduleLayout(); + } + } + if ((header && header->item == item) || (footer && footer->item == item)) { + if (header) + updateHeader(); + if (footer) + updateFooter(); + } + if (currentItem && currentItem->item == item) + updateHighlight(); + if (trackedItem && trackedItem->item == item) + q->trackedPositionChanged(); + } + + // for debugging only + void checkVisible() const { + int skip = 0; + for (int i = 0; i < visibleItems.count(); ++i) { + FxListItem *listItem = visibleItems.at(i); + if (listItem->index == -1) { + ++skip; + } else if (listItem->index != visibleIndex + i - skip) { + qFatal("index %d %d %d", visibleIndex, i, listItem->index); + } + } + } + + void refill(qreal from, qreal to, bool doBuffer = false); + void scheduleLayout(); + void layout(); + void updateUnrequestedIndexes(); + void updateUnrequestedPositions(); + void updateTrackedItem(); + void createHighlight(); + void updateHighlight(); + void createSection(FxListItem *); + void updateSections(); + void updateCurrentSection(); + void updateCurrent(int); + void updateAverage(); + void updateHeader(); + void updateFooter(); + void fixupPosition(); + void positionViewAtIndex(int index, int mode); + virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent); + virtual void flick(QDeclarativeFlickablePrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, + QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity); + + QDeclarativeGuard model; + QVariant modelVariant; + QList visibleItems; + QHash unrequestedItems; + FxListItem *currentItem; + QDeclarativeListView::Orientation orient; + Qt::LayoutDirection layoutDirection; + qreal visiblePos; + int visibleIndex; + qreal averageSize; + int currentIndex; + int requestedIndex; + int itemCount; + qreal highlightRangeStart; + qreal highlightRangeEnd; + bool highlightRangeStartValid; + bool highlightRangeEndValid; + QDeclarativeComponent *highlightComponent; + FxListItem *highlight; + FxListItem *trackedItem; + enum MovementReason { Other, SetIndex, Mouse }; + MovementReason moveReason; + int buffer; + QSmoothedAnimation *highlightPosAnimator; + QSmoothedAnimation *highlightSizeAnimator; + QDeclarativeViewSection *sectionCriteria; + QString currentSection; + static const int sectionCacheSize = 4; + QDeclarativeItem *sectionCache[sectionCacheSize]; + qreal spacing; + qreal highlightMoveSpeed; + int highlightMoveDuration; + qreal highlightResizeSpeed; + int highlightResizeDuration; + QDeclarativeListView::HighlightRangeMode highlightRange; + QDeclarativeListView::SnapMode snapMode; + qreal overshootDist; + QDeclarativeComponent *footerComponent; + FxListItem *footer; + QDeclarativeComponent *headerComponent; + FxListItem *header; + enum BufferMode { NoBuffer = 0x00, BufferBefore = 0x01, BufferAfter = 0x02 }; + int bufferMode; + mutable qreal minExtent; + mutable qreal maxExtent; + + bool ownModel : 1; + bool wrap : 1; + bool autoHighlight : 1; + bool haveHighlightRange : 1; + bool correctFlick : 1; + bool inFlickCorrection : 1; + bool lazyRelease : 1; + bool deferredRelease : 1; + bool layoutScheduled : 1; + bool currentIndexCleared : 1; + bool inViewportMoved : 1; + mutable bool minExtentDirty : 1; + mutable bool maxExtentDirty : 1; +}; + +void QDeclarativeListViewPrivate::init() +{ + Q_Q(QDeclarativeListView); + q->setFlag(QGraphicsItem::ItemIsFocusScope); + addItemChangeListener(this, Geometry); + QObject::connect(q, SIGNAL(movementEnded()), q, SLOT(animStopped())); + q->setFlickableDirection(QDeclarativeFlickable::VerticalFlick); + ::memset(sectionCache, 0, sizeof(QDeclarativeItem*) * sectionCacheSize); +} + +void QDeclarativeListViewPrivate::clear() +{ + timeline.clear(); + for (int i = 0; i < visibleItems.count(); ++i) + releaseItem(visibleItems.at(i)); + visibleItems.clear(); + for (int i = 0; i < sectionCacheSize; ++i) { + delete sectionCache[i]; + sectionCache[i] = 0; + } + visiblePos = header ? header->size() : 0; + visibleIndex = 0; + releaseItem(currentItem); + currentItem = 0; + createHighlight(); + trackedItem = 0; + minExtentDirty = true; + maxExtentDirty = true; + itemCount = 0; +} + +FxListItem *QDeclarativeListViewPrivate::createItem(int modelIndex) +{ + Q_Q(QDeclarativeListView); + // create object + requestedIndex = modelIndex; + FxListItem *listItem = 0; + if (QDeclarativeItem *item = model->item(modelIndex, false)) { + listItem = new FxListItem(item, q); + listItem->index = modelIndex; + // initialise attached properties + if (sectionCriteria) { + QString propValue = model->stringValue(modelIndex, sectionCriteria->property()); + listItem->attached->m_section = sectionCriteria->sectionString(propValue); + if (modelIndex > 0) { + if (FxListItem *item = itemBefore(modelIndex)) + listItem->attached->m_prevSection = item->attached->section(); + else + listItem->attached->m_prevSection = sectionAt(modelIndex-1); + } + if (modelIndex < model->count()-1) { + if (FxListItem *item = visibleItem(modelIndex+1)) + listItem->attached->m_nextSection = item->attached->section(); + else + listItem->attached->m_nextSection = sectionAt(modelIndex+1); + } + } + if (model->completePending()) { + // complete + listItem->item->setZValue(1); + listItem->item->setParentItem(q->contentItem()); + model->completeItem(); + } else { + listItem->item->setParentItem(q->contentItem()); + } + QDeclarativeItemPrivate *itemPrivate = static_cast(QGraphicsItemPrivate::get(item)); + itemPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry); + if (sectionCriteria && sectionCriteria->delegate()) { + if (listItem->attached->m_prevSection != listItem->attached->m_section) + createSection(listItem); + } + unrequestedItems.remove(listItem->item); + } + requestedIndex = -1; + + return listItem; +} + +void QDeclarativeListViewPrivate::releaseItem(FxListItem *item) +{ + Q_Q(QDeclarativeListView); + if (!item || !model) + return; + if (trackedItem == item) + trackedItem = 0; + QDeclarativeItemPrivate *itemPrivate = static_cast(QGraphicsItemPrivate::get(item->item)); + itemPrivate->removeItemChangeListener(this, QDeclarativeItemPrivate::Geometry); + if (model->release(item->item) == 0) { + // item was not destroyed, and we no longer reference it. + unrequestedItems.insert(item->item, model->indexOf(item->item, q)); + } + if (item->section) { + int i = 0; + do { + if (!sectionCache[i]) { + sectionCache[i] = item->section; + sectionCache[i]->setVisible(false); + item->section = 0; + break; + } + ++i; + } while (i < sectionCacheSize); + delete item->section; + } + delete item; +} + +void QDeclarativeListViewPrivate::refill(qreal from, qreal to, bool doBuffer) +{ + Q_Q(QDeclarativeListView); + if (!isValid() || !q->isComponentComplete()) + return; + itemCount = model->count(); + qreal bufferFrom = from - buffer; + qreal bufferTo = to + buffer; + qreal fillFrom = from; + qreal fillTo = to; + if (doBuffer && (bufferMode & BufferAfter)) + fillTo = bufferTo; + if (doBuffer && (bufferMode & BufferBefore)) + fillFrom = bufferFrom; + + bool haveValidItems = false; + int modelIndex = visibleIndex; + qreal itemEnd = visiblePos-1; + if (!visibleItems.isEmpty()) { + visiblePos = (*visibleItems.constBegin())->position(); + itemEnd = (*(--visibleItems.constEnd()))->endPosition() + spacing; + int i = visibleItems.count() - 1; + while (i > 0 && visibleItems.at(i)->index == -1) + --i; + if (visibleItems.at(i)->index != -1) { + haveValidItems = true; + modelIndex = visibleItems.at(i)->index + 1; + } + } + + if (haveValidItems && (fillFrom > itemEnd+averageSize+spacing + || fillTo < visiblePos - averageSize - spacing)) { + // We've jumped more than a page. Estimate which items are now + // visible and fill from there. + int count = (fillFrom - itemEnd) / (averageSize + spacing); + for (int i = 0; i < visibleItems.count(); ++i) + releaseItem(visibleItems.at(i)); + visibleItems.clear(); + modelIndex += count; + if (modelIndex >= model->count()) { + count -= modelIndex - model->count() + 1; + modelIndex = model->count() - 1; + } else if (modelIndex < 0) { + count -= modelIndex; + modelIndex = 0; + } + visibleIndex = modelIndex; + visiblePos = itemEnd + count * (averageSize + spacing) + 1; + itemEnd = visiblePos-1; + } + + bool changed = false; + FxListItem *item = 0; + qreal pos = itemEnd + 1; + while (modelIndex < model->count() && pos <= fillTo) { +// qDebug() << "refill: append item" << modelIndex << "pos" << pos; + if (!(item = createItem(modelIndex))) + break; + item->setPosition(pos); + pos += item->size() + spacing; + visibleItems.append(item); + ++modelIndex; + changed = true; + if (doBuffer) // never buffer more than one item per frame + break; + } + while (visibleIndex > 0 && visibleIndex <= model->count() && visiblePos-1 >= fillFrom) { +// qDebug() << "refill: prepend item" << visibleIndex-1 << "current top pos" << visiblePos; + if (!(item = createItem(visibleIndex-1))) + break; + --visibleIndex; + visiblePos -= item->size() + spacing; + item->setPosition(visiblePos); + visibleItems.prepend(item); + changed = true; + if (doBuffer) // never buffer more than one item per frame + break; + } + + if (!lazyRelease || !changed || deferredRelease) { // avoid destroying items in the same frame that we create + while (visibleItems.count() > 1 && (item = visibleItems.first()) && item->endPosition() < bufferFrom) { + if (item->attached->delayRemove()) + break; +// qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endPosition(); + if (item->index != -1) + visibleIndex++; + visibleItems.removeFirst(); + releaseItem(item); + changed = true; + } + while (visibleItems.count() > 1 && (item = visibleItems.last()) && item->position() > bufferTo) { + if (item->attached->delayRemove()) + break; +// qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1 << item->position(); + visibleItems.removeLast(); + releaseItem(item); + changed = true; + } + deferredRelease = false; + } else { + deferredRelease = true; + } + if (changed) { + minExtentDirty = true; + maxExtentDirty = true; + if (visibleItems.count()) + visiblePos = (*visibleItems.constBegin())->position(); + updateAverage(); + if (currentIndex >= 0 && currentItem && !visibleItem(currentIndex)) { + currentItem->setPosition(positionAt(currentIndex)); + updateHighlight(); + } + + if (sectionCriteria) + updateCurrentSection(); + if (header) + updateHeader(); + if (footer) + updateFooter(); + updateViewport(); + updateUnrequestedPositions(); + } else if (!doBuffer && buffer && bufferMode != NoBuffer) { + refill(from, to, true); + } + lazyRelease = false; +} + +void QDeclarativeListViewPrivate::scheduleLayout() +{ + Q_Q(QDeclarativeListView); + if (!layoutScheduled) { + layoutScheduled = true; + QCoreApplication::postEvent(q, new QEvent(QEvent::User), Qt::HighEventPriority); + } +} + +void QDeclarativeListViewPrivate::layout() +{ + Q_Q(QDeclarativeListView); + layoutScheduled = false; + if (!isValid() && !visibleItems.count()) { + clear(); + setPosition(0); + return; + } + if (!visibleItems.isEmpty()) { + bool fixedCurrent = currentItem && visibleItems.first()->item == currentItem->item; + qreal sum = visibleItems.first()->size(); + qreal pos = visibleItems.first()->position() + visibleItems.first()->size() + spacing; + for (int i=1; i < visibleItems.count(); ++i) { + FxListItem *item = visibleItems.at(i); + item->setPosition(pos); + pos += item->size() + spacing; + sum += item->size(); + fixedCurrent = fixedCurrent || (currentItem && item->item == currentItem->item); + } + averageSize = qRound(sum / visibleItems.count()); + // move current item if it is not a visible item. + if (currentIndex >= 0 && currentItem && !fixedCurrent) + currentItem->setPosition(positionAt(currentIndex)); + } + q->refill(); + minExtentDirty = true; + maxExtentDirty = true; + updateHighlight(); + if (!q->isMoving() && !q->isFlicking()) { + fixupPosition(); + q->refill(); + } + if (header) + updateHeader(); + if (footer) + updateFooter(); + updateViewport(); +} + +void QDeclarativeListViewPrivate::updateUnrequestedIndexes() +{ + Q_Q(QDeclarativeListView); + QHash::iterator it; + for (it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it) + *it = model->indexOf(it.key(), q); +} + +void QDeclarativeListViewPrivate::updateUnrequestedPositions() +{ + Q_Q(QDeclarativeListView); + if (unrequestedItems.count()) { + qreal pos = position(); + QHash::const_iterator it; + for (it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it) { + QDeclarativeItem *item = it.key(); + if (orient == QDeclarativeListView::Vertical) { + if (item->y() + item->height() > pos && item->y() < pos + q->height()) + item->setY(positionAt(*it)); + } else { + if (item->x() + item->width() > pos && item->x() < pos + q->width()) { + if (isRightToLeft()) + item->setX(-positionAt(*it)-item->width()); + else + item->setX(positionAt(*it)); + } + } + } + } +} + +void QDeclarativeListViewPrivate::updateTrackedItem() +{ + Q_Q(QDeclarativeListView); + FxListItem *item = currentItem; + if (highlight) + item = highlight; + trackedItem = item; + if (trackedItem) + q->trackedPositionChanged(); +} + +void QDeclarativeListViewPrivate::createHighlight() +{ + Q_Q(QDeclarativeListView); + bool changed = false; + if (highlight) { + if (trackedItem == highlight) + trackedItem = 0; + if (highlight->item->scene()) + highlight->item->scene()->removeItem(highlight->item); + highlight->item->deleteLater(); + delete highlight; + highlight = 0; + delete highlightPosAnimator; + delete highlightSizeAnimator; + highlightPosAnimator = 0; + highlightSizeAnimator = 0; + changed = true; + } + + if (currentItem) { + QDeclarativeItem *item = 0; + if (highlightComponent) { + QDeclarativeContext *highlightContext = new QDeclarativeContext(qmlContext(q)); + QObject *nobj = highlightComponent->create(highlightContext); + if (nobj) { + QDeclarative_setParent_noEvent(highlightContext, nobj); + item = qobject_cast(nobj); + if (!item) + delete nobj; + } else { + delete highlightContext; + } + } else { + item = new QDeclarativeItem; + } + if (item) { + QDeclarative_setParent_noEvent(item, q->contentItem()); + item->setParentItem(q->contentItem()); + highlight = new FxListItem(item, q); + if (currentItem && autoHighlight) { + if (orient == QDeclarativeListView::Vertical) { + highlight->item->setHeight(currentItem->item->height()); + } else { + highlight->item->setWidth(currentItem->item->width()); + } + highlight->setPosition(currentItem->itemPosition()); + } + QDeclarativeItemPrivate *itemPrivate = static_cast(QGraphicsItemPrivate::get(item)); + itemPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry); + const QLatin1String posProp(orient == QDeclarativeListView::Vertical ? "y" : "x"); + highlightPosAnimator = new QSmoothedAnimation(q); + highlightPosAnimator->target = QDeclarativeProperty(highlight->item, posProp); + highlightPosAnimator->velocity = highlightMoveSpeed; + highlightPosAnimator->userDuration = highlightMoveDuration; + const QLatin1String sizeProp(orient == QDeclarativeListView::Vertical ? "height" : "width"); + highlightSizeAnimator = new QSmoothedAnimation(q); + highlightSizeAnimator->velocity = highlightResizeSpeed; + highlightSizeAnimator->userDuration = highlightResizeDuration; + highlightSizeAnimator->target = QDeclarativeProperty(highlight->item, sizeProp); + if (autoHighlight) { + highlightPosAnimator->restart(); + highlightSizeAnimator->restart(); + } + changed = true; + } + } + if (changed) + emit q->highlightItemChanged(); +} + +void QDeclarativeListViewPrivate::updateHighlight() +{ + if ((!currentItem && highlight) || (currentItem && !highlight)) + createHighlight(); + if (currentItem && autoHighlight && highlight && !hData.moving && !vData.moving) { + // auto-update highlight + highlightPosAnimator->to = isRightToLeft() + ? -currentItem->itemPosition()-currentItem->itemSize() + : currentItem->itemPosition(); + highlightSizeAnimator->to = currentItem->itemSize(); + if (orient == QDeclarativeListView::Vertical) { + if (highlight->item->width() == 0) + highlight->item->setWidth(currentItem->item->width()); + } else { + if (highlight->item->height() == 0) + highlight->item->setHeight(currentItem->item->height()); + } + highlightPosAnimator->restart(); + highlightSizeAnimator->restart(); + } + updateTrackedItem(); +} + +void QDeclarativeListViewPrivate::createSection(FxListItem *listItem) +{ + Q_Q(QDeclarativeListView); + if (!sectionCriteria || !sectionCriteria->delegate()) + return; + if (listItem->attached->m_prevSection != listItem->attached->m_section) { + if (!listItem->section) { + qreal pos = listItem->position(); + int i = sectionCacheSize-1; + while (i >= 0 && !sectionCache[i]) + --i; + if (i >= 0) { + listItem->section = sectionCache[i]; + sectionCache[i] = 0; + listItem->section->setVisible(true); + QDeclarativeContext *context = QDeclarativeEngine::contextForObject(listItem->section)->parentContext(); + context->setContextProperty(QLatin1String("section"), listItem->attached->m_section); + } else { + QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q)); + context->setContextProperty(QLatin1String("section"), listItem->attached->m_section); + QObject *nobj = sectionCriteria->delegate()->beginCreate(context); + if (nobj) { + QDeclarative_setParent_noEvent(context, nobj); + listItem->section = qobject_cast(nobj); + if (!listItem->section) { + delete nobj; + } else { + listItem->section->setZValue(1); + QDeclarative_setParent_noEvent(listItem->section, q->contentItem()); + listItem->section->setParentItem(q->contentItem()); + } + } else { + delete context; + } + sectionCriteria->delegate()->completeCreate(); + } + listItem->setPosition(pos); + } else { + QDeclarativeContext *context = QDeclarativeEngine::contextForObject(listItem->section)->parentContext(); + context->setContextProperty(QLatin1String("section"), listItem->attached->m_section); + } + } else if (listItem->section) { + qreal pos = listItem->position(); + int i = 0; + do { + if (!sectionCache[i]) { + sectionCache[i] = listItem->section; + sectionCache[i]->setVisible(false); + listItem->section = 0; + return; + } + ++i; + } while (i < sectionCacheSize); + delete listItem->section; + listItem->section = 0; + listItem->setPosition(pos); + } +} + +void QDeclarativeListViewPrivate::updateSections() +{ + if (sectionCriteria && !visibleItems.isEmpty()) { + QString prevSection; + if (visibleIndex > 0) + prevSection = sectionAt(visibleIndex-1); + QDeclarativeListViewAttached *prevAtt = 0; + int idx = -1; + for (int i = 0; i < visibleItems.count(); ++i) { + QDeclarativeListViewAttached *attached = visibleItems.at(i)->attached; + attached->setPrevSection(prevSection); + if (visibleItems.at(i)->index != -1) { + QString propValue = model->stringValue(visibleItems.at(i)->index, sectionCriteria->property()); + attached->setSection(sectionCriteria->sectionString(propValue)); + idx = visibleItems.at(i)->index; + } + createSection(visibleItems.at(i)); + if (prevAtt) + prevAtt->setNextSection(attached->section()); + prevSection = attached->section(); + prevAtt = attached; + } + if (prevAtt) { + if (idx > 0 && idx < model->count()-1) + prevAtt->setNextSection(sectionAt(idx+1)); + else + prevAtt->setNextSection(QString()); + } + } +} + +void QDeclarativeListViewPrivate::updateCurrentSection() +{ + Q_Q(QDeclarativeListView); + if (!sectionCriteria || visibleItems.isEmpty()) { + if (!currentSection.isEmpty()) { + currentSection.clear(); + emit q->currentSectionChanged(); + } + return; + } + int index = 0; + while (index < visibleItems.count() && visibleItems.at(index)->endPosition() < position()) + ++index; + + QString newSection = currentSection; + if (index < visibleItems.count()) + newSection = visibleItems.at(index)->attached->section(); + else + newSection = visibleItems.first()->attached->section(); + if (newSection != currentSection) { + currentSection = newSection; + emit q->currentSectionChanged(); + } +} + +void QDeclarativeListViewPrivate::updateCurrent(int modelIndex) +{ + Q_Q(QDeclarativeListView); + if (!q->isComponentComplete() || !isValid() || modelIndex < 0 || modelIndex >= model->count()) { + if (currentItem) { + currentItem->attached->setIsCurrentItem(false); + releaseItem(currentItem); + currentItem = 0; + currentIndex = modelIndex; + emit q->currentIndexChanged(); + updateHighlight(); + } else if (currentIndex != modelIndex) { + currentIndex = modelIndex; + emit q->currentIndexChanged(); + } + return; + } + + if (currentItem && currentIndex == modelIndex) { + updateHighlight(); + return; + } + FxListItem *oldCurrentItem = currentItem; + currentIndex = modelIndex; + currentItem = createItem(modelIndex); + if (oldCurrentItem && (!currentItem || oldCurrentItem->item != currentItem->item)) + oldCurrentItem->attached->setIsCurrentItem(false); + if (currentItem) { + if (modelIndex == visibleIndex - 1 && visibleItems.count()) { + // We can calculate exact postion in this case + currentItem->setPosition(visibleItems.first()->position() - currentItem->size() - spacing); + } else { + // Create current item now and position as best we can. + // Its position will be corrected when it becomes visible. + currentItem->setPosition(positionAt(modelIndex)); + } + currentItem->item->setFocus(true); + currentItem->attached->setIsCurrentItem(true); + // Avoid showing section delegate twice. We still need the section heading so that + // currentItem positioning works correctly. + // This is slightly sub-optimal, but section heading caching minimizes the impact. + if (currentItem->section) + currentItem->section->setVisible(false); + if (visibleItems.isEmpty()) + averageSize = currentItem->size(); + } + updateHighlight(); + emit q->currentIndexChanged(); + // Release the old current item + releaseItem(oldCurrentItem); +} + +void QDeclarativeListViewPrivate::updateAverage() +{ + if (!visibleItems.count()) + return; + qreal sum = 0.0; + for (int i = 0; i < visibleItems.count(); ++i) + sum += visibleItems.at(i)->size(); + averageSize = qRound(sum / visibleItems.count()); +} + +void QDeclarativeListViewPrivate::updateFooter() +{ + Q_Q(QDeclarativeListView); + if (!footer && footerComponent) { + QDeclarativeItem *item = 0; + QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q)); + QObject *nobj = footerComponent->create(context); + if (nobj) { + QDeclarative_setParent_noEvent(context, nobj); + item = qobject_cast(nobj); + if (!item) + delete nobj; + } else { + delete context; + } + if (item) { + QDeclarative_setParent_noEvent(item, q->contentItem()); + item->setParentItem(q->contentItem()); + item->setZValue(1); + QDeclarativeItemPrivate *itemPrivate = static_cast(QGraphicsItemPrivate::get(item)); + itemPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry); + footer = new FxListItem(item, q); + } + } + if (footer) { + if (visibleItems.count()) { + qreal endPos = lastPosition() + 1; + if (lastVisibleIndex() == model->count()-1) { + footer->setPosition(endPos); + } else { + qreal visiblePos = position() + q->height(); + if (endPos <= visiblePos || footer->position() < endPos) + footer->setPosition(endPos); + } + } else { + footer->setPosition(visiblePos); + } + } +} + +void QDeclarativeListViewPrivate::updateHeader() +{ + Q_Q(QDeclarativeListView); + if (!header && headerComponent) { + QDeclarativeItem *item = 0; + QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q)); + QObject *nobj = headerComponent->create(context); + if (nobj) { + QDeclarative_setParent_noEvent(context, nobj); + item = qobject_cast(nobj); + if (!item) + delete nobj; + } else { + delete context; + } + if (item) { + QDeclarative_setParent_noEvent(item, q->contentItem()); + item->setParentItem(q->contentItem()); + item->setZValue(1); + QDeclarativeItemPrivate *itemPrivate = static_cast(QGraphicsItemPrivate::get(item)); + itemPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry); + header = new FxListItem(item, q); + } + } + if (header) { + if (visibleItems.count()) { + qreal startPos = originPosition(); + if (visibleIndex == 0) { + header->setPosition(startPos - header->size()); + } else { + if (position() <= startPos || header->position() > startPos - header->size()) + header->setPosition(startPos - header->size()); + } + } else { + if (itemCount == 0) + visiblePos = header->size(); + header->setPosition(0); + } + } +} + +void QDeclarativeListViewPrivate::fixupPosition() +{ + if ((haveHighlightRange && highlightRange == QDeclarativeListView::StrictlyEnforceRange) + || snapMode != QDeclarativeListView::NoSnap) + moveReason = Other; + if (orient == QDeclarativeListView::Vertical) + fixupY(); + else + fixupX(); +} + +void QDeclarativeListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent) +{ + if ((orient == QDeclarativeListView::Horizontal && &data == &vData) + || (orient == QDeclarativeListView::Vertical && &data == &hData)) + return; + + correctFlick = false; + fixupMode = moveReason == Mouse ? fixupMode : Immediate; + bool strictHighlightRange = haveHighlightRange && highlightRange == QDeclarativeListView::StrictlyEnforceRange; + + qreal highlightStart; + qreal highlightEnd; + qreal viewPos; + if (isRightToLeft()) { + // Handle Right-To-Left exceptions + viewPos = -position()-size(); + highlightStart = highlightRangeStartValid ? size() - highlightRangeEnd : highlightRangeStart; + highlightEnd = highlightRangeEndValid ? size() - highlightRangeStart : highlightRangeEnd; + } else { + viewPos = position(); + highlightStart = highlightRangeStart; + highlightEnd = highlightRangeEnd; + } + + if (snapMode != QDeclarativeListView::NoSnap && moveReason != QDeclarativeListViewPrivate::SetIndex) { + qreal tempPosition = isRightToLeft() ? -position()-size() : position(); + if (snapMode == QDeclarativeListView::SnapOneItem && moveReason == Mouse) { + // if we've been dragged < averageSize/2 then bias towards the next item + qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset); + qreal bias = 0; + if (data.velocity > 0 && dist > QML_FLICK_SNAPONETHRESHOLD && dist < averageSize/2) + bias = averageSize/2; + else if (data.velocity < 0 && dist < -QML_FLICK_SNAPONETHRESHOLD && dist > -averageSize/2) + bias = -averageSize/2; + if (isRightToLeft()) + bias = -bias; + tempPosition -= bias; + } + FxListItem *topItem = snapItemAt(tempPosition+highlightStart); + if (!topItem && strictHighlightRange && currentItem) { + // StrictlyEnforceRange always keeps an item in range + updateHighlight(); + topItem = currentItem; + } + FxListItem *bottomItem = snapItemAt(tempPosition+highlightEnd); + if (!bottomItem && strictHighlightRange && currentItem) { + // StrictlyEnforceRange always keeps an item in range + updateHighlight(); + bottomItem = currentItem; + } + qreal pos; + bool isInBounds = -position() > maxExtent && -position() <= minExtent; + if (topItem && (isInBounds || strictHighlightRange)) { + if (topItem->index == 0 && header && tempPosition+highlightStart < header->position()+header->size()/2 && !strictHighlightRange) { + pos = isRightToLeft() ? - header->position() + highlightStart - size() : header->position() - highlightStart; + } else { + if (isRightToLeft()) + pos = qMax(qMin(-topItem->position() + highlightStart - size(), -maxExtent), -minExtent); + else + pos = qMax(qMin(topItem->position() - highlightStart, -maxExtent), -minExtent); + } + } else if (bottomItem && isInBounds) { + if (isRightToLeft()) + pos = qMax(qMin(-bottomItem->position() + highlightEnd - size(), -maxExtent), -minExtent); + else + pos = qMax(qMin(bottomItem->position() - highlightEnd, -maxExtent), -minExtent); + } else { + QDeclarativeFlickablePrivate::fixup(data, minExtent, maxExtent); + return; + } + + qreal dist = qAbs(data.move + pos); + if (dist > 0) { + timeline.reset(data.move); + if (fixupMode != Immediate) { + timeline.move(data.move, -pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2); + data.fixingUp = true; + } else { + timeline.set(data.move, -pos); + } + vTime = timeline.time(); + } + } else if (currentItem && strictHighlightRange + && moveReason != QDeclarativeListViewPrivate::SetIndex) { + updateHighlight(); + qreal pos = currentItem->itemPosition(); + if (viewPos < pos + currentItem->itemSize() - highlightEnd) + viewPos = pos + currentItem->itemSize() - highlightEnd; + if (viewPos > pos - highlightStart) + viewPos = pos - highlightStart; + if (isRightToLeft()) + viewPos = -viewPos-size(); + + timeline.reset(data.move); + if (viewPos != position()) { + if (fixupMode != Immediate) { + timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2); + data.fixingUp = true; + } else { + timeline.set(data.move, -viewPos); + } + } + vTime = timeline.time(); + } else { + QDeclarativeFlickablePrivate::fixup(data, minExtent, maxExtent); + } + data.inOvershoot = false; + fixupMode = Normal; +} + +void QDeclarativeListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, + QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity) +{ + Q_Q(QDeclarativeListView); + + data.fixingUp = false; + moveReason = Mouse; + if ((!haveHighlightRange || highlightRange != QDeclarativeListView::StrictlyEnforceRange) && snapMode == QDeclarativeListView::NoSnap) { + correctFlick = true; + QDeclarativeFlickablePrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity); + return; + } + qreal maxDistance = 0; + qreal dataValue = isRightToLeft() ? -data.move.value()+size() : data.move.value(); + qreal highlightStart = isRightToLeft() && highlightRangeStartValid ? size()-highlightRangeEnd : highlightRangeStart; + // -ve velocity means list is moving up/left + if (velocity > 0) { + if (data.move.value() < minExtent) { + if (snapMode == QDeclarativeListView::SnapOneItem && !hData.flicking && !vData.flicking) { + // if we've been dragged < averageSize/2 then bias towards the next item + qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset); + qreal bias = dist < averageSize/2 ? averageSize/2 : 0; + if (isRightToLeft()) + bias = -bias; + data.flickTarget = -snapPosAt(-(dataValue - highlightStart) - bias) + highlightStart; + maxDistance = qAbs(data.flickTarget - data.move.value()); + velocity = maxVelocity; + } else { + maxDistance = qAbs(minExtent - data.move.value()); + } + } + if (snapMode == QDeclarativeListView::NoSnap && highlightRange != QDeclarativeListView::StrictlyEnforceRange) + data.flickTarget = minExtent; + } else { + if (data.move.value() > maxExtent) { + if (snapMode == QDeclarativeListView::SnapOneItem && !hData.flicking && !vData.flicking) { + // if we've been dragged < averageSize/2 then bias towards the next item + qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset); + qreal bias = -dist < averageSize/2 ? averageSize/2 : 0; + if (isRightToLeft()) + bias = -bias; + data.flickTarget = -snapPosAt(-(dataValue - highlightStart) + bias) + highlightStart; + maxDistance = qAbs(data.flickTarget - data.move.value()); + velocity = -maxVelocity; + } else { + maxDistance = qAbs(maxExtent - data.move.value()); + } + } + if (snapMode == QDeclarativeListView::NoSnap && highlightRange != QDeclarativeListView::StrictlyEnforceRange) + data.flickTarget = maxExtent; + } + + bool overShoot = boundsBehavior == QDeclarativeFlickable::DragAndOvershootBounds; + + if (maxDistance > 0 || overShoot) { + // These modes require the list to stop exactly on an item boundary. + // The initial flick will estimate the boundary to stop on. + // Since list items can have variable sizes, the boundary will be + // reevaluated and adjusted as we approach the boundary. + qreal v = velocity; + if (maxVelocity != -1 && maxVelocity < qAbs(v)) { + if (v < 0) + v = -maxVelocity; + else + v = maxVelocity; + } + if (!hData.flicking && !vData.flicking) { + // the initial flick - estimate boundary + qreal accel = deceleration; + qreal v2 = v * v; + overshootDist = qreal(0.0); + // + averageSize/4 to encourage moving at least one item in the flick direction + qreal dist = v2 / (accel * qreal(2.0)) + averageSize/4; + if (maxDistance > 0) + dist = qMin(dist, maxDistance); + if (v > 0) + dist = -dist; + if ((maxDistance > qreal(0.0) && v2 / (2.0f * maxDistance) < accel) || snapMode == QDeclarativeListView::SnapOneItem) { + if (snapMode != QDeclarativeListView::SnapOneItem) { + qreal distTemp = isRightToLeft() ? -dist : dist; + data.flickTarget = -snapPosAt(-(dataValue - highlightStart) + distTemp) + highlightStart; + } + data.flickTarget = isRightToLeft() ? -data.flickTarget+size() : data.flickTarget; + if (overShoot) { + if (data.flickTarget >= minExtent) { + overshootDist = overShootDistance(vSize); + data.flickTarget += overshootDist; + } else if (data.flickTarget <= maxExtent) { + overshootDist = overShootDistance(vSize); + data.flickTarget -= overshootDist; + } + } + qreal adjDist = -data.flickTarget + data.move.value(); + if (qAbs(adjDist) > qAbs(dist)) { + // Prevent painfully slow flicking - adjust velocity to suit flickDeceleration + qreal adjv2 = accel * 2.0f * qAbs(adjDist); + if (adjv2 > v2) { + v2 = adjv2; + v = qSqrt(v2); + if (dist > 0) + v = -v; + } + } + dist = adjDist; + accel = v2 / (2.0f * qAbs(dist)); + } else if (overShoot) { + data.flickTarget = data.move.value() - dist; + if (data.flickTarget >= minExtent) { + overshootDist = overShootDistance(vSize); + data.flickTarget += overshootDist; + } else if (data.flickTarget <= maxExtent) { + overshootDist = overShootDistance(vSize); + data.flickTarget -= overshootDist; + } + } + + timeline.reset(data.move); + timeline.accel(data.move, v, accel, maxDistance + overshootDist); + timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this)); + if (!hData.flicking && q->xflick()) { + hData.flicking = true; + emit q->flickingChanged(); + emit q->flickingHorizontallyChanged(); + emit q->flickStarted(); + } + if (!vData.flicking && q->yflick()) { + vData.flicking = true; + emit q->flickingChanged(); + emit q->flickingVerticallyChanged(); + emit q->flickStarted(); + } + correctFlick = true; + } else { + // reevaluate the target boundary. + qreal newtarget = data.flickTarget; + if (snapMode != QDeclarativeListView::NoSnap || highlightRange == QDeclarativeListView::StrictlyEnforceRange) { + qreal tempFlickTarget = isRightToLeft() ? -data.flickTarget+size() : data.flickTarget; + newtarget = -snapPosAt(-(tempFlickTarget - highlightStart)) + highlightStart; + newtarget = isRightToLeft() ? -newtarget+size() : newtarget; + } + if (velocity < 0 && newtarget <= maxExtent) + newtarget = maxExtent - overshootDist; + else if (velocity > 0 && newtarget >= minExtent) + newtarget = minExtent + overshootDist; + if (newtarget == data.flickTarget) { // boundary unchanged - nothing to do + if (qAbs(velocity) < MinimumFlickVelocity) + correctFlick = false; + return; + } + data.flickTarget = newtarget; + qreal dist = -newtarget + data.move.value(); + if ((v < 0 && dist < 0) || (v > 0 && dist > 0)) { + correctFlick = false; + timeline.reset(data.move); + fixup(data, minExtent, maxExtent); + return; + } + + timeline.reset(data.move); + timeline.accelDistance(data.move, v, -dist); + timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this)); + } + } else { + correctFlick = false; + timeline.reset(data.move); + fixup(data, minExtent, maxExtent); + } +} + +//---------------------------------------------------------------------------- + +/*! + \qmlclass ListView QDeclarativeListView + \ingroup qml-view-elements + \since 4.7 + \inherits Flickable + \brief The ListView item provides a list view of items provided by a model. + + A ListView displays data from models created from built-in QML elements like ListModel + and XmlListModel, or custom model classes defined in C++ that inherit from + QAbstractListModel. + + A ListView has a \l model, which defines the data to be displayed, and + a \l delegate, which defines how the data should be displayed. Items in a + ListView are laid out horizontally or vertically. List views are inherently + flickable because ListView inherits from \l Flickable. + + \section1 Example Usage + + The following example shows the definition of a simple list model defined + in a file called \c ContactModel.qml: + + \snippet doc/src/snippets/declarative/listview/ContactModel.qml 0 + + Another component can display this model data in a ListView, like this: + + \snippet doc/src/snippets/declarative/listview/listview.qml import + \codeline + \snippet doc/src/snippets/declarative/listview/listview.qml classdocs simple + + \image listview-simple.png + + Here, the ListView creates a \c ContactModel component for its model, and a \l Text element + for its delegate. The view will create a new \l Text component for each item in the model. Notice + the delegate is able to access the model's \c name and \c number data directly. + + An improved list view is shown below. The delegate is visually improved and is moved + into a separate \c contactDelegate component. + + \snippet doc/src/snippets/declarative/listview/listview.qml classdocs advanced + \image listview-highlight.png + + The currently selected item is highlighted with a blue \l Rectangle using the \l highlight property, + and \c focus is set to \c true to enable keyboard navigation for the list view. + The list view itself is a focus scope (see \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} for more details). + + Delegates are instantiated as needed and may be destroyed at any time. + State should \e never be stored in a delegate. + + ListView attaches a number of properties to the root item of the delegate, for example + \c {ListView.isCurrentItem}. In the following example, the root delegate item can access + this attached property directly as \c ListView.isCurrentItem, while the child + \c contactInfo object must refer to this property as \c wrapper.ListView.isCurrentItem. + + \snippet doc/src/snippets/declarative/listview/listview.qml isCurrentItem + + \note Views do not enable \e clip automatically. If the view + is not clipped by another item or the screen, it will be necessary + to set \e {clip: true} in order to have the out of view items clipped + nicely. + + \sa {QML Data Models}, GridView, {Models and Views: ListView Examples}{ListView examples} +*/ + +QDeclarativeListView::QDeclarativeListView(QDeclarativeItem *parent) + : QDeclarativeFlickable(*(new QDeclarativeListViewPrivate), parent) +{ + Q_D(QDeclarativeListView); + d->init(); +} + +QDeclarativeListView::~QDeclarativeListView() +{ + Q_D(QDeclarativeListView); + d->clear(); + if (d->ownModel) + delete d->model; + delete d->header; + delete d->footer; +} + +/*! + \qmlattachedproperty bool ListView::isCurrentItem + This attached property is true if this delegate is the current item; otherwise false. + + It is attached to each instance of the delegate. + + This property may be used to adjust the appearance of the current item, for example: + + \snippet doc/src/snippets/declarative/listview/listview.qml isCurrentItem +*/ + +/*! + \qmlattachedproperty ListView ListView::view + This attached property holds the view that manages this delegate instance. + + It is attached to each instance of the delegate. +*/ + +/*! + \qmlattachedproperty string ListView::previousSection + This attached property holds the section of the previous element. + + It is attached to each instance of the delegate. + + The section is evaluated using the \l {ListView::section.property}{section} properties. +*/ + +/*! + \qmlattachedproperty string ListView::nextSection + This attached property holds the section of the next element. + + It is attached to each instance of the delegate. + + The section is evaluated using the \l {ListView::section.property}{section} properties. +*/ + +/*! + \qmlattachedproperty string ListView::section + This attached property holds the section of this element. + + It is attached to each instance of the delegate. + + The section is evaluated using the \l {ListView::section.property}{section} properties. +*/ + +/*! + \qmlattachedproperty bool ListView::delayRemove + This attached property holds whether the delegate may be destroyed. + + It is attached to each instance of the delegate. + + It is sometimes necessary to delay the destruction of an item + until an animation completes. + + The example delegate below ensures that the animation completes before + the item is removed from the list. + + \snippet doc/src/snippets/declarative/listview/listview.qml delayRemove +*/ + +/*! + \qmlattachedsignal ListView::onAdd() + This attached handler is called immediately after an item is added to the view. +*/ + +/*! + \qmlattachedsignal ListView::onRemove() + This attached handler is called immediately before an item is removed from the view. +*/ + +/*! + \qmlproperty model ListView::model + This property holds the model providing data for the list. + + The model provides the set of data that is used to create the items + in the view. Models can be created directly in QML using \l ListModel, \l XmlListModel + or \l VisualItemModel, or provided by C++ model classes. If a C++ model class is + used, it must be a subclass of \l QAbstractItemModel or a simple list. + + \sa {qmlmodels}{Data Models} +*/ +QVariant QDeclarativeListView::model() const +{ + Q_D(const QDeclarativeListView); + return d->modelVariant; +} + +void QDeclarativeListView::setModel(const QVariant &model) +{ + Q_D(QDeclarativeListView); + if (d->modelVariant == model) + return; + if (d->model) { + disconnect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int))); + disconnect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int))); + disconnect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int))); + disconnect(d->model, SIGNAL(itemsChanged(int,int)), this, SLOT(itemsChanged(int,int))); + disconnect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset())); + disconnect(d->model, SIGNAL(createdItem(int,QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*))); + disconnect(d->model, SIGNAL(destroyingItem(QDeclarativeItem*)), this, SLOT(destroyingItem(QDeclarativeItem*))); + } + d->clear(); + QDeclarativeVisualModel *oldModel = d->model; + d->model = 0; + d->setPosition(0); + d->modelVariant = model; + QObject *object = qvariant_cast(model); + QDeclarativeVisualModel *vim = 0; + if (object && (vim = qobject_cast(object))) { + if (d->ownModel) { + delete oldModel; + d->ownModel = false; + } + d->model = vim; + } else { + if (!d->ownModel) { + d->model = new QDeclarativeVisualDataModel(qmlContext(this), this); + d->ownModel = true; + } else { + d->model = oldModel; + } + if (QDeclarativeVisualDataModel *dataModel = qobject_cast(d->model)) + dataModel->setModel(model); + } + if (d->model) { + d->bufferMode = QDeclarativeListViewPrivate::BufferBefore | QDeclarativeListViewPrivate::BufferAfter; + if (isComponentComplete()) { + updateSections(); + refill(); + if ((d->currentIndex >= d->model->count() || d->currentIndex < 0) && !d->currentIndexCleared) { + setCurrentIndex(0); + } else { + d->moveReason = QDeclarativeListViewPrivate::SetIndex; + d->updateCurrent(d->currentIndex); + if (d->highlight && d->currentItem) { + if (d->autoHighlight) + d->highlight->setPosition(d->currentItem->position()); + d->updateTrackedItem(); + } + } + d->updateViewport(); + } + connect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int))); + connect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int))); + connect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int))); + connect(d->model, SIGNAL(itemsChanged(int,int)), this, SLOT(itemsChanged(int,int))); + connect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset())); + connect(d->model, SIGNAL(createdItem(int,QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*))); + connect(d->model, SIGNAL(destroyingItem(QDeclarativeItem*)), this, SLOT(destroyingItem(QDeclarativeItem*))); + emit countChanged(); + } + emit modelChanged(); +} + +/*! + \qmlproperty Component ListView::delegate + + The delegate provides a template defining each item instantiated by the view. + The index is exposed as an accessible \c index property. Properties of the + model are also available depending upon the type of \l {qmlmodels}{Data Model}. + + The number of elements in the delegate has a direct effect on the + flicking performance of the view. If at all possible, place functionality + that is not needed for the normal display of the delegate in a \l Loader which + can load additional elements when needed. + + The ListView will lay out the items based on the size of the root item + in the delegate. + + It is recommended that the delagate's size be a whole number to avoid sub-pixel + alignment of items. + + \note Delegates are instantiated as needed and may be destroyed at any time. + State should \e never be stored in a delegate. +*/ +QDeclarativeComponent *QDeclarativeListView::delegate() const +{ + Q_D(const QDeclarativeListView); + if (d->model) { + if (QDeclarativeVisualDataModel *dataModel = qobject_cast(d->model)) + return dataModel->delegate(); + } + + return 0; +} + +void QDeclarativeListView::setDelegate(QDeclarativeComponent *delegate) +{ + Q_D(QDeclarativeListView); + if (delegate == this->delegate()) + return; + if (!d->ownModel) { + d->model = new QDeclarativeVisualDataModel(qmlContext(this)); + d->ownModel = true; + } + if (QDeclarativeVisualDataModel *dataModel = qobject_cast(d->model)) { + int oldCount = dataModel->count(); + dataModel->setDelegate(delegate); + if (isComponentComplete()) { + for (int i = 0; i < d->visibleItems.count(); ++i) + d->releaseItem(d->visibleItems.at(i)); + d->visibleItems.clear(); + d->releaseItem(d->currentItem); + d->currentItem = 0; + updateSections(); + refill(); + d->moveReason = QDeclarativeListViewPrivate::SetIndex; + d->updateCurrent(d->currentIndex); + if (d->highlight && d->currentItem) { + if (d->autoHighlight) + d->highlight->setPosition(d->currentItem->position()); + d->updateTrackedItem(); + } + d->updateViewport(); + } + if (oldCount != dataModel->count()) + emit countChanged(); + } + emit delegateChanged(); +} + +/*! + \qmlproperty int ListView::currentIndex + \qmlproperty Item ListView::currentItem + + The \c currentIndex property holds the index of the current item, and + \c currentItem holds the current item. Setting the currentIndex to -1 + will clear the highlight and set currentItem to null. + + If highlightFollowsCurrentItem is \c true, setting either of these + properties will smoothly scroll the ListView so that the current + item becomes visible. + + Note that the position of the current item + may only be approximate until it becomes visible in the view. +*/ +int QDeclarativeListView::currentIndex() const +{ + Q_D(const QDeclarativeListView); + return d->currentIndex; +} + +void QDeclarativeListView::setCurrentIndex(int index) +{ + Q_D(QDeclarativeListView); + if (d->requestedIndex >= 0) // currently creating item + return; + d->currentIndexCleared = (index == -1); + if (index == d->currentIndex) + return; + if (isComponentComplete() && d->isValid()) { + if (d->layoutScheduled) + d->layout(); + d->moveReason = QDeclarativeListViewPrivate::SetIndex; + d->updateCurrent(index); + } else if (d->currentIndex != index) { + d->currentIndex = index; + emit currentIndexChanged(); + } +} + +QDeclarativeItem *QDeclarativeListView::currentItem() +{ + Q_D(QDeclarativeListView); + if (!d->currentItem) + return 0; + return d->currentItem->item; +} + +/*! + \qmlproperty Item ListView::highlightItem + + This holds the highlight item created from the \l highlight component. + + The \c highlightItem is managed by the view unless + \l highlightFollowsCurrentItem is set to false. + + \sa highlight, highlightFollowsCurrentItem +*/ +QDeclarativeItem *QDeclarativeListView::highlightItem() +{ + Q_D(QDeclarativeListView); + if (!d->highlight) + return 0; + return d->highlight->item; +} + +/*! + \qmlproperty int ListView::count + This property holds the number of items in the view. +*/ +int QDeclarativeListView::count() const +{ + Q_D(const QDeclarativeListView); + if (d->model) + return d->model->count(); + return 0; +} + +/*! + \qmlproperty Component ListView::highlight + This property holds the component to use as the highlight. + + An instance of the highlight component is created for each list. + The geometry of the resulting component instance is managed by the list + so as to stay with the current item, unless the highlightFollowsCurrentItem + property is false. + + \sa highlightItem, highlightFollowsCurrentItem, {Models and Views: ListView Examples}{ListView examples} +*/ +QDeclarativeComponent *QDeclarativeListView::highlight() const +{ + Q_D(const QDeclarativeListView); + return d->highlightComponent; +} + +void QDeclarativeListView::setHighlight(QDeclarativeComponent *highlight) +{ + Q_D(QDeclarativeListView); + if (highlight != d->highlightComponent) { + d->highlightComponent = highlight; + d->createHighlight(); + if (d->currentItem) + d->updateHighlight(); + emit highlightChanged(); + } +} + +/*! + \qmlproperty bool ListView::highlightFollowsCurrentItem + This property holds whether the highlight is managed by the view. + + If this property is true (the default value), the highlight is moved smoothly + to follow the current item. Otherwise, the + highlight is not moved by the view, and any movement must be implemented + by the highlight. + + Here is a highlight with its motion defined by a \l {SpringAnimation} item: + + \snippet doc/src/snippets/declarative/listview/listview.qml highlightFollowsCurrentItem + + Note that the highlight animation also affects the way that the view + is scrolled. This is because the view moves to maintain the + highlight within the preferred highlight range (or visible viewport). + + \sa highlight, highlightMoveSpeed +*/ +bool QDeclarativeListView::highlightFollowsCurrentItem() const +{ + Q_D(const QDeclarativeListView); + return d->autoHighlight; +} + +void QDeclarativeListView::setHighlightFollowsCurrentItem(bool autoHighlight) +{ + Q_D(QDeclarativeListView); + if (d->autoHighlight != autoHighlight) { + d->autoHighlight = autoHighlight; + if (autoHighlight) { + d->updateHighlight(); + } else { + if (d->highlightPosAnimator) + d->highlightPosAnimator->stop(); + if (d->highlightSizeAnimator) + d->highlightSizeAnimator->stop(); + } + emit highlightFollowsCurrentItemChanged(); + } +} + +//###Possibly rename these properties, since they are very useful even without a highlight? +/*! + \qmlproperty real ListView::preferredHighlightBegin + \qmlproperty real ListView::preferredHighlightEnd + \qmlproperty enumeration ListView::highlightRangeMode + + These properties define the preferred range of the highlight (for the current item) + within the view. The \c preferredHighlightBegin value must be less than the + \c preferredHighlightEnd value. + + These properties affect the position of the current item when the list is scrolled. + For example, if the currently selected item should stay in the middle of the + list when the view is scrolled, set the \c preferredHighlightBegin and + \c preferredHighlightEnd values to the top and bottom coordinates of where the middle + item would be. If the \c currentItem is changed programmatically, the list will + automatically scroll so that the current item is in the middle of the view. + Furthermore, the behavior of the current item index will occur whether or not a + highlight exists. + + Valid values for \c highlightRangeMode are: + + \list + \o ListView.ApplyRange - the view attempts to maintain the highlight within the range. + However, the highlight can move outside of the range at the ends of the list or due + to mouse interaction. + \o ListView.StrictlyEnforceRange - the highlight never moves outside of the range. + The current item changes if a keyboard or mouse action would cause the highlight to move + outside of the range. + \o ListView.NoHighlightRange - this is the default value. + \endlist +*/ +qreal QDeclarativeListView::preferredHighlightBegin() const +{ + Q_D(const QDeclarativeListView); + return d->highlightRangeStart; +} + +void QDeclarativeListView::setPreferredHighlightBegin(qreal start) +{ + Q_D(QDeclarativeListView); + d->highlightRangeStartValid = true; + if (d->highlightRangeStart == start) + return; + d->highlightRangeStart = start; + d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; + emit preferredHighlightBeginChanged(); +} + +void QDeclarativeListView::resetPreferredHighlightBegin() +{ + Q_D(QDeclarativeListView); + d->highlightRangeStartValid = false; + if (d->highlightRangeStart == 0) + return; + d->highlightRangeStart = 0; + emit preferredHighlightBeginChanged(); +} + +qreal QDeclarativeListView::preferredHighlightEnd() const +{ + Q_D(const QDeclarativeListView); + return d->highlightRangeEnd; +} + +void QDeclarativeListView::setPreferredHighlightEnd(qreal end) +{ + Q_D(QDeclarativeListView); + d->highlightRangeEndValid = true; + if (d->highlightRangeEnd == end) + return; + d->highlightRangeEnd = end; + d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; + emit preferredHighlightEndChanged(); +} + +void QDeclarativeListView::resetPreferredHighlightEnd() +{ + Q_D(QDeclarativeListView); + d->highlightRangeEndValid = false; + if (d->highlightRangeEnd == 0) + return; + d->highlightRangeEnd = 0; + emit preferredHighlightEndChanged(); +} + +QDeclarativeListView::HighlightRangeMode QDeclarativeListView::highlightRangeMode() const +{ + Q_D(const QDeclarativeListView); + return d->highlightRange; +} + +void QDeclarativeListView::setHighlightRangeMode(HighlightRangeMode mode) +{ + Q_D(QDeclarativeListView); + if (d->highlightRange == mode) + return; + d->highlightRange = mode; + d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; + emit highlightRangeModeChanged(); +} + +/*! + \qmlproperty real ListView::spacing + + This property holds the spacing between items. + + The default value is 0. +*/ +qreal QDeclarativeListView::spacing() const +{ + Q_D(const QDeclarativeListView); + return d->spacing; +} + +void QDeclarativeListView::setSpacing(qreal spacing) +{ + Q_D(QDeclarativeListView); + if (spacing != d->spacing) { + d->spacing = spacing; + d->layout(); + emit spacingChanged(); + } +} + +/*! + \qmlproperty enumeration ListView::orientation + This property holds the orientation of the list. + + Possible values: + + \list + \o ListView.Horizontal - Items are laid out horizontally + \o ListView.Vertical (default) - Items are laid out vertically + \endlist + + \table + \row + \o Horizontal orientation: + \image ListViewHorizontal.png + + \row + \o Vertical orientation: + \image listview-highlight.png + \endtable +*/ +QDeclarativeListView::Orientation QDeclarativeListView::orientation() const +{ + Q_D(const QDeclarativeListView); + return d->orient; +} + +void QDeclarativeListView::setOrientation(QDeclarativeListView::Orientation orientation) +{ + Q_D(QDeclarativeListView); + if (d->orient != orientation) { + d->orient = orientation; + if (d->orient == QDeclarativeListView::Vertical) { + setContentWidth(-1); + setFlickableDirection(VerticalFlick); + setContentX(0); + } else { + setContentHeight(-1); + setFlickableDirection(HorizontalFlick); + setContentY(0); + } + d->regenerate(); + emit orientationChanged(); + } +} + +/*! + \qmlproperty enumeration ListView::layoutDirection + This property holds the layout direction of the horizontal list. + + Possible values: + + \list + \o Qt.LeftToRight (default) - Items will be laid out from left to right. + \o Qt.RightToLeft - Items will be laid out from right to let. + \endlist + + When using the attached property \l {LayoutMirroring::enabled} for locale layouts, + the layout direction of the horizontal list will be mirrored. However, the actual property + \c layoutDirection will remain unchanged. You can use the property + \l {LayoutMirroring::enabled} to determine whether the direction has been mirrored. + + \sa {LayoutMirroring}{LayoutMirroring} +*/ + +Qt::LayoutDirection QDeclarativeListView::layoutDirection() const +{ + Q_D(const QDeclarativeListView); + return d->layoutDirection; +} + +void QDeclarativeListView::setLayoutDirection(Qt::LayoutDirection layoutDirection) +{ + Q_D(QDeclarativeListView); + if (d->layoutDirection != layoutDirection) { + d->layoutDirection = layoutDirection; + d->regenerate(); + emit layoutDirectionChanged(); + } +} + +Qt::LayoutDirection QDeclarativeListView::effectiveLayoutDirection() const +{ + Q_D(const QDeclarativeListView); + if (d->effectiveLayoutMirror) + return d->layoutDirection == Qt::RightToLeft ? Qt::LeftToRight : Qt::RightToLeft; + else + return d->layoutDirection; +} + +/*! + \qmlproperty bool ListView::keyNavigationWraps + This property holds whether the list wraps key navigation. + + If this is true, key navigation that would move the current item selection + past the end of the list instead wraps around and moves the selection to + the start of the list, and vice-versa. + + By default, key navigation is not wrapped. +*/ +bool QDeclarativeListView::isWrapEnabled() const +{ + Q_D(const QDeclarativeListView); + return d->wrap; +} + +void QDeclarativeListView::setWrapEnabled(bool wrap) +{ + Q_D(QDeclarativeListView); + if (d->wrap == wrap) + return; + d->wrap = wrap; + emit keyNavigationWrapsChanged(); +} + +/*! + \qmlproperty int ListView::cacheBuffer + This property determines whether delegates are retained outside the + visible area of the view. + + If this value is non-zero, the view keeps as many delegates + instantiated as it can fit within the buffer specified. For example, + if in a vertical view the delegate is 20 pixels high and \c cacheBuffer is + set to 40, then up to 2 delegates above and 2 delegates below the visible + area may be retained. + + Note that cacheBuffer is not a pixel buffer - it only maintains additional + instantiated delegates. + + Setting this value can improve the smoothness of scrolling behavior at the expense + of additional memory usage. It is not a substitute for creating efficient + delegates; the fewer elements in a delegate, the faster a view can be + scrolled. +*/ +int QDeclarativeListView::cacheBuffer() const +{ + Q_D(const QDeclarativeListView); + return d->buffer; +} + +void QDeclarativeListView::setCacheBuffer(int b) +{ + Q_D(QDeclarativeListView); + if (d->buffer != b) { + d->buffer = b; + if (isComponentComplete()) { + d->bufferMode = QDeclarativeListViewPrivate::BufferBefore | QDeclarativeListViewPrivate::BufferAfter; + refill(); + } + emit cacheBufferChanged(); + } +} + +/*! + \qmlproperty string ListView::section.property + \qmlproperty enumeration ListView::section.criteria + \qmlproperty Component ListView::section.delegate + + These properties hold the expression to be evaluated for the \l section attached property. + + The \l section attached property enables a ListView to be visually + separated into different parts. These properties determine how sections + are created. + + \c section.property holds the name of the property that is the basis + of each section. + + \c section.criteria holds the criteria for forming each section based on + \c section.property. This value can be one of: + + \list + \o ViewSection.FullString (default) - sections are created based on the + \c section.property value. + \o ViewSection.FirstCharacter - sections are created based on the first + character of the \c section.property value (for example, 'A', 'B', 'C' + sections, etc. for an address book) + \endlist + + \c section.delegate holds the delegate component for each section. + + Each item in the list has attached properties named \c ListView.section, + \c ListView.previousSection and \c ListView.nextSection. These may be + used to place a section header for related items. + + For example, here is a ListView that displays a list of animals, separated + into sections. Each item in the ListView is placed in a different section + depending on the "size" property of the model item. The \c sectionHeading + delegate component provides the light blue bar that marks the beginning of + each section. + + + \snippet examples/declarative/modelviews/listview/sections.qml 0 + + \image qml-listview-sections-example.png + + \note Adding sections to a ListView does not automatically re-order the + list items by the section criteria. + If the model is not ordered by section, then it is possible that + the sections created will not be unique; each boundary between + differing sections will result in a section header being created + even if that section exists elsewhere. + + \sa {Models and Views: ListView Examples}{ListView examples} +*/ +QDeclarativeViewSection *QDeclarativeListView::sectionCriteria() +{ + Q_D(QDeclarativeListView); + if (!d->sectionCriteria) { + d->sectionCriteria = new QDeclarativeViewSection(this); + connect(d->sectionCriteria, SIGNAL(propertyChanged()), this, SLOT(updateSections())); + } + return d->sectionCriteria; +} + +/*! + \qmlproperty string ListView::currentSection + This property holds the section that is currently at the beginning of the view. +*/ +QString QDeclarativeListView::currentSection() const +{ + Q_D(const QDeclarativeListView); + return d->currentSection; +} + +/*! + \qmlproperty real ListView::highlightMoveSpeed + \qmlproperty int ListView::highlightMoveDuration + \qmlproperty real ListView::highlightResizeSpeed + \qmlproperty int ListView::highlightResizeDuration + + These properties hold the move and resize animation speed of the highlight delegate. + + \l highlightFollowsCurrentItem must be true for these properties + to have effect. + + The default value for the speed properties is 400 pixels/second. + The default value for the duration properties is -1, i.e. the + highlight will take as much time as necessary to move at the set speed. + + These properties have the same characteristics as a SmoothedAnimation. + + \sa highlightFollowsCurrentItem +*/ +qreal QDeclarativeListView::highlightMoveSpeed() const +{ + Q_D(const QDeclarativeListView);\ + return d->highlightMoveSpeed; +} + +void QDeclarativeListView::setHighlightMoveSpeed(qreal speed) +{ + Q_D(QDeclarativeListView);\ + if (d->highlightMoveSpeed != speed) { + d->highlightMoveSpeed = speed; + if (d->highlightPosAnimator) + d->highlightPosAnimator->velocity = d->highlightMoveSpeed; + emit highlightMoveSpeedChanged(); + } +} + +int QDeclarativeListView::highlightMoveDuration() const +{ + Q_D(const QDeclarativeListView); + return d->highlightMoveDuration; +} + +void QDeclarativeListView::setHighlightMoveDuration(int duration) +{ + Q_D(QDeclarativeListView);\ + if (d->highlightMoveDuration != duration) { + d->highlightMoveDuration = duration; + if (d->highlightPosAnimator) + d->highlightPosAnimator->userDuration = d->highlightMoveDuration; + emit highlightMoveDurationChanged(); + } +} + +qreal QDeclarativeListView::highlightResizeSpeed() const +{ + Q_D(const QDeclarativeListView);\ + return d->highlightResizeSpeed; +} + +void QDeclarativeListView::setHighlightResizeSpeed(qreal speed) +{ + Q_D(QDeclarativeListView);\ + if (d->highlightResizeSpeed != speed) { + d->highlightResizeSpeed = speed; + if (d->highlightSizeAnimator) + d->highlightSizeAnimator->velocity = d->highlightResizeSpeed; + emit highlightResizeSpeedChanged(); + } +} + +int QDeclarativeListView::highlightResizeDuration() const +{ + Q_D(const QDeclarativeListView); + return d->highlightResizeDuration; +} + +void QDeclarativeListView::setHighlightResizeDuration(int duration) +{ + Q_D(QDeclarativeListView);\ + if (d->highlightResizeDuration != duration) { + d->highlightResizeDuration = duration; + if (d->highlightSizeAnimator) + d->highlightSizeAnimator->userDuration = d->highlightResizeDuration; + emit highlightResizeDurationChanged(); + } +} + +/*! + \qmlproperty enumeration ListView::snapMode + + This property determines how the view scrolling will settle following a drag or flick. + The possible values are: + + \list + \o ListView.NoSnap (default) - the view stops anywhere within the visible area. + \o ListView.SnapToItem - the view settles with an item aligned with the start of + the view. + \o ListView.SnapOneItem - the view settles no more than one item away from the first + visible item at the time the mouse button is released. This mode is particularly + useful for moving one page at a time. + \endlist + + \c snapMode does not affect the \l currentIndex. To update the + \l currentIndex as the list is moved, set \l highlightRangeMode + to \c ListView.StrictlyEnforceRange. + + \sa highlightRangeMode +*/ +QDeclarativeListView::SnapMode QDeclarativeListView::snapMode() const +{ + Q_D(const QDeclarativeListView); + return d->snapMode; +} + +void QDeclarativeListView::setSnapMode(SnapMode mode) +{ + Q_D(QDeclarativeListView); + if (d->snapMode != mode) { + d->snapMode = mode; + emit snapModeChanged(); + } +} + +/*! + \qmlproperty Component ListView::footer + This property holds the component to use as the footer. + + An instance of the footer component is created for each view. The + footer is positioned at the end of the view, after any items. + + \sa header +*/ +QDeclarativeComponent *QDeclarativeListView::footer() const +{ + Q_D(const QDeclarativeListView); + return d->footerComponent; +} + +void QDeclarativeListView::setFooter(QDeclarativeComponent *footer) +{ + Q_D(QDeclarativeListView); + if (d->footerComponent != footer) { + if (d->footer) { + if (scene()) + scene()->removeItem(d->footer->item); + d->footer->item->deleteLater(); + delete d->footer; + d->footer = 0; + } + d->footerComponent = footer; + d->minExtentDirty = true; + d->maxExtentDirty = true; + if (isComponentComplete()) { + d->updateFooter(); + d->updateViewport(); + d->fixupPosition(); + } + emit footerChanged(); + } +} + +/*! + \qmlproperty Component ListView::header + This property holds the component to use as the header. + + An instance of the header component is created for each view. The + header is positioned at the beginning of the view, before any items. + + \sa footer +*/ +QDeclarativeComponent *QDeclarativeListView::header() const +{ + Q_D(const QDeclarativeListView); + return d->headerComponent; +} + +void QDeclarativeListView::setHeader(QDeclarativeComponent *header) +{ + Q_D(QDeclarativeListView); + if (d->headerComponent != header) { + if (d->header) { + if (scene()) + scene()->removeItem(d->header->item); + d->header->item->deleteLater(); + delete d->header; + d->header = 0; + } + d->headerComponent = header; + d->minExtentDirty = true; + d->maxExtentDirty = true; + if (isComponentComplete()) { + d->updateHeader(); + d->updateFooter(); + d->updateViewport(); + d->fixupPosition(); + } + emit headerChanged(); + } +} + +void QDeclarativeListView::setContentX(qreal pos) +{ + Q_D(QDeclarativeListView); + // Positioning the view manually should override any current movement state + d->moveReason = QDeclarativeListViewPrivate::Other; + QDeclarativeFlickable::setContentX(pos); +} + +void QDeclarativeListView::setContentY(qreal pos) +{ + Q_D(QDeclarativeListView); + // Positioning the view manually should override any current movement state + d->moveReason = QDeclarativeListViewPrivate::Other; + QDeclarativeFlickable::setContentY(pos); +} + +bool QDeclarativeListView::event(QEvent *event) +{ + Q_D(QDeclarativeListView); + if (event->type() == QEvent::User) { + if (d->layoutScheduled) + d->layout(); + return true; + } + + return QDeclarativeFlickable::event(event); +} + +void QDeclarativeListView::viewportMoved() +{ + Q_D(QDeclarativeListView); + QDeclarativeFlickable::viewportMoved(); + if (!d->itemCount) + return; + // Recursion can occur due to refill changing the content size. + if (d->inViewportMoved) + return; + d->inViewportMoved = true; + d->lazyRelease = true; + refill(); + if (d->hData.flicking || d->vData.flicking || d->hData.moving || d->vData.moving) + d->moveReason = QDeclarativeListViewPrivate::Mouse; + if (d->moveReason != QDeclarativeListViewPrivate::SetIndex) { + if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) { + // reposition highlight + qreal pos = d->highlight->position(); + qreal viewPos; + qreal highlightStart; + qreal highlightEnd; + if (d->isRightToLeft()) { + // Handle Right-To-Left exceptions + viewPos = -d->position()-d->size(); + highlightStart = d->highlightRangeStartValid ? d->size()-d->highlightRangeEnd : d->highlightRangeStart; + highlightEnd = d->highlightRangeEndValid ? d->size()-d->highlightRangeStart : d->highlightRangeEnd; + } else { + viewPos = d->position(); + highlightStart = d->highlightRangeStart; + highlightEnd = d->highlightRangeEnd; + } + if (pos > viewPos + highlightEnd - d->highlight->size()) + pos = viewPos + highlightEnd - d->highlight->size(); + if (pos < viewPos + highlightStart) + pos = viewPos + highlightStart; + d->highlightPosAnimator->stop(); + d->highlight->setPosition(qRound(pos)); + + // update current index + if (FxListItem *snapItem = d->snapItemAt(d->highlight->position())) { + if (snapItem->index >= 0 && snapItem->index != d->currentIndex) + d->updateCurrent(snapItem->index); + } + } + } + + if ((d->hData.flicking || d->vData.flicking) && d->correctFlick && !d->inFlickCorrection) { + d->inFlickCorrection = true; + // Near an end and it seems that the extent has changed? + // Recalculate the flick so that we don't end up in an odd position. + if (yflick() && !d->vData.inOvershoot) { + if (d->vData.velocity > 0) { + const qreal minY = minYExtent(); + if ((minY - d->vData.move.value() < height()/2 || d->vData.flickTarget - d->vData.move.value() < height()/2) + && minY != d->vData.flickTarget) + d->flickY(-d->vData.smoothVelocity.value()); + d->bufferMode = QDeclarativeListViewPrivate::BufferBefore; + } else if (d->vData.velocity < 0) { + const qreal maxY = maxYExtent(); + if ((d->vData.move.value() - maxY < height()/2 || d->vData.move.value() - d->vData.flickTarget < height()/2) + && maxY != d->vData.flickTarget) + d->flickY(-d->vData.smoothVelocity.value()); + d->bufferMode = QDeclarativeListViewPrivate::BufferAfter; + } + } + + if (xflick() && !d->hData.inOvershoot) { + if (d->hData.velocity > 0) { + const qreal minX = minXExtent(); + if ((minX - d->hData.move.value() < width()/2 || d->hData.flickTarget - d->hData.move.value() < width()/2) + && minX != d->hData.flickTarget) + d->flickX(-d->hData.smoothVelocity.value()); + d->bufferMode = d->isRightToLeft() + ? QDeclarativeListViewPrivate::BufferAfter : QDeclarativeListViewPrivate::BufferBefore; + } else if (d->hData.velocity < 0) { + const qreal maxX = maxXExtent(); + if ((d->hData.move.value() - maxX < width()/2 || d->hData.move.value() - d->hData.flickTarget < width()/2) + && maxX != d->hData.flickTarget) + d->flickX(-d->hData.smoothVelocity.value()); + d->bufferMode = d->isRightToLeft() + ? QDeclarativeListViewPrivate::BufferBefore : QDeclarativeListViewPrivate::BufferAfter; + } + } + d->inFlickCorrection = false; + } + d->inViewportMoved = false; +} + +qreal QDeclarativeListView::minYExtent() const +{ + Q_D(const QDeclarativeListView); + if (d->orient == QDeclarativeListView::Horizontal) + return QDeclarativeFlickable::minYExtent(); + if (d->minExtentDirty) { + d->minExtent = -d->startPosition(); + if (d->header && d->visibleItems.count()) + d->minExtent += d->header->size(); + if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { + d->minExtent += d->highlightRangeStart; + if (d->sectionCriteria) { + if (d->visibleItem(0)) + d->minExtent -= d->visibleItem(0)->sectionSize(); + } + d->minExtent = qMax(d->minExtent, -(d->endPositionAt(0) - d->highlightRangeEnd + 1)); + } + d->minExtentDirty = false; + } + + return d->minExtent; +} + +qreal QDeclarativeListView::maxYExtent() const +{ + Q_D(const QDeclarativeListView); + if (d->orient == QDeclarativeListView::Horizontal) + return height(); + if (d->maxExtentDirty) { + if (!d->model || !d->model->count()) { + d->maxExtent = d->header ? -d->header->size() : 0; + d->maxExtent += height(); + } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { + d->maxExtent = -(d->positionAt(d->model->count()-1) - d->highlightRangeStart); + if (d->highlightRangeEnd != d->highlightRangeStart) + d->maxExtent = qMin(d->maxExtent, -(d->endPosition() - d->highlightRangeEnd + 1)); + } else { + d->maxExtent = -(d->endPosition() - height() + 1); + } + if (d->footer) + d->maxExtent -= d->footer->size(); + qreal minY = minYExtent(); + if (d->maxExtent > minY) + d->maxExtent = minY; + d->maxExtentDirty = false; + } + return d->maxExtent; +} + +qreal QDeclarativeListView::minXExtent() const +{ + Q_D(const QDeclarativeListView); + if (d->orient == QDeclarativeListView::Vertical) + return QDeclarativeFlickable::minXExtent(); + if (d->minExtentDirty) { + d->minExtent = -d->startPosition(); + + qreal highlightStart; + qreal highlightEnd; + qreal endPositionFirstItem = 0; + if (d->isRightToLeft()) { + if (d->model && d->model->count()) + endPositionFirstItem = d->positionAt(d->model->count()-1); + else if (d->header) + d->minExtent += d->header->size(); + highlightStart = d->highlightRangeStartValid + ? d->highlightRangeStart - (d->lastPosition()-endPositionFirstItem) + : d->size() - (d->lastPosition()-endPositionFirstItem); + highlightEnd = d->highlightRangeEndValid ? d->highlightRangeEnd : d->size(); + if (d->footer) + d->minExtent += d->footer->size(); + qreal maxX = maxXExtent(); + if (d->minExtent < maxX) + d->minExtent = maxX; + } else { + endPositionFirstItem = d->endPositionAt(0); + highlightStart = d->highlightRangeStart; + highlightEnd = d->highlightRangeEnd; + if (d->header && d->visibleItems.count()) + d->minExtent += d->header->size(); + } + if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { + d->minExtent += d->isRightToLeft() ? -highlightStart : highlightStart; + d->minExtent = qMax(d->minExtent, -(endPositionFirstItem - highlightEnd + 1)); + } + d->minExtentDirty = false; + } + + return d->minExtent; +} + +qreal QDeclarativeListView::maxXExtent() const +{ + Q_D(const QDeclarativeListView); + if (d->orient == QDeclarativeListView::Vertical) + return width(); + if (d->maxExtentDirty) { + qreal highlightStart; + qreal highlightEnd; + qreal lastItemPosition = 0; + d->maxExtent = 0; + if (d->isRightToLeft()) { + highlightStart = d->highlightRangeStartValid ? d->highlightRangeEnd : d->size(); + highlightEnd = d->highlightRangeEndValid ? d->highlightRangeStart : d->size(); + lastItemPosition = d->endPosition(); + } else { + highlightStart = d->highlightRangeStart; + highlightEnd = d->highlightRangeEnd; + if (d->model && d->model->count()) + lastItemPosition = d->positionAt(d->model->count()-1); + } + if (!d->model || !d->model->count()) { + if (!d->isRightToLeft()) + d->maxExtent = d->header ? -d->header->size() : 0; + d->maxExtent += width(); + } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { + d->maxExtent = -(lastItemPosition - highlightStart); + if (highlightEnd != highlightStart) { + d->maxExtent = d->isRightToLeft() + ? qMax(d->maxExtent, -(d->endPosition() - highlightEnd + 1)) + : qMin(d->maxExtent, -(d->endPosition() - highlightEnd + 1)); + } + } else { + d->maxExtent = -(d->endPosition() - width() + 1); + } + if (d->isRightToLeft()) { + if (d->header && d->visibleItems.count()) + d->maxExtent -= d->header->size(); + } else { + if (d->footer) + d->maxExtent -= d->footer->size(); + qreal minX = minXExtent(); + if (d->maxExtent > minX) + d->maxExtent = minX; + } + d->maxExtentDirty = false; + } + return d->maxExtent; +} + +void QDeclarativeListView::keyPressEvent(QKeyEvent *event) +{ + Q_D(QDeclarativeListView); + keyPressPreHandler(event); + if (event->isAccepted()) + return; + + if (d->model && d->model->count() && d->interactive) { + if ((d->orient == QDeclarativeListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Left) + || (d->orient == QDeclarativeListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Right) + || (d->orient == QDeclarativeListView::Vertical && event->key() == Qt::Key_Up)) { + if (currentIndex() > 0 || (d->wrap && !event->isAutoRepeat())) { + decrementCurrentIndex(); + event->accept(); + return; + } else if (d->wrap) { + event->accept(); + return; + } + } else if ((d->orient == QDeclarativeListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Right) + || (d->orient == QDeclarativeListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Left) + || (d->orient == QDeclarativeListView::Vertical && event->key() == Qt::Key_Down)) { + if (currentIndex() < d->model->count() - 1 || (d->wrap && !event->isAutoRepeat())) { + incrementCurrentIndex(); + event->accept(); + return; + } else if (d->wrap) { + event->accept(); + return; + } + } + } + event->ignore(); + QDeclarativeFlickable::keyPressEvent(event); +} + +void QDeclarativeListView::geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry) +{ + Q_D(QDeclarativeListView); + d->maxExtentDirty = true; + d->minExtentDirty = true; + if (d->isRightToLeft() && d->orient == QDeclarativeListView::Horizontal) { + // maintain position relative to the right edge + int dx = newGeometry.width() - oldGeometry.width(); + setContentX(contentX() - dx); + } + QDeclarativeFlickable::geometryChanged(newGeometry, oldGeometry); +} + + +/*! + \qmlmethod ListView::incrementCurrentIndex() + + Increments the current index. The current index will wrap + if keyNavigationWraps is true and it is currently at the end. + This method has no effect if the \l count is zero. + + \bold Note: methods should only be called after the Component has completed. +*/ +void QDeclarativeListView::incrementCurrentIndex() +{ + Q_D(QDeclarativeListView); + int count = d->model ? d->model->count() : 0; + if (count && (currentIndex() < count - 1 || d->wrap)) { + d->moveReason = QDeclarativeListViewPrivate::SetIndex; + int index = currentIndex()+1; + setCurrentIndex((index >= 0 && index < count) ? index : 0); + } +} + +/*! + \qmlmethod ListView::decrementCurrentIndex() + + Decrements the current index. The current index will wrap + if keyNavigationWraps is true and it is currently at the beginning. + This method has no effect if the \l count is zero. + + \bold Note: methods should only be called after the Component has completed. +*/ +void QDeclarativeListView::decrementCurrentIndex() +{ + Q_D(QDeclarativeListView); + int count = d->model ? d->model->count() : 0; + if (count && (currentIndex() > 0 || d->wrap)) { + d->moveReason = QDeclarativeListViewPrivate::SetIndex; + int index = currentIndex()-1; + setCurrentIndex((index >= 0 && index < count) ? index : count-1); + } +} + +void QDeclarativeListViewPrivate::positionViewAtIndex(int index, int mode) +{ + Q_Q(QDeclarativeListView); + if (!isValid()) + return; + if (mode < QDeclarativeListView::Beginning || mode > QDeclarativeListView::Contain) + return; + int idx = qMax(qMin(index, model->count()-1), 0); + + if (layoutScheduled) + layout(); + qreal pos = isRightToLeft() ? -position() - size() : position(); + FxListItem *item = visibleItem(idx); + qreal maxExtent; + if (orient == QDeclarativeListView::Vertical) + maxExtent = -q->maxYExtent(); + else + maxExtent = isRightToLeft() ? q->minXExtent()-size(): -q->maxXExtent(); + + if (!item) { + int itemPos = positionAt(idx); + // save the currently visible items in case any of them end up visible again + QList oldVisible = visibleItems; + visibleItems.clear(); + visiblePos = itemPos; + visibleIndex = idx; + setPosition(qMin(qreal(itemPos), maxExtent)); + // now release the reference to all the old visible items. + for (int i = 0; i < oldVisible.count(); ++i) + releaseItem(oldVisible.at(i)); + item = visibleItem(idx); + } + if (item) { + const qreal itemPos = item->position(); + switch (mode) { + case QDeclarativeListView::Beginning: + pos = itemPos; + if (index < 0 && header) + pos -= header->size(); + break; + case QDeclarativeListView::Center: + pos = itemPos - (size() - item->size())/2; + break; + case QDeclarativeListView::End: + pos = itemPos - size() + item->size(); + if (index >= model->count() && footer) + pos += footer->size(); + break; + case QDeclarativeListView::Visible: + if (itemPos > pos + size()) + pos = itemPos - size() + item->size(); + else if (item->endPosition() < pos) + pos = itemPos; + break; + case QDeclarativeListView::Contain: + if (item->endPosition() > pos + size()) + pos = itemPos - size() + item->size(); + if (itemPos < pos) + pos = itemPos; + } + pos = qMin(pos, maxExtent); + qreal minExtent; + if (orient == QDeclarativeListView::Vertical) { + minExtent = -q->minYExtent(); + } else { + minExtent = isRightToLeft() ? q->maxXExtent()-size(): -q->minXExtent(); + } + pos = qMax(pos, minExtent); + moveReason = QDeclarativeListViewPrivate::Other; + q->cancelFlick(); + setPosition(pos); + if (highlight) { + if (autoHighlight) { + highlight->setPosition(currentItem->itemPosition()); + highlight->setSize(currentItem->itemSize()); + } + updateHighlight(); + } + } + fixupPosition(); +} + +/*! + \qmlmethod ListView::positionViewAtIndex(int index, PositionMode mode) + + Positions the view such that the \a index is at the position specified by + \a mode: + + \list + \o ListView.Beginning - position item at the top (or left for horizontal orientation) of the view. + \o ListView.Center - position item in the center of the view. + \o ListView.End - position item at bottom (or right for horizontal orientation) of the view. + \o ListView.Visible - if any part of the item is visible then take no action, otherwise + bring the item into view. + \o ListView.Contain - ensure the entire item is visible. If the item is larger than + the view the item is positioned at the top (or left for horizontal orientation) of the view. + \endlist + + If positioning the view at \a index would cause empty space to be displayed at + the beginning or end of the view, the view will be positioned at the boundary. + + It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view + at a particular index. This is unreliable since removing items from the start + of the list does not cause all other items to be repositioned, and because + the actual start of the view can vary based on the size of the delegates. + The correct way to bring an item into view is with \c positionViewAtIndex. + + \bold Note: methods should only be called after the Component has completed. To position + the view at startup, this method should be called by Component.onCompleted. For + example, to position the view at the end: + + \code + Component.onCompleted: positionViewAtIndex(count - 1, ListView.Beginning) + \endcode +*/ +void QDeclarativeListView::positionViewAtIndex(int index, int mode) +{ + Q_D(QDeclarativeListView); + if (!d->isValid() || index < 0 || index >= d->model->count()) + return; + d->positionViewAtIndex(index, mode); +} + +/*! + \qmlmethod ListView::positionViewAtBeginning() + \qmlmethod ListView::positionViewAtEnd() + \since QtQuick 1.1 + + Positions the view at the beginning or end, taking into account any header or footer. + + It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view + at a particular index. This is unreliable since removing items from the start + of the list does not cause all other items to be repositioned, and because + the actual start of the view can vary based on the size of the delegates. + + \bold Note: methods should only be called after the Component has completed. To position + the view at startup, this method should be called by Component.onCompleted. For + example, to position the view at the end on startup: + + \code + Component.onCompleted: positionViewAtEnd() + \endcode +*/ +void QDeclarativeListView::positionViewAtBeginning() +{ + Q_D(QDeclarativeListView); + if (!d->isValid()) + return; + d->positionViewAtIndex(-1, Beginning); +} + +void QDeclarativeListView::positionViewAtEnd() +{ + Q_D(QDeclarativeListView); + if (!d->isValid()) + return; + d->positionViewAtIndex(d->model->count(), End); +} + +/*! + \qmlmethod int ListView::indexAt(int x, int y) + + Returns the index of the visible item containing the point \a x, \a y in content + coordinates. If there is no item at the point specified, or the item is + not visible -1 is returned. + + If the item is outside the visible area, -1 is returned, regardless of + whether an item will exist at that point when scrolled into view. + + \bold Note: methods should only be called after the Component has completed. +*/ +int QDeclarativeListView::indexAt(qreal x, qreal y) const +{ + Q_D(const QDeclarativeListView); + for (int i = 0; i < d->visibleItems.count(); ++i) { + const FxListItem *listItem = d->visibleItems.at(i); + if(listItem->contains(x, y)) + return listItem->index; + } + + return -1; +} + +void QDeclarativeListView::componentComplete() +{ + Q_D(QDeclarativeListView); + QDeclarativeFlickable::componentComplete(); + updateSections(); + d->updateHeader(); + d->updateFooter(); + if (d->isValid()) { + refill(); + d->moveReason = QDeclarativeListViewPrivate::SetIndex; + if (d->currentIndex < 0 && !d->currentIndexCleared) + d->updateCurrent(0); + else + d->updateCurrent(d->currentIndex); + if (d->highlight && d->currentItem) { + if (d->autoHighlight) + d->highlight->setPosition(d->currentItem->position()); + d->updateTrackedItem(); + } + d->moveReason = QDeclarativeListViewPrivate::Other; + d->fixupPosition(); + } +} + +void QDeclarativeListView::updateSections() +{ + Q_D(QDeclarativeListView); + if (isComponentComplete() && d->model) { + QList roles; + if (d->sectionCriteria && !d->sectionCriteria->property().isEmpty()) + roles << d->sectionCriteria->property().toUtf8(); + d->model->setWatchedRoles(roles); + d->updateSections(); + if (d->itemCount) + d->layout(); + } +} + +void QDeclarativeListView::refill() +{ + Q_D(QDeclarativeListView); + if (d->isRightToLeft()) + d->refill(-d->position()-d->size()+1, -d->position()); + else + d->refill(d->position(), d->position()+d->size()-1); +} + +void QDeclarativeListView::trackedPositionChanged() +{ + Q_D(QDeclarativeListView); + if (!d->trackedItem || !d->currentItem) + return; + if (d->moveReason == QDeclarativeListViewPrivate::SetIndex) { + qreal trackedPos = qCeil(d->trackedItem->position()); + qreal trackedSize = d->trackedItem->size(); + if (d->trackedItem != d->currentItem) { + trackedPos -= d->currentItem->sectionSize(); + trackedSize += d->currentItem->sectionSize(); + } + qreal viewPos; + qreal highlightStart; + qreal highlightEnd; + if (d->isRightToLeft()) { + viewPos = -d->position()-d->size(); + highlightStart = d->highlightRangeStartValid ? d->size()-d->highlightRangeEnd : d->highlightRangeStart; + highlightEnd = d->highlightRangeEndValid ? d->size()-d->highlightRangeStart : d->highlightRangeEnd; + } else { + viewPos = d->position(); + highlightStart = d->highlightRangeStart; + highlightEnd = d->highlightRangeEnd; + } + qreal pos = viewPos; + if (d->haveHighlightRange) { + if (d->highlightRange == StrictlyEnforceRange) { + if (trackedPos > pos + highlightEnd - d->trackedItem->size()) + pos = trackedPos - highlightEnd + d->trackedItem->size(); + if (trackedPos < pos + highlightStart) + pos = trackedPos - highlightStart; + } else { + if (trackedPos < d->startPosition() + highlightStart) { + pos = d->startPosition(); + } else if (d->trackedItem->endPosition() > d->endPosition() - d->size() + highlightEnd) { + pos = d->endPosition() - d->size() + 1; + if (pos < d->startPosition()) + pos = d->startPosition(); + } else { + if (trackedPos < viewPos + highlightStart) { + pos = trackedPos - highlightStart; + } else if (trackedPos > viewPos + highlightEnd - trackedSize) { + pos = trackedPos - highlightEnd + trackedSize; + } + } + } + } else { + if (trackedPos < viewPos && d->currentItem->position() < viewPos) { + pos = d->currentItem->position() < trackedPos ? trackedPos : d->currentItem->position(); + } else if (d->trackedItem->endPosition() >= viewPos + d->size() + && d->currentItem->endPosition() >= viewPos + d->size()) { + if (d->trackedItem->endPosition() <= d->currentItem->endPosition()) { + pos = d->trackedItem->endPosition() - d->size() + 1; + if (trackedSize > d->size()) + pos = trackedPos; + } else { + pos = d->currentItem->endPosition() - d->size() + 1; + if (d->currentItem->size() > d->size()) + pos = d->currentItem->position(); + } + } + } + if (viewPos != pos) { + cancelFlick(); + d->calcVelocity = true; + d->setPosition(pos); + d->calcVelocity = false; + } + } +} + +void QDeclarativeListView::itemsInserted(int modelIndex, int count) +{ + Q_D(QDeclarativeListView); + if (!isComponentComplete()) + return; + d->updateUnrequestedIndexes(); + d->moveReason = QDeclarativeListViewPrivate::Other; + + qreal tempPos = d->isRightToLeft() ? -d->position()-d->size() : d->position(); + int index = d->visibleItems.count() ? d->mapFromModel(modelIndex) : 0; + + if (index < 0) { + int i = d->visibleItems.count() - 1; + while (i > 0 && d->visibleItems.at(i)->index == -1) + --i; + if (i == 0 && d->visibleItems.first()->index == -1) { + // there are no visible items except items marked for removal + index = d->visibleItems.count(); + } else if (d->visibleItems.at(i)->index + 1 == modelIndex + && d->visibleItems.at(i)->endPosition() < d->buffer+tempPos+d->size()-1) { + // Special case of appending an item to the model. + index = d->visibleItems.count(); + } else { + if (modelIndex < d->visibleIndex) { + // Insert before visible items + d->visibleIndex += count; + for (int i = 0; i < d->visibleItems.count(); ++i) { + FxListItem *listItem = d->visibleItems.at(i); + if (listItem->index != -1 && listItem->index >= modelIndex) + listItem->index += count; + } + } + if (d->currentIndex >= modelIndex) { + // adjust current item index + d->currentIndex += count; + if (d->currentItem) + d->currentItem->index = d->currentIndex; + emit currentIndexChanged(); + } + d->scheduleLayout(); + d->itemCount += count; + emit countChanged(); + return; + } + } + + // index can be the next item past the end of the visible items list (i.e. appended) + int pos = 0; + if (d->visibleItems.count()) { + pos = index < d->visibleItems.count() ? d->visibleItems.at(index)->position() + : d->visibleItems.last()->endPosition()+d->spacing+1; + } else if (d->itemCount == 0 && d->header) { + pos = d->header->size(); + } + + int initialPos = pos; + int diff = 0; + QList added; + bool addedVisible = false; + FxListItem *firstVisible = d->firstVisibleItem(); + if (firstVisible && pos < firstVisible->position()) { + // Insert items before the visible item. + int insertionIdx = index; + int i = 0; + int from = tempPos - d->buffer; + for (i = count-1; i >= 0 && pos > from; --i) { + if (!addedVisible) { + d->scheduleLayout(); + addedVisible = true; + } + FxListItem *item = d->createItem(modelIndex + i); + if (!item) { + // broken or no delegate + d->clear(); + return; + } + d->visibleItems.insert(insertionIdx, item); + pos -= item->size() + d->spacing; + item->setPosition(pos); + index++; + } + if (i >= 0) { + // If we didn't insert all our new items - anything + // before the current index is not visible - remove it. + while (insertionIdx--) { + FxListItem *item = d->visibleItems.takeFirst(); + if (item->index != -1) + d->visibleIndex++; + d->releaseItem(item); + } + } else { + // adjust pos of items before inserted items. + for (int i = insertionIdx-1; i >= 0; i--) { + FxListItem *listItem = d->visibleItems.at(i); + listItem->setPosition(listItem->position() - (initialPos - pos)); + } + } + } else { + int i = 0; + int to = d->buffer+tempPos+d->size()-1; + for (i = 0; i < count && pos <= to; ++i) { + if (!addedVisible) { + d->scheduleLayout(); + addedVisible = true; + } + FxListItem *item = d->createItem(modelIndex + i); + if (!item) { + // broken or no delegate + d->clear(); + return; + } + d->visibleItems.insert(index, item); + item->setPosition(pos); + added.append(item); + pos += item->size() + d->spacing; + ++index; + } + if (i != count) { + // We didn't insert all our new items, which means anything + // beyond the current index is not visible - remove it. + while (d->visibleItems.count() > index) + d->releaseItem(d->visibleItems.takeLast()); + } + diff = pos - initialPos; + } + if (d->itemCount && d->currentIndex >= modelIndex) { + // adjust current item index + d->currentIndex += count; + if (d->currentItem) { + d->currentItem->index = d->currentIndex; + d->currentItem->setPosition(d->currentItem->position() + diff); + } + emit currentIndexChanged(); + } else if (!d->itemCount && (!d->currentIndex || (d->currentIndex < 0 && !d->currentIndexCleared))) { + d->updateCurrent(0); + } + // Update the indexes of the following visible items. + for (; index < d->visibleItems.count(); ++index) { + FxListItem *listItem = d->visibleItems.at(index); + if (d->currentItem && listItem->item != d->currentItem->item) + listItem->setPosition(listItem->position() + diff); + if (listItem->index != -1) + listItem->index += count; + } + // everything is in order now - emit add() signal + for (int j = 0; j < added.count(); ++j) + added.at(j)->attached->emitAdd(); + + d->updateSections(); + d->itemCount += count; + emit countChanged(); +} + +void QDeclarativeListView::itemsRemoved(int modelIndex, int count) +{ + Q_D(QDeclarativeListView); + if (!isComponentComplete()) + return; + d->moveReason = QDeclarativeListViewPrivate::Other; + d->updateUnrequestedIndexes(); + d->itemCount -= count; + + FxListItem *firstVisible = d->firstVisibleItem(); + int preRemovedSize = 0; + bool removedVisible = false; + // Remove the items from the visible list, skipping anything already marked for removal + QList::Iterator it = d->visibleItems.begin(); + while (it != d->visibleItems.end()) { + FxListItem *item = *it; + if (item->index == -1 || item->index < modelIndex) { + // already removed, or before removed items + ++it; + } else if (item->index >= modelIndex + count) { + // after removed items + item->index -= count; + ++it; + } else { + // removed item + if (!removedVisible) { + d->scheduleLayout(); + removedVisible = true; + } + item->attached->emitRemove(); + if (item->attached->delayRemove()) { + item->index = -1; + connect(item->attached, SIGNAL(delayRemoveChanged()), this, SLOT(destroyRemoved()), Qt::QueuedConnection); + ++it; + } else { + if (item == firstVisible) + firstVisible = 0; + if (firstVisible && item->position() < firstVisible->position()) + preRemovedSize += item->size(); + it = d->visibleItems.erase(it); + d->releaseItem(item); + } + } + } + + if (firstVisible && d->visibleItems.first() != firstVisible) + d->visibleItems.first()->setPosition(d->visibleItems.first()->position() + preRemovedSize); + + // fix current + if (d->currentIndex >= modelIndex + count) { + d->currentIndex -= count; + if (d->currentItem) + d->currentItem->index -= count; + emit currentIndexChanged(); + } else if (d->currentIndex >= modelIndex && d->currentIndex < modelIndex + count) { + // current item has been removed. + d->currentItem->attached->setIsCurrentItem(false); + d->releaseItem(d->currentItem); + d->currentItem = 0; + d->currentIndex = -1; + if (d->itemCount) + d->updateCurrent(qMin(modelIndex, d->itemCount-1)); + else + emit currentIndexChanged(); + } + + // update visibleIndex + bool haveVisibleIndex = false; + for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) { + if ((*it)->index != -1) { + d->visibleIndex = (*it)->index; + haveVisibleIndex = true; + break; + } + } + + if (!haveVisibleIndex) { + d->timeline.clear(); + if (removedVisible && d->itemCount == 0) { + d->visibleIndex = 0; + d->visiblePos = d->header ? d->header->size() : 0; + d->setPosition(0); + d->updateHeader(); + d->updateFooter(); + update(); + } else { + if (modelIndex < d->visibleIndex) + d->visibleIndex = modelIndex+1; + d->visibleIndex = qMax(qMin(d->visibleIndex, d->itemCount-1), 0); + } + } + + d->updateSections(); + emit countChanged(); +} + +void QDeclarativeListView::destroyRemoved() +{ + Q_D(QDeclarativeListView); + for (QList::Iterator it = d->visibleItems.begin(); + it != d->visibleItems.end();) { + FxListItem *listItem = *it; + if (listItem->index == -1 && listItem->attached->delayRemove() == false) { + d->releaseItem(listItem); + it = d->visibleItems.erase(it); + } else { + ++it; + } + } + + // Correct the positioning of the items + d->updateSections(); + d->layout(); +} + +void QDeclarativeListView::itemsMoved(int from, int to, int count) +{ + Q_D(QDeclarativeListView); + if (!isComponentComplete()) + return; + d->updateUnrequestedIndexes(); + + if (d->visibleItems.isEmpty()) { + refill(); + return; + } + + d->moveReason = QDeclarativeListViewPrivate::Other; + FxListItem *firstVisible = d->firstVisibleItem(); + qreal firstItemPos = firstVisible->position(); + QHash moved; + int moveBy = 0; + + QList::Iterator it = d->visibleItems.begin(); + while (it != d->visibleItems.end()) { + FxListItem *item = *it; + if (item->index >= from && item->index < from + count) { + // take the items that are moving + item->index += (to-from); + moved.insert(item->index, item); + if (item->position() < firstItemPos) + moveBy += item->size(); + it = d->visibleItems.erase(it); + } else { + // move everything after the moved items. + if (item->index > from && item->index != -1) + item->index -= count; + ++it; + } + } + + int remaining = count; + int endIndex = d->visibleIndex; + it = d->visibleItems.begin(); + while (it != d->visibleItems.end()) { + FxListItem *item = *it; + if (remaining && item->index >= to && item->index < to + count) { + // place items in the target position, reusing any existing items + FxListItem *movedItem = moved.take(item->index); + if (!movedItem) + movedItem = d->createItem(item->index); + if (!movedItem) { + // broken or no delegate + d->clear(); + return; + } + if (item->index <= firstVisible->index) + moveBy -= movedItem->size(); + it = d->visibleItems.insert(it, movedItem); + ++it; + --remaining; + } else { + if (item->index != -1) { + if (item->index >= to) { + // update everything after the moved items. + item->index += count; + } + endIndex = item->index; + } + ++it; + } + } + + // If we have moved items to the end of the visible items + // then add any existing moved items that we have + while (FxListItem *item = moved.take(endIndex+1)) { + d->visibleItems.append(item); + ++endIndex; + } + + // update visibleIndex + for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) { + if ((*it)->index != -1) { + d->visibleIndex = (*it)->index; + break; + } + } + + // Fix current index + if (d->currentIndex >= 0 && d->currentItem) { + int oldCurrent = d->currentIndex; + d->currentIndex = d->model->indexOf(d->currentItem->item, this); + if (oldCurrent != d->currentIndex) { + d->currentItem->index = d->currentIndex; + emit currentIndexChanged(); + } + } + + // Whatever moved items remain are no longer visible items. + while (moved.count()) { + int idx = moved.begin().key(); + FxListItem *item = moved.take(idx); + if (d->currentItem && item->item == d->currentItem->item) + item->setPosition(d->positionAt(idx)); + d->releaseItem(item); + } + + // Ensure we don't cause an ugly list scroll. + d->visibleItems.first()->setPosition(d->visibleItems.first()->position() + moveBy); + + d->updateSections(); + d->layout(); +} + +void QDeclarativeListView::itemsChanged(int, int) +{ + Q_D(QDeclarativeListView); + d->updateSections(); + d->layout(); +} + +void QDeclarativeListView::modelReset() +{ + Q_D(QDeclarativeListView); + d->moveReason = QDeclarativeListViewPrivate::SetIndex; + d->regenerate(); + if (d->highlight && d->currentItem) { + if (d->autoHighlight) + d->highlight->setPosition(d->currentItem->position()); + d->updateTrackedItem(); + } + d->moveReason = QDeclarativeListViewPrivate::Other; + emit countChanged(); +} + +void QDeclarativeListView::createdItem(int index, QDeclarativeItem *item) +{ + Q_D(QDeclarativeListView); + if (d->requestedIndex != index) { + item->setParentItem(contentItem()); + d->unrequestedItems.insert(item, index); + if (d->orient == QDeclarativeListView::Vertical) { + item->setY(d->positionAt(index)); + } else { + if (d->isRightToLeft()) + item->setX(-d->positionAt(index)-item->width()); + else + item->setX(d->positionAt(index)); + } + } +} + +void QDeclarativeListView::destroyingItem(QDeclarativeItem *item) +{ + Q_D(QDeclarativeListView); + d->unrequestedItems.remove(item); +} + +void QDeclarativeListView::animStopped() +{ + Q_D(QDeclarativeListView); + d->bufferMode = QDeclarativeListViewPrivate::NoBuffer; + if (d->haveHighlightRange && d->highlightRange == QDeclarativeListView::StrictlyEnforceRange) + d->updateHighlight(); +} + +QDeclarativeListViewAttached *QDeclarativeListView::qmlAttachedProperties(QObject *obj) +{ + return new QDeclarativeListViewAttached(obj); +} + +QT_END_NAMESPACE diff --git a/src/declarative/graphicsitems/qdeclarativelistview_p.h b/src/declarative/graphicsitems/qdeclarativelistview_p.h new file mode 100644 index 00000000..e3c4ffe1 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativelistview_p.h @@ -0,0 +1,370 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVELISTVIEW_H +#define QDECLARATIVELISTVIEW_H + +#include "private/qdeclarativeflickable_p.h" +#include "private/qdeclarativeguard_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class Q_AUTOTEST_EXPORT QDeclarativeViewSection : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString property READ property WRITE setProperty NOTIFY propertyChanged) + Q_PROPERTY(SectionCriteria criteria READ criteria WRITE setCriteria NOTIFY criteriaChanged) + Q_PROPERTY(QDeclarativeComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged) + Q_ENUMS(SectionCriteria) +public: + QDeclarativeViewSection(QObject *parent=0) : QObject(parent), m_criteria(FullString), m_delegate(0) {} + + QString property() const { return m_property; } + void setProperty(const QString &); + + enum SectionCriteria { FullString, FirstCharacter }; + SectionCriteria criteria() const { return m_criteria; } + void setCriteria(SectionCriteria); + + QDeclarativeComponent *delegate() const { return m_delegate; } + void setDelegate(QDeclarativeComponent *delegate); + + QString sectionString(const QString &value); + +Q_SIGNALS: + void propertyChanged(); + void criteriaChanged(); + void delegateChanged(); + +private: + QString m_property; + SectionCriteria m_criteria; + QDeclarativeComponent *m_delegate; +}; + + +class QDeclarativeVisualModel; +class QDeclarativeListViewAttached; +class QDeclarativeListViewPrivate; +class Q_AUTOTEST_EXPORT QDeclarativeListView : public QDeclarativeFlickable +{ + Q_OBJECT + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarativeListView) + + Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged) + Q_PROPERTY(QDeclarativeComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged) + Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) + Q_PROPERTY(QDeclarativeItem *currentItem READ currentItem NOTIFY currentIndexChanged) + Q_PROPERTY(int count READ count NOTIFY countChanged) + + Q_PROPERTY(QDeclarativeComponent *highlight READ highlight WRITE setHighlight NOTIFY highlightChanged) + Q_PROPERTY(QDeclarativeItem *highlightItem READ highlightItem NOTIFY highlightItemChanged) + Q_PROPERTY(bool highlightFollowsCurrentItem READ highlightFollowsCurrentItem WRITE setHighlightFollowsCurrentItem NOTIFY highlightFollowsCurrentItemChanged) + Q_PROPERTY(qreal highlightMoveSpeed READ highlightMoveSpeed WRITE setHighlightMoveSpeed NOTIFY highlightMoveSpeedChanged) + Q_PROPERTY(int highlightMoveDuration READ highlightMoveDuration WRITE setHighlightMoveDuration NOTIFY highlightMoveDurationChanged) + Q_PROPERTY(qreal highlightResizeSpeed READ highlightResizeSpeed WRITE setHighlightResizeSpeed NOTIFY highlightResizeSpeedChanged) + Q_PROPERTY(int highlightResizeDuration READ highlightResizeDuration WRITE setHighlightResizeDuration NOTIFY highlightResizeDurationChanged) + + Q_PROPERTY(qreal preferredHighlightBegin READ preferredHighlightBegin WRITE setPreferredHighlightBegin NOTIFY preferredHighlightBeginChanged RESET resetPreferredHighlightBegin) + Q_PROPERTY(qreal preferredHighlightEnd READ preferredHighlightEnd WRITE setPreferredHighlightEnd NOTIFY preferredHighlightEndChanged RESET resetPreferredHighlightEnd) + Q_PROPERTY(HighlightRangeMode highlightRangeMode READ highlightRangeMode WRITE setHighlightRangeMode NOTIFY highlightRangeModeChanged) + + Q_PROPERTY(qreal spacing READ spacing WRITE setSpacing NOTIFY spacingChanged) + Q_PROPERTY(Orientation orientation READ orientation WRITE setOrientation NOTIFY orientationChanged) + Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection NOTIFY layoutDirectionChanged REVISION 1) + Q_PROPERTY(bool keyNavigationWraps READ isWrapEnabled WRITE setWrapEnabled NOTIFY keyNavigationWrapsChanged) + Q_PROPERTY(int cacheBuffer READ cacheBuffer WRITE setCacheBuffer NOTIFY cacheBufferChanged) + Q_PROPERTY(QDeclarativeViewSection *section READ sectionCriteria CONSTANT) + Q_PROPERTY(QString currentSection READ currentSection NOTIFY currentSectionChanged) + + Q_PROPERTY(SnapMode snapMode READ snapMode WRITE setSnapMode NOTIFY snapModeChanged) + + Q_PROPERTY(QDeclarativeComponent *header READ header WRITE setHeader NOTIFY headerChanged) + Q_PROPERTY(QDeclarativeComponent *footer READ footer WRITE setFooter NOTIFY footerChanged) + + Q_ENUMS(HighlightRangeMode) + Q_ENUMS(Orientation) + Q_ENUMS(SnapMode) + Q_ENUMS(PositionMode) + Q_CLASSINFO("DefaultProperty", "data") + +public: + QDeclarativeListView(QDeclarativeItem *parent=0); + ~QDeclarativeListView(); + + QVariant model() const; + void setModel(const QVariant &); + + QDeclarativeComponent *delegate() const; + void setDelegate(QDeclarativeComponent *); + + int currentIndex() const; + void setCurrentIndex(int idx); + + QDeclarativeItem *currentItem(); + QDeclarativeItem *highlightItem(); + int count() const; + + QDeclarativeComponent *highlight() const; + void setHighlight(QDeclarativeComponent *highlight); + + bool highlightFollowsCurrentItem() const; + void setHighlightFollowsCurrentItem(bool); + + enum HighlightRangeMode { NoHighlightRange, ApplyRange, StrictlyEnforceRange }; + HighlightRangeMode highlightRangeMode() const; + void setHighlightRangeMode(HighlightRangeMode mode); + + qreal preferredHighlightBegin() const; + void setPreferredHighlightBegin(qreal); + void resetPreferredHighlightBegin(); + + qreal preferredHighlightEnd() const; + void setPreferredHighlightEnd(qreal); + void resetPreferredHighlightEnd(); + + qreal spacing() const; + void setSpacing(qreal spacing); + + enum Orientation { Horizontal = Qt::Horizontal, Vertical = Qt::Vertical }; + Orientation orientation() const; + void setOrientation(Orientation); + + Qt::LayoutDirection layoutDirection() const; + void setLayoutDirection(Qt::LayoutDirection); + Qt::LayoutDirection effectiveLayoutDirection() const; + + bool isWrapEnabled() const; + void setWrapEnabled(bool); + + int cacheBuffer() const; + void setCacheBuffer(int); + + QDeclarativeViewSection *sectionCriteria(); + QString currentSection() const; + + qreal highlightMoveSpeed() const; + void setHighlightMoveSpeed(qreal); + + int highlightMoveDuration() const; + void setHighlightMoveDuration(int); + + qreal highlightResizeSpeed() const; + void setHighlightResizeSpeed(qreal); + + int highlightResizeDuration() const; + void setHighlightResizeDuration(int); + + enum SnapMode { NoSnap, SnapToItem, SnapOneItem }; + SnapMode snapMode() const; + void setSnapMode(SnapMode mode); + + QDeclarativeComponent *footer() const; + void setFooter(QDeclarativeComponent *); + + QDeclarativeComponent *header() const; + void setHeader(QDeclarativeComponent *); + + virtual void setContentX(qreal pos); + virtual void setContentY(qreal pos); + + static QDeclarativeListViewAttached *qmlAttachedProperties(QObject *); + + enum PositionMode { Beginning, Center, End, Visible, Contain }; + + Q_INVOKABLE void positionViewAtIndex(int index, int mode); + Q_INVOKABLE int indexAt(qreal x, qreal y) const; + Q_INVOKABLE Q_REVISION(1) void positionViewAtBeginning(); + Q_INVOKABLE Q_REVISION(1) void positionViewAtEnd(); + +public Q_SLOTS: + void incrementCurrentIndex(); + void decrementCurrentIndex(); + +Q_SIGNALS: + void countChanged(); + void spacingChanged(); + void orientationChanged(); + Q_REVISION(1) void layoutDirectionChanged(); + void currentIndexChanged(); + void currentSectionChanged(); + void highlightMoveSpeedChanged(); + void highlightMoveDurationChanged(); + void highlightResizeSpeedChanged(); + void highlightResizeDurationChanged(); + void highlightChanged(); + void highlightItemChanged(); + void modelChanged(); + void delegateChanged(); + void highlightFollowsCurrentItemChanged(); + void preferredHighlightBeginChanged(); + void preferredHighlightEndChanged(); + void highlightRangeModeChanged(); + void keyNavigationWrapsChanged(); + void cacheBufferChanged(); + void snapModeChanged(); + void headerChanged(); + void footerChanged(); + +protected: + virtual bool event(QEvent *event); + virtual void viewportMoved(); + virtual qreal minYExtent() const; + virtual qreal maxYExtent() const; + virtual qreal minXExtent() const; + virtual qreal maxXExtent() const; + virtual void keyPressEvent(QKeyEvent *); + virtual void geometryChanged(const QRectF &newGeometry,const QRectF &oldGeometry); + virtual void componentComplete(); + +private Q_SLOTS: + void updateSections(); + void refill(); + void trackedPositionChanged(); + void itemsInserted(int index, int count); + void itemsRemoved(int index, int count); + void itemsMoved(int from, int to, int count); + void itemsChanged(int index, int count); + void modelReset(); + void destroyRemoved(); + void createdItem(int index, QDeclarativeItem *item); + void destroyingItem(QDeclarativeItem *item); + void animStopped(); +}; + +class QDeclarativeListViewAttached : public QObject +{ + Q_OBJECT +public: + QDeclarativeListViewAttached(QObject *parent) + : QObject(parent), m_view(0), m_isCurrent(false), m_delayRemove(false) {} + ~QDeclarativeListViewAttached() {} + + Q_PROPERTY(QDeclarativeListView *view READ view NOTIFY viewChanged) + QDeclarativeListView *view() { return m_view; } + void setView(QDeclarativeListView *view) { + if (view != m_view) { + m_view = view; + emit viewChanged(); + } + } + + Q_PROPERTY(bool isCurrentItem READ isCurrentItem NOTIFY currentItemChanged) + bool isCurrentItem() const { return m_isCurrent; } + void setIsCurrentItem(bool c) { + if (m_isCurrent != c) { + m_isCurrent = c; + emit currentItemChanged(); + } + } + + Q_PROPERTY(QString previousSection READ prevSection NOTIFY prevSectionChanged) + QString prevSection() const { return m_prevSection; } + void setPrevSection(const QString §) { + if (m_prevSection != sect) { + m_prevSection = sect; + emit prevSectionChanged(); + } + } + + Q_PROPERTY(QString nextSection READ nextSection NOTIFY nextSectionChanged) + QString nextSection() const { return m_nextSection; } + void setNextSection(const QString §) { + if (m_nextSection != sect) { + m_nextSection = sect; + emit nextSectionChanged(); + } + } + + Q_PROPERTY(QString section READ section NOTIFY sectionChanged) + QString section() const { return m_section; } + void setSection(const QString §) { + if (m_section != sect) { + m_section = sect; + emit sectionChanged(); + } + } + + Q_PROPERTY(bool delayRemove READ delayRemove WRITE setDelayRemove NOTIFY delayRemoveChanged) + bool delayRemove() const { return m_delayRemove; } + void setDelayRemove(bool delay) { + if (m_delayRemove != delay) { + m_delayRemove = delay; + emit delayRemoveChanged(); + } + } + + void emitAdd() { emit add(); } + void emitRemove() { emit remove(); } + +Q_SIGNALS: + void currentItemChanged(); + void sectionChanged(); + void prevSectionChanged(); + void nextSectionChanged(); + void delayRemoveChanged(); + void add(); + void remove(); + void viewChanged(); + +public: + QDeclarativeGuard m_view; + mutable QString m_section; + QString m_prevSection; + QString m_nextSection; + bool m_isCurrent : 1; + bool m_delayRemove : 1; +}; + + +QT_END_NAMESPACE + +QML_DECLARE_TYPEINFO(QDeclarativeListView, QML_HAS_ATTACHED_PROPERTIES) +QML_DECLARE_TYPE(QDeclarativeListView) +QML_DECLARE_TYPE(QDeclarativeViewSection) + +QT_END_HEADER + +#endif diff --git a/src/declarative/graphicsitems/qdeclarativeloader.cpp b/src/declarative/graphicsitems/qdeclarativeloader.cpp new file mode 100644 index 00000000..f55cc9b9 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativeloader.cpp @@ -0,0 +1,597 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativeloader_p_p.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QDeclarativeLoaderPrivate::QDeclarativeLoaderPrivate() + : item(0), component(0), ownComponent(false), updatingSize(false), + itemWidthValid(false), itemHeightValid(false) +{ +} + +QDeclarativeLoaderPrivate::~QDeclarativeLoaderPrivate() +{ +} + +void QDeclarativeLoaderPrivate::itemGeometryChanged(QDeclarativeItem *resizeItem, const QRectF &newGeometry, const QRectF &oldGeometry) +{ + if (resizeItem == item) { + if (!updatingSize && newGeometry.width() != oldGeometry.width()) + itemWidthValid = true; + if (!updatingSize && newGeometry.height() != oldGeometry.height()) + itemHeightValid = true; + _q_updateSize(false); + } + QDeclarativeItemChangeListener::itemGeometryChanged(resizeItem, newGeometry, oldGeometry); +} + +void QDeclarativeLoaderPrivate::clear() +{ + if (ownComponent) { + component->deleteLater(); + component = 0; + ownComponent = false; + } + source = QUrl(); + + if (item) { + if (QDeclarativeItem *qmlItem = qobject_cast(item)) { + QDeclarativeItemPrivate *p = + static_cast(QGraphicsItemPrivate::get(qmlItem)); + p->removeItemChangeListener(this, QDeclarativeItemPrivate::Geometry); + } + + // We can't delete immediately because our item may have triggered + // the Loader to load a different item. + if (item->scene()) { + item->scene()->removeItem(item); + } else { + item->setParentItem(0); + item->setVisible(false); + } + item->deleteLater(); + item = 0; + } +} + +void QDeclarativeLoaderPrivate::initResize() +{ + Q_Q(QDeclarativeLoader); + if (QDeclarativeItem *qmlItem = qobject_cast(item)) { + QDeclarativeItemPrivate *p = + static_cast(QGraphicsItemPrivate::get(qmlItem)); + p->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry); + // We may override the item's size, so we need to remember + // whether the item provided its own valid size. + itemWidthValid = p->widthValid; + itemHeightValid = p->heightValid; + } else if (item && item->isWidget()) { + QGraphicsWidget *widget = static_cast(item); + widget->installEventFilter(q); + } + _q_updateSize(); +} + +/*! + \qmlclass Loader QDeclarativeLoader + \ingroup qml-utility-elements + \since 4.7 + \inherits Item + + \brief The Loader item allows dynamically loading an Item-based + subtree from a URL or Component. + + Loader is used to dynamically load visual QML components. It can load a + QML file (using the \l source property) or a \l Component object (using + the \l sourceComponent property). It is useful for delaying the creation + of a component until it is required: for example, when a component should + be created on demand, or when a component should not be created + unnecessarily for performance reasons. + + Here is a Loader that loads "Page1.qml" as a component when the + \l MouseArea is clicked: + + \snippet doc/src/snippets/declarative/loader/simple.qml 0 + + The loaded item can be accessed using the \l item property. + + If the \l source or \l sourceComponent changes, any previously instantiated + items are destroyed. Setting \l source to an empty string or setting + \l sourceComponent to \c undefined destroys the currently loaded item, + freeing resources and leaving the Loader empty. + + \section2 Loader sizing behavior + + Loader is like any other visual item and must be positioned and sized + accordingly to become visible. + + \list + \o If an explicit size is not specified for the Loader, the Loader + is automatically resized to the size of the loaded item once the + component is loaded. + \o If the size of the Loader is specified explicitly by setting + the width, height or by anchoring, the loaded item will be resized + to the size of the Loader. + \endlist + + In both scenarios the size of the item and the Loader are identical. + This ensures that anchoring to the Loader is equivalent to anchoring + to the loaded item. + + \table + \row + \o sizeloader.qml + \o sizeitem.qml + \row + \o \snippet doc/src/snippets/declarative/loader/sizeloader.qml 0 + \o \snippet doc/src/snippets/declarative/loader/sizeitem.qml 0 + \row + \o The red rectangle will be sized to the size of the root item. + \o The red rectangle will be 50x50, centered in the root item. + \endtable + + + \section2 Receiving signals from loaded items + + Any signals emitted from the loaded item can be received using the + \l Connections element. For example, the following \c application.qml + loads \c MyItem.qml, and is able to receive the \c message signal from + the loaded item through a \l Connections object: + + \table + \row + \o application.qml + \o MyItem.qml + \row + \o \snippet doc/src/snippets/declarative/loader/connections.qml 0 + \o \snippet doc/src/snippets/declarative/loader/MyItem.qml 0 + \endtable + + Alternatively, since \c MyItem.qml is loaded within the scope of the + Loader, it could also directly call any function defined in the Loader or + its parent \l Item. + + + \section2 Focus and key events + + Loader is a focus scope. Its \l {Item::}{focus} property must be set to + \c true for any of its children to get the \e {active focus}. (See + \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} + for more details.) Any key events received in the loaded item should likely + also be \l {KeyEvent::}{accepted} so they are not propagated to the Loader. + + For example, the following \c application.qml loads \c KeyReader.qml when + the \l MouseArea is clicked. Notice the \l {Item::}{focus} property is + set to \c true for the Loader as well as the \l Item in the dynamically + loaded object: + + \table + \row + \o application.qml + \o KeyReader.qml + \row + \o \snippet doc/src/snippets/declarative/loader/focus.qml 0 + \o \snippet doc/src/snippets/declarative/loader/KeyReader.qml 0 + \endtable + + Once \c KeyReader.qml is loaded, it accepts key events and sets + \c event.accepted to \c true so that the event is not propagated to the + parent \l Rectangle. + + \sa {dynamic-object-creation}{Dynamic Object Creation} +*/ + +QDeclarativeLoader::QDeclarativeLoader(QDeclarativeItem *parent) + : QDeclarativeImplicitSizeItem(*(new QDeclarativeLoaderPrivate), parent) +{ + Q_D(QDeclarativeLoader); + d->flags |= QGraphicsItem::ItemIsFocusScope; +} + +QDeclarativeLoader::~QDeclarativeLoader() +{ + Q_D(QDeclarativeLoader); + if (d->item) { + if (QDeclarativeItem *qmlItem = qobject_cast(d->item)) { + QDeclarativeItemPrivate *p = + static_cast(QGraphicsItemPrivate::get(qmlItem)); + p->removeItemChangeListener(d, QDeclarativeItemPrivate::Geometry); + } + } +} + +/*! + \qmlproperty url Loader::source + This property holds the URL of the QML component to instantiate. + + Note the QML component must be an \l{Item}-based component. The loader + cannot load non-visual components. + + To unload the currently loaded item, set this property to an empty string, + or set \l sourceComponent to \c undefined. Setting \c source to a + new URL will also cause the item created by the previous URL to be unloaded. + + \sa sourceComponent, status, progress +*/ +QUrl QDeclarativeLoader::source() const +{ + Q_D(const QDeclarativeLoader); + return d->source; +} + +void QDeclarativeLoader::setSource(const QUrl &url) +{ + Q_D(QDeclarativeLoader); + if (d->source == url) + return; + + d->clear(); + + d->source = url; + + if (d->source.isEmpty()) { + emit sourceChanged(); + emit statusChanged(); + emit progressChanged(); + emit itemChanged(); + return; + } + + d->component = new QDeclarativeComponent(qmlEngine(this), d->source, this); + d->ownComponent = true; + + if (isComponentComplete()) + d->load(); +} + +/*! + \qmlproperty Component Loader::sourceComponent + This property holds the \l{Component} to instantiate. + + \qml + Item { + Component { + id: redSquare + Rectangle { color: "red"; width: 10; height: 10 } + } + + Loader { sourceComponent: redSquare } + Loader { sourceComponent: redSquare; x: 10 } + } + \endqml + + To unload the currently loaded item, set this property to an empty string + or \c undefined. + + \sa source, progress +*/ + +QDeclarativeComponent *QDeclarativeLoader::sourceComponent() const +{ + Q_D(const QDeclarativeLoader); + return d->component; +} + +void QDeclarativeLoader::setSourceComponent(QDeclarativeComponent *comp) +{ + Q_D(QDeclarativeLoader); + if (comp == d->component) + return; + + d->clear(); + + d->component = comp; + d->ownComponent = false; + + if (!d->component) { + emit sourceChanged(); + emit statusChanged(); + emit progressChanged(); + emit itemChanged(); + return; + } + + if (isComponentComplete()) + d->load(); +} + +void QDeclarativeLoader::resetSourceComponent() +{ + setSourceComponent(0); +} + +void QDeclarativeLoaderPrivate::load() +{ + Q_Q(QDeclarativeLoader); + + if (!q->isComponentComplete() || !component) + return; + + if (!component->isLoading()) { + _q_sourceLoaded(); + } else { + QObject::connect(component, SIGNAL(statusChanged(QDeclarativeComponent::Status)), + q, SLOT(_q_sourceLoaded())); + QObject::connect(component, SIGNAL(progressChanged(qreal)), + q, SIGNAL(progressChanged())); + emit q->statusChanged(); + emit q->progressChanged(); + emit q->sourceChanged(); + emit q->itemChanged(); + } +} + +void QDeclarativeLoaderPrivate::_q_sourceLoaded() +{ + Q_Q(QDeclarativeLoader); + + if (component) { + if (!component->errors().isEmpty()) { + QDeclarativeEnginePrivate::warning(qmlEngine(q), component->errors()); + emit q->sourceChanged(); + emit q->statusChanged(); + emit q->progressChanged(); + return; + } + + QDeclarativeContext *creationContext = component->creationContext(); + if (!creationContext) creationContext = qmlContext(q); + QDeclarativeContext *ctxt = new QDeclarativeContext(creationContext); + ctxt->setContextObject(q); + + QDeclarativeGuard c = component; + QObject *obj = component->beginCreate(ctxt); + if (component != c) { + // component->create could trigger a change in source that causes + // component to be set to something else. In that case we just + // need to cleanup. + if (c) + c->completeCreate(); + delete obj; + delete ctxt; + return; + } + if (obj) { + item = qobject_cast(obj); + if (item) { + QDeclarative_setParent_noEvent(ctxt, obj); + QDeclarative_setParent_noEvent(item, q); + item->setParentItem(q); +// item->setFocus(true); + initResize(); + } else { + qmlInfo(q) << QDeclarativeLoader::tr("Loader does not support loading non-visual elements."); + delete obj; + delete ctxt; + } + } else { + if (!component->errors().isEmpty()) + QDeclarativeEnginePrivate::warning(qmlEngine(q), component->errors()); + delete obj; + delete ctxt; + source = QUrl(); + } + component->completeCreate(); + emit q->sourceChanged(); + emit q->statusChanged(); + emit q->progressChanged(); + emit q->itemChanged(); + emit q->loaded(); + } +} + +/*! + \qmlproperty enumeration Loader::status + + This property holds the status of QML loading. It can be one of: + \list + \o Loader.Null - no QML source has been set + \o Loader.Ready - the QML source has been loaded + \o Loader.Loading - the QML source is currently being loaded + \o Loader.Error - an error occurred while loading the QML source + \endlist + + Use this status to provide an update or respond to the status change in some way. + For example, you could: + + \list + \o Trigger a state change: + \qml + State { name: 'loaded'; when: loader.status == Loader.Ready } + \endqml + + \o Implement an \c onStatusChanged signal handler: + \qml + Loader { + id: loader + onStatusChanged: if (loader.status == Loader.Ready) console.log('Loaded') + } + \endqml + + \o Bind to the status value: + \qml + Text { text: loader.status == Loader.Ready ? 'Loaded' : 'Not loaded' } + \endqml + \endlist + + Note that if the source is a local file, the status will initially be Ready (or Error). While + there will be no onStatusChanged signal in that case, the onLoaded will still be invoked. + + \sa progress +*/ + +QDeclarativeLoader::Status QDeclarativeLoader::status() const +{ + Q_D(const QDeclarativeLoader); + + if (d->component) + return static_cast(d->component->status()); + + if (d->item) + return Ready; + + return d->source.isEmpty() ? Null : Error; +} + +void QDeclarativeLoader::componentComplete() +{ + Q_D(QDeclarativeLoader); + + QDeclarativeItem::componentComplete(); + d->load(); +} + + +/*! + \qmlsignal Loader::onLoaded() + + This handler is called when the \l status becomes \c Loader.Ready, or on successful + initial load. +*/ + + +/*! +\qmlproperty real Loader::progress + +This property holds the progress of loading QML data from the network, from +0.0 (nothing loaded) to 1.0 (finished). Most QML files are quite small, so +this value will rapidly change from 0 to 1. + +\sa status +*/ +qreal QDeclarativeLoader::progress() const +{ + Q_D(const QDeclarativeLoader); + + if (d->item) + return 1.0; + + if (d->component) + return d->component->progress(); + + return 0.0; +} + +void QDeclarativeLoaderPrivate::_q_updateSize(bool loaderGeometryChanged) +{ + Q_Q(QDeclarativeLoader); + if (!item || updatingSize) + return; + + updatingSize = true; + if (QDeclarativeItem *qmlItem = qobject_cast(item)) { + if (!itemWidthValid) + q->setImplicitWidth(qmlItem->implicitWidth()); + else + q->setImplicitWidth(qmlItem->width()); + if (loaderGeometryChanged && q->widthValid()) + qmlItem->setWidth(q->width()); + if (!itemHeightValid) + q->setImplicitHeight(qmlItem->implicitHeight()); + else + q->setImplicitHeight(qmlItem->height()); + if (loaderGeometryChanged && q->heightValid()) + qmlItem->setHeight(q->height()); + } else if (item && item->isWidget()) { + QGraphicsWidget *widget = static_cast(item); + QSizeF widgetSize = widget->size(); + q->setImplicitWidth(widgetSize.width()); + if (loaderGeometryChanged && q->widthValid()) + widgetSize.setWidth(q->width()); + q->setImplicitHeight(widgetSize.height()); + if (loaderGeometryChanged && q->heightValid()) + widgetSize.setHeight(q->height()); + if (widget->size() != widgetSize) + widget->resize(widgetSize); + } + updatingSize = false; +} + +/*! + \qmlproperty Item Loader::item + This property holds the top-level item that is currently loaded. +*/ +QGraphicsObject *QDeclarativeLoader::item() const +{ + Q_D(const QDeclarativeLoader); + return d->item; +} + +void QDeclarativeLoader::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_D(QDeclarativeLoader); + if (newGeometry != oldGeometry) { + d->_q_updateSize(); + } + QDeclarativeItem::geometryChanged(newGeometry, oldGeometry); +} + +QVariant QDeclarativeLoader::itemChange(GraphicsItemChange change, const QVariant &value) +{ + Q_D(QDeclarativeLoader); + if (change == ItemSceneHasChanged) { + if (d->item && d->item->isWidget()) { + d->item->removeEventFilter(this); + d->item->installEventFilter(this); + } + } + return QDeclarativeItem::itemChange(change, value); +} + +bool QDeclarativeLoader::eventFilter(QObject *watched, QEvent *e) +{ + Q_D(QDeclarativeLoader); + if (watched == d->item && e->type() == QEvent::GraphicsSceneResize) { + if (d->item && d->item->isWidget()) + d->_q_updateSize(false); + } + return QDeclarativeItem::eventFilter(watched, e); +} + +#include + +QT_END_NAMESPACE diff --git a/src/declarative/graphicsitems/qdeclarativeloader_p.h b/src/declarative/graphicsitems/qdeclarativeloader_p.h new file mode 100644 index 00000000..e9f4f57e --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativeloader_p.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVELOADER_H +#define QDECLARATIVELOADER_H + +#include "qdeclarativeimplicitsizeitem_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeLoaderPrivate; +class Q_AUTOTEST_EXPORT QDeclarativeLoader : public QDeclarativeImplicitSizeItem +{ + Q_OBJECT + Q_ENUMS(Status) + + Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) + Q_PROPERTY(QDeclarativeComponent *sourceComponent READ sourceComponent WRITE setSourceComponent RESET resetSourceComponent NOTIFY sourceChanged) + Q_PROPERTY(QGraphicsObject *item READ item NOTIFY itemChanged) + Q_PROPERTY(Status status READ status NOTIFY statusChanged) + Q_PROPERTY(qreal progress READ progress NOTIFY progressChanged) + +public: + QDeclarativeLoader(QDeclarativeItem *parent=0); + virtual ~QDeclarativeLoader(); + + QUrl source() const; + void setSource(const QUrl &); + + QDeclarativeComponent *sourceComponent() const; + void setSourceComponent(QDeclarativeComponent *); + void resetSourceComponent(); + + enum Status { Null, Ready, Loading, Error }; + Status status() const; + qreal progress() const; + + QGraphicsObject *item() const; + +Q_SIGNALS: + void itemChanged(); + void sourceChanged(); + void statusChanged(); + void progressChanged(); + void loaded(); + +protected: + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); + QVariant itemChange(GraphicsItemChange change, const QVariant &value); + bool eventFilter(QObject *watched, QEvent *e); + void componentComplete(); + +private: + Q_DISABLE_COPY(QDeclarativeLoader) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarativeLoader) + Q_PRIVATE_SLOT(d_func(), void _q_sourceLoaded()) + Q_PRIVATE_SLOT(d_func(), void _q_updateSize()) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeLoader) + +QT_END_HEADER + +#endif // QDECLARATIVELOADER_H diff --git a/src/declarative/graphicsitems/qdeclarativeloader_p_p.h b/src/declarative/graphicsitems/qdeclarativeloader_p_p.h new file mode 100644 index 00000000..dee20e57 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativeloader_p_p.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVELOADER_P_H +#define QDECLARATIVELOADER_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 "private/qdeclarativeloader_p.h" + +#include "private/qdeclarativeimplicitsizeitem_p_p.h" +#include "private/qdeclarativeitemchangelistener_p.h" + +QT_BEGIN_NAMESPACE + +class QDeclarativeContext; +class QDeclarativeLoaderPrivate : public QDeclarativeImplicitSizeItemPrivate, public QDeclarativeItemChangeListener +{ + Q_DECLARE_PUBLIC(QDeclarativeLoader) + +public: + QDeclarativeLoaderPrivate(); + ~QDeclarativeLoaderPrivate(); + + void itemGeometryChanged(QDeclarativeItem *item, const QRectF &newGeometry, const QRectF &oldGeometry); + void clear(); + void initResize(); + void load(); + + QUrl source; + QGraphicsObject *item; + QDeclarativeComponent *component; + bool ownComponent : 1; + bool updatingSize: 1; + bool itemWidthValid : 1; + bool itemHeightValid : 1; + + void _q_sourceLoaded(); + void _q_updateSize(bool loaderGeometryChanged = true); +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVELOADER_P_H diff --git a/src/declarative/graphicsitems/qdeclarativemousearea.cpp b/src/declarative/graphicsitems/qdeclarativemousearea.cpp new file mode 100644 index 00000000..36511806 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativemousearea.cpp @@ -0,0 +1,988 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativemousearea_p.h" +#include "private/qdeclarativemousearea_p_p.h" + +#include "private/qdeclarativeevents_p_p.h" + +#include + +#include + +QT_BEGIN_NAMESPACE +static const int PressAndHoldDelay = 800; + +QDeclarativeDrag::QDeclarativeDrag(QObject *parent) +: QObject(parent), _target(0), _axis(XandYAxis), _xmin(-FLT_MAX), _xmax(FLT_MAX), _ymin(-FLT_MAX), _ymax(FLT_MAX), +_active(false), _filterChildren(false) +{ +} + +QDeclarativeDrag::~QDeclarativeDrag() +{ +} + +QGraphicsObject *QDeclarativeDrag::target() const +{ + return _target; +} + +void QDeclarativeDrag::setTarget(QGraphicsObject *t) +{ + if (_target == t) + return; + _target = t; + emit targetChanged(); +} + +void QDeclarativeDrag::resetTarget() +{ + if (!_target) + return; + _target = 0; + emit targetChanged(); +} + +QDeclarativeDrag::Axis QDeclarativeDrag::axis() const +{ + return _axis; +} + +void QDeclarativeDrag::setAxis(QDeclarativeDrag::Axis a) +{ + if (_axis == a) + return; + _axis = a; + emit axisChanged(); +} + +qreal QDeclarativeDrag::xmin() const +{ + return _xmin; +} + +void QDeclarativeDrag::setXmin(qreal m) +{ + if (_xmin == m) + return; + _xmin = m; + emit minimumXChanged(); +} + +qreal QDeclarativeDrag::xmax() const +{ + return _xmax; +} + +void QDeclarativeDrag::setXmax(qreal m) +{ + if (_xmax == m) + return; + _xmax = m; + emit maximumXChanged(); +} + +qreal QDeclarativeDrag::ymin() const +{ + return _ymin; +} + +void QDeclarativeDrag::setYmin(qreal m) +{ + if (_ymin == m) + return; + _ymin = m; + emit minimumYChanged(); +} + +qreal QDeclarativeDrag::ymax() const +{ + return _ymax; +} + +void QDeclarativeDrag::setYmax(qreal m) +{ + if (_ymax == m) + return; + _ymax = m; + emit maximumYChanged(); +} + +bool QDeclarativeDrag::active() const +{ + return _active; +} + +void QDeclarativeDrag::setActive(bool drag) +{ + if (_active == drag) + return; + _active = drag; + emit activeChanged(); +} + +bool QDeclarativeDrag::filterChildren() const +{ + return _filterChildren; +} + +void QDeclarativeDrag::setFilterChildren(bool filter) +{ + if (_filterChildren == filter) + return; + _filterChildren = filter; + emit filterChildrenChanged(); +} + +QDeclarativeMouseAreaPrivate::~QDeclarativeMouseAreaPrivate() +{ + delete drag; +} + +/*! + \qmlclass MouseArea QDeclarativeMouseArea + \ingroup qml-basic-interaction-elements + \since 4.7 + \brief The MouseArea item enables simple mouse handling. + \inherits Item + + A MouseArea is an invisible item that is typically used in conjunction with + a visible item in order to provide mouse handling for that item. + By effectively acting as a proxy, the logic for mouse handling can be + contained within a MouseArea item. + + For basic key handling, see the \l{Keys}{Keys attached property}. + + The \l enabled property is used to enable and disable mouse handling for + the proxied item. When disabled, the mouse area becomes transparent to + mouse events. + + The \l pressed read-only property indicates whether or not the user is + holding down a mouse button over the mouse area. This property is often + used in bindings between properties in a user interface. The containsMouse + read-only property indicates the presence of the mouse cursor over the + mouse area but, by default, only when a mouse button is held down; see below + for further details. + + Information about the mouse position and button clicks are provided via + signals for which event handler properties are defined. The most commonly + used involved handling mouse presses and clicks: onClicked, onDoubleClicked, + onPressed, onReleased and onPressAndHold. + + By default, MouseArea items only report mouse clicks and not changes to the + position of the mouse cursor. Setting the hoverEnabled property ensures that + handlers defined for onPositionChanged, onEntered and onExited are used and + that the containsMouse property is updated even when no mouse buttons are + pressed. + + \section1 Example Usage + + \div {class="float-right"} + \inlineimage qml-mousearea-snippet.png + \enddiv + + The following example uses a MouseArea in a \l Rectangle that changes + the \l Rectangle color to red when clicked: + + \snippet doc/src/snippets/declarative/mousearea/mousearea.qml import + \codeline + \snippet doc/src/snippets/declarative/mousearea/mousearea.qml intro + + \clearfloat + Many MouseArea signals pass a \l{MouseEvent}{mouse} parameter that contains + additional information about the mouse event, such as the position, button, + and any key modifiers. + + Here is an extension of the previous example that produces a different + color when the area is right clicked: + + \snippet doc/src/snippets/declarative/mousearea/mousearea.qml intro-extended + + \sa MouseEvent, {declarative/touchinteraction/mousearea}{MouseArea example} +*/ + +/*! + \qmlsignal MouseArea::onEntered() + + This handler is called when the mouse enters the mouse area. + + By default the onEntered handler is only called while a button is + pressed. Setting hoverEnabled to true enables handling of + onEntered when no mouse button is pressed. + + \sa hoverEnabled +*/ + +/*! + \qmlsignal MouseArea::onExited() + + This handler is called when the mouse exists the mouse area. + + By default the onExited handler is only called while a button is + pressed. Setting hoverEnabled to true enables handling of + onExited when no mouse button is pressed. + + \sa hoverEnabled +*/ + +/*! + \qmlsignal MouseArea::onPositionChanged(MouseEvent mouse) + + This handler is called when the mouse position changes. + + The \l {MouseEvent}{mouse} parameter provides information about the mouse, including the x and y + position, and any buttons currently pressed. + + The \e accepted property of the MouseEvent parameter is ignored in this handler. + + By default the onPositionChanged handler is only called while a button is + pressed. Setting hoverEnabled to true enables handling of + onPositionChanged when no mouse button is pressed. +*/ + +/*! + \qmlsignal MouseArea::onClicked(MouseEvent mouse) + + This handler is called when there is a click. A click is defined as a press followed by a release, + both inside the MouseArea (pressing, moving outside the MouseArea, and then moving back inside and + releasing is also considered a click). + + The \l {MouseEvent}{mouse} parameter provides information about the click, including the x and y + position of the release of the click, and whether the click was held. + + The \e accepted property of the MouseEvent parameter is ignored in this handler. +*/ + +/*! + \qmlsignal MouseArea::onPressed(MouseEvent mouse) + + This handler is called when there is a press. + The \l {MouseEvent}{mouse} parameter provides information about the press, including the x and y + position and which button was pressed. + + The \e accepted property of the MouseEvent parameter determines whether this MouseArea + will handle the press and all future mouse events until release. The default is to accept + the event and not allow other MouseArea beneath this one to handle the event. If \e accepted + is set to false, no further events will be sent to this MouseArea until the button is next + pressed. +*/ + +/*! + \qmlsignal MouseArea::onReleased(MouseEvent mouse) + + This handler is called when there is a release. + The \l {MouseEvent}{mouse} parameter provides information about the click, including the x and y + position of the release of the click, and whether the click was held. + + The \e accepted property of the MouseEvent parameter is ignored in this handler. + + \sa onCanceled +*/ + +/*! + \qmlsignal MouseArea::onPressAndHold(MouseEvent mouse) + + This handler is called when there is a long press (currently 800ms). + The \l {MouseEvent}{mouse} parameter provides information about the press, including the x and y + position of the press, and which button is pressed. + + The \e accepted property of the MouseEvent parameter is ignored in this handler. +*/ + +/*! + \qmlsignal MouseArea::onDoubleClicked(MouseEvent mouse) + + This handler is called when there is a double-click (a press followed by a release followed by a press). + The \l {MouseEvent}{mouse} parameter provides information about the click, including the x and y + position of the release of the click, and whether the click was held. + + If the \e accepted property of the \l {MouseEvent}{mouse} parameter is set to false + in the handler, the onPressed/onReleased/onClicked handlers will be called for the second + click; otherwise they are suppressed. The accepted property defaults to true. +*/ + +/*! + \qmlsignal MouseArea::onCanceled() + + This handler is called when mouse events have been canceled, either because an event was not accepted, or + because another element stole the mouse event handling. + + This signal is for advanced use: it is useful when there is more than one MouseArea + that is handling input, or when there is a MouseArea inside a \l Flickable. In the latter + case, if you execute some logic on the pressed signal and then start dragging, the + \l Flickable will steal the mouse handling from the MouseArea. In these cases, to reset + the logic when the MouseArea has lost the mouse handling to the \l Flickable, + \c onCanceled should be used in addition to onReleased. +*/ + +QDeclarativeMouseArea::QDeclarativeMouseArea(QDeclarativeItem *parent) + : QDeclarativeItem(*(new QDeclarativeMouseAreaPrivate), parent) +{ + Q_D(QDeclarativeMouseArea); + d->init(); +} + +QDeclarativeMouseArea::~QDeclarativeMouseArea() +{ +} + +/*! + \qmlproperty real MouseArea::mouseX + \qmlproperty real MouseArea::mouseY + These properties hold the coordinates of the mouse cursor. + + If the hoverEnabled property is false then these properties will only be valid + while a button is pressed, and will remain valid as long as the button is held + down even if the mouse is moved outside the area. + + By default, this property is false. + + If hoverEnabled is true then these properties will be valid when: + \list + \i no button is pressed, but the mouse is within the MouseArea (containsMouse is true). + \i a button is pressed and held, even if it has since moved out of the area. + \endlist + + The coordinates are relative to the MouseArea. +*/ +qreal QDeclarativeMouseArea::mouseX() const +{ + Q_D(const QDeclarativeMouseArea); + return d->lastPos.x(); +} + +qreal QDeclarativeMouseArea::mouseY() const +{ + Q_D(const QDeclarativeMouseArea); + return d->lastPos.y(); +} + +/*! + \qmlproperty bool MouseArea::enabled + This property holds whether the item accepts mouse events. + + By default, this property is true. +*/ +bool QDeclarativeMouseArea::isEnabled() const +{ + Q_D(const QDeclarativeMouseArea); + return d->absorb; +} + +void QDeclarativeMouseArea::setEnabled(bool a) +{ + Q_D(QDeclarativeMouseArea); + if (a != d->absorb) { + d->absorb = a; + emit enabledChanged(); + } +} + +/*! + \qmlproperty bool MouseArea::preventStealing + \since QtQuick 1.1 + This property holds whether the mouse events may be stolen from this + MouseArea. + + If a MouseArea is placed within an item that filters child mouse + events, such as Flickable, the mouse + events may be stolen from the MouseArea if a gesture is recognized + by the parent element, e.g. a flick gesture. If preventStealing is + set to true, no element will steal the mouse events. + + Note that setting preventStealing to true once an element has started + stealing events will have no effect until the next press event. + + By default this property is false. +*/ +bool QDeclarativeMouseArea::preventStealing() const +{ + Q_D(const QDeclarativeMouseArea); + return d->preventStealing; +} + +void QDeclarativeMouseArea::setPreventStealing(bool prevent) +{ + Q_D(QDeclarativeMouseArea); + if (prevent != d->preventStealing) { + d->preventStealing = prevent; + setKeepMouseGrab(d->preventStealing && d->absorb); + emit preventStealingChanged(); + } +} + +/*! + \qmlproperty MouseButtons MouseArea::pressedButtons + This property holds the mouse buttons currently pressed. + + It contains a bitwise combination of: + \list + \o Qt.LeftButton + \o Qt.RightButton + \o Qt.MiddleButton + \endlist + + The code below displays "right" when the right mouse buttons is pressed: + + \snippet doc/src/snippets/declarative/mousearea/mousearea.qml mousebuttons + + \sa acceptedButtons +*/ +Qt::MouseButtons QDeclarativeMouseArea::pressedButtons() const +{ + Q_D(const QDeclarativeMouseArea); + return d->lastButtons; +} + +void QDeclarativeMouseArea::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarativeMouseArea); + d->moved = false; + d->stealMouse = d->preventStealing; + if (!d->absorb) + QDeclarativeItem::mousePressEvent(event); + else { + d->longPress = false; + d->saveEvent(event); + if (d->drag) { + d->dragX = drag()->axis() & QDeclarativeDrag::XAxis; + d->dragY = drag()->axis() & QDeclarativeDrag::YAxis; + } + if (d->drag) + d->drag->setActive(false); + setHovered(true); + d->startScene = event->scenePos(); + // we should only start timer if pressAndHold is connected to. + if (d->isPressAndHoldConnected()) + d->pressAndHoldTimer.start(PressAndHoldDelay, this); + setKeepMouseGrab(d->stealMouse); + event->setAccepted(setPressed(true)); + } +} + +void QDeclarativeMouseArea::mouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarativeMouseArea); + if (!d->absorb) { + QDeclarativeItem::mouseMoveEvent(event); + return; + } + + d->saveEvent(event); + + // ### we should skip this if these signals aren't used + // ### can GV handle this for us? + bool contains = boundingRect().contains(d->lastPos); + if (d->hovered && !contains) + setHovered(false); + else if (!d->hovered && contains) + setHovered(true); + + if (d->drag && d->drag->target()) { + if (!d->moved) { + d->startX = drag()->target()->x(); + d->startY = drag()->target()->y(); + } + + QPointF startLocalPos; + QPointF curLocalPos; + if (drag()->target()->parentItem()) { + startLocalPos = drag()->target()->parentItem()->mapFromScene(d->startScene); + curLocalPos = drag()->target()->parentItem()->mapFromScene(event->scenePos()); + } else { + startLocalPos = d->startScene; + curLocalPos = event->scenePos(); + } + + const int dragThreshold = QApplication::startDragDistance(); + qreal dx = qAbs(curLocalPos.x() - startLocalPos.x()); + qreal dy = qAbs(curLocalPos.y() - startLocalPos.y()); + + if (keepMouseGrab() && d->stealMouse) + d->drag->setActive(true); + + if (d->dragX && d->drag->active()) { + qreal x = (curLocalPos.x() - startLocalPos.x()) + d->startX; + if (x < drag()->xmin()) + x = drag()->xmin(); + else if (x > drag()->xmax()) + x = drag()->xmax(); + drag()->target()->setX(x); + } + if (d->dragY && d->drag->active()) { + qreal y = (curLocalPos.y() - startLocalPos.y()) + d->startY; + if (y < drag()->ymin()) + y = drag()->ymin(); + else if (y > drag()->ymax()) + y = drag()->ymax(); + drag()->target()->setY(y); + } + + if (!keepMouseGrab()) { + if ((!d->dragY && dy < dragThreshold && d->dragX && dx > dragThreshold) + || (!d->dragX && dx < dragThreshold && d->dragY && dy > dragThreshold) + || (d->dragX && d->dragY && (dx > dragThreshold || dy > dragThreshold))) { + setKeepMouseGrab(true); + d->stealMouse = true; + } + } + + d->moved = true; + } + QDeclarativeMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress); + emit mousePositionChanged(&me); + me.setX(d->lastPos.x()); + me.setY(d->lastPos.y()); + emit positionChanged(&me); +} + + +void QDeclarativeMouseArea::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarativeMouseArea); + d->stealMouse = false; + if (!d->absorb) { + QDeclarativeItem::mouseReleaseEvent(event); + } else { + d->saveEvent(event); + setPressed(false); + if (d->drag) + d->drag->setActive(false); + // If we don't accept hover, we need to reset containsMouse. + if (!acceptHoverEvents()) + setHovered(false); + QGraphicsScene *s = scene(); + if (s && s->mouseGrabberItem() == this) + ungrabMouse(); + setKeepMouseGrab(false); + } + d->doubleClick = false; +} + +void QDeclarativeMouseArea::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarativeMouseArea); + if (!d->absorb) { + QDeclarativeItem::mouseDoubleClickEvent(event); + } else { + if (d->isDoubleClickConnected()) + d->doubleClick = true; + d->saveEvent(event); + QDeclarativeMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, true, false); + me.setAccepted(d->isDoubleClickConnected()); + emit this->doubleClicked(&me); + QDeclarativeItem::mouseDoubleClickEvent(event); + } +} + +void QDeclarativeMouseArea::hoverEnterEvent(QGraphicsSceneHoverEvent *event) +{ + Q_D(QDeclarativeMouseArea); + if (!d->absorb) + QDeclarativeItem::hoverEnterEvent(event); + else { + d->lastPos = event->pos(); + setHovered(true); + QDeclarativeMouseEvent me(d->lastPos.x(), d->lastPos.y(), Qt::NoButton, Qt::NoButton, event->modifiers(), false, false); + emit mousePositionChanged(&me); + } +} + +void QDeclarativeMouseArea::hoverMoveEvent(QGraphicsSceneHoverEvent *event) +{ + Q_D(QDeclarativeMouseArea); + if (!d->absorb) { + QDeclarativeItem::hoverMoveEvent(event); + } else { + d->lastPos = event->pos(); + QDeclarativeMouseEvent me(d->lastPos.x(), d->lastPos.y(), Qt::NoButton, Qt::NoButton, event->modifiers(), false, false); + emit mousePositionChanged(&me); + me.setX(d->lastPos.x()); + me.setY(d->lastPos.y()); + emit positionChanged(&me); + } +} + +void QDeclarativeMouseArea::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) +{ + Q_D(QDeclarativeMouseArea); + if (!d->absorb) + QDeclarativeItem::hoverLeaveEvent(event); + else + setHovered(false); +} + +#ifndef QT_NO_CONTEXTMENU +void QDeclarativeMouseArea::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) +{ + bool acceptsContextMenuButton; +#if defined(Q_OS_SYMBIAN) + // In Symbian a Long Tap on the screen will trigger. See QSymbianControl::HandleLongTapEventL(). + acceptsContextMenuButton = acceptedButtons() & Qt::LeftButton; +#elif defined(Q_WS_WINCE) + // ### WinCE can trigger context menu event with a gesture in the left button or a + // click with the right button. Since we have no way here to differentiate them when + // event happens, accepting either of the them will block the event. + acceptsContextMenuButton = acceptedButtons() & (Qt::LeftButton | Qt::RightButton); +#else + acceptsContextMenuButton = acceptedButtons() & Qt::RightButton; +#endif + + if (isEnabled() && event->reason() == QGraphicsSceneContextMenuEvent::Mouse + && acceptsContextMenuButton) { + // Do not let the context menu event propagate to items behind. + return; + } + + QDeclarativeItem::contextMenuEvent(event); +} +#endif // QT_NO_CONTEXTMENU + +bool QDeclarativeMouseArea::sceneEvent(QEvent *event) +{ + bool rv = QDeclarativeItem::sceneEvent(event); + if (event->type() == QEvent::UngrabMouse) { + Q_D(QDeclarativeMouseArea); + if (d->pressed) { + // if our mouse grab has been removed (probably by Flickable), fix our + // state + d->pressed = false; + d->stealMouse = false; + setKeepMouseGrab(false); + emit canceled(); + emit pressedChanged(); + if (d->hovered) { + d->hovered = false; + emit hoveredChanged(); + } + } + } + return rv; +} + +bool QDeclarativeMouseArea::sendMouseEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarativeMouseArea); + QGraphicsSceneMouseEvent mouseEvent(event->type()); + QRectF myRect = mapToScene(QRectF(0, 0, width(), height())).boundingRect(); + + QGraphicsScene *s = scene(); + QDeclarativeItem *grabber = s ? qobject_cast(s->mouseGrabberItem()) : 0; + bool stealThisEvent = d->stealMouse; + if ((stealThisEvent || myRect.contains(event->scenePos().toPoint())) && (!grabber || !grabber->keepMouseGrab())) { + mouseEvent.setAccepted(false); + for (int i = 0x1; i <= 0x10; i <<= 1) { + if (event->buttons() & i) { + Qt::MouseButton button = Qt::MouseButton(i); + mouseEvent.setButtonDownPos(button, mapFromScene(event->buttonDownPos(button))); + } + } + mouseEvent.setScenePos(event->scenePos()); + mouseEvent.setLastScenePos(event->lastScenePos()); + mouseEvent.setPos(mapFromScene(event->scenePos())); + mouseEvent.setLastPos(mapFromScene(event->lastScenePos())); + + switch(mouseEvent.type()) { + case QEvent::GraphicsSceneMouseMove: + mouseMoveEvent(&mouseEvent); + break; + case QEvent::GraphicsSceneMousePress: + mousePressEvent(&mouseEvent); + break; + case QEvent::GraphicsSceneMouseRelease: + mouseReleaseEvent(&mouseEvent); + break; + default: + break; + } + grabber = qobject_cast(s->mouseGrabberItem()); + if (grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) + grabMouse(); + + return stealThisEvent; + } + if (mouseEvent.type() == QEvent::GraphicsSceneMouseRelease) { + if (d->pressed) { + d->pressed = false; + d->stealMouse = false; + if (s && s->mouseGrabberItem() == this) + ungrabMouse(); + emit canceled(); + emit pressedChanged(); + if (d->hovered) { + d->hovered = false; + emit hoveredChanged(); + } + } + } + return false; +} + +bool QDeclarativeMouseArea::sceneEventFilter(QGraphicsItem *i, QEvent *e) +{ + Q_D(QDeclarativeMouseArea); + if (!d->absorb || !isVisible() || !d->drag || !d->drag->filterChildren()) + return QDeclarativeItem::sceneEventFilter(i, e); + switch (e->type()) { + case QEvent::GraphicsSceneMousePress: + case QEvent::GraphicsSceneMouseMove: + case QEvent::GraphicsSceneMouseRelease: + return sendMouseEvent(static_cast(e)); + default: + break; + } + + return QDeclarativeItem::sceneEventFilter(i, e); +} + +void QDeclarativeMouseArea::timerEvent(QTimerEvent *event) +{ + Q_D(QDeclarativeMouseArea); + if (event->timerId() == d->pressAndHoldTimer.timerId()) { + d->pressAndHoldTimer.stop(); + bool dragged = d->drag && d->drag->active(); + if (d->pressed && dragged == false && d->hovered == true) { + d->longPress = true; + QDeclarativeMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress); + emit pressAndHold(&me); + } + } +} + +void QDeclarativeMouseArea::geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry) +{ + Q_D(QDeclarativeMouseArea); + QDeclarativeItem::geometryChanged(newGeometry, oldGeometry); + + if (d->lastScenePos.isNull) + d->lastScenePos = mapToScene(d->lastPos); + else if (newGeometry.x() != oldGeometry.x() || newGeometry.y() != oldGeometry.y()) + d->lastPos = mapFromScene(d->lastScenePos); +} + +QVariant QDeclarativeMouseArea::itemChange(GraphicsItemChange change, + const QVariant &value) +{ + Q_D(QDeclarativeMouseArea); + switch (change) { + case ItemVisibleHasChanged: + if (acceptHoverEvents() && d->hovered != (isVisible() && isUnderMouse())) + setHovered(!d->hovered); + break; + default: + break; + } + + return QDeclarativeItem::itemChange(change, value); +} + +/*! + \qmlproperty bool MouseArea::hoverEnabled + This property holds whether hover events are handled. + + By default, mouse events are only handled in response to a button event, or when a button is + pressed. Hover enables handling of all mouse events even when no mouse button is + pressed. + + This property affects the containsMouse property and the onEntered, onExited and + onPositionChanged signals. +*/ +bool QDeclarativeMouseArea::hoverEnabled() const +{ + return acceptHoverEvents(); +} + +void QDeclarativeMouseArea::setHoverEnabled(bool h) +{ + Q_D(QDeclarativeMouseArea); + if (h == acceptHoverEvents()) + return; + + setAcceptHoverEvents(h); + emit hoverEnabledChanged(); + if (d->hovered != isUnderMouse()) + setHovered(!d->hovered); +} + +/*! + \qmlproperty bool MouseArea::containsMouse + This property holds whether the mouse is currently inside the mouse area. + + \warning This property is not updated if the area moves under the mouse: \e containsMouse will not change. + In addition, if hoverEnabled is false, containsMouse will only be valid when the mouse is pressed. +*/ +bool QDeclarativeMouseArea::hovered() const +{ + Q_D(const QDeclarativeMouseArea); + return d->hovered; +} + +/*! + \qmlproperty bool MouseArea::pressed + This property holds whether the mouse area is currently pressed. +*/ +bool QDeclarativeMouseArea::pressed() const +{ + Q_D(const QDeclarativeMouseArea); + return d->pressed; +} + +void QDeclarativeMouseArea::setHovered(bool h) +{ + Q_D(QDeclarativeMouseArea); + if (d->hovered != h) { + d->hovered = h; + emit hoveredChanged(); + d->hovered ? emit entered() : emit exited(); + } +} + +/*! + \qmlproperty Qt::MouseButtons MouseArea::acceptedButtons + This property holds the mouse buttons that the mouse area reacts to. + + The available buttons are: + \list + \o Qt.LeftButton + \o Qt.RightButton + \o Qt.MiddleButton + \endlist + + To accept more than one button the flags can be combined with the + "|" (or) operator: + + \code + MouseArea { acceptedButtons: Qt.LeftButton | Qt.RightButton } + \endcode + + The default value is \c Qt.LeftButton. +*/ +Qt::MouseButtons QDeclarativeMouseArea::acceptedButtons() const +{ + return acceptedMouseButtons(); +} + +void QDeclarativeMouseArea::setAcceptedButtons(Qt::MouseButtons buttons) +{ + if (buttons != acceptedMouseButtons()) { + setAcceptedMouseButtons(buttons); + emit acceptedButtonsChanged(); + } +} + +bool QDeclarativeMouseArea::setPressed(bool p) +{ + Q_D(QDeclarativeMouseArea); + bool dragged = d->drag && d->drag->active(); + bool isclick = d->pressed == true && p == false && dragged == false && d->hovered == true; + + if (d->pressed != p) { + d->pressed = p; + QDeclarativeMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, isclick, d->longPress); + if (d->pressed) { + if (!d->doubleClick) + emit pressed(&me); + me.setX(d->lastPos.x()); + me.setY(d->lastPos.y()); + emit mousePositionChanged(&me); + emit pressedChanged(); + } else { + emit released(&me); + me.setX(d->lastPos.x()); + me.setY(d->lastPos.y()); + emit pressedChanged(); + if (isclick && !d->longPress && !d->doubleClick) + emit clicked(&me); + } + + return me.isAccepted(); + } + return false; +} + +QDeclarativeDrag *QDeclarativeMouseArea::drag() +{ + Q_D(QDeclarativeMouseArea); + if (!d->drag) + d->drag = new QDeclarativeDrag; + return d->drag; +} + +/*! + \qmlproperty Item MouseArea::drag.target + \qmlproperty bool MouseArea::drag.active + \qmlproperty enumeration MouseArea::drag.axis + \qmlproperty real MouseArea::drag.minimumX + \qmlproperty real MouseArea::drag.maximumX + \qmlproperty real MouseArea::drag.minimumY + \qmlproperty real MouseArea::drag.maximumY + \qmlproperty bool MouseArea::drag.filterChildren + + \c drag provides a convenient way to make an item draggable. + + \list + \i \c drag.target specifies the id of the item to drag. + \i \c drag.active specifies if the target item is currently being dragged. + \i \c drag.axis specifies whether dragging can be done horizontally (\c Drag.XAxis), vertically (\c Drag.YAxis), or both (\c Drag.XandYAxis) + \i \c drag.minimum and \c drag.maximum limit how far the target can be dragged along the corresponding axes. + \endlist + + The following example displays a \l Rectangle that can be dragged along the X-axis. The opacity + of the rectangle is reduced when it is dragged to the right. + + \snippet doc/src/snippets/declarative/mousearea/mousearea.qml drag + + \note Items cannot be dragged if they are anchored for the requested + \c drag.axis. For example, if \c anchors.left or \c anchors.right was set + for \c rect in the above example, it cannot be dragged along the X-axis. + This can be avoided by settng the anchor value to \c undefined in + an \l onPressed handler. + + If \c drag.filterChildren is set to true, a drag can override descendant MouseAreas. This + enables a parent MouseArea to handle drags, for example, while descendants handle clicks: + + \snippet doc/src/snippets/declarative/mousearea/mouseareadragfilter.qml dragfilter + +*/ + +QT_END_NAMESPACE diff --git a/src/declarative/graphicsitems/qdeclarativemousearea_p.h b/src/declarative/graphicsitems/qdeclarativemousearea_p.h new file mode 100644 index 00000000..23795907 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativemousearea_p.h @@ -0,0 +1,218 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEMOUSEAREA_H +#define QDECLARATIVEMOUSEAREA_H + +#include "qdeclarativeitem.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class Q_AUTOTEST_EXPORT QDeclarativeDrag : public QObject +{ + Q_OBJECT + + Q_ENUMS(Axis) + Q_PROPERTY(QGraphicsObject *target READ target WRITE setTarget NOTIFY targetChanged RESET resetTarget) + Q_PROPERTY(Axis axis READ axis WRITE setAxis NOTIFY axisChanged) + Q_PROPERTY(qreal minimumX READ xmin WRITE setXmin NOTIFY minimumXChanged) + Q_PROPERTY(qreal maximumX READ xmax WRITE setXmax NOTIFY maximumXChanged) + Q_PROPERTY(qreal minimumY READ ymin WRITE setYmin NOTIFY minimumYChanged) + Q_PROPERTY(qreal maximumY READ ymax WRITE setYmax NOTIFY maximumYChanged) + Q_PROPERTY(bool active READ active NOTIFY activeChanged) + Q_PROPERTY(bool filterChildren READ filterChildren WRITE setFilterChildren NOTIFY filterChildrenChanged) + //### consider drag and drop + +public: + QDeclarativeDrag(QObject *parent=0); + ~QDeclarativeDrag(); + + QGraphicsObject *target() const; + void setTarget(QGraphicsObject *); + void resetTarget(); + + enum Axis { XAxis=0x01, YAxis=0x02, XandYAxis=0x03 }; + Axis axis() const; + void setAxis(Axis); + + qreal xmin() const; + void setXmin(qreal); + qreal xmax() const; + void setXmax(qreal); + qreal ymin() const; + void setYmin(qreal); + qreal ymax() const; + void setYmax(qreal); + + bool active() const; + void setActive(bool); + + bool filterChildren() const; + void setFilterChildren(bool); + +Q_SIGNALS: + void targetChanged(); + void axisChanged(); + void minimumXChanged(); + void maximumXChanged(); + void minimumYChanged(); + void maximumYChanged(); + void activeChanged(); + void filterChildrenChanged(); + +private: + QGraphicsObject *_target; + Axis _axis; + qreal _xmin; + qreal _xmax; + qreal _ymin; + qreal _ymax; + bool _active : 1; + bool _filterChildren: 1; + Q_DISABLE_COPY(QDeclarativeDrag) +}; + +class QDeclarativeMouseEvent; +class QDeclarativeMouseAreaPrivate; +class Q_AUTOTEST_EXPORT QDeclarativeMouseArea : public QDeclarativeItem +{ + Q_OBJECT + + Q_PROPERTY(qreal mouseX READ mouseX NOTIFY mousePositionChanged) + Q_PROPERTY(qreal mouseY READ mouseY NOTIFY mousePositionChanged) + Q_PROPERTY(bool containsMouse READ hovered NOTIFY hoveredChanged) + Q_PROPERTY(bool pressed READ pressed NOTIFY pressedChanged) + Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged) + Q_PROPERTY(Qt::MouseButtons pressedButtons READ pressedButtons NOTIFY pressedChanged) + Q_PROPERTY(Qt::MouseButtons acceptedButtons READ acceptedButtons WRITE setAcceptedButtons NOTIFY acceptedButtonsChanged) + Q_PROPERTY(bool hoverEnabled READ hoverEnabled WRITE setHoverEnabled NOTIFY hoverEnabledChanged) + Q_PROPERTY(QDeclarativeDrag *drag READ drag CONSTANT) //### add flicking to QDeclarativeDrag or add a QDeclarativeFlick ??? + Q_PROPERTY(bool preventStealing READ preventStealing WRITE setPreventStealing NOTIFY preventStealingChanged REVISION 1) + +public: + QDeclarativeMouseArea(QDeclarativeItem *parent=0); + ~QDeclarativeMouseArea(); + + qreal mouseX() const; + qreal mouseY() const; + + bool isEnabled() const; + void setEnabled(bool); + + bool hovered() const; + bool pressed() const; + + Qt::MouseButtons pressedButtons() const; + + Qt::MouseButtons acceptedButtons() const; + void setAcceptedButtons(Qt::MouseButtons buttons); + + bool hoverEnabled() const; + void setHoverEnabled(bool h); + + QDeclarativeDrag *drag(); + + bool preventStealing() const; + void setPreventStealing(bool prevent); + +Q_SIGNALS: + void hoveredChanged(); + void pressedChanged(); + void enabledChanged(); + void acceptedButtonsChanged(); + void hoverEnabledChanged(); + void positionChanged(QDeclarativeMouseEvent *mouse); + void mousePositionChanged(QDeclarativeMouseEvent *mouse); + Q_REVISION(1) void preventStealingChanged(); + + void pressed(QDeclarativeMouseEvent *mouse); + void pressAndHold(QDeclarativeMouseEvent *mouse); + void released(QDeclarativeMouseEvent *mouse); + void clicked(QDeclarativeMouseEvent *mouse); + void doubleClicked(QDeclarativeMouseEvent *mouse); + void entered(); + void exited(); + void canceled(); + +protected: + void setHovered(bool); + bool setPressed(bool); + + void mousePressEvent(QGraphicsSceneMouseEvent *event); + void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); + void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event); + void mouseMoveEvent(QGraphicsSceneMouseEvent *event); + void hoverEnterEvent(QGraphicsSceneHoverEvent *event); + void hoverMoveEvent(QGraphicsSceneHoverEvent *event); + void hoverLeaveEvent(QGraphicsSceneHoverEvent *event); +#ifndef QT_NO_CONTEXTMENU + void contextMenuEvent(QGraphicsSceneContextMenuEvent *event); +#endif // QT_NO_CONTEXTMENU + bool sceneEvent(QEvent *); + bool sendMouseEvent(QGraphicsSceneMouseEvent *event); + bool sceneEventFilter(QGraphicsItem *i, QEvent *e); + void timerEvent(QTimerEvent *event); + + virtual void geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry); + virtual QVariant itemChange(GraphicsItemChange change, const QVariant& value); + +private: + void handlePress(); + void handleRelease(); + +private: + Q_DISABLE_COPY(QDeclarativeMouseArea) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarativeMouseArea) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeDrag) +QML_DECLARE_TYPE(QDeclarativeMouseArea) + +QT_END_HEADER + +#endif // QDECLARATIVEMOUSEAREA_H diff --git a/src/declarative/graphicsitems/qdeclarativemousearea_p_p.h b/src/declarative/graphicsitems/qdeclarativemousearea_p_p.h new file mode 100644 index 00000000..00c6eed8 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativemousearea_p_p.h @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEMOUSEREGION_P_H +#define QDECLARATIVEMOUSEREGION_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 "private/qdeclarativeitem_p.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeMouseAreaPrivate : public QDeclarativeItemPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeMouseArea) + +public: + QDeclarativeMouseAreaPrivate() + : absorb(true), hovered(false), pressed(false), longPress(false), + moved(false), stealMouse(false), doubleClick(false), preventStealing(false), drag(0) + { + } + + ~QDeclarativeMouseAreaPrivate(); + + void init() + { + Q_Q(QDeclarativeMouseArea); + q->setAcceptedMouseButtons(Qt::LeftButton); + q->setFiltersChildEvents(true); + } + + void saveEvent(QGraphicsSceneMouseEvent *event) { + lastPos = event->pos(); + lastScenePos = event->scenePos(); + lastButton = event->button(); + lastButtons = event->buttons(); + lastModifiers = event->modifiers(); + } + + bool isPressAndHoldConnected() { + Q_Q(QDeclarativeMouseArea); + static int idx = QObjectPrivate::get(q)->signalIndex("pressAndHold(QDeclarativeMouseEvent*)"); + return QObjectPrivate::get(q)->isSignalConnected(idx); + } + + bool isDoubleClickConnected() { + Q_Q(QDeclarativeMouseArea); + static int idx = QObjectPrivate::get(q)->signalIndex("doubleClicked(QDeclarativeMouseEvent*)"); + return QObjectPrivate::get(q)->isSignalConnected(idx); + } + + bool absorb : 1; + bool hovered : 1; + bool pressed : 1; + bool longPress : 1; + bool moved : 1; + bool dragX : 1; + bool dragY : 1; + bool stealMouse : 1; + bool doubleClick : 1; + bool preventStealing : 1; + QDeclarativeDrag *drag; + QPointF startScene; + qreal startX; + qreal startY; + QPointF lastPos; + QDeclarativeNullableValue lastScenePos; + Qt::MouseButton lastButton; + Qt::MouseButtons lastButtons; + Qt::KeyboardModifiers lastModifiers; + QBasicTimer pressAndHoldTimer; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVEMOUSEREGION_P_H diff --git a/src/declarative/graphicsitems/qdeclarativepainteditem.cpp b/src/declarative/graphicsitems/qdeclarativepainteditem.cpp new file mode 100644 index 00000000..ecf072dc --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativepainteditem.cpp @@ -0,0 +1,497 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativepainteditem_p.h" +#include "private/qdeclarativepainteditem_p_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \class QDeclarativePaintedItem + \brief The QDeclarativePaintedItem class is an abstract base class for QDeclarativeView items that want cached painting. + \internal + + This is a convenience class for implementing items that cache their painting. + The contents of the item are cached behind the scenes. + The dirtyCache() function should be called if the contents change to + ensure the cache is refreshed the next time painting occurs. + + To subclass QDeclarativePaintedItem, you must reimplement drawContents() to draw + the contents of the item. +*/ + +/*! + \fn void QDeclarativePaintedItem::drawContents(QPainter *painter, const QRect &rect) + + This function is called when the cache needs to be refreshed. When + sub-classing QDeclarativePaintedItem this function should be implemented so as to + paint the contents of the item using the given \a painter for the + area of the contents specified by \a rect. +*/ + +/*! + \property QDeclarativePaintedItem::contentsSize + \brief The size of the contents + + The contents size is the size of the item in regards to how it is painted + using the drawContents() function. This is distinct from the size of the + item in regards to height() and width(). +*/ + +// XXX bug in WebKit - can call repaintRequested and other cache-changing functions from within render! +static int inpaint=0; +static int inpaint_clearcache=0; + +extern Q_GUI_EXPORT bool qt_applefontsmoothing_enabled; + +/*! + Marks areas of the cache that intersect with the given \a rect as dirty and + in need of being refreshed. + + \sa clearCache() +*/ +void QDeclarativePaintedItem::dirtyCache(const QRect& rect) +{ + Q_D(QDeclarativePaintedItem); + QRect srect(qCeil(rect.x()*d->contentsScale), + qCeil(rect.y()*d->contentsScale), + qCeil(rect.width()*d->contentsScale), + qCeil(rect.height()*d->contentsScale)); + for (int i=0; i < d->imagecache.count(); ) { + QDeclarativePaintedItemPrivate::ImageCacheItem *c = d->imagecache[i]; + QRect isect = (c->area & srect) | c->dirty; + if (isect == c->area && !inpaint) { + delete d->imagecache.takeAt(i); + } else { + c->dirty = isect; + ++i; + } + } +} + +/*! + Marks the entirety of the contents cache as dirty. + + \sa dirtyCache() +*/ +void QDeclarativePaintedItem::clearCache() +{ + if (inpaint) { + inpaint_clearcache=1; + return; + } + Q_D(QDeclarativePaintedItem); + qDeleteAll(d->imagecache); + d->imagecache.clear(); +} + +/*! + Returns the size of the contents. + + \sa setContentsSize() +*/ +QSize QDeclarativePaintedItem::contentsSize() const +{ + Q_D(const QDeclarativePaintedItem); + return d->contentsSize; +} + +/*! + Sets the size of the contents to the given \a size. + + \sa contentsSize() +*/ +void QDeclarativePaintedItem::setContentsSize(const QSize &size) +{ + Q_D(QDeclarativePaintedItem); + if (d->contentsSize == size) return; + prepareGeometryChange(); + d->contentsSize = size; + clearCache(); + update(); + emit contentsSizeChanged(); +} + +qreal QDeclarativePaintedItem::contentsScale() const +{ + Q_D(const QDeclarativePaintedItem); + return d->contentsScale; +} + +void QDeclarativePaintedItem::setContentsScale(qreal scale) +{ + Q_D(QDeclarativePaintedItem); + if (d->contentsScale == scale) return; + d->contentsScale = scale; + clearCache(); + update(); + emit contentsScaleChanged(); +} + + +/*! + Constructs a new QDeclarativePaintedItem with the given \a parent. +*/ +QDeclarativePaintedItem::QDeclarativePaintedItem(QDeclarativeItem *parent) + : QDeclarativeItem(*(new QDeclarativePaintedItemPrivate), parent) +{ +} + +/*! + \internal + Constructs a new QDeclarativePaintedItem with the given \a parent and + initialized private data member \a dd. +*/ +QDeclarativePaintedItem::QDeclarativePaintedItem(QDeclarativePaintedItemPrivate &dd, QDeclarativeItem *parent) + : QDeclarativeItem(dd, parent) +{ +} + +/*! + Destroys the image item. +*/ +QDeclarativePaintedItem::~QDeclarativePaintedItem() +{ + clearCache(); +} + +void QDeclarativePaintedItem::geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry) +{ + if (newGeometry.width() != oldGeometry.width() || + newGeometry.height() != oldGeometry.height()) + clearCache(); + + QDeclarativeItem::geometryChanged(newGeometry, oldGeometry); +} + +QVariant QDeclarativePaintedItem::itemChange(GraphicsItemChange change, + const QVariant &value) +{ + if (change == ItemVisibleHasChanged) + clearCache(); + + return QDeclarativeItem::itemChange(change, value); +} + +void QDeclarativePaintedItem::setCacheFrozen(bool frozen) +{ + Q_D(QDeclarativePaintedItem); + if (d->cachefrozen == frozen) + return; + d->cachefrozen = frozen; + // XXX clear cache? +} + +QRectF QDeclarativePaintedItem::boundingRect() const +{ + Q_D(const QDeclarativePaintedItem); + qreal w = d->mWidth; + QSizeF sz = d->contentsSize * d->contentsScale; + if (w < sz.width()) + w = sz.width(); + qreal h = d->mHeight; + if (h < sz.height()) + h = sz.height(); + return QRectF(0.0,0.0,w,h); +} + +/*! + \internal +*/ +void QDeclarativePaintedItem::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *) +{ + Q_D(QDeclarativePaintedItem); + const QRect content = boundingRect().toRect(); + if (content.width() <= 0 || content.height() <= 0) + return; + + ++inpaint; + + const QTransform &x = p->deviceTransform(); + QTransform xinv = x.inverted(); + QRegion effectiveClip; + QRegion sysClip = p->paintEngine()->systemClip(); + if (xinv.type() <= QTransform::TxScale && sysClip.numRects() < 5) { + // simple transform, region gets no more complicated... + effectiveClip = xinv.map(sysClip); + } else { + // do not make complicated regions... + effectiveClip = xinv.mapRect(sysClip.boundingRect()); + } + + QRegion topaint = p->clipRegion(); + if (topaint.isEmpty()) { + if (effectiveClip.isEmpty()) + topaint = QRect(0,0,p->device()->width(),p->device()->height()); + else + topaint = effectiveClip; + } else if (!effectiveClip.isEmpty()) { + topaint &= effectiveClip; + } + + topaint &= content; + QRegion uncached(content); + p->setRenderHints(QPainter::SmoothPixmapTransform, d->smooth); + + int cachesize=0; + for (int i=0; iimagecache.count(); ++i) { + QRect area = d->imagecache[i]->area; + if (topaint.contains(area)) { + QRectF target(area.x(), area.y(), area.width(), area.height()); + if (!d->cachefrozen) { + if (!d->imagecache[i]->dirty.isNull() && topaint.contains(d->imagecache[i]->dirty)) { +#ifdef Q_WS_MAC + bool oldSmooth = qt_applefontsmoothing_enabled; + qt_applefontsmoothing_enabled = false; +#endif + QPainter qp(&d->imagecache[i]->image); +#ifdef Q_WS_MAC + qt_applefontsmoothing_enabled = oldSmooth; +#endif + qp.setRenderHints(QPainter::HighQualityAntialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform, d->smoothCache); + qp.translate(-area.x(), -area.y()); + qp.scale(d->contentsScale,d->contentsScale); + QRect clip = d->imagecache[i]->dirty; + QRect sclip(qFloor(clip.x()/d->contentsScale), + qFloor(clip.y()/d->contentsScale), + qCeil(clip.width()/d->contentsScale+clip.x()/d->contentsScale-qFloor(clip.x()/d->contentsScale)), + qCeil(clip.height()/d->contentsScale+clip.y()/d->contentsScale-qFloor(clip.y()/d->contentsScale))); + qp.setClipRect(sclip); + if (d->fillColor.isValid()){ + if(d->fillColor.alpha() < 255){ + // ### Might not work outside of raster paintengine + QPainter::CompositionMode prev = qp.compositionMode(); + qp.setCompositionMode(QPainter::CompositionMode_Source); + qp.fillRect(sclip,d->fillColor); + qp.setCompositionMode(prev); + }else{ + qp.fillRect(sclip,d->fillColor); + } + } + drawContents(&qp, sclip); + d->imagecache[i]->dirty = QRect(); + } + } + p->drawPixmap(target.toRect(), d->imagecache[i]->image); + topaint -= area; + d->imagecache[i]->age=0; + } else { + d->imagecache[i]->age++; + } + cachesize += area.width()*area.height(); + uncached -= area; + } + + if (!topaint.isEmpty()) { + if (!d->cachefrozen) { + // Find a sensible larger area, otherwise will paint lots of tiny images. + QRect biggerrect = topaint.boundingRect().adjusted(-64,-64,128,128); + cachesize += biggerrect.width() * biggerrect.height(); + while (d->imagecache.count() && cachesize > d->max_imagecache_size) { + int oldest=-1; + int age=-1; + for (int i=0; iimagecache.count(); ++i) { + int a = d->imagecache[i]->age; + if (a > age) { + oldest = i; + age = a; + } + } + cachesize -= d->imagecache[oldest]->area.width()*d->imagecache[oldest]->area.height(); + uncached += d->imagecache[oldest]->area; + delete d->imagecache.takeAt(oldest); + } + const QRegion bigger = QRegion(biggerrect) & uncached; + const QVector rects = bigger.rects(); + for (int i = 0; i < rects.count(); ++i) { + const QRect &r = rects.at(i); + QPixmap img(r.size()); + if (d->fillColor.isValid()) + img.fill(d->fillColor); + { +#ifdef Q_WS_MAC + bool oldSmooth = qt_applefontsmoothing_enabled; + qt_applefontsmoothing_enabled = false; +#endif + QPainter qp(&img); +#ifdef Q_WS_MAC + qt_applefontsmoothing_enabled = oldSmooth; +#endif + qp.setRenderHints(QPainter::HighQualityAntialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform, d->smoothCache); + + qp.translate(-r.x(),-r.y()); + qp.scale(d->contentsScale,d->contentsScale); + QRect sclip(qFloor(r.x()/d->contentsScale), + qFloor(r.y()/d->contentsScale), + qCeil(r.width()/d->contentsScale+r.x()/d->contentsScale-qFloor(r.x()/d->contentsScale)), + qCeil(r.height()/d->contentsScale+r.y()/d->contentsScale-qFloor(r.y()/d->contentsScale))); + drawContents(&qp, sclip); + } + QDeclarativePaintedItemPrivate::ImageCacheItem *newitem = new QDeclarativePaintedItemPrivate::ImageCacheItem; + newitem->area = r; + newitem->image = img; + d->imagecache.append(newitem); + p->drawPixmap(r, newitem->image); + } + } else { + const QVector rects = uncached.rects(); + for (int i = 0; i < rects.count(); ++i) + p->fillRect(rects.at(i), Qt::lightGray); + } + } + + if (inpaint_clearcache) { + clearCache(); + inpaint_clearcache = 0; + } + + --inpaint; +} + +/*! + \qmlproperty int PaintedItem::pixelCacheSize + + This property holds the maximum number of pixels of image cache to + allow. The default is 0.1 megapixels. The cache will not be larger + than the (unscaled) size of the WebView. +*/ +/*! + \property QDeclarativePaintedItem::pixelCacheSize + + The maximum number of pixels of image cache to allow. The default + is 0.1 megapixels. The cache will not be larger than the (unscaled) + size of the QDeclarativePaintedItem. +*/ +int QDeclarativePaintedItem::pixelCacheSize() const +{ + Q_D(const QDeclarativePaintedItem); + return d->max_imagecache_size; +} + +void QDeclarativePaintedItem::setPixelCacheSize(int pixels) +{ + Q_D(QDeclarativePaintedItem); + if (pixels < d->max_imagecache_size) { + int cachesize=0; + for (int i=0; iimagecache.count(); ++i) { + QRect area = d->imagecache[i]->area; + cachesize += area.width()*area.height(); + } + while (d->imagecache.count() && cachesize > pixels) { + int oldest=-1; + int age=-1; + for (int i=0; iimagecache.count(); ++i) { + int a = d->imagecache[i]->age; + if (a > age) { + oldest = i; + age = a; + } + } + cachesize -= d->imagecache[oldest]->area.width()*d->imagecache[oldest]->area.height(); + delete d->imagecache.takeAt(oldest); + } + } + d->max_imagecache_size = pixels; +} + + + +/*! + \property QDeclarativePaintedItem::fillColor + + The color to be used to fill the item prior to calling drawContents(). + By default, this is Qt::transparent. + + Performance improvements can be achieved if subclasses call this with either an + invalid color (QColor()), or an appropriate solid color. +*/ +void QDeclarativePaintedItem::setFillColor(const QColor& c) +{ + Q_D(QDeclarativePaintedItem); + if (d->fillColor == c) + return; + d->fillColor = c; + emit fillColorChanged(); + update(); +} + +QColor QDeclarativePaintedItem::fillColor() const +{ + Q_D(const QDeclarativePaintedItem); + return d->fillColor; +} + +/*! + \qmlproperty bool PaintedItem::smoothCache + + Controls whether the cached tiles of which the item is composed are + rendered smoothly when they are generated. + + This is in addition toe Item::smooth, which controls the smooth painting of + the already-painted cached tiles under transformation. +*/ +bool QDeclarativePaintedItem::smoothCache() const +{ + Q_D(const QDeclarativePaintedItem); + return d->smoothCache; +} + +void QDeclarativePaintedItem::setSmoothCache(bool on) +{ + Q_D(QDeclarativePaintedItem); + if (d->smoothCache != on) { + d->smoothCache = on; + clearCache(); + } +} + + +QT_END_NAMESPACE diff --git a/src/declarative/graphicsitems/qdeclarativepainteditem_p.h b/src/declarative/graphicsitems/qdeclarativepainteditem_p.h new file mode 100644 index 00000000..c0b2e88b --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativepainteditem_p.h @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEIMAGEITEM_H +#define QDECLARATIVEIMAGEITEM_H + +#include "qdeclarativeitem.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativePaintedItemPrivate; +class Q_AUTOTEST_EXPORT QDeclarativePaintedItem : public QDeclarativeItem +{ + Q_OBJECT + + Q_PROPERTY(QSize contentsSize READ contentsSize WRITE setContentsSize NOTIFY contentsSizeChanged) + Q_PROPERTY(QColor fillColor READ fillColor WRITE setFillColor NOTIFY fillColorChanged) + Q_PROPERTY(int pixelCacheSize READ pixelCacheSize WRITE setPixelCacheSize) + Q_PROPERTY(bool smoothCache READ smoothCache WRITE setSmoothCache) + Q_PROPERTY(qreal contentsScale READ contentsScale WRITE setContentsScale NOTIFY contentsScaleChanged) + + +public: + QDeclarativePaintedItem(QDeclarativeItem *parent=0); + ~QDeclarativePaintedItem(); + + QSize contentsSize() const; + void setContentsSize(const QSize &); + + qreal contentsScale() const; + void setContentsScale(qreal); + + int pixelCacheSize() const; + void setPixelCacheSize(int pixels); + + bool smoothCache() const; + void setSmoothCache(bool on); + + QColor fillColor() const; + void setFillColor(const QColor&); + + void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *); + +protected: + QDeclarativePaintedItem(QDeclarativePaintedItemPrivate &dd, QDeclarativeItem *parent); + + virtual void drawContents(QPainter *p, const QRect &) = 0; + virtual void geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry); + virtual QVariant itemChange(GraphicsItemChange change, + const QVariant &value); + + void setCacheFrozen(bool); + QRectF boundingRect() const; + +Q_SIGNALS: + void fillColorChanged(); + void contentsSizeChanged(); + void contentsScaleChanged(); + +protected Q_SLOTS: + void dirtyCache(const QRect &); + void clearCache(); + +private: + Q_DISABLE_COPY(QDeclarativePaintedItem) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarativePaintedItem) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativePaintedItem) + +QT_END_HEADER + +#endif diff --git a/src/declarative/graphicsitems/qdeclarativepainteditem_p_p.h b/src/declarative/graphicsitems/qdeclarativepainteditem_p_p.h new file mode 100644 index 00000000..66aea6bf --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativepainteditem_p_p.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEIMAGEITEM_P_H +#define QDECLARATIVEIMAGEITEM_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 "private/qdeclarativeitem_p.h" + +QT_BEGIN_NAMESPACE + +class QDeclarativePaintedItemPrivate : public QDeclarativeItemPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativePaintedItem) + +public: + QDeclarativePaintedItemPrivate() + : max_imagecache_size(100000), contentsScale(1.0), fillColor(Qt::transparent), cachefrozen(false), smoothCache(true) + { + } + + struct ImageCacheItem { + ImageCacheItem() : age(0) {} + ~ImageCacheItem() { } + int age; + QRect area; + QRect dirty; // one dirty area (allows optimization of common cases) + QPixmap image; + }; + + QList imagecache; + + int max_imagecache_size; + QSize contentsSize; + qreal contentsScale; + QColor fillColor; + bool cachefrozen; + bool smoothCache; +}; + +QT_END_NAMESPACE +#endif diff --git a/src/declarative/graphicsitems/qdeclarativepath.cpp b/src/declarative/graphicsitems/qdeclarativepath.cpp new file mode 100644 index 00000000..867a3b45 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativepath.cpp @@ -0,0 +1,922 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativepath_p.h" +#include "private/qdeclarativepath_p_p.h" + +#include +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmlclass PathElement QDeclarativePathElement + \ingroup qml-view-elements + \since 4.7 + \brief PathElement is the base path type. + + This type is the base for all path types. It cannot + be instantiated. + + \sa Path, PathAttribute, PathPercent, PathLine, PathQuad, PathCubic +*/ + +/*! + \qmlclass Path QDeclarativePath + \ingroup qml-view-elements + \since 4.7 + \brief A Path object defines a path for use by \l PathView. + + A Path is composed of one or more path segments - PathLine, PathQuad, + PathCubic. + + The spacing of the items along the Path can be adjusted via a + PathPercent object. + + PathAttribute allows named attributes with values to be defined + along the path. + + \sa PathView, PathAttribute, PathPercent, PathLine, PathQuad, PathCubic +*/ +QDeclarativePath::QDeclarativePath(QObject *parent) + : QObject(*(new QDeclarativePathPrivate), parent) +{ +} + +QDeclarativePath::~QDeclarativePath() +{ +} + +/*! + \qmlproperty real Path::startX + \qmlproperty real Path::startY + These properties hold the starting position of the path. +*/ +qreal QDeclarativePath::startX() const +{ + Q_D(const QDeclarativePath); + return d->startX; +} + +void QDeclarativePath::setStartX(qreal x) +{ + Q_D(QDeclarativePath); + if (qFuzzyCompare(x, d->startX)) + return; + d->startX = x; + emit startXChanged(); + processPath(); +} + +qreal QDeclarativePath::startY() const +{ + Q_D(const QDeclarativePath); + return d->startY; +} + +void QDeclarativePath::setStartY(qreal y) +{ + Q_D(QDeclarativePath); + if (qFuzzyCompare(y, d->startY)) + return; + d->startY = y; + emit startYChanged(); + processPath(); +} + +/*! + \qmlproperty bool Path::closed + This property holds whether the start and end of the path are identical. +*/ +bool QDeclarativePath::isClosed() const +{ + Q_D(const QDeclarativePath); + return d->closed; +} + +/*! + \qmlproperty list Path::pathElements + This property holds the objects composing the path. + + \default + + A path can contain the following path objects: + \list + \i \l PathLine - a straight line to a given position. + \i \l PathQuad - a quadratic Bezier curve to a given position with a control point. + \i \l PathCubic - a cubic Bezier curve to a given position with two control points. + \i \l PathAttribute - an attribute at a given position in the path. + \i \l PathPercent - a way to spread out items along various segments of the path. + \endlist + + \snippet doc/src/snippets/declarative/pathview/pathattributes.qml 2 +*/ + +QDeclarativeListProperty QDeclarativePath::pathElements() +{ + Q_D(QDeclarativePath); + return QDeclarativeListProperty(this, d->_pathElements); +} + +void QDeclarativePath::interpolate(int idx, const QString &name, qreal value) +{ + Q_D(QDeclarativePath); + if (!idx) + return; + + qreal lastValue = 0; + qreal lastPercent = 0; + int search = idx - 1; + while(search >= 0) { + const AttributePoint &point = d->_attributePoints.at(search); + if (point.values.contains(name)) { + lastValue = point.values.value(name); + lastPercent = point.origpercent; + break; + } + --search; + } + + ++search; + + const AttributePoint &curPoint = d->_attributePoints.at(idx); + + for (int ii = search; ii < idx; ++ii) { + AttributePoint &point = d->_attributePoints[ii]; + + qreal val = lastValue + (value - lastValue) * (point.origpercent - lastPercent) / (curPoint.origpercent - lastPercent); + point.values.insert(name, val); + } +} + +void QDeclarativePath::endpoint(const QString &name) +{ + Q_D(QDeclarativePath); + const AttributePoint &first = d->_attributePoints.first(); + qreal val = first.values.value(name); + for (int ii = d->_attributePoints.count() - 1; ii >= 0; ii--) { + const AttributePoint &point = d->_attributePoints.at(ii); + if (point.values.contains(name)) { + for (int jj = ii + 1; jj < d->_attributePoints.count(); ++jj) { + AttributePoint &setPoint = d->_attributePoints[jj]; + setPoint.values.insert(name, val); + } + return; + } + } +} + +void QDeclarativePath::processPath() +{ + Q_D(QDeclarativePath); + + if (!d->componentComplete) + return; + + d->_pointCache.clear(); + d->_attributePoints.clear(); + d->_path = QPainterPath(); + + AttributePoint first; + for (int ii = 0; ii < d->_attributes.count(); ++ii) + first.values[d->_attributes.at(ii)] = 0; + d->_attributePoints << first; + + d->_path.moveTo(d->startX, d->startY); + + QDeclarativeCurve *lastCurve = 0; + foreach (QDeclarativePathElement *pathElement, d->_pathElements) { + if (QDeclarativeCurve *curve = qobject_cast(pathElement)) { + curve->addToPath(d->_path); + AttributePoint p; + p.origpercent = d->_path.length(); + d->_attributePoints << p; + lastCurve = curve; + } else if (QDeclarativePathAttribute *attribute = qobject_cast(pathElement)) { + AttributePoint &point = d->_attributePoints.last(); + point.values[attribute->name()] = attribute->value(); + interpolate(d->_attributePoints.count() - 1, attribute->name(), attribute->value()); + } else if (QDeclarativePathPercent *percent = qobject_cast(pathElement)) { + AttributePoint &point = d->_attributePoints.last(); + point.values[QLatin1String("_qfx_percent")] = percent->value(); + interpolate(d->_attributePoints.count() - 1, QLatin1String("_qfx_percent"), percent->value()); + } + } + + // Fixup end points + const AttributePoint &last = d->_attributePoints.last(); + for (int ii = 0; ii < d->_attributes.count(); ++ii) { + if (!last.values.contains(d->_attributes.at(ii))) + endpoint(d->_attributes.at(ii)); + } + + // Adjust percent + qreal length = d->_path.length(); + qreal prevpercent = 0; + qreal prevorigpercent = 0; + for (int ii = 0; ii < d->_attributePoints.count(); ++ii) { + const AttributePoint &point = d->_attributePoints.at(ii); + if (point.values.contains(QLatin1String("_qfx_percent"))) { //special string for QDeclarativePathPercent + if ( ii > 0) { + qreal scale = (d->_attributePoints[ii].origpercent/length - prevorigpercent) / + (point.values.value(QLatin1String("_qfx_percent"))-prevpercent); + d->_attributePoints[ii].scale = scale; + } + d->_attributePoints[ii].origpercent /= length; + d->_attributePoints[ii].percent = point.values.value(QLatin1String("_qfx_percent")); + prevorigpercent = d->_attributePoints[ii].origpercent; + prevpercent = d->_attributePoints[ii].percent; + } else { + d->_attributePoints[ii].origpercent /= length; + d->_attributePoints[ii].percent = d->_attributePoints[ii].origpercent; + } + } + + d->closed = lastCurve && d->startX == lastCurve->x() && d->startY == lastCurve->y(); + + emit changed(); +} + +void QDeclarativePath::classBegin() +{ + Q_D(QDeclarativePath); + d->componentComplete = false; +} + +void QDeclarativePath::componentComplete() +{ + Q_D(QDeclarativePath); + QSet attrs; + d->componentComplete = true; + + // First gather up all the attributes + foreach (QDeclarativePathElement *pathElement, d->_pathElements) { + if (QDeclarativePathAttribute *attribute = + qobject_cast(pathElement)) + attrs.insert(attribute->name()); + } + d->_attributes = attrs.toList(); + + processPath(); + + foreach (QDeclarativePathElement *pathElement, d->_pathElements) + connect(pathElement, SIGNAL(changed()), this, SLOT(processPath())); +} + +QPainterPath QDeclarativePath::path() const +{ + Q_D(const QDeclarativePath); + return d->_path; +} + +QStringList QDeclarativePath::attributes() const +{ + Q_D(const QDeclarativePath); + if (!d->componentComplete) { + QSet attrs; + + // First gather up all the attributes + foreach (QDeclarativePathElement *pathElement, d->_pathElements) { + if (QDeclarativePathAttribute *attribute = + qobject_cast(pathElement)) + attrs.insert(attribute->name()); + } + return attrs.toList(); + } + return d->_attributes; +} + +static inline QBezier nextBezier(const QPainterPath &path, int *from, qreal *bezLength) +{ + const int lastElement = path.elementCount() - 1; + for (int i=*from; i <= lastElement; ++i) { + const QPainterPath::Element &e = path.elementAt(i); + + switch (e.type) { + case QPainterPath::MoveToElement: + break; + case QPainterPath::LineToElement: + { + QLineF line(path.elementAt(i-1), e); + *bezLength = line.length(); + QPointF a = path.elementAt(i-1); + QPointF delta = e - a; + *from = i+1; + return QBezier::fromPoints(a, a + delta / 3, a + 2 * delta / 3, e); + } + case QPainterPath::CurveToElement: + { + QBezier b = QBezier::fromPoints(path.elementAt(i-1), + e, + path.elementAt(i+1), + path.elementAt(i+2)); + *bezLength = b.length(); + *from = i+3; + return b; + } + default: + break; + } + } + *from = lastElement; + *bezLength = 0; + return QBezier(); +} + +void QDeclarativePath::createPointCache() const +{ + Q_D(const QDeclarativePath); + qreal pathLength = d->_path.length(); + if (pathLength <= 0 || qIsNaN(pathLength)) + return; + // more points means less jitter between items as they move along the + // path, but takes longer to generate + const int points = qCeil(pathLength*5); + const int lastElement = d->_path.elementCount() - 1; + d->_pointCache.resize(points+1); + + int currElement = 0; + qreal bezLength = 0; + QBezier currBez = nextBezier(d->_path, &currElement, &bezLength); + qreal currLength = bezLength; + qreal epc = currLength / pathLength; + + for (int i = 0; i < d->_pointCache.size(); i++) { + //find which set we are in + qreal prevPercent = 0; + qreal prevOrigPercent = 0; + for (int ii = 0; ii < d->_attributePoints.count(); ++ii) { + qreal percent = qreal(i)/points; + const AttributePoint &point = d->_attributePoints.at(ii); + if (percent < point.percent || ii == d->_attributePoints.count() - 1) { //### || is special case for very last item + qreal elementPercent = (percent - prevPercent); + + qreal spc = prevOrigPercent + elementPercent * point.scale; + + while (spc > epc) { + if (currElement > lastElement) + break; + currBez = nextBezier(d->_path, &currElement, &bezLength); + if (bezLength == 0.0) { + currLength = pathLength; + epc = 1.0; + break; + } + currLength += bezLength; + epc = currLength / pathLength; + } + qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength; + d->_pointCache[i] = currBez.pointAt(qBound(qreal(0), realT, qreal(1))); + break; + } + prevOrigPercent = point.origpercent; + prevPercent = point.percent; + } + } +} + +QPointF QDeclarativePath::pointAt(qreal p) const +{ + Q_D(const QDeclarativePath); + if (d->_pointCache.isEmpty()) { + createPointCache(); + if (d->_pointCache.isEmpty()) + return QPointF(); + } + int idx = qRound(p*d->_pointCache.size()); + if (idx >= d->_pointCache.size()) + idx = d->_pointCache.size() - 1; + else if (idx < 0) + idx = 0; + return d->_pointCache.at(idx); +} + +qreal QDeclarativePath::attributeAt(const QString &name, qreal percent) const +{ + Q_D(const QDeclarativePath); + if (percent < 0 || percent > 1) + return 0; + + for (int ii = 0; ii < d->_attributePoints.count(); ++ii) { + const AttributePoint &point = d->_attributePoints.at(ii); + + if (point.percent == percent) { + return point.values.value(name); + } else if (point.percent > percent) { + qreal lastValue = + ii?(d->_attributePoints.at(ii - 1).values.value(name)):0; + qreal lastPercent = + ii?(d->_attributePoints.at(ii - 1).percent):0; + qreal curValue = point.values.value(name); + qreal curPercent = point.percent; + + return lastValue + (curValue - lastValue) * (percent - lastPercent) / (curPercent - lastPercent); + } + } + + return 0; +} + +/****************************************************************************/ + +qreal QDeclarativeCurve::x() const +{ + return _x; +} + +void QDeclarativeCurve::setX(qreal x) +{ + if (_x != x) { + _x = x; + emit xChanged(); + emit changed(); + } +} + +qreal QDeclarativeCurve::y() const +{ + return _y; +} + +void QDeclarativeCurve::setY(qreal y) +{ + if (_y != y) { + _y = y; + emit yChanged(); + emit changed(); + } +} + +/****************************************************************************/ + +/*! + \qmlclass PathAttribute QDeclarativePathAttribute + \ingroup qml-view-elements + \since 4.7 + \brief The PathAttribute allows setting an attribute at a given position in a Path. + + The PathAttribute object allows attributes consisting of a name and + a value to be specified for various points along a path. The + attributes are exposed to the delegate as + \l{qdeclarativeintroduction.html#attached-properties} {Attached Properties}. + The value of an attribute at any particular point along the path is interpolated + from the PathAttributes bounding that point. + + The example below shows a path with the items scaled to 30% with + opacity 50% at the top of the path and scaled 100% with opacity + 100% at the bottom. Note the use of the PathView.iconScale and + PathView.iconOpacity attached properties to set the scale and opacity + of the delegate. + + \table + \row + \o \image declarative-pathattribute.png + \o + \snippet doc/src/snippets/declarative/pathview/pathattributes.qml 0 + (see the PathView documentation for the specification of ContactModel.qml + used for ContactModel above.) + \endtable + + + \sa Path +*/ + +/*! + \qmlproperty string PathAttribute::name + This property holds the name of the attribute to change. + + This attribute will be available to the delegate as PathView. + + Note that using an existing Item property name such as "opacity" as an + attribute is allowed. This is because path attributes add a new + \l{qdeclarativeintroduction.html#attached-properties} {Attached Property} + which in no way clashes with existing properties. +*/ + +/*! + the name of the attribute to change. +*/ + +QString QDeclarativePathAttribute::name() const +{ + return _name; +} + +void QDeclarativePathAttribute::setName(const QString &name) +{ + if (_name == name) + return; + _name = name; + emit nameChanged(); +} + +/*! + \qmlproperty real PathAttribute::value + This property holds the value for the attribute. + + The value specified can be used to influence the visual appearance + of an item along the path. For example, the following Path specifies + an attribute named \e itemRotation, which has the value \e 0 at the + beginning of the path, and the value 90 at the end of the path. + + \qml + Path { + startX: 0 + startY: 0 + PathAttribute { name: "itemRotation"; value: 0 } + PathLine { x: 100; y: 100 } + PathAttribute { name: "itemRotation"; value: 90 } + } + \endqml + + In our delegate, we can then bind the \e rotation property to the + \l{qdeclarativeintroduction.html#attached-properties} {Attached Property} + \e PathView.itemRotation created for this attribute. + + \qml + Rectangle { + width: 10; height: 10 + rotation: PathView.itemRotation + } + \endqml + + As each item is positioned along the path, it will be rotated accordingly: + an item at the beginning of the path with be not be rotated, an item at + the end of the path will be rotated 90 degrees, and an item mid-way along + the path will be rotated 45 degrees. +*/ + +/*! + the new value of the attribute. +*/ +qreal QDeclarativePathAttribute::value() const +{ + return _value; +} + +void QDeclarativePathAttribute::setValue(qreal value) +{ + if (_value != value) { + _value = value; + emit valueChanged(); + emit changed(); + } +} + +/****************************************************************************/ + +/*! + \qmlclass PathLine QDeclarativePathLine + \ingroup qml-view-elements + \since 4.7 + \brief The PathLine defines a straight line. + + The example below creates a path consisting of a straight line from + 0,100 to 200,100: + + \qml + Path { + startX: 0; startY: 100 + PathLine { x: 200; y: 100 } + } + \endqml + + \sa Path, PathQuad, PathCubic +*/ + +/*! + \qmlproperty real PathLine::x + \qmlproperty real PathLine::y + + Defines the end point of the line. +*/ + +void QDeclarativePathLine::addToPath(QPainterPath &path) +{ + path.lineTo(x(), y()); +} + +/****************************************************************************/ + +/*! + \qmlclass PathQuad QDeclarativePathQuad + \ingroup qml-view-elements + \since 4.7 + \brief The PathQuad defines a quadratic Bezier curve with a control point. + + The following QML produces the path shown below: + \table + \row + \o \image declarative-pathquad.png + \o + \qml + Path { + startX: 0; startY: 0 + PathQuad { x: 200; y: 0; controlX: 100; controlY: 150 } + } + \endqml + \endtable + + \sa Path, PathCubic, PathLine +*/ + +/*! + \qmlproperty real PathQuad::x + \qmlproperty real PathQuad::y + + Defines the end point of the curve. +*/ + +/*! + \qmlproperty real PathQuad::controlX + \qmlproperty real PathQuad::controlY + + Defines the position of the control point. +*/ + +/*! + the x position of the control point. +*/ +qreal QDeclarativePathQuad::controlX() const +{ + return _controlX; +} + +void QDeclarativePathQuad::setControlX(qreal x) +{ + if (_controlX != x) { + _controlX = x; + emit controlXChanged(); + emit changed(); + } +} + + +/*! + the y position of the control point. +*/ +qreal QDeclarativePathQuad::controlY() const +{ + return _controlY; +} + +void QDeclarativePathQuad::setControlY(qreal y) +{ + if (_controlY != y) { + _controlY = y; + emit controlYChanged(); + emit changed(); + } +} + +void QDeclarativePathQuad::addToPath(QPainterPath &path) +{ + path.quadTo(controlX(), controlY(), x(), y()); +} + +/****************************************************************************/ + +/*! + \qmlclass PathCubic QDeclarativePathCubic + \ingroup qml-view-elements + \since 4.7 + \brief The PathCubic defines a cubic Bezier curve with two control points. + + The following QML produces the path shown below: + \table + \row + \o \image declarative-pathcubic.png + \o + \qml + Path { + startX: 20; startY: 0 + PathCubic { + x: 180; y: 0 + control1X: -10; control1Y: 90 + control2X: 210; control2Y: 90 + } + } + \endqml + \endtable + + \sa Path, PathQuad, PathLine +*/ + +/*! + \qmlproperty real PathCubic::x + \qmlproperty real PathCubic::y + + Defines the end point of the curve. +*/ + +/*! + \qmlproperty real PathCubic::control1X + \qmlproperty real PathCubic::control1Y + + Defines the position of the first control point. +*/ +qreal QDeclarativePathCubic::control1X() const +{ + return _control1X; +} + +void QDeclarativePathCubic::setControl1X(qreal x) +{ + if (_control1X != x) { + _control1X = x; + emit control1XChanged(); + emit changed(); + } +} + +qreal QDeclarativePathCubic::control1Y() const +{ + return _control1Y; +} + +void QDeclarativePathCubic::setControl1Y(qreal y) +{ + if (_control1Y != y) { + _control1Y = y; + emit control1YChanged(); + emit changed(); + } +} + +/*! + \qmlproperty real PathCubic::control2X + \qmlproperty real PathCubic::control2Y + + Defines the position of the second control point. +*/ +qreal QDeclarativePathCubic::control2X() const +{ + return _control2X; +} + +void QDeclarativePathCubic::setControl2X(qreal x) +{ + if (_control2X != x) { + _control2X = x; + emit control2XChanged(); + emit changed(); + } +} + +qreal QDeclarativePathCubic::control2Y() const +{ + return _control2Y; +} + +void QDeclarativePathCubic::setControl2Y(qreal y) +{ + if (_control2Y != y) { + _control2Y = y; + emit control2YChanged(); + emit changed(); + } +} + +void QDeclarativePathCubic::addToPath(QPainterPath &path) +{ + path.cubicTo(control1X(), control1Y(), control2X(), control2Y(), x(), y()); +} + +/****************************************************************************/ + +/*! + \qmlclass PathPercent QDeclarativePathPercent + \ingroup qml-view-elements + \since 4.7 + \brief The PathPercent manipulates the way a path is interpreted. + + PathPercent allows you to manipulate the spacing between items on a + PathView's path. You can use it to bunch together items on part of + the path, and spread them out on other parts of the path. + + The examples below show the normal distrubution of items along a path + compared to a distribution which places 50% of the items along the + PathLine section of the path. + \table + \row + \o \image declarative-nopercent.png + \o + \qml + PathView { + // ... + Path { + startX: 20; startY: 0 + PathQuad { x: 50; y: 80; controlX: 0; controlY: 80 } + PathLine { x: 150; y: 80 } + PathQuad { x: 180; y: 0; controlX: 200; controlY: 80 } + } + } + \endqml + \row + \o \image declarative-percent.png + \o + \qml + PathView { + // ... + Path { + startX: 20; startY: 0 + PathQuad { x: 50; y: 80; controlX: 0; controlY: 80 } + PathPercent { value: 0.25 } + PathLine { x: 150; y: 80 } + PathPercent { value: 0.75 } + PathQuad { x: 180; y: 0; controlX: 200; controlY: 80 } + PathPercent { value: 1 } + } + } + \endqml + \endtable + + \sa Path +*/ + +/*! + \qmlproperty real PathPercent::value + The proporation of items that should be laid out up to this point. + + This value should always be higher than the last value specified + by a PathPercent at a previous position in the Path. + + In the following example we have a Path made up of three PathLines. + Normally, the items of the PathView would be laid out equally along + this path, with an equal number of items per line segment. PathPercent + allows us to specify that the first and third lines should each hold + 10% of the laid out items, while the second line should hold the remaining + 80%. + + \qml + PathView { + // ... + Path { + startX: 0; startY: 0 + PathLine { x:100; y: 0; } + PathPercent { value: 0.1 } + PathLine { x: 100; y: 100 } + PathPercent { value: 0.9 } + PathLine { x: 100; y: 0 } + PathPercent { value: 1 } + } + } + \endqml +*/ + +qreal QDeclarativePathPercent::value() const +{ + return _value; +} + +void QDeclarativePathPercent::setValue(qreal value) +{ + if (_value != value) { + _value = value; + emit valueChanged(); + emit changed(); + } +} +QT_END_NAMESPACE diff --git a/src/declarative/graphicsitems/qdeclarativepath_p.h b/src/declarative/graphicsitems/qdeclarativepath_p.h new file mode 100644 index 00000000..d782428d --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativepath_p.h @@ -0,0 +1,286 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPATH_H +#define QDECLARATIVEPATH_H + +#include "qdeclarativeitem.h" + +#include + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class Q_AUTOTEST_EXPORT QDeclarativePathElement : public QObject +{ + Q_OBJECT +public: + QDeclarativePathElement(QObject *parent=0) : QObject(parent) {} +Q_SIGNALS: + void changed(); +}; + +class Q_AUTOTEST_EXPORT QDeclarativePathAttribute : public QDeclarativePathElement +{ + Q_OBJECT + + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_PROPERTY(qreal value READ value WRITE setValue NOTIFY valueChanged) +public: + QDeclarativePathAttribute(QObject *parent=0) : QDeclarativePathElement(parent), _value(0) {} + + + QString name() const; + void setName(const QString &name); + + qreal value() const; + void setValue(qreal value); + +Q_SIGNALS: + void nameChanged(); + void valueChanged(); + +private: + QString _name; + qreal _value; +}; + +class Q_AUTOTEST_EXPORT QDeclarativeCurve : public QDeclarativePathElement +{ + Q_OBJECT + + Q_PROPERTY(qreal x READ x WRITE setX NOTIFY xChanged) + Q_PROPERTY(qreal y READ y WRITE setY NOTIFY yChanged) +public: + QDeclarativeCurve(QObject *parent=0) : QDeclarativePathElement(parent), _x(0), _y(0) {} + + qreal x() const; + void setX(qreal x); + + qreal y() const; + void setY(qreal y); + + virtual void addToPath(QPainterPath &) {} + +Q_SIGNALS: + void xChanged(); + void yChanged(); + +private: + qreal _x; + qreal _y; +}; + +class Q_AUTOTEST_EXPORT QDeclarativePathLine : public QDeclarativeCurve +{ + Q_OBJECT +public: + QDeclarativePathLine(QObject *parent=0) : QDeclarativeCurve(parent) {} + + void addToPath(QPainterPath &path); +}; + +class Q_AUTOTEST_EXPORT QDeclarativePathQuad : public QDeclarativeCurve +{ + Q_OBJECT + + Q_PROPERTY(qreal controlX READ controlX WRITE setControlX NOTIFY controlXChanged) + Q_PROPERTY(qreal controlY READ controlY WRITE setControlY NOTIFY controlYChanged) +public: + QDeclarativePathQuad(QObject *parent=0) : QDeclarativeCurve(parent), _controlX(0), _controlY(0) {} + + qreal controlX() const; + void setControlX(qreal x); + + qreal controlY() const; + void setControlY(qreal y); + + void addToPath(QPainterPath &path); + +Q_SIGNALS: + void controlXChanged(); + void controlYChanged(); + +private: + qreal _controlX; + qreal _controlY; +}; + +class Q_AUTOTEST_EXPORT QDeclarativePathCubic : public QDeclarativeCurve +{ + Q_OBJECT + + Q_PROPERTY(qreal control1X READ control1X WRITE setControl1X NOTIFY control1XChanged) + Q_PROPERTY(qreal control1Y READ control1Y WRITE setControl1Y NOTIFY control1YChanged) + Q_PROPERTY(qreal control2X READ control2X WRITE setControl2X NOTIFY control2XChanged) + Q_PROPERTY(qreal control2Y READ control2Y WRITE setControl2Y NOTIFY control2YChanged) +public: + QDeclarativePathCubic(QObject *parent=0) : QDeclarativeCurve(parent), _control1X(0), _control1Y(0), _control2X(0), _control2Y(0) {} + + qreal control1X() const; + void setControl1X(qreal x); + + qreal control1Y() const; + void setControl1Y(qreal y); + + qreal control2X() const; + void setControl2X(qreal x); + + qreal control2Y() const; + void setControl2Y(qreal y); + + void addToPath(QPainterPath &path); + +Q_SIGNALS: + void control1XChanged(); + void control1YChanged(); + void control2XChanged(); + void control2YChanged(); + +private: + qreal _control1X; + qreal _control1Y; + qreal _control2X; + qreal _control2Y; +}; + +class Q_AUTOTEST_EXPORT QDeclarativePathPercent : public QDeclarativePathElement +{ + Q_OBJECT + Q_PROPERTY(qreal value READ value WRITE setValue NOTIFY valueChanged) +public: + QDeclarativePathPercent(QObject *parent=0) : QDeclarativePathElement(parent) {} + + qreal value() const; + void setValue(qreal value); + +signals: + void valueChanged(); + +private: + qreal _value; +}; + +class QDeclarativePathPrivate; +class Q_AUTOTEST_EXPORT QDeclarativePath : public QObject, public QDeclarativeParserStatus +{ + Q_OBJECT + + Q_INTERFACES(QDeclarativeParserStatus) + Q_PROPERTY(QDeclarativeListProperty pathElements READ pathElements) + Q_PROPERTY(qreal startX READ startX WRITE setStartX NOTIFY startXChanged) + Q_PROPERTY(qreal startY READ startY WRITE setStartY NOTIFY startYChanged) + Q_PROPERTY(bool closed READ isClosed NOTIFY changed) + Q_CLASSINFO("DefaultProperty", "pathElements") + Q_INTERFACES(QDeclarativeParserStatus) +public: + QDeclarativePath(QObject *parent=0); + ~QDeclarativePath(); + + QDeclarativeListProperty pathElements(); + + qreal startX() const; + void setStartX(qreal x); + + qreal startY() const; + void setStartY(qreal y); + + bool isClosed() const; + + QPainterPath path() const; + QStringList attributes() const; + qreal attributeAt(const QString &, qreal) const; + QPointF pointAt(qreal) const; + +Q_SIGNALS: + void changed(); + void startXChanged(); + void startYChanged(); + +protected: + virtual void componentComplete(); + virtual void classBegin(); + +private Q_SLOTS: + void processPath(); + +private: + struct AttributePoint { + AttributePoint() : percent(0), scale(1), origpercent(0) {} + AttributePoint(const AttributePoint &other) + : percent(other.percent), scale(other.scale), origpercent(other.origpercent), values(other.values) {} + AttributePoint &operator=(const AttributePoint &other) { + percent = other.percent; scale = other.scale; origpercent = other.origpercent; values = other.values; return *this; + } + qreal percent; //massaged percent along the painter path + qreal scale; + qreal origpercent; //'real' percent along the painter path + QHash values; + }; + + void interpolate(int idx, const QString &name, qreal value); + void endpoint(const QString &name); + void createPointCache() const; + +private: + Q_DISABLE_COPY(QDeclarativePath) + Q_DECLARE_PRIVATE(QDeclarativePath) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativePathElement) +QML_DECLARE_TYPE(QDeclarativePathAttribute) +QML_DECLARE_TYPE(QDeclarativeCurve) +QML_DECLARE_TYPE(QDeclarativePathLine) +QML_DECLARE_TYPE(QDeclarativePathQuad) +QML_DECLARE_TYPE(QDeclarativePathCubic) +QML_DECLARE_TYPE(QDeclarativePathPercent) +QML_DECLARE_TYPE(QDeclarativePath) + +QT_END_HEADER + +#endif // QDECLARATIVEPATH_H diff --git a/src/declarative/graphicsitems/qdeclarativepath_p_p.h b/src/declarative/graphicsitems/qdeclarativepath_p_p.h new file mode 100644 index 00000000..83587516 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativepath_p_p.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPATH_P_H +#define QDECLARATIVEPATH_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 "private/qdeclarativepath_p.h" + +#include + +#include + +QT_BEGIN_NAMESPACE +class QDeclarativePathPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativePath) + +public: + QDeclarativePathPrivate() : startX(0), startY(0), closed(false), componentComplete(true) { } + + QPainterPath _path; + QList _pathElements; + mutable QVector _pointCache; + QList _attributePoints; + QStringList _attributes; + int startX; + int startY; + bool closed; + bool componentComplete; +}; + +QT_END_NAMESPACE +#endif diff --git a/src/declarative/graphicsitems/qdeclarativepathview.cpp b/src/declarative/graphicsitems/qdeclarativepathview.cpp new file mode 100644 index 00000000..e0bc514b --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativepathview.cpp @@ -0,0 +1,1729 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativepathview_p.h" +#include "private/qdeclarativepathview_p_p.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +inline qreal qmlMod(qreal x, qreal y) +{ +#ifdef QT_USE_MATH_H_FLOATS + if(sizeof(qreal) == sizeof(float)) + return fmodf(float(x), float(y)); + else +#endif + return fmod(x, y); +} + +static QDeclarativeOpenMetaObjectType *qPathViewAttachedType = 0; + +QDeclarativePathViewAttached::QDeclarativePathViewAttached(QObject *parent) +: QObject(parent), m_percent(-1), m_view(0), m_onPath(false), m_isCurrent(false) +{ + if (qPathViewAttachedType) { + m_metaobject = new QDeclarativeOpenMetaObject(this, qPathViewAttachedType); + m_metaobject->setCached(true); + } else { + m_metaobject = new QDeclarativeOpenMetaObject(this); + } +} + +QDeclarativePathViewAttached::~QDeclarativePathViewAttached() +{ +} + +QVariant QDeclarativePathViewAttached::value(const QByteArray &name) const +{ + return m_metaobject->value(name); +} +void QDeclarativePathViewAttached::setValue(const QByteArray &name, const QVariant &val) +{ + m_metaobject->setValue(name, val); +} + + +void QDeclarativePathViewPrivate::init() +{ + Q_Q(QDeclarativePathView); + offset = 0; + q->setAcceptedMouseButtons(Qt::LeftButton); + q->setFlag(QGraphicsItem::ItemIsFocusScope); + q->setFiltersChildEvents(true); + q->connect(&tl, SIGNAL(updated()), q, SLOT(ticked())); + lastPosTime.invalidate(); + static int timelineCompletedIdx = -1; + static int movementEndingIdx = -1; + if (timelineCompletedIdx == -1) { + timelineCompletedIdx = QDeclarativeTimeLine::staticMetaObject.indexOfSignal("completed()"); + movementEndingIdx = QDeclarativePathView::staticMetaObject.indexOfSlot("movementEnding()"); + } + QMetaObject::connect(&tl, timelineCompletedIdx, + q, movementEndingIdx, Qt::DirectConnection); +} + +QDeclarativeItem *QDeclarativePathViewPrivate::getItem(int modelIndex) +{ + Q_Q(QDeclarativePathView); + requestedIndex = modelIndex; + QDeclarativeItem *item = model->item(modelIndex, false); + if (item) { + if (!attType) { + // pre-create one metatype to share with all attached objects + attType = new QDeclarativeOpenMetaObjectType(&QDeclarativePathViewAttached::staticMetaObject, qmlEngine(q)); + foreach(const QString &attr, path->attributes()) + attType->createProperty(attr.toUtf8()); + } + qPathViewAttachedType = attType; + QDeclarativePathViewAttached *att = static_cast(qmlAttachedPropertiesObject(item)); + qPathViewAttachedType = 0; + if (att) { + att->m_view = q; + att->setOnPath(true); + } + item->setParentItem(q); + QDeclarativeItemPrivate *itemPrivate = static_cast(QGraphicsItemPrivate::get(item)); + itemPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry); + } + requestedIndex = -1; + return item; +} + +void QDeclarativePathViewPrivate::releaseItem(QDeclarativeItem *item) +{ + if (!item || !model) + return; + QDeclarativeItemPrivate *itemPrivate = static_cast(QGraphicsItemPrivate::get(item)); + itemPrivate->removeItemChangeListener(this, QDeclarativeItemPrivate::Geometry); + if (model->release(item) == 0) { + // item was not destroyed, and we no longer reference it. + if (QDeclarativePathViewAttached *att = attached(item)) + att->setOnPath(false); + } +} + +QDeclarativePathViewAttached *QDeclarativePathViewPrivate::attached(QDeclarativeItem *item) +{ + return static_cast(qmlAttachedPropertiesObject(item, false)); +} + +void QDeclarativePathViewPrivate::clear() +{ + for (int i=0; i= 0 && index < modelCount) { + qreal start = 0.0; + if (haveHighlightRange && highlightRangeMode != QDeclarativePathView::NoHighlightRange) + start = highlightRangeStart; + qreal globalPos = index + offset; + globalPos = qmlMod(globalPos, qreal(modelCount)) / modelCount; + if (pathItems != -1 && pathItems < modelCount) { + globalPos += start * mappedRange; + globalPos = qmlMod(globalPos, 1.0); + if (globalPos < mappedRange) + pos = globalPos / mappedRange; + } else { + pos = qmlMod(globalPos + start, 1.0); + } + } + + return pos; +} + +void QDeclarativePathViewPrivate::createHighlight() +{ + Q_Q(QDeclarativePathView); + if (!q->isComponentComplete()) + return; + + bool changed = false; + if (highlightItem) { + if (highlightItem->scene()) + highlightItem->scene()->removeItem(highlightItem); + highlightItem->deleteLater(); + highlightItem = 0; + changed = true; + } + + QDeclarativeItem *item = 0; + if (highlightComponent) { + QDeclarativeContext *highlightContext = new QDeclarativeContext(qmlContext(q)); + QObject *nobj = highlightComponent->create(highlightContext); + if (nobj) { + QDeclarative_setParent_noEvent(highlightContext, nobj); + item = qobject_cast(nobj); + if (!item) + delete nobj; + } else { + delete highlightContext; + } + } else { + item = new QDeclarativeItem; + } + if (item) { + QDeclarative_setParent_noEvent(item, q); + item->setParentItem(q); + highlightItem = item; + changed = true; + } + if (changed) + emit q->highlightItemChanged(); +} + +void QDeclarativePathViewPrivate::updateHighlight() +{ + Q_Q(QDeclarativePathView); + if (!q->isComponentComplete() || !isValid()) + return; + if (highlightItem) { + if (haveHighlightRange && highlightRangeMode == QDeclarativePathView::StrictlyEnforceRange) { + updateItem(highlightItem, highlightRangeStart); + } else { + qreal target = currentIndex; + + offsetAdj = qreal(0.0); + tl.reset(moveHighlight); + moveHighlight.setValue(highlightPosition); + + const int duration = highlightMoveDuration; + + if (target - highlightPosition > modelCount/2) { + highlightUp = false; + qreal distance = modelCount - target + highlightPosition; + tl.move(moveHighlight, qreal(0.0), QEasingCurve(QEasingCurve::InQuad), int(duration * highlightPosition / distance)); + tl.set(moveHighlight, modelCount-qreal(0.01)); + tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::OutQuad), int(duration * (modelCount-target) / distance)); + } else if (target - highlightPosition <= -modelCount/2) { + highlightUp = true; + qreal distance = modelCount - highlightPosition + target; + tl.move(moveHighlight, modelCount-qreal(0.01), QEasingCurve(QEasingCurve::InQuad), int(duration * (modelCount-highlightPosition) / distance)); + tl.set(moveHighlight, qreal(0.0)); + tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::OutQuad), int(duration * target / distance)); + } else { + highlightUp = highlightPosition - target < 0; + tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::InOutQuad), duration); + } + } + } +} + +void QDeclarativePathViewPrivate::setHighlightPosition(qreal pos) +{ + if (pos != highlightPosition) { + qreal start = 0.0; + qreal end = 1.0; + if (haveHighlightRange && highlightRangeMode != QDeclarativePathView::NoHighlightRange) { + start = highlightRangeStart; + end = highlightRangeEnd; + } + + qreal range = qreal(modelCount); + // calc normalized position of highlight relative to offset + qreal relativeHighlight = qmlMod(pos + offset, range) / range; + + if (!highlightUp && relativeHighlight > end * mappedRange) { + qreal diff = qreal(1.0) - relativeHighlight; + setOffset(offset + diff * range); + } else if (highlightUp && relativeHighlight >= (end - start) * mappedRange) { + qreal diff = relativeHighlight - (end - start) * mappedRange; + setOffset(offset - diff * range - qreal(0.00001)); + } + + highlightPosition = pos; + qreal pathPos = positionOfIndex(pos); + updateItem(highlightItem, pathPos); + if (QDeclarativePathViewAttached *att = attached(highlightItem)) + att->setOnPath(pathPos != qreal(-1.0)); + } +} + +void QDeclarativePathView::pathUpdated() +{ + Q_D(QDeclarativePathView); + QList::iterator it = d->items.begin(); + while (it != d->items.end()) { + QDeclarativeItem *item = *it; + if (QDeclarativePathViewAttached *att = d->attached(item)) + att->m_percent = -1; + ++it; + } + refill(); +} + +void QDeclarativePathViewPrivate::updateItem(QDeclarativeItem *item, qreal percent) +{ + if (QDeclarativePathViewAttached *att = attached(item)) { + if (qFuzzyCompare(att->m_percent, percent)) + return; + att->m_percent = percent; + foreach(const QString &attr, path->attributes()) + att->setValue(attr.toUtf8(), path->attributeAt(attr, percent)); + } + QPointF pf = path->pointAt(percent); + item->setX(qRound(pf.x() - item->width()/2)); + item->setY(qRound(pf.y() - item->height()/2)); +} + +void QDeclarativePathViewPrivate::regenerate() +{ + Q_Q(QDeclarativePathView); + if (!q->isComponentComplete()) + return; + + clear(); + + if (!isValid()) + return; + + firstIndex = -1; + updateMappedRange(); + q->refill(); +} + +/*! + \qmlclass PathView QDeclarativePathView + \ingroup qml-view-elements + \since 4.7 + \brief The PathView element lays out model-provided items on a path. + \inherits Item + + A PathView displays data from models created from built-in QML elements like ListModel + and XmlListModel, or custom model classes defined in C++ that inherit from + QAbstractListModel. + + The view has a \l model, which defines the data to be displayed, and + a \l delegate, which defines how the data should be displayed. + The \l delegate is instantiated for each item on the \l path. + The items may be flicked to move them along the path. + + For example, if there is a simple list model defined in a file \c ContactModel.qml like this: + + \snippet doc/src/snippets/declarative/pathview/ContactModel.qml 0 + + This data can be represented as a PathView, like this: + + \snippet doc/src/snippets/declarative/pathview/pathview.qml 0 + + \image pathview.gif + + (Note the above example uses PathAttribute to scale and modify the + opacity of the items as they rotate. This additional code can be seen in the + PathAttribute documentation.) + + PathView does not automatically handle keyboard navigation. This is because + the keys to use for navigation will depend upon the shape of the path. Navigation + can be added quite simply by setting \c focus to \c true and calling + \l decrementCurrentIndex() or \l incrementCurrentIndex(), for example to navigate + using the left and right arrow keys: + + \qml + PathView { + // ... + focus: true + Keys.onLeftPressed: decrementCurrentIndex() + Keys.onRightPressed: incrementCurrentIndex() + } + \endqml + + The path view itself is a focus scope (see \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} for more details). + + Delegates are instantiated as needed and may be destroyed at any time. + State should \e never be stored in a delegate. + + PathView attaches a number of properties to the root item of the delegate, for example + \c {PathView.isCurrentItem}. In the following example, the root delegate item can access + this attached property directly as \c PathView.isCurrentItem, while the child + \c nameText object must refer to this property as \c wrapper.PathView.isCurrentItem. + + \snippet doc/src/snippets/declarative/pathview/pathview.qml 1 + + \bold Note that views do not enable \e clip automatically. If the view + is not clipped by another item or the screen, it will be necessary + to set \e {clip: true} in order to have the out of view items clipped + nicely. + + \sa Path, {declarative/modelviews/pathview}{PathView example} +*/ + +QDeclarativePathView::QDeclarativePathView(QDeclarativeItem *parent) + : QDeclarativeItem(*(new QDeclarativePathViewPrivate), parent) +{ + Q_D(QDeclarativePathView); + d->init(); +} + +QDeclarativePathView::~QDeclarativePathView() +{ + Q_D(QDeclarativePathView); + d->clear(); + if (d->attType) + d->attType->release(); + if (d->ownModel) + delete d->model; +} + +/*! + \qmlattachedproperty PathView PathView::view + This attached property holds the view that manages this delegate instance. + + It is attached to each instance of the delegate. +*/ + +/*! + \qmlattachedproperty bool PathView::onPath + This attached property holds whether the item is currently on the path. + + If a pathItemCount has been set, it is possible that some items may + be instantiated, but not considered to be currently on the path. + Usually, these items would be set invisible, for example: + + \qml + Component { + Rectangle { + visible: PathView.onPath + // ... + } + } + \endqml + + It is attached to each instance of the delegate. +*/ + +/*! + \qmlattachedproperty bool PathView::isCurrentItem + This attached property is true if this delegate is the current item; otherwise false. + + It is attached to each instance of the delegate. + + This property may be used to adjust the appearance of the current item. + + \snippet doc/src/snippets/declarative/pathview/pathview.qml 1 +*/ + +/*! + \qmlproperty model PathView::model + This property holds the model providing data for the view. + + The model provides a set of data that is used to create the items for the view. + For large or dynamic datasets the model is usually provided by a C++ model object. + Models can also be created directly in QML, using the ListModel element. + + \sa {qmlmodels}{Data Models} +*/ +QVariant QDeclarativePathView::model() const +{ + Q_D(const QDeclarativePathView); + return d->modelVariant; +} + +void QDeclarativePathView::setModel(const QVariant &model) +{ + Q_D(QDeclarativePathView); + if (d->modelVariant == model) + return; + + if (d->model) { + disconnect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int))); + disconnect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int))); + disconnect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int))); + disconnect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset())); + disconnect(d->model, SIGNAL(createdItem(int,QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*))); + for (int i=0; iitems.count(); i++){ + QDeclarativeItem *p = d->items[i]; + d->model->release(p); + } + d->items.clear(); + } + + d->modelVariant = model; + QObject *object = qvariant_cast(model); + QDeclarativeVisualModel *vim = 0; + if (object && (vim = qobject_cast(object))) { + if (d->ownModel) { + delete d->model; + d->ownModel = false; + } + d->model = vim; + } else { + if (!d->ownModel) { + d->model = new QDeclarativeVisualDataModel(qmlContext(this), this); + d->ownModel = true; + } + if (QDeclarativeVisualDataModel *dataModel = qobject_cast(d->model)) + dataModel->setModel(model); + } + d->modelCount = 0; + if (d->model) { + connect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int))); + connect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int))); + connect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int))); + connect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset())); + connect(d->model, SIGNAL(createdItem(int,QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*))); + d->modelCount = d->model->count(); + if (d->model->count()) + d->offset = qmlMod(d->offset, qreal(d->model->count())); + if (d->offset < 0) + d->offset = d->model->count() + d->offset; +} + d->regenerate(); + d->fixOffset(); + emit countChanged(); + emit modelChanged(); +} + +/*! + \qmlproperty int PathView::count + This property holds the number of items in the model. +*/ +int QDeclarativePathView::count() const +{ + Q_D(const QDeclarativePathView); + return d->model ? d->modelCount : 0; +} + +/*! + \qmlproperty Path PathView::path + This property holds the path used to lay out the items. + For more information see the \l Path documentation. +*/ +QDeclarativePath *QDeclarativePathView::path() const +{ + Q_D(const QDeclarativePathView); + return d->path; +} + +void QDeclarativePathView::setPath(QDeclarativePath *path) +{ + Q_D(QDeclarativePathView); + if (d->path == path) + return; + if (d->path) + disconnect(d->path, SIGNAL(changed()), this, SLOT(pathUpdated())); + d->path = path; + connect(d->path, SIGNAL(changed()), this, SLOT(pathUpdated())); + if (d->isValid() && isComponentComplete()) { + d->clear(); + if (d->attType) { + d->attType->release(); + d->attType = 0; + } + d->regenerate(); + } + emit pathChanged(); +} + +/*! + \qmlproperty int PathView::currentIndex + This property holds the index of the current item. +*/ +int QDeclarativePathView::currentIndex() const +{ + Q_D(const QDeclarativePathView); + return d->currentIndex; +} + +void QDeclarativePathView::setCurrentIndex(int idx) +{ + Q_D(QDeclarativePathView); + if (d->model && d->modelCount) + idx = qAbs(idx % d->modelCount); + if (d->model && idx != d->currentIndex) { + if (d->modelCount) { + int itemIndex = (d->currentIndex - d->firstIndex + d->modelCount) % d->modelCount; + if (itemIndex < d->items.count()) { + if (QDeclarativeItem *item = d->items.at(itemIndex)) { + if (QDeclarativePathViewAttached *att = d->attached(item)) + att->setIsCurrentItem(false); + } + } + } + d->currentItem = 0; + d->moveReason = QDeclarativePathViewPrivate::SetIndex; + d->currentIndex = idx; + if (d->modelCount) { + if (d->haveHighlightRange && d->highlightRangeMode == QDeclarativePathView::StrictlyEnforceRange) + d->snapToCurrent(); + int itemIndex = (idx - d->firstIndex + d->modelCount) % d->modelCount; + if (itemIndex < d->items.count()) { + d->currentItem = d->items.at(itemIndex); + d->currentItem->setFocus(true); + if (QDeclarativePathViewAttached *att = d->attached(d->currentItem)) + att->setIsCurrentItem(true); + } + d->currentItemOffset = d->positionOfIndex(d->currentIndex); + d->updateHighlight(); + } + emit currentIndexChanged(); + } +} + +/*! + \qmlmethod PathView::incrementCurrentIndex() + + Increments the current index. + + \bold Note: methods should only be called after the Component has completed. +*/ +void QDeclarativePathView::incrementCurrentIndex() +{ + Q_D(QDeclarativePathView); + d->moveDirection = QDeclarativePathViewPrivate::Positive; + setCurrentIndex(currentIndex()+1); +} + + +/*! + \qmlmethod PathView::decrementCurrentIndex() + + Decrements the current index. + + \bold Note: methods should only be called after the Component has completed. +*/ +void QDeclarativePathView::decrementCurrentIndex() +{ + Q_D(QDeclarativePathView); + if (d->model && d->modelCount) { + int idx = currentIndex()-1; + if (idx < 0) + idx = d->modelCount - 1; + d->moveDirection = QDeclarativePathViewPrivate::Negative; + setCurrentIndex(idx); + } +} + +/*! + \qmlproperty real PathView::offset + + The offset specifies how far along the path the items are from their initial positions. + This is a real number that ranges from 0.0 to the count of items in the model. +*/ +qreal QDeclarativePathView::offset() const +{ + Q_D(const QDeclarativePathView); + return d->offset; +} + +void QDeclarativePathView::setOffset(qreal offset) +{ + Q_D(QDeclarativePathView); + d->setOffset(offset); + d->updateCurrent(); +} + +void QDeclarativePathViewPrivate::setOffset(qreal o) +{ + Q_Q(QDeclarativePathView); + if (offset != o) { + if (isValid() && q->isComponentComplete()) { + offset = qmlMod(o, qreal(modelCount)); + if (offset < 0) + offset += qreal(modelCount); + q->refill(); + } else { + offset = o; + } + emit q->offsetChanged(); + } +} + +void QDeclarativePathViewPrivate::setAdjustedOffset(qreal o) +{ + setOffset(o+offsetAdj); +} + +/*! + \qmlproperty Component PathView::highlight + This property holds the component to use as the highlight. + + An instance of the highlight component will be created for each view. + The geometry of the resultant component instance will be managed by the view + so as to stay with the current item. + + The below example demonstrates how to make a simple highlight. Note the use + of the \l{PathView::onPath}{PathView.onPath} attached property to ensure that + the highlight is hidden when flicked away from the path. + + \qml + Component { + Rectangle { + visible: PathView.onPath + // ... + } + } + \endqml + + \sa highlightItem, highlightRangeMode +*/ + +QDeclarativeComponent *QDeclarativePathView::highlight() const +{ + Q_D(const QDeclarativePathView); + return d->highlightComponent; +} + +void QDeclarativePathView::setHighlight(QDeclarativeComponent *highlight) +{ + Q_D(QDeclarativePathView); + if (highlight != d->highlightComponent) { + d->highlightComponent = highlight; + d->createHighlight(); + d->updateHighlight(); + emit highlightChanged(); + } +} + +/*! + \qmlproperty Item PathView::highlightItem + + \c highlightItem holds the highlight item, which was created + from the \l highlight component. + + \sa highlight +*/ +QDeclarativeItem *QDeclarativePathView::highlightItem() +{ + Q_D(const QDeclarativePathView); + return d->highlightItem; +} +/*! + \qmlproperty real PathView::preferredHighlightBegin + \qmlproperty real PathView::preferredHighlightEnd + \qmlproperty enumeration PathView::highlightRangeMode + + These properties set the preferred range of the highlight (current item) + within the view. The preferred values must be in the range 0.0-1.0. + + If highlightRangeMode is set to \e PathView.NoHighlightRange + + If highlightRangeMode is set to \e PathView.ApplyRange the view will + attempt to maintain the highlight within the range, however + the highlight can move outside of the range at the ends of the path + or due to a mouse interaction. + + If highlightRangeMode is set to \e PathView.StrictlyEnforceRange the highlight will never + move outside of the range. This means that the current item will change + if a keyboard or mouse action would cause the highlight to move + outside of the range. + + Note that this is the correct way to influence where the + current item ends up when the view moves. For example, if you want the + currently selected item to be in the middle of the path, then set the + highlight range to be 0.5,0.5 and highlightRangeMode to PathView.StrictlyEnforceRange. + Then, when the path scrolls, + the currently selected item will be the item at that position. This also applies to + when the currently selected item changes - it will scroll to within the preferred + highlight range. Furthermore, the behaviour of the current item index will occur + whether or not a highlight exists. + + The default value is \e PathView.StrictlyEnforceRange. + + Note that a valid range requires preferredHighlightEnd to be greater + than or equal to preferredHighlightBegin. +*/ +qreal QDeclarativePathView::preferredHighlightBegin() const +{ + Q_D(const QDeclarativePathView); + return d->highlightRangeStart; +} + +void QDeclarativePathView::setPreferredHighlightBegin(qreal start) +{ + Q_D(QDeclarativePathView); + if (d->highlightRangeStart == start || start < 0 || start > 1.0) + return; + d->highlightRangeStart = start; + d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; + refill(); + emit preferredHighlightBeginChanged(); +} + +qreal QDeclarativePathView::preferredHighlightEnd() const +{ + Q_D(const QDeclarativePathView); + return d->highlightRangeEnd; +} + +void QDeclarativePathView::setPreferredHighlightEnd(qreal end) +{ + Q_D(QDeclarativePathView); + if (d->highlightRangeEnd == end || end < 0 || end > 1.0) + return; + d->highlightRangeEnd = end; + d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; + refill(); + emit preferredHighlightEndChanged(); +} + +QDeclarativePathView::HighlightRangeMode QDeclarativePathView::highlightRangeMode() const +{ + Q_D(const QDeclarativePathView); + return d->highlightRangeMode; +} + +void QDeclarativePathView::setHighlightRangeMode(HighlightRangeMode mode) +{ + Q_D(QDeclarativePathView); + if (d->highlightRangeMode == mode) + return; + d->highlightRangeMode = mode; + d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; + emit highlightRangeModeChanged(); +} + + +/*! + \qmlproperty int PathView::highlightMoveDuration + This property holds the move animation duration of the highlight delegate. + + If the highlightRangeMode is StrictlyEnforceRange then this property + determines the speed that the items move along the path. + + The default value for the duration is 300ms. +*/ +int QDeclarativePathView::highlightMoveDuration() const +{ + Q_D(const QDeclarativePathView); + return d->highlightMoveDuration; +} + +void QDeclarativePathView::setHighlightMoveDuration(int duration) +{ + Q_D(QDeclarativePathView); + if (d->highlightMoveDuration == duration) + return; + d->highlightMoveDuration = duration; + emit highlightMoveDurationChanged(); +} + +/*! + \qmlproperty real PathView::dragMargin + This property holds the maximum distance from the path that initiate mouse dragging. + + By default the path can only be dragged by clicking on an item. If + dragMargin is greater than zero, a drag can be initiated by clicking + within dragMargin pixels of the path. +*/ +qreal QDeclarativePathView::dragMargin() const +{ + Q_D(const QDeclarativePathView); + return d->dragMargin; +} + +void QDeclarativePathView::setDragMargin(qreal dragMargin) +{ + Q_D(QDeclarativePathView); + if (d->dragMargin == dragMargin) + return; + d->dragMargin = dragMargin; + emit dragMarginChanged(); +} + +/*! + \qmlproperty real PathView::flickDeceleration + This property holds the rate at which a flick will decelerate. + + The default is 100. +*/ +qreal QDeclarativePathView::flickDeceleration() const +{ + Q_D(const QDeclarativePathView); + return d->deceleration; +} + +void QDeclarativePathView::setFlickDeceleration(qreal dec) +{ + Q_D(QDeclarativePathView); + if (d->deceleration == dec) + return; + d->deceleration = dec; + emit flickDecelerationChanged(); +} + +/*! + \qmlproperty bool PathView::interactive + + A user cannot drag or flick a PathView that is not interactive. + + This property is useful for temporarily disabling flicking. This allows + special interaction with PathView's children. +*/ +bool QDeclarativePathView::isInteractive() const +{ + Q_D(const QDeclarativePathView); + return d->interactive; +} + +void QDeclarativePathView::setInteractive(bool interactive) +{ + Q_D(QDeclarativePathView); + if (interactive != d->interactive) { + d->interactive = interactive; + if (!interactive) + d->tl.clear(); + emit interactiveChanged(); + } +} + +/*! + \qmlproperty bool PathView::moving + + This property holds whether the view is currently moving + due to the user either dragging or flicking the view. +*/ +bool QDeclarativePathView::isMoving() const +{ + Q_D(const QDeclarativePathView); + return d->moving; +} + +/*! + \qmlproperty bool PathView::flicking + + This property holds whether the view is currently moving + due to the user flicking the view. +*/ +bool QDeclarativePathView::isFlicking() const +{ + Q_D(const QDeclarativePathView); + return d->flicking; +} + +/*! + \qmlsignal PathView::onMovementStarted() + + This handler is called when the view begins moving due to user + interaction. +*/ + +/*! + \qmlsignal PathView::onMovementEnded() + + This handler is called when the view stops moving due to user + interaction. If a flick was generated, this handler will + be triggered once the flick stops. If a flick was not + generated, the handler will be triggered when the + user stops dragging - i.e. a mouse or touch release. +*/ + +/*! + \qmlsignal PathView::onFlickStarted() + + This handler is called when the view is flicked. A flick + starts from the point that the mouse or touch is released, + while still in motion. +*/ + +/*! + \qmlsignal PathView::onFlickEnded() + + This handler is called when the view stops moving due to a flick. +*/ + +/*! + \qmlproperty Component PathView::delegate + + The delegate provides a template defining each item instantiated by the view. + The index is exposed as an accessible \c index property. Properties of the + model are also available depending upon the type of \l {qmlmodels}{Data Model}. + + The number of elements in the delegate has a direct effect on the + flicking performance of the view when pathItemCount is specified. If at all possible, place functionality + that is not needed for the normal display of the delegate in a \l Loader which + can load additional elements when needed. + + Note that the PathView will layout the items based on the size of the root + item in the delegate. + + Here is an example delegate: + \snippet doc/src/snippets/declarative/pathview/pathview.qml 1 +*/ +QDeclarativeComponent *QDeclarativePathView::delegate() const +{ + Q_D(const QDeclarativePathView); + if (d->model) { + if (QDeclarativeVisualDataModel *dataModel = qobject_cast(d->model)) + return dataModel->delegate(); + } + + return 0; +} + +void QDeclarativePathView::setDelegate(QDeclarativeComponent *delegate) +{ + Q_D(QDeclarativePathView); + if (delegate == this->delegate()) + return; + if (!d->ownModel) { + d->model = new QDeclarativeVisualDataModel(qmlContext(this)); + d->ownModel = true; + } + if (QDeclarativeVisualDataModel *dataModel = qobject_cast(d->model)) { + int oldCount = dataModel->count(); + dataModel->setDelegate(delegate); + d->modelCount = dataModel->count(); + d->regenerate(); + if (oldCount != dataModel->count()) + emit countChanged(); + emit delegateChanged(); + } +} + +/*! + \qmlproperty int PathView::pathItemCount + This property holds the number of items visible on the path at any one time. +*/ +int QDeclarativePathView::pathItemCount() const +{ + Q_D(const QDeclarativePathView); + return d->pathItems; +} + +void QDeclarativePathView::setPathItemCount(int i) +{ + Q_D(QDeclarativePathView); + if (i == d->pathItems) + return; + if (i < 1) + i = 1; + d->pathItems = i; + d->updateMappedRange(); + if (d->isValid() && isComponentComplete()) { + d->regenerate(); + } + emit pathItemCountChanged(); +} + +QPointF QDeclarativePathViewPrivate::pointNear(const QPointF &point, qreal *nearPercent) const +{ + //XXX maybe do recursively at increasing resolution. + qreal mindist = 1e10; // big number + QPointF nearPoint = path->pointAt(0); + qreal nearPc = 0; + for (qreal i=1; i < 1000; i++) { + QPointF pt = path->pointAt(i/1000.0); + QPointF diff = pt - point; + qreal dist = diff.x()*diff.x() + diff.y()*diff.y(); + if (dist < mindist) { + nearPoint = pt; + nearPc = i; + mindist = dist; + } + } + + if (nearPercent) + *nearPercent = nearPc / 1000.0; + + return nearPoint; +} + +void QDeclarativePathView::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarativePathView); + if (d->interactive) { + d->handleMousePressEvent(event); + event->accept(); + } else { + QDeclarativeItem::mousePressEvent(event); + } +} + +void QDeclarativePathViewPrivate::handleMousePressEvent(QGraphicsSceneMouseEvent *event) +{ + Q_Q(QDeclarativePathView); + if (!interactive || !items.count()) + return; + QPointF scenePoint = q->mapToScene(event->pos()); + int idx = 0; + for (; idx < items.count(); ++idx) { + QRectF rect = items.at(idx)->boundingRect(); + rect = items.at(idx)->mapToScene(rect).boundingRect(); + if (rect.contains(scenePoint)) + break; + } + if (idx == items.count() && dragMargin == 0.) // didn't click on an item + return; + + startPoint = pointNear(event->pos(), &startPc); + if (idx == items.count()) { + qreal distance = qAbs(event->pos().x() - startPoint.x()) + qAbs(event->pos().y() - startPoint.y()); + if (distance > dragMargin) + return; + } + + if (tl.isActive() && flicking) + stealMouse = true; // If we've been flicked then steal the click. + else + stealMouse = false; + + lastElapsed = 0; + lastDist = 0; + QDeclarativeItemPrivate::start(lastPosTime); + tl.clear(); +} + +void QDeclarativePathView::mouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarativePathView); + if (d->interactive) { + d->handleMouseMoveEvent(event); + if (d->stealMouse) + setKeepMouseGrab(true); + event->accept(); + } else { + QDeclarativeItem::mouseMoveEvent(event); + } +} + +void QDeclarativePathViewPrivate::handleMouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + Q_Q(QDeclarativePathView); + if (!interactive || !lastPosTime.isValid()) + return; + + qreal newPc; + QPointF pathPoint = pointNear(event->pos(), &newPc); + if (!stealMouse) { + QPointF delta = pathPoint - startPoint; + if (qAbs(delta.x()) > QApplication::startDragDistance() || qAbs(delta.y()) > QApplication::startDragDistance()) { + stealMouse = true; + startPc = newPc; + } + } + + if (stealMouse) { + moveReason = QDeclarativePathViewPrivate::Mouse; + qreal diff = (newPc - startPc)*modelCount*mappedRange; + if (diff) { + q->setOffset(offset + diff); + + if (diff > modelCount/2) + diff -= modelCount; + else if (diff < -modelCount/2) + diff += modelCount; + + lastElapsed = QDeclarativeItemPrivate::restart(lastPosTime); + lastDist = diff; + startPc = newPc; + } + if (!moving) { + moving = true; + emit q->movingChanged(); + emit q->movementStarted(); + } + } +} + +void QDeclarativePathView::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarativePathView); + if (d->interactive) { + d->handleMouseReleaseEvent(event); + event->accept(); + ungrabMouse(); + } else { + QDeclarativeItem::mouseReleaseEvent(event); + } +} + +void QDeclarativePathViewPrivate::handleMouseReleaseEvent(QGraphicsSceneMouseEvent *) +{ + Q_Q(QDeclarativePathView); + stealMouse = false; + q->setKeepMouseGrab(false); + if (!interactive || !lastPosTime.isValid()) + return; + + qreal elapsed = qreal(lastElapsed + QDeclarativeItemPrivate::elapsed(lastPosTime)) / 1000.; + qreal velocity = elapsed > 0. ? lastDist / elapsed : 0; + if (model && modelCount && qAbs(velocity) > qreal(1.)) { + qreal count = pathItems == -1 ? modelCount : pathItems; + if (qAbs(velocity) > count * 2) // limit velocity + velocity = (velocity > 0 ? count : -count) * 2; + // Calculate the distance to be travelled + qreal v2 = velocity*velocity; + qreal accel = deceleration/10; + // + 0.25 to encourage moving at least one item in the flick direction + qreal dist = qMin(qreal(modelCount-1), qreal(v2 / (accel * qreal(2.0)) + qreal(0.25))); + if (haveHighlightRange && highlightRangeMode == QDeclarativePathView::StrictlyEnforceRange) { + // round to nearest item. + if (velocity > 0.) + dist = qRound(dist + offset) - offset; + else + dist = qRound(dist - offset) + offset; + // Calculate accel required to stop on item boundary + if (dist <= 0.) { + dist = qreal(0.); + accel = qreal(0.); + } else { + accel = v2 / (2.0f * qAbs(dist)); + } + } + offsetAdj = qreal(0.0); + moveOffset.setValue(offset); + tl.accel(moveOffset, velocity, accel, dist); + tl.callback(QDeclarativeTimeLineCallback(&moveOffset, fixOffsetCallback, this)); + if (!flicking) { + flicking = true; + emit q->flickingChanged(); + emit q->flickStarted(); + } + } else { + fixOffset(); + } + + lastPosTime.invalidate(); + if (!tl.isActive()) + q->movementEnding(); +} + +bool QDeclarativePathView::sendMouseEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarativePathView); + QGraphicsSceneMouseEvent mouseEvent(event->type()); + QRectF myRect = mapToScene(QRectF(0, 0, width(), height())).boundingRect(); + QGraphicsScene *s = scene(); + QDeclarativeItem *grabber = s ? qobject_cast(s->mouseGrabberItem()) : 0; + bool stealThisEvent = d->stealMouse; + if ((stealThisEvent || myRect.contains(event->scenePos().toPoint())) && (!grabber || !grabber->keepMouseGrab())) { + mouseEvent.setAccepted(false); + for (int i = 0x1; i <= 0x10; i <<= 1) { + if (event->buttons() & i) { + Qt::MouseButton button = Qt::MouseButton(i); + mouseEvent.setButtonDownPos(button, mapFromScene(event->buttonDownPos(button))); + } + } + mouseEvent.setScenePos(event->scenePos()); + mouseEvent.setLastScenePos(event->lastScenePos()); + mouseEvent.setPos(mapFromScene(event->scenePos())); + mouseEvent.setLastPos(mapFromScene(event->lastScenePos())); + + switch(mouseEvent.type()) { + case QEvent::GraphicsSceneMouseMove: + d->handleMouseMoveEvent(&mouseEvent); + break; + case QEvent::GraphicsSceneMousePress: + d->handleMousePressEvent(&mouseEvent); + stealThisEvent = d->stealMouse; // Update stealThisEvent in case changed by function call above + break; + case QEvent::GraphicsSceneMouseRelease: + d->handleMouseReleaseEvent(&mouseEvent); + break; + default: + break; + } + grabber = qobject_cast(s->mouseGrabberItem()); + if (grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) + grabMouse(); + + return d->stealMouse; + } else if (d->lastPosTime.isValid()) { + d->lastPosTime.invalidate(); + } + if (mouseEvent.type() == QEvent::GraphicsSceneMouseRelease) + d->stealMouse = false; + return false; +} + +bool QDeclarativePathView::sceneEventFilter(QGraphicsItem *i, QEvent *e) +{ + Q_D(QDeclarativePathView); + if (!isVisible() || !d->interactive) + return QDeclarativeItem::sceneEventFilter(i, e); + + switch (e->type()) { + case QEvent::GraphicsSceneMousePress: + case QEvent::GraphicsSceneMouseMove: + case QEvent::GraphicsSceneMouseRelease: + return sendMouseEvent(static_cast(e)); + default: + break; + } + + return QDeclarativeItem::sceneEventFilter(i, e); +} + +bool QDeclarativePathView::event(QEvent *event) +{ + if (event->type() == QEvent::User) { + refill(); + return true; + } + + return QDeclarativeItem::event(event); +} + +void QDeclarativePathView::componentComplete() +{ + Q_D(QDeclarativePathView); + QDeclarativeItem::componentComplete(); + d->createHighlight(); + // It is possible that a refill has already happended to to Path + // bindings being handled in the componentComplete(). If so + // don't do it again. + if (d->items.count() == 0 && d->model) { + d->modelCount = d->model->count(); + d->regenerate(); + } + d->updateHighlight(); +} + +void QDeclarativePathView::refill() +{ + Q_D(QDeclarativePathView); + if (!d->isValid() || !isComponentComplete()) + return; + + d->layoutScheduled = false; + bool currentVisible = false; + + // first move existing items and remove items off path + int idx = d->firstIndex; + QList::iterator it = d->items.begin(); + while (it != d->items.end()) { + qreal pos = d->positionOfIndex(idx); + QDeclarativeItem *item = *it; + if (pos >= 0.0) { + d->updateItem(item, pos); + if (idx == d->currentIndex) { + currentVisible = true; + d->currentItemOffset = pos; + } + ++it; + } else { +// qDebug() << "release"; + d->updateItem(item, 1.0); + d->releaseItem(item); + if (it == d->items.begin()) { + if (++d->firstIndex >= d->modelCount) + d->firstIndex = 0; + } + it = d->items.erase(it); + } + ++idx; + if (idx >= d->modelCount) + idx = 0; + } + if (!d->items.count()) + d->firstIndex = -1; + + if (d->modelCount) { + // add items to beginning and end + int count = d->pathItems == -1 ? d->modelCount : qMin(d->pathItems, d->modelCount); + if (d->items.count() < count) { + int idx = qRound(d->modelCount - d->offset) % d->modelCount; + qreal startPos = 0.0; + if (d->haveHighlightRange && d->highlightRangeMode != QDeclarativePathView::NoHighlightRange) + startPos = d->highlightRangeStart; + if (d->firstIndex >= 0) { + startPos = d->positionOfIndex(d->firstIndex); + idx = (d->firstIndex + d->items.count()) % d->modelCount; + } + qreal pos = d->positionOfIndex(idx); + while ((pos > startPos || !d->items.count()) && d->items.count() < count) { + // qDebug() << "append" << idx; + QDeclarativeItem *item = d->getItem(idx); + if (d->model->completePending()) + item->setZValue(idx+1); + if (d->currentIndex == idx) { + item->setFocus(true); + if (QDeclarativePathViewAttached *att = d->attached(item)) + att->setIsCurrentItem(true); + currentVisible = true; + d->currentItemOffset = pos; + d->currentItem = item; + } + if (d->items.count() == 0) + d->firstIndex = idx; + d->items.append(item); + d->updateItem(item, pos); + if (d->model->completePending()) + d->model->completeItem(); + ++idx; + if (idx >= d->modelCount) + idx = 0; + pos = d->positionOfIndex(idx); + } + + idx = d->firstIndex - 1; + if (idx < 0) + idx = d->modelCount - 1; + pos = d->positionOfIndex(idx); + while (pos >= 0.0 && pos < startPos) { + // qDebug() << "prepend" << idx; + QDeclarativeItem *item = d->getItem(idx); + if (d->model->completePending()) + item->setZValue(idx+1); + if (d->currentIndex == idx) { + item->setFocus(true); + if (QDeclarativePathViewAttached *att = d->attached(item)) + att->setIsCurrentItem(true); + currentVisible = true; + d->currentItemOffset = pos; + d->currentItem = item; + } + d->items.prepend(item); + d->updateItem(item, pos); + if (d->model->completePending()) + d->model->completeItem(); + d->firstIndex = idx; + idx = d->firstIndex - 1; + if (idx < 0) + idx = d->modelCount - 1; + pos = d->positionOfIndex(idx); + } + } + } + + if (!currentVisible) + d->currentItemOffset = 1.0; + + if (d->highlightItem && d->haveHighlightRange && d->highlightRangeMode == QDeclarativePathView::StrictlyEnforceRange) { + d->updateItem(d->highlightItem, d->highlightRangeStart); + if (QDeclarativePathViewAttached *att = d->attached(d->highlightItem)) + att->setOnPath(true); + } else if (d->highlightItem && d->moveReason != QDeclarativePathViewPrivate::SetIndex) { + d->updateItem(d->highlightItem, d->currentItemOffset); + if (QDeclarativePathViewAttached *att = d->attached(d->highlightItem)) + att->setOnPath(currentVisible); + } + while (d->itemCache.count()) + d->releaseItem(d->itemCache.takeLast()); +} + +void QDeclarativePathView::itemsInserted(int modelIndex, int count) +{ + //XXX support animated insertion + Q_D(QDeclarativePathView); + if (!d->isValid() || !isComponentComplete()) + return; + + if (d->modelCount) { + d->itemCache += d->items; + d->items.clear(); + if (modelIndex <= d->currentIndex) { + d->currentIndex += count; + emit currentIndexChanged(); + } else if (d->offset != 0) { + d->offset += count; + d->offsetAdj += count; + } + } + d->modelCount += count; + if (d->flicking || d->moving) { + d->regenerate(); + d->updateCurrent(); + } else { + d->firstIndex = -1; + d->updateMappedRange(); + d->scheduleLayout(); + } + emit countChanged(); +} + +void QDeclarativePathView::itemsRemoved(int modelIndex, int count) +{ + //XXX support animated removal + Q_D(QDeclarativePathView); + if (!d->model || !d->modelCount || !d->model->isValid() || !d->path || !isComponentComplete()) + return; + + // fix current + bool currentChanged = false; + if (d->currentIndex >= modelIndex + count) { + d->currentIndex -= count; + currentChanged = true; + } else if (d->currentIndex >= modelIndex && d->currentIndex < modelIndex + count) { + // current item has been removed. + d->currentIndex = qMin(modelIndex, d->modelCount-count-1); + if (d->currentItem) { + if (QDeclarativePathViewAttached *att = d->attached(d->currentItem)) + att->setIsCurrentItem(true); + } + currentChanged = true; + } + + d->itemCache += d->items; + d->items.clear(); + + bool changedOffset = false; + if (modelIndex > d->currentIndex) { + if (d->offset >= count) { + changedOffset = true; + d->offset -= count; + d->offsetAdj -= count; + } + } + + d->modelCount -= count; + if (!d->modelCount) { + while (d->itemCache.count()) + d->releaseItem(d->itemCache.takeLast()); + d->offset = 0; + changedOffset = true; + d->tl.reset(d->moveOffset); + update(); + } else { + d->regenerate(); + d->updateCurrent(); + if (!d->flicking && !d->moving && d->haveHighlightRange && d->highlightRangeMode == QDeclarativePathView::StrictlyEnforceRange) + d->snapToCurrent(); + } + if (changedOffset) + emit offsetChanged(); + if (currentChanged) + emit currentIndexChanged(); + emit countChanged(); +} + +void QDeclarativePathView::itemsMoved(int /*from*/, int /*to*/, int /*count*/) +{ + Q_D(QDeclarativePathView); + if (!d->isValid() || !isComponentComplete()) + return; + + QList removedItems = d->items; + d->items.clear(); + d->regenerate(); + while (removedItems.count()) + d->releaseItem(removedItems.takeLast()); + + // Fix current index + if (d->currentIndex >= 0 && d->currentItem) { + int oldCurrent = d->currentIndex; + d->currentIndex = d->model->indexOf(d->currentItem, this); + if (oldCurrent != d->currentIndex) + emit currentIndexChanged(); + } + d->updateCurrent(); +} + +void QDeclarativePathView::modelReset() +{ + Q_D(QDeclarativePathView); + d->modelCount = d->model->count(); + d->regenerate(); + emit countChanged(); +} + +void QDeclarativePathView::createdItem(int index, QDeclarativeItem *item) +{ + Q_D(QDeclarativePathView); + if (d->requestedIndex != index) { + if (!d->attType) { + // pre-create one metatype to share with all attached objects + d->attType = new QDeclarativeOpenMetaObjectType(&QDeclarativePathViewAttached::staticMetaObject, qmlEngine(this)); + foreach(const QString &attr, d->path->attributes()) + d->attType->createProperty(attr.toUtf8()); + } + qPathViewAttachedType = d->attType; + QDeclarativePathViewAttached *att = static_cast(qmlAttachedPropertiesObject(item)); + qPathViewAttachedType = 0; + if (att) { + att->m_view = this; + att->setOnPath(false); + } + item->setParentItem(this); + d->updateItem(item, index < d->firstIndex ? qreal(0.0) : qreal(1.0)); + } +} + +void QDeclarativePathView::destroyingItem(QDeclarativeItem *item) +{ + Q_UNUSED(item); +} + +void QDeclarativePathView::ticked() +{ + Q_D(QDeclarativePathView); + d->updateCurrent(); +} + +void QDeclarativePathView::movementEnding() +{ + Q_D(QDeclarativePathView); + if (d->flicking) { + d->flicking = false; + emit flickingChanged(); + emit flickEnded(); + } + if (d->moving && !d->stealMouse) { + d->moving = false; + emit movingChanged(); + emit movementEnded(); + } +} + +// find the item closest to the snap position +int QDeclarativePathViewPrivate::calcCurrentIndex() +{ + int current = -1; + if (modelCount && model && items.count()) { + offset = qmlMod(offset, modelCount); + if (offset < 0) + offset += modelCount; + current = qRound(qAbs(qmlMod(modelCount - offset, modelCount))); + current = current % modelCount; + } + + return current; +} + +void QDeclarativePathViewPrivate::updateCurrent() +{ + Q_Q(QDeclarativePathView); + if (moveReason != Mouse) + return; + if (!modelCount || !haveHighlightRange || highlightRangeMode != QDeclarativePathView::StrictlyEnforceRange) + return; + + int idx = calcCurrentIndex(); + if (model && idx != currentIndex) { + int itemIndex = (currentIndex - firstIndex + modelCount) % modelCount; + if (itemIndex < items.count()) { + if (QDeclarativeItem *item = items.at(itemIndex)) { + if (QDeclarativePathViewAttached *att = attached(item)) + att->setIsCurrentItem(false); + } + } + currentIndex = idx; + currentItem = 0; + itemIndex = (idx - firstIndex + modelCount) % modelCount; + if (itemIndex < items.count()) { + currentItem = items.at(itemIndex); + currentItem->setFocus(true); + if (QDeclarativePathViewAttached *att = attached(currentItem)) + att->setIsCurrentItem(true); + } + emit q->currentIndexChanged(); + } +} + +void QDeclarativePathViewPrivate::fixOffsetCallback(void *d) +{ + ((QDeclarativePathViewPrivate *)d)->fixOffset(); +} + +void QDeclarativePathViewPrivate::fixOffset() +{ + Q_Q(QDeclarativePathView); + if (model && items.count()) { + if (haveHighlightRange && highlightRangeMode == QDeclarativePathView::StrictlyEnforceRange) { + int curr = calcCurrentIndex(); + if (curr != currentIndex) + q->setCurrentIndex(curr); + else + snapToCurrent(); + } + } +} + +void QDeclarativePathViewPrivate::snapToCurrent() +{ + if (!model || modelCount <= 0) + return; + + qreal targetOffset = qmlMod(modelCount - currentIndex, modelCount); + + moveReason = Other; + offsetAdj = 0.0; + tl.reset(moveOffset); + moveOffset.setValue(offset); + + const int duration = highlightMoveDuration; + + if (moveDirection == Positive || (moveDirection == Shortest && targetOffset - offset > modelCount/2)) { + qreal distance = modelCount - targetOffset + offset; + if (targetOffset > moveOffset) { + tl.move(moveOffset, 0.0, QEasingCurve(QEasingCurve::InQuad), int(duration * offset / distance)); + tl.set(moveOffset, modelCount); + tl.move(moveOffset, targetOffset, QEasingCurve(offset == 0.0 ? QEasingCurve::InOutQuad : QEasingCurve::OutQuad), int(duration * (modelCount-targetOffset) / distance)); + } else { + tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration); + } + } else if (moveDirection == Negative || targetOffset - offset <= -modelCount/2) { + qreal distance = modelCount - offset + targetOffset; + if (targetOffset < moveOffset) { + tl.move(moveOffset, modelCount, QEasingCurve(targetOffset == 0 ? QEasingCurve::InOutQuad : QEasingCurve::InQuad), int(duration * (modelCount-offset) / distance)); + tl.set(moveOffset, 0.0); + tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::OutQuad), int(duration * targetOffset / distance)); + } else { + tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration); + } + } else { + tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration); + } + moveDirection = Shortest; +} + +QDeclarativePathViewAttached *QDeclarativePathView::qmlAttachedProperties(QObject *obj) +{ + return new QDeclarativePathViewAttached(obj); +} + +QT_END_NAMESPACE + diff --git a/src/declarative/graphicsitems/qdeclarativepathview_p.h b/src/declarative/graphicsitems/qdeclarativepathview_p.h new file mode 100644 index 00000000..88ef68b0 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativepathview_p.h @@ -0,0 +1,252 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPATHVIEW_H +#define QDECLARATIVEPATHVIEW_H + +#include "qdeclarativeitem.h" +#include "private/qdeclarativepath_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativePathViewPrivate; +class QDeclarativePathViewAttached; +class Q_AUTOTEST_EXPORT QDeclarativePathView : public QDeclarativeItem +{ + Q_OBJECT + + Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged) + Q_PROPERTY(QDeclarativePath *path READ path WRITE setPath NOTIFY pathChanged) + Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) + Q_PROPERTY(qreal offset READ offset WRITE setOffset NOTIFY offsetChanged) + + Q_PROPERTY(QDeclarativeComponent *highlight READ highlight WRITE setHighlight NOTIFY highlightChanged) + Q_PROPERTY(QDeclarativeItem *highlightItem READ highlightItem NOTIFY highlightItemChanged) + + Q_PROPERTY(qreal preferredHighlightBegin READ preferredHighlightBegin WRITE setPreferredHighlightBegin NOTIFY preferredHighlightBeginChanged) + Q_PROPERTY(qreal preferredHighlightEnd READ preferredHighlightEnd WRITE setPreferredHighlightEnd NOTIFY preferredHighlightEndChanged) + Q_PROPERTY(HighlightRangeMode highlightRangeMode READ highlightRangeMode WRITE setHighlightRangeMode NOTIFY highlightRangeModeChanged) + Q_PROPERTY(int highlightMoveDuration READ highlightMoveDuration WRITE setHighlightMoveDuration NOTIFY highlightMoveDurationChanged) + + Q_PROPERTY(qreal dragMargin READ dragMargin WRITE setDragMargin NOTIFY dragMarginChanged) + Q_PROPERTY(qreal flickDeceleration READ flickDeceleration WRITE setFlickDeceleration NOTIFY flickDecelerationChanged) + Q_PROPERTY(bool interactive READ isInteractive WRITE setInteractive NOTIFY interactiveChanged) + + Q_PROPERTY(bool moving READ isMoving NOTIFY movingChanged) + Q_PROPERTY(bool flicking READ isFlicking NOTIFY flickingChanged) + + Q_PROPERTY(int count READ count NOTIFY countChanged) + Q_PROPERTY(QDeclarativeComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged) + Q_PROPERTY(int pathItemCount READ pathItemCount WRITE setPathItemCount NOTIFY pathItemCountChanged) + + Q_ENUMS(HighlightRangeMode) + +public: + QDeclarativePathView(QDeclarativeItem *parent=0); + virtual ~QDeclarativePathView(); + + QVariant model() const; + void setModel(const QVariant &); + + QDeclarativePath *path() const; + void setPath(QDeclarativePath *); + + int currentIndex() const; + void setCurrentIndex(int idx); + + qreal offset() const; + void setOffset(qreal offset); + + QDeclarativeComponent *highlight() const; + void setHighlight(QDeclarativeComponent *highlight); + QDeclarativeItem *highlightItem(); + + enum HighlightRangeMode { NoHighlightRange, ApplyRange, StrictlyEnforceRange }; + HighlightRangeMode highlightRangeMode() const; + void setHighlightRangeMode(HighlightRangeMode mode); + + qreal preferredHighlightBegin() const; + void setPreferredHighlightBegin(qreal); + + qreal preferredHighlightEnd() const; + void setPreferredHighlightEnd(qreal); + + int highlightMoveDuration() const; + void setHighlightMoveDuration(int); + + qreal dragMargin() const; + void setDragMargin(qreal margin); + + qreal flickDeceleration() const; + void setFlickDeceleration(qreal dec); + + bool isInteractive() const; + void setInteractive(bool); + + bool isMoving() const; + bool isFlicking() const; + + int count() const; + + QDeclarativeComponent *delegate() const; + void setDelegate(QDeclarativeComponent *); + + int pathItemCount() const; + void setPathItemCount(int); + + static QDeclarativePathViewAttached *qmlAttachedProperties(QObject *); + +public Q_SLOTS: + void incrementCurrentIndex(); + void decrementCurrentIndex(); + +Q_SIGNALS: + void currentIndexChanged(); + void offsetChanged(); + void modelChanged(); + void countChanged(); + void pathChanged(); + void preferredHighlightBeginChanged(); + void preferredHighlightEndChanged(); + void highlightRangeModeChanged(); + void dragMarginChanged(); + void snapPositionChanged(); + void delegateChanged(); + void pathItemCountChanged(); + void flickDecelerationChanged(); + void interactiveChanged(); + void movingChanged(); + void flickingChanged(); + void highlightChanged(); + void highlightItemChanged(); + void highlightMoveDurationChanged(); + void movementStarted(); + void movementEnded(); + void flickStarted(); + void flickEnded(); + +protected: + void mousePressEvent(QGraphicsSceneMouseEvent *event); + void mouseMoveEvent(QGraphicsSceneMouseEvent *event); + void mouseReleaseEvent(QGraphicsSceneMouseEvent *); + bool sendMouseEvent(QGraphicsSceneMouseEvent *event); + bool sceneEventFilter(QGraphicsItem *, QEvent *); + bool event(QEvent *event); + void componentComplete(); + +private Q_SLOTS: + void refill(); + void ticked(); + void movementEnding(); + void itemsInserted(int index, int count); + void itemsRemoved(int index, int count); + void itemsMoved(int,int,int); + void modelReset(); + void createdItem(int index, QDeclarativeItem *item); + void destroyingItem(QDeclarativeItem *item); + void pathUpdated(); + +private: + friend class QDeclarativePathViewAttached; + Q_DISABLE_COPY(QDeclarativePathView) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarativePathView) +}; + +class QDeclarativeOpenMetaObject; +class QDeclarativePathViewAttached : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QDeclarativePathView *view READ view CONSTANT) + Q_PROPERTY(bool isCurrentItem READ isCurrentItem NOTIFY currentItemChanged) + Q_PROPERTY(bool onPath READ isOnPath NOTIFY pathChanged) + +public: + QDeclarativePathViewAttached(QObject *parent); + ~QDeclarativePathViewAttached(); + + QDeclarativePathView *view() { return m_view; } + + bool isCurrentItem() const { return m_isCurrent; } + void setIsCurrentItem(bool c) { + if (m_isCurrent != c) { + m_isCurrent = c; + emit currentItemChanged(); + } + } + + QVariant value(const QByteArray &name) const; + void setValue(const QByteArray &name, const QVariant &val); + + bool isOnPath() const { return m_onPath; } + void setOnPath(bool on) { + if (on != m_onPath) { + m_onPath = on; + emit pathChanged(); + } + } + qreal m_percent; + +Q_SIGNALS: + void currentItemChanged(); + void pathChanged(); + +private: + friend class QDeclarativePathViewPrivate; + friend class QDeclarativePathView; + QDeclarativePathView *m_view; + QDeclarativeOpenMetaObject *m_metaobject; + bool m_onPath : 1; + bool m_isCurrent : 1; +}; + + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativePathView) +QML_DECLARE_TYPEINFO(QDeclarativePathView, QML_HAS_ATTACHED_PROPERTIES) +QT_END_HEADER + +#endif // QDECLARATIVEPATHVIEW_H diff --git a/src/declarative/graphicsitems/qdeclarativepathview_p_p.h b/src/declarative/graphicsitems/qdeclarativepathview_p_p.h new file mode 100644 index 00000000..b226f79e --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativepathview_p_p.h @@ -0,0 +1,192 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPATHVIEW_P_H +#define QDECLARATIVEPATHVIEW_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 "private/qdeclarativepathview_p.h" + +#include "private/qdeclarativeitem_p.h" +#include "private/qdeclarativevisualitemmodel_p.h" + +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeOpenMetaObjectType; +class QDeclarativePathViewAttached; +class QDeclarativePathViewPrivate : public QDeclarativeItemPrivate, public QDeclarativeItemChangeListener +{ + Q_DECLARE_PUBLIC(QDeclarativePathView) + +public: + QDeclarativePathViewPrivate() + : path(0), currentIndex(0), currentItemOffset(0.0), startPc(0), lastDist(0) + , lastElapsed(0), offset(0.0), offsetAdj(0.0), mappedRange(1.0) + , stealMouse(false), ownModel(false), interactive(true), haveHighlightRange(true) + , autoHighlight(true), highlightUp(false), layoutScheduled(false) + , moving(false), flicking(false) + , dragMargin(0), deceleration(100) + , moveOffset(this, &QDeclarativePathViewPrivate::setAdjustedOffset) + , firstIndex(-1), pathItems(-1), requestedIndex(-1) + , moveReason(Other), moveDirection(Shortest), attType(0), highlightComponent(0), highlightItem(0) + , moveHighlight(this, &QDeclarativePathViewPrivate::setHighlightPosition) + , highlightPosition(0) + , highlightRangeStart(0), highlightRangeEnd(0) + , highlightRangeMode(QDeclarativePathView::StrictlyEnforceRange) + , highlightMoveDuration(300), modelCount(0) + { + } + + void init(); + + void itemGeometryChanged(QDeclarativeItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) { + if ((newGeometry.size() != oldGeometry.size()) + && (!highlightItem || item != highlightItem)) { + if (QDeclarativePathViewAttached *att = attached(item)) + att->m_percent = -1; + scheduleLayout(); + } + } + + void scheduleLayout() { + Q_Q(QDeclarativePathView); + if (!layoutScheduled) { + layoutScheduled = true; + QCoreApplication::postEvent(q, new QEvent(QEvent::User), Qt::HighEventPriority); + } + } + + QDeclarativeItem *getItem(int modelIndex); + void releaseItem(QDeclarativeItem *item); + QDeclarativePathViewAttached *attached(QDeclarativeItem *item); + void clear(); + void updateMappedRange(); + qreal positionOfIndex(qreal index) const; + void createHighlight(); + void updateHighlight(); + void setHighlightPosition(qreal pos); + bool isValid() const { + return model && model->count() > 0 && model->isValid() && path; + } + + void handleMousePressEvent(QGraphicsSceneMouseEvent *event); + void handleMouseMoveEvent(QGraphicsSceneMouseEvent *event); + void handleMouseReleaseEvent(QGraphicsSceneMouseEvent *); + + int calcCurrentIndex(); + void updateCurrent(); + static void fixOffsetCallback(void*); + void fixOffset(); + void setOffset(qreal offset); + void setAdjustedOffset(qreal offset); + void regenerate(); + void updateItem(QDeclarativeItem *, qreal); + void snapToCurrent(); + QPointF pointNear(const QPointF &point, qreal *nearPercent=0) const; + + QDeclarativePath *path; + int currentIndex; + QDeclarativeGuard currentItem; + qreal currentItemOffset; + qreal startPc; + QPointF startPoint; + qreal lastDist; + int lastElapsed; + qreal offset; + qreal offsetAdj; + qreal mappedRange; + bool stealMouse : 1; + bool ownModel : 1; + bool interactive : 1; + bool haveHighlightRange : 1; + bool autoHighlight : 1; + bool highlightUp : 1; + bool layoutScheduled : 1; + bool moving : 1; + bool flicking : 1; + QElapsedTimer lastPosTime; + QPointF lastPos; + qreal dragMargin; + qreal deceleration; + QDeclarativeTimeLine tl; + QDeclarativeTimeLineValueProxy moveOffset; + int firstIndex; + int pathItems; + int requestedIndex; + QList items; + QList itemCache; + QDeclarativeGuard model; + QVariant modelVariant; + enum MovementReason { Other, SetIndex, Mouse }; + MovementReason moveReason; + enum MovementDirection { Shortest, Negative, Positive }; + MovementDirection moveDirection; + QDeclarativeOpenMetaObjectType *attType; + QDeclarativeComponent *highlightComponent; + QDeclarativeItem *highlightItem; + QDeclarativeTimeLineValueProxy moveHighlight; + qreal highlightPosition; + qreal highlightRangeStart; + qreal highlightRangeEnd; + QDeclarativePathView::HighlightRangeMode highlightRangeMode; + int highlightMoveDuration; + int modelCount; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/declarative/graphicsitems/qdeclarativepincharea.cpp b/src/declarative/graphicsitems/qdeclarativepincharea.cpp new file mode 100644 index 00000000..3d79f7a8 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativepincharea.cpp @@ -0,0 +1,607 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativepincharea_p.h" +#include "qdeclarativepincharea_p_p.h" + +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + + +/*! + \qmlclass PinchEvent QDeclarativePinchEvent + \ingroup qml-event-elements + \brief The PinchEvent object provides information about a pinch event. + + \bold {The PinchEvent element was added in QtQuick 1.1} + + The \c center, \c startCenter, \c previousCenter properties provide the center position between the two touch points. + + The \c scale and \c previousScale properties provide the scale factor. + + The \c angle, \c previousAngle and \c rotation properties provide the angle between the two points and the amount of rotation. + + The \c point1, \c point2, \c startPoint1, \c startPoint2 properties provide the positions of the touch points. + + The \c accepted property may be set to false in the \c onPinchStarted handler if the gesture should not + be handled. + + \sa PinchArea +*/ + +/*! + \qmlproperty QPointF PinchEvent::center + \qmlproperty QPointF PinchEvent::startCenter + \qmlproperty QPointF PinchEvent::previousCenter + + These properties hold the position of the center point between the two touch points. + + \list + \o \c center is the current center point + \o \c previousCenter is the center point of the previous event. + \o \c startCenter is the center point when the gesture began + \endlist +*/ + +/*! + \qmlproperty real PinchEvent::scale + \qmlproperty real PinchEvent::previousScale + + These properties hold the scale factor determined by the change in distance between the two touch points. + + \list + \o \c scale is the current scale factor. + \o \c previousScale is the scale factor of the previous event. + \endlist + + When a pinch gesture is started, the scale is 1.0. +*/ + +/*! + \qmlproperty real PinchEvent::angle + \qmlproperty real PinchEvent::previousAngle + \qmlproperty real PinchEvent::rotation + + These properties hold the angle between the two touch points. + + \list + \o \c angle is the current angle between the two points in the range -180 to 180. + \o \c previousAngle is the angle of the previous event. + \o \c rotation is the total rotation since the pinch gesture started. + \endlist + + When a pinch gesture is started, the rotation is 0.0. +*/ + +/*! + \qmlproperty QPointF PinchEvent::point1 + \qmlproperty QPointF PinchEvent::startPoint1 + \qmlproperty QPointF PinchEvent::point2 + \qmlproperty QPointF PinchEvent::startPoint2 + + These properties provide the actual touch points generating the pinch. + + \list + \o \c point1 and \c point2 hold the current positions of the points. + \o \c startPoint1 and \c startPoint2 hold the positions of the points when the second point was touched. + \endlist +*/ + +/*! + \qmlproperty bool PinchEvent::accepted + + Setting this property to false in the \c PinchArea::onPinchStarted handler + will result in no further pinch events being generated, and the gesture + ignored. +*/ + +/*! + \qmlproperty int PinchEvent::pointCount + + Holds the number of points currently touched. The PinchArea will not react + until two touch points have initited a gesture, but will remain active until + all touch points have been released. +*/ + +QDeclarativePinch::QDeclarativePinch() + : m_target(0), m_minScale(1.0), m_maxScale(1.0) + , m_minRotation(0.0), m_maxRotation(0.0) + , m_axis(NoDrag), m_xmin(-FLT_MAX), m_xmax(FLT_MAX) + , m_ymin(-FLT_MAX), m_ymax(FLT_MAX), m_active(false) +{ +} + +QDeclarativePinchAreaPrivate::~QDeclarativePinchAreaPrivate() +{ + delete pinch; +} + +/*! + \qmlclass PinchArea QDeclarativePinchArea + \brief The PinchArea item enables simple pinch gesture handling. + \inherits Item + + \bold {The PinchArea element was added in QtQuick 1.1} + + A PinchArea is an invisible item that is typically used in conjunction with + a visible item in order to provide pinch gesture handling for that item. + + The \l enabled property is used to enable and disable pinch handling for + the proxied item. When disabled, the pinch area becomes transparent to + mouse/touch events. + + PinchArea can be used in two ways: + + \list + \o setting a \c pinch.target to provide automatic interaction with an element + \o using the onPinchStarted, onPinchUpdated and onPinchFinished handlers + \endlist + + \sa PinchEvent +*/ + +/*! + \qmlsignal PinchArea::onPinchStarted() + + This handler is called when the pinch area detects that a pinch gesture has started. + + The \l {PinchEvent}{pinch} parameter provides information about the pinch gesture, + including the scale, center and angle of the pinch. + + To ignore this gesture set the \c pinch.accepted property to false. The gesture + will be cancelled and no further events will be sent. +*/ + +/*! + \qmlsignal PinchArea::onPinchUpdated() + + This handler is called when the pinch area detects that a pinch gesture has changed. + + The \l {PinchEvent}{pinch} parameter provides information about the pinch gesture, + including the scale, center and angle of the pinch. +*/ + +/*! + \qmlsignal PinchArea::onPinchFinished() + + This handler is called when the pinch area detects that a pinch gesture has finished. + + The \l {PinchEvent}{pinch} parameter provides information about the pinch gesture, + including the scale, center and angle of the pinch. +*/ + + +/*! + \qmlproperty Item PinchArea::pinch.target + \qmlproperty bool PinchArea::pinch.active + \qmlproperty real PinchArea::pinch.minimumScale + \qmlproperty real PinchArea::pinch.maximumScale + \qmlproperty real PinchArea::pinch.minimumRotation + \qmlproperty real PinchArea::pinch.maximumRotation + \qmlproperty enumeration PinchArea::pinch.dragAxis + \qmlproperty real PinchArea::pinch.minimumX + \qmlproperty real PinchArea::pinch.maximumX + \qmlproperty real PinchArea::pinch.minimumY + \qmlproperty real PinchArea::pinch.maximumY + + \c pinch provides a convenient way to make an item react to pinch gestures. + + \list + \i \c pinch.target specifies the id of the item to drag. + \i \c pinch.active specifies if the target item is currently being dragged. + \i \c pinch.minimumScale and \c pinch.maximumScale limit the range of the Item::scale property. + \i \c pinch.minimumRotation and \c pinch.maximumRotation limit the range of the Item::rotation property. + \i \c pinch.dragAxis specifies whether dragging in not allowed (\c Pinch.NoDrag), can be done horizontally (\c Pinch.XAxis), vertically (\c Pinch.YAxis), or both (\c Pinch.XandYAxis) + \i \c pinch.minimum and \c pinch.maximum limit how far the target can be dragged along the corresponding axes. + \endlist +*/ + +QDeclarativePinchArea::QDeclarativePinchArea(QDeclarativeItem *parent) + : QDeclarativeItem(*(new QDeclarativePinchAreaPrivate), parent) +{ + Q_D(QDeclarativePinchArea); + d->init(); +} + +QDeclarativePinchArea::~QDeclarativePinchArea() +{ +} + +/*! + \qmlproperty bool PinchArea::enabled + This property holds whether the item accepts pinch gestures. + + This property defaults to true. +*/ +bool QDeclarativePinchArea::isEnabled() const +{ + Q_D(const QDeclarativePinchArea); + return d->absorb; +} + +void QDeclarativePinchArea::setEnabled(bool a) +{ + Q_D(QDeclarativePinchArea); + if (a != d->absorb) { + d->absorb = a; + emit enabledChanged(); + } +} + +bool QDeclarativePinchArea::event(QEvent *event) +{ + Q_D(QDeclarativePinchArea); + if (!d->absorb || !isVisible()) + return QDeclarativeItem::event(event); + switch (event->type()) { + case QEvent::TouchBegin: + case QEvent::TouchUpdate: { + QTouchEvent *touch = static_cast(event); + d->touchPoints.clear(); + for (int i = 0; i < touch->touchPoints().count(); ++i) { + if (!(touch->touchPoints().at(i).state() & Qt::TouchPointReleased)) { + d->touchPoints << touch->touchPoints().at(i); + } + } + updatePinch(); + } + return true; + case QEvent::TouchEnd: + d->touchPoints.clear(); + updatePinch(); + break; + default: + return QDeclarativeItem::event(event); + } + + return QDeclarativeItem::event(event); +} + +void QDeclarativePinchArea::updatePinch() +{ + Q_D(QDeclarativePinchArea); + if (d->touchPoints.count() == 0) { + if (d->inPinch) { + d->stealMouse = false; + setKeepMouseGrab(false); + d->inPinch = false; + QPointF pinchCenter = mapFromScene(d->sceneLastCenter); + QDeclarativePinchEvent pe(pinchCenter, d->pinchLastScale, d->pinchLastAngle, d->pinchRotation); + pe.setStartCenter(d->pinchStartCenter); + pe.setPreviousCenter(pinchCenter); + pe.setPreviousAngle(d->pinchLastAngle); + pe.setPreviousScale(d->pinchLastScale); + pe.setStartPoint1(mapFromScene(d->sceneStartPoint1)); + pe.setStartPoint2(mapFromScene(d->sceneStartPoint2)); + pe.setPoint1(mapFromScene(d->lastPoint1)); + pe.setPoint2(mapFromScene(d->lastPoint2)); + emit pinchFinished(&pe); + d->pinchStartDist = 0; + d->pinchActivated = false; + if (d->pinch && d->pinch->target()) + d->pinch->setActive(false); + } + return; + } + QTouchEvent::TouchPoint touchPoint1 = d->touchPoints.at(0); + QTouchEvent::TouchPoint touchPoint2 = d->touchPoints.at(d->touchPoints. count() >= 2 ? 1 : 0); + if (d->touchPoints.count() == 2 + && (touchPoint1.state() & Qt::TouchPointPressed || touchPoint2.state() & Qt::TouchPointPressed)) { + d->id1 = touchPoint1.id(); + d->sceneStartPoint1 = touchPoint1.scenePos(); + d->sceneStartPoint2 = touchPoint2.scenePos(); + d->inPinch = false; + d->pinchRejected = false; + d->pinchActivated = true; + } else if (d->pinchActivated && !d->pinchRejected) { + const int dragThreshold = QApplication::startDragDistance(); + QPointF p1 = touchPoint1.scenePos(); + QPointF p2 = touchPoint2.scenePos(); + qreal dx = p1.x() - p2.x(); + qreal dy = p1.y() - p2.y(); + qreal dist = sqrt(dx*dx + dy*dy); + QPointF sceneCenter = (p1 + p2)/2; + qreal angle = QLineF(p1, p2).angle(); + if (d->touchPoints.count() == 1) { + // If we only have one point then just move the center + if (d->id1 == touchPoint1.id()) + sceneCenter = d->sceneLastCenter + touchPoint1.scenePos() - d->lastPoint1; + else + sceneCenter = d->sceneLastCenter + touchPoint2.scenePos() - d->lastPoint2; + angle = d->pinchLastAngle; + } + d->id1 = touchPoint1.id(); + if (angle > 180) + angle -= 360; + if (!d->inPinch) { + if (d->touchPoints.count() >= 2 + && (qAbs(p1.x()-d->sceneStartPoint1.x()) > dragThreshold + || qAbs(p1.y()-d->sceneStartPoint1.y()) > dragThreshold + || qAbs(p2.x()-d->sceneStartPoint2.x()) > dragThreshold + || qAbs(p2.y()-d->sceneStartPoint2.y()) > dragThreshold)) { + d->sceneStartCenter = sceneCenter; + d->sceneLastCenter = sceneCenter; + d->pinchStartCenter = mapFromScene(sceneCenter); + d->pinchStartDist = dist; + d->pinchStartAngle = angle; + d->pinchLastScale = 1.0; + d->pinchLastAngle = angle; + d->pinchRotation = 0.0; + d->lastPoint1 = p1; + d->lastPoint2 = p2; + QDeclarativePinchEvent pe(d->pinchStartCenter, 1.0, angle, 0.0); + pe.setStartCenter(d->pinchStartCenter); + pe.setPreviousCenter(d->pinchStartCenter); + pe.setPreviousAngle(d->pinchLastAngle); + pe.setPreviousScale(d->pinchLastScale); + pe.setStartPoint1(mapFromScene(d->sceneStartPoint1)); + pe.setStartPoint2(mapFromScene(d->sceneStartPoint2)); + pe.setPoint1(mapFromScene(d->lastPoint1)); + pe.setPoint2(mapFromScene(d->lastPoint2)); + pe.setPointCount(d->touchPoints.count()); + emit pinchStarted(&pe); + if (pe.accepted()) { + d->inPinch = true; + d->stealMouse = true; + QGraphicsScene *s = scene(); + if (s && s->mouseGrabberItem() != this) + grabMouse(); + setKeepMouseGrab(true); + if (d->pinch && d->pinch->target()) { + d->pinchStartPos = pinch()->target()->pos(); + d->pinchStartScale = d->pinch->target()->scale(); + d->pinchStartRotation = d->pinch->target()->rotation(); + d->pinch->setActive(true); + } + } else { + d->pinchRejected = true; + } + } + } else if (d->pinchStartDist > 0) { + qreal scale = dist ? dist / d->pinchStartDist : d->pinchLastScale; + qreal da = d->pinchLastAngle - angle; + if (da > 180) + da -= 360; + else if (da < -180) + da += 360; + d->pinchRotation += da; + QPointF pinchCenter = mapFromScene(sceneCenter); + QDeclarativePinchEvent pe(pinchCenter, scale, angle, d->pinchRotation); + pe.setStartCenter(d->pinchStartCenter); + pe.setPreviousCenter(mapFromScene(d->sceneLastCenter)); + pe.setPreviousAngle(d->pinchLastAngle); + pe.setPreviousScale(d->pinchLastScale); + pe.setStartPoint1(mapFromScene(d->sceneStartPoint1)); + pe.setStartPoint2(mapFromScene(d->sceneStartPoint2)); + pe.setPoint1(touchPoint1.pos()); + pe.setPoint2(touchPoint2.pos()); + pe.setPointCount(d->touchPoints.count()); + d->pinchLastScale = scale; + d->sceneLastCenter = sceneCenter; + d->pinchLastAngle = angle; + d->lastPoint1 = touchPoint1.scenePos(); + d->lastPoint2 = touchPoint2.scenePos(); + emit pinchUpdated(&pe); + if (d->pinch && d->pinch->target()) { + qreal s = d->pinchStartScale * scale; + s = qMin(qMax(pinch()->minimumScale(),s), pinch()->maximumScale()); + pinch()->target()->setScale(s); + QPointF pos = sceneCenter - d->sceneStartCenter + d->pinchStartPos; + if (pinch()->axis() & QDeclarativePinch::XAxis) { + qreal x = pos.x(); + if (x < pinch()->xmin()) + x = pinch()->xmin(); + else if (x > pinch()->xmax()) + x = pinch()->xmax(); + pinch()->target()->setX(x); + } + if (pinch()->axis() & QDeclarativePinch::YAxis) { + qreal y = pos.y(); + if (y < pinch()->ymin()) + y = pinch()->ymin(); + else if (y > pinch()->ymax()) + y = pinch()->ymax(); + pinch()->target()->setY(y); + } + if (d->pinchStartRotation >= pinch()->minimumRotation() + && d->pinchStartRotation <= pinch()->maximumRotation()) { + qreal r = d->pinchRotation + d->pinchStartRotation; + r = qMin(qMax(pinch()->minimumRotation(),r), pinch()->maximumRotation()); + pinch()->target()->setRotation(r); + } + } + } + } +} + +void QDeclarativePinchArea::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarativePinchArea); + d->stealMouse = false; + if (!d->absorb) + QDeclarativeItem::mousePressEvent(event); + else { + setKeepMouseGrab(false); + event->setAccepted(true); + } +} + +void QDeclarativePinchArea::mouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarativePinchArea); + if (!d->absorb) { + QDeclarativeItem::mouseMoveEvent(event); + return; + } +} + +void QDeclarativePinchArea::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarativePinchArea); + d->stealMouse = false; + if (!d->absorb) { + QDeclarativeItem::mouseReleaseEvent(event); + } else { + QGraphicsScene *s = scene(); + if (s && s->mouseGrabberItem() == this) + ungrabMouse(); + setKeepMouseGrab(false); + } +} + +bool QDeclarativePinchArea::sceneEvent(QEvent *event) +{ + bool rv = QDeclarativeItem::sceneEvent(event); + if (event->type() == QEvent::UngrabMouse) { + setKeepMouseGrab(false); + } + return rv; +} + +bool QDeclarativePinchArea::sendMouseEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarativePinchArea); + QGraphicsSceneMouseEvent mouseEvent(event->type()); + QRectF myRect = mapToScene(QRectF(0, 0, width(), height())).boundingRect(); + + QGraphicsScene *s = scene(); + QDeclarativeItem *grabber = s ? qobject_cast(s->mouseGrabberItem()) : 0; + bool stealThisEvent = d->stealMouse; + if ((stealThisEvent || myRect.contains(event->scenePos().toPoint())) && (!grabber || !grabber->keepMouseGrab())) { + mouseEvent.setAccepted(false); + for (int i = 0x1; i <= 0x10; i <<= 1) { + if (event->buttons() & i) { + Qt::MouseButton button = Qt::MouseButton(i); + mouseEvent.setButtonDownPos(button, mapFromScene(event->buttonDownPos(button))); + } + } + mouseEvent.setScenePos(event->scenePos()); + mouseEvent.setLastScenePos(event->lastScenePos()); + mouseEvent.setPos(mapFromScene(event->scenePos())); + mouseEvent.setLastPos(mapFromScene(event->lastScenePos())); + + switch(mouseEvent.type()) { + case QEvent::GraphicsSceneMouseMove: + mouseMoveEvent(&mouseEvent); + break; + case QEvent::GraphicsSceneMousePress: + mousePressEvent(&mouseEvent); + break; + case QEvent::GraphicsSceneMouseRelease: + mouseReleaseEvent(&mouseEvent); + break; + default: + break; + } + grabber = qobject_cast(s->mouseGrabberItem()); + if (grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) + grabMouse(); + + return stealThisEvent; + } + if (mouseEvent.type() == QEvent::GraphicsSceneMouseRelease) { + d->stealMouse = false; + if (s && s->mouseGrabberItem() == this) + ungrabMouse(); + setKeepMouseGrab(false); + } + return false; +} + +bool QDeclarativePinchArea::sceneEventFilter(QGraphicsItem *i, QEvent *e) +{ + Q_D(QDeclarativePinchArea); + if (!d->absorb || !isVisible()) + return QDeclarativeItem::sceneEventFilter(i, e); + switch (e->type()) { + case QEvent::GraphicsSceneMousePress: + case QEvent::GraphicsSceneMouseMove: + case QEvent::GraphicsSceneMouseRelease: + return sendMouseEvent(static_cast(e)); + break; + case QEvent::TouchBegin: + case QEvent::TouchUpdate: { + QTouchEvent *touch = static_cast(e); + d->touchPoints.clear(); + for (int i = 0; i < touch->touchPoints().count(); ++i) + if (!(touch->touchPoints().at(i).state() & Qt::TouchPointReleased)) + d->touchPoints << touch->touchPoints().at(i); + updatePinch(); + } + return d->inPinch; + case QEvent::TouchEnd: + d->touchPoints.clear(); + updatePinch(); + break; + default: + break; + } + + return QDeclarativeItem::sceneEventFilter(i, e); +} + +void QDeclarativePinchArea::geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry) +{ + QDeclarativeItem::geometryChanged(newGeometry, oldGeometry); +} + +QVariant QDeclarativePinchArea::itemChange(GraphicsItemChange change, + const QVariant &value) +{ + return QDeclarativeItem::itemChange(change, value); +} + +QDeclarativePinch *QDeclarativePinchArea::pinch() +{ + Q_D(QDeclarativePinchArea); + if (!d->pinch) + d->pinch = new QDeclarativePinch; + return d->pinch; +} + + +QT_END_NAMESPACE diff --git a/src/declarative/graphicsitems/qdeclarativepincharea_p.h b/src/declarative/graphicsitems/qdeclarativepincharea_p.h new file mode 100644 index 00000000..07c2d1c9 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativepincharea_p.h @@ -0,0 +1,313 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPINCHAREA_H +#define QDECLARATIVEPINCHAREA_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class Q_AUTOTEST_EXPORT QDeclarativePinch : public QObject +{ + Q_OBJECT + + Q_ENUMS(Axis) + Q_PROPERTY(QGraphicsObject *target READ target WRITE setTarget RESET resetTarget) + Q_PROPERTY(qreal minimumScale READ minimumScale WRITE setMinimumScale NOTIFY minimumScaleChanged) + Q_PROPERTY(qreal maximumScale READ maximumScale WRITE setMaximumScale NOTIFY maximumScaleChanged) + Q_PROPERTY(qreal minimumRotation READ minimumRotation WRITE setMinimumRotation NOTIFY minimumRotationChanged) + Q_PROPERTY(qreal maximumRotation READ maximumRotation WRITE setMaximumRotation NOTIFY maximumRotationChanged) + Q_PROPERTY(Axis dragAxis READ axis WRITE setAxis NOTIFY dragAxisChanged) + Q_PROPERTY(qreal minimumX READ xmin WRITE setXmin NOTIFY minimumXChanged) + Q_PROPERTY(qreal maximumX READ xmax WRITE setXmax NOTIFY maximumXChanged) + Q_PROPERTY(qreal minimumY READ ymin WRITE setYmin NOTIFY minimumYChanged) + Q_PROPERTY(qreal maximumY READ ymax WRITE setYmax NOTIFY maximumYChanged) + Q_PROPERTY(bool active READ active NOTIFY activeChanged) + +public: + QDeclarativePinch(); + + QGraphicsObject *target() const { return m_target; } + void setTarget(QGraphicsObject *target) { + if (target == m_target) + return; + m_target = target; + emit targetChanged(); + } + void resetTarget() { + if (!m_target) + return; + m_target = 0; + emit targetChanged(); + } + + qreal minimumScale() const { return m_minScale; } + void setMinimumScale(qreal s) { + if (s == m_minScale) + return; + m_minScale = s; + emit minimumScaleChanged(); + } + qreal maximumScale() const { return m_maxScale; } + void setMaximumScale(qreal s) { + if (s == m_maxScale) + return; + m_maxScale = s; + emit maximumScaleChanged(); + } + + qreal minimumRotation() const { return m_minRotation; } + void setMinimumRotation(qreal r) { + if (r == m_minRotation) + return; + m_minRotation = r; + emit minimumRotationChanged(); + } + qreal maximumRotation() const { return m_maxRotation; } + void setMaximumRotation(qreal r) { + if (r == m_maxRotation) + return; + m_maxRotation = r; + emit maximumRotationChanged(); + } + + enum Axis { NoDrag=0x00, XAxis=0x01, YAxis=0x02, XandYAxis=0x03 }; + Axis axis() const { return m_axis; } + void setAxis(Axis a) { + if (a == m_axis) + return; + m_axis = a; + emit dragAxisChanged(); + } + + qreal xmin() const { return m_xmin; } + void setXmin(qreal x) { + if (x == m_xmin) + return; + m_xmin = x; + emit minimumXChanged(); + } + qreal xmax() const { return m_xmax; } + void setXmax(qreal x) { + if (x == m_xmax) + return; + m_xmax = x; + emit maximumXChanged(); + } + qreal ymin() const { return m_ymin; } + void setYmin(qreal y) { + if (y == m_ymin) + return; + m_ymin = y; + emit minimumYChanged(); + } + qreal ymax() const { return m_ymax; } + void setYmax(qreal y) { + if (y == m_ymax) + return; + m_ymax = y; + emit maximumYChanged(); + } + + bool active() const { return m_active; } + void setActive(bool a) { + if (a == m_active) + return; + m_active = a; + emit activeChanged(); + } + +signals: + void targetChanged(); + void minimumScaleChanged(); + void maximumScaleChanged(); + void minimumRotationChanged(); + void maximumRotationChanged(); + void dragAxisChanged(); + void minimumXChanged(); + void maximumXChanged(); + void minimumYChanged(); + void maximumYChanged(); + void activeChanged(); + +private: + QGraphicsObject *m_target; + qreal m_minScale; + qreal m_maxScale; + qreal m_minRotation; + qreal m_maxRotation; + Axis m_axis; + qreal m_xmin; + qreal m_xmax; + qreal m_ymin; + qreal m_ymax; + bool m_active; +}; + +class Q_AUTOTEST_EXPORT QDeclarativePinchEvent : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QPointF center READ center) + Q_PROPERTY(QPointF startCenter READ startCenter) + Q_PROPERTY(QPointF previousCenter READ previousCenter) + Q_PROPERTY(qreal scale READ scale) + Q_PROPERTY(qreal previousScale READ previousScale) + Q_PROPERTY(qreal angle READ angle) + Q_PROPERTY(qreal previousAngle READ previousAngle) + Q_PROPERTY(qreal rotation READ rotation) + Q_PROPERTY(QPointF point1 READ point1) + Q_PROPERTY(QPointF startPoint1 READ startPoint1) + Q_PROPERTY(QPointF point2 READ point2) + Q_PROPERTY(QPointF startPoint2 READ startPoint2) + Q_PROPERTY(int pointCount READ pointCount) + Q_PROPERTY(bool accepted READ accepted WRITE setAccepted) + +public: + QDeclarativePinchEvent(QPointF c, qreal s, qreal a, qreal r) + : QObject(), m_center(c), m_scale(s), m_angle(a), m_rotation(r) + , m_pointCount(0), m_accepted(true) {} + + QPointF center() const { return m_center; } + QPointF startCenter() const { return m_startCenter; } + void setStartCenter(QPointF c) { m_startCenter = c; } + QPointF previousCenter() const { return m_lastCenter; } + void setPreviousCenter(QPointF c) { m_lastCenter = c; } + qreal scale() const { return m_scale; } + qreal previousScale() const { return m_lastScale; } + void setPreviousScale(qreal s) { m_lastScale = s; } + qreal angle() const { return m_angle; } + qreal previousAngle() const { return m_lastAngle; } + void setPreviousAngle(qreal a) { m_lastAngle = a; } + qreal rotation() const { return m_rotation; } + QPointF point1() const { return m_point1; } + void setPoint1(QPointF p) { m_point1 = p; } + QPointF startPoint1() const { return m_startPoint1; } + void setStartPoint1(QPointF p) { m_startPoint1 = p; } + QPointF point2() const { return m_point2; } + void setPoint2(QPointF p) { m_point2 = p; } + QPointF startPoint2() const { return m_startPoint2; } + void setStartPoint2(QPointF p) { m_startPoint2 = p; } + int pointCount() const { return m_pointCount; } + void setPointCount(int count) { m_pointCount = count; } + + bool accepted() const { return m_accepted; } + void setAccepted(bool a) { m_accepted = a; } + +private: + QPointF m_center; + QPointF m_startCenter; + QPointF m_lastCenter; + qreal m_scale; + qreal m_lastScale; + qreal m_angle; + qreal m_lastAngle; + qreal m_rotation; + QPointF m_point1; + QPointF m_point2; + QPointF m_startPoint1; + QPointF m_startPoint2; + int m_pointCount; + bool m_accepted; +}; + + +class QDeclarativeMouseEvent; +class QDeclarativePinchAreaPrivate; +class Q_AUTOTEST_EXPORT QDeclarativePinchArea : public QDeclarativeItem +{ + Q_OBJECT + + Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged) + Q_PROPERTY(QDeclarativePinch *pinch READ pinch CONSTANT) + +public: + QDeclarativePinchArea(QDeclarativeItem *parent=0); + ~QDeclarativePinchArea(); + + bool isEnabled() const; + void setEnabled(bool); + + QDeclarativePinch *pinch(); + +Q_SIGNALS: + void enabledChanged(); + void pinchStarted(QDeclarativePinchEvent *pinch); + void pinchUpdated(QDeclarativePinchEvent *pinch); + void pinchFinished(QDeclarativePinchEvent *pinch); + +protected: + void mousePressEvent(QGraphicsSceneMouseEvent *event); + void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); + void mouseMoveEvent(QGraphicsSceneMouseEvent *event); + bool sceneEvent(QEvent *); + bool sendMouseEvent(QGraphicsSceneMouseEvent *event); + bool sceneEventFilter(QGraphicsItem *i, QEvent *e); + bool event(QEvent *); + + virtual void geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry); + virtual QVariant itemChange(GraphicsItemChange change, const QVariant& value); + +private: + void updatePinch(); + void handlePress(); + void handleRelease(); + +private: + Q_DISABLE_COPY(QDeclarativePinchArea) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarativePinchArea) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativePinch) +QML_DECLARE_TYPE(QDeclarativePinchEvent) +QML_DECLARE_TYPE(QDeclarativePinchArea) + +QT_END_HEADER + +#endif // QDECLARATIVEPINCHAREA_H diff --git a/src/declarative/graphicsitems/qdeclarativepincharea_p_p.h b/src/declarative/graphicsitems/qdeclarativepincharea_p_p.h new file mode 100644 index 00000000..b6993e98 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativepincharea_p_p.h @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPINCHAREA_P_H +#define QDECLARATIVEPINCHAREA_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 +#include +#include +#include +#include "qdeclarativeitem_p.h" + +QT_BEGIN_NAMESPACE + +class QDeclarativePinch; +class QDeclarativePinchAreaPrivate : public QDeclarativeItemPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativePinchArea) +public: + QDeclarativePinchAreaPrivate() + : absorb(true), stealMouse(false), inPinch(false) + , pinchRejected(false), pinchActivated(false) + , pinch(0), pinchStartDist(0), pinchStartScale(1.0) + , pinchLastScale(1.0), pinchStartRotation(0.0), pinchStartAngle(0.0) + , pinchLastAngle(0.0), pinchRotation(0.0) + { + } + + ~QDeclarativePinchAreaPrivate(); + + void init() + { + Q_Q(QDeclarativePinchArea); + q->setAcceptedMouseButtons(Qt::LeftButton); + q->setAcceptTouchEvents(true); + q->setFiltersChildEvents(true); + } + + bool absorb : 1; + bool stealMouse : 1; + bool inPinch : 1; + bool pinchRejected : 1; + bool pinchActivated : 1; + QDeclarativePinch *pinch; + QPointF sceneStartPoint1; + QPointF sceneStartPoint2; + QPointF lastPoint1; + QPointF lastPoint2; + qreal pinchStartDist; + qreal pinchStartScale; + qreal pinchLastScale; + qreal pinchStartRotation; + qreal pinchStartAngle; + qreal pinchLastAngle; + qreal pinchRotation; + QPointF sceneStartCenter; + QPointF pinchStartCenter; + QPointF sceneLastCenter; + QPointF pinchStartPos; + QList touchPoints; + int id1; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVEPINCHAREA_P_H diff --git a/src/declarative/graphicsitems/qdeclarativepositioners.cpp b/src/declarative/graphicsitems/qdeclarativepositioners.cpp new file mode 100644 index 00000000..cc28fb10 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativepositioners.cpp @@ -0,0 +1,1371 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativepositioners_p.h" +#include "private/qdeclarativepositioners_p_p.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +static const QDeclarativeItemPrivate::ChangeTypes watchedChanges + = QDeclarativeItemPrivate::Geometry + | QDeclarativeItemPrivate::SiblingOrder + | QDeclarativeItemPrivate::Visibility + | QDeclarativeItemPrivate::Opacity + | QDeclarativeItemPrivate::Destroyed; + +void QDeclarativeBasePositionerPrivate::watchChanges(QGraphicsObject *other) +{ + if (QGraphicsItemPrivate::get(other)->isDeclarativeItem) { + QDeclarativeItemPrivate *otherPrivate = static_cast(QGraphicsItemPrivate::get(other)); + otherPrivate->addItemChangeListener(this, watchedChanges); + } else { + Q_Q(QDeclarativeBasePositioner); + QObject::connect(other, SIGNAL(widthChanged()), q, SLOT(graphicsWidgetGeometryChanged())); + QObject::connect(other, SIGNAL(heightChanged()), q, SLOT(graphicsWidgetGeometryChanged())); + QObject::connect(other, SIGNAL(opacityChanged()), q, SLOT(graphicsWidgetGeometryChanged())); + QObject::connect(other, SIGNAL(visibleChanged()), q, SLOT(graphicsWidgetGeometryChanged())); + } +} + +void QDeclarativeBasePositionerPrivate::unwatchChanges(QGraphicsObject* other) +{ + if (QGraphicsItemPrivate::get(other)->isDeclarativeItem) { + QDeclarativeItemPrivate *otherPrivate = static_cast(QGraphicsItemPrivate::get(other)); + otherPrivate->removeItemChangeListener(this, watchedChanges); + } else { + Q_Q(QDeclarativeBasePositioner); + QObject::disconnect(other, SIGNAL(widthChanged()), q, SLOT(graphicsWidgetGeometryChanged())); + QObject::disconnect(other, SIGNAL(heightChanged()), q, SLOT(graphicsWidgetGeometryChanged())); + QObject::disconnect(other, SIGNAL(opacityChanged()), q, SLOT(graphicsWidgetGeometryChanged())); + QObject::disconnect(other, SIGNAL(visibleChanged()), q, SLOT(graphicsWidgetGeometryChanged())); + } +} + +void QDeclarativeBasePositioner::graphicsWidgetGeometryChanged() +{ + prePositioning(); +} + +/*! + \internal + \class QDeclarativeBasePositioner + \brief The QDeclarativeBasePositioner class provides a base for QDeclarativeGraphics layouts. + + To create a QDeclarativeGraphics Positioner, simply subclass QDeclarativeBasePositioner and implement + doLayout(), which is automatically called when the layout might need + updating. In doLayout() use the setX and setY functions from QDeclarativeBasePositioner, and the + base class will apply the positions along with the appropriate transitions. The items to + position are provided in order as the protected member positionedItems. + + You also need to set a PositionerType, to declare whether you are positioning the x, y or both + for the child items. Depending on the chosen type, only x or y changes will be applied. + + Note that the subclass is responsible for adding the spacing in between items. +*/ +QDeclarativeBasePositioner::QDeclarativeBasePositioner(PositionerType at, QDeclarativeItem *parent) + : QDeclarativeImplicitSizeItem(*(new QDeclarativeBasePositionerPrivate), parent) +{ + Q_D(QDeclarativeBasePositioner); + d->init(at); +} + +QDeclarativeBasePositioner::QDeclarativeBasePositioner(QDeclarativeBasePositionerPrivate &dd, PositionerType at, QDeclarativeItem *parent) + : QDeclarativeImplicitSizeItem(dd, parent) +{ + Q_D(QDeclarativeBasePositioner); + d->init(at); +} + +QDeclarativeBasePositioner::~QDeclarativeBasePositioner() +{ + Q_D(QDeclarativeBasePositioner); + for (int i = 0; i < positionedItems.count(); ++i) + d->unwatchChanges(positionedItems.at(i).item); + positionedItems.clear(); +} + +int QDeclarativeBasePositioner::spacing() const +{ + Q_D(const QDeclarativeBasePositioner); + return d->spacing; +} + +void QDeclarativeBasePositioner::setSpacing(int s) +{ + Q_D(QDeclarativeBasePositioner); + if (s==d->spacing) + return; + d->spacing = s; + prePositioning(); + emit spacingChanged(); +} + +QDeclarativeTransition *QDeclarativeBasePositioner::move() const +{ + Q_D(const QDeclarativeBasePositioner); + return d->moveTransition; +} + +void QDeclarativeBasePositioner::setMove(QDeclarativeTransition *mt) +{ + Q_D(QDeclarativeBasePositioner); + if (mt == d->moveTransition) + return; + d->moveTransition = mt; + emit moveChanged(); +} + +QDeclarativeTransition *QDeclarativeBasePositioner::add() const +{ + Q_D(const QDeclarativeBasePositioner); + return d->addTransition; +} + +void QDeclarativeBasePositioner::setAdd(QDeclarativeTransition *add) +{ + Q_D(QDeclarativeBasePositioner); + if (add == d->addTransition) + return; + + d->addTransition = add; + emit addChanged(); +} + +void QDeclarativeBasePositioner::componentComplete() +{ + Q_D(QDeclarativeBasePositioner); + QDeclarativeItem::componentComplete(); + positionedItems.reserve(d->QGraphicsItemPrivate::children.count()); + prePositioning(); + reportConflictingAnchors(); +} + +QVariant QDeclarativeBasePositioner::itemChange(GraphicsItemChange change, + const QVariant &value) +{ + Q_D(QDeclarativeBasePositioner); + if (change == ItemChildAddedChange){ + QGraphicsItem* item = value.value(); + QGraphicsObject* child = 0; + if(item) + child = item->toGraphicsObject(); + if (child) + prePositioning(); + } else if (change == ItemChildRemovedChange) { + QGraphicsItem* item = value.value(); + QGraphicsObject* child = 0; + if(item) + child = item->toGraphicsObject(); + if (child) { + QDeclarativeBasePositioner::PositionedItem posItem(child); + int idx = positionedItems.find(posItem); + if (idx >= 0) { + d->unwatchChanges(child); + positionedItems.remove(idx); + } + prePositioning(); + } + } + return QDeclarativeItem::itemChange(change, value); +} + +void QDeclarativeBasePositioner::prePositioning() +{ + Q_D(QDeclarativeBasePositioner); + if (!isComponentComplete()) + return; + + if (d->doingPositioning) + return; + + d->queuedPositioning = false; + d->doingPositioning = true; + //Need to order children by creation order modified by stacking order + QList children = d->QGraphicsItemPrivate::children; + qSort(children.begin(), children.end(), d->insertionOrder); + + QPODVector oldItems; + positionedItems.copyAndClear(oldItems); + for (int ii = 0; ii < children.count(); ++ii) { + QGraphicsObject *child = children.at(ii)->toGraphicsObject(); + if (!child) + continue; + QGraphicsItemPrivate *childPrivate = static_cast(QGraphicsItemPrivate::get(child)); + PositionedItem *item = 0; + PositionedItem posItem(child); + int wIdx = oldItems.find(posItem); + if (wIdx < 0) { + d->watchChanges(child); + positionedItems.append(posItem); + item = &positionedItems[positionedItems.count()-1]; + item->isNew = true; + if (child->opacity() <= 0.0 || childPrivate->explicitlyHidden || !childPrivate->width() || !childPrivate->height()) + item->isVisible = false; + } else { + item = &oldItems[wIdx]; + // Items are only omitted from positioning if they are explicitly hidden + // i.e. their positioning is not affected if an ancestor is hidden. + if (child->opacity() <= 0.0 || childPrivate->explicitlyHidden || !childPrivate->width() || !childPrivate->height()) { + item->isVisible = false; + } else if (!item->isVisible) { + item->isVisible = true; + item->isNew = true; + } else { + item->isNew = false; + } + positionedItems.append(*item); + } + } + QSizeF contentSize; + doPositioning(&contentSize); + if(d->addTransition || d->moveTransition) + finishApplyTransitions(); + d->doingPositioning = false; + //Set implicit size to the size of its children + setImplicitHeight(contentSize.height()); + setImplicitWidth(contentSize.width()); +} + +void QDeclarativeBasePositioner::positionX(int x, const PositionedItem &target) +{ + Q_D(QDeclarativeBasePositioner); + if(d->type == Horizontal || d->type == Both){ + if (target.isNew) { + if (!d->addTransition) + target.item->setX(x); + else + d->addActions << QDeclarativeAction(target.item, QLatin1String("x"), QVariant(x)); + } else if (x != target.item->x()) { + if (!d->moveTransition) + target.item->setX(x); + else + d->moveActions << QDeclarativeAction(target.item, QLatin1String("x"), QVariant(x)); + } + } +} + +void QDeclarativeBasePositioner::positionY(int y, const PositionedItem &target) +{ + Q_D(QDeclarativeBasePositioner); + if(d->type == Vertical || d->type == Both){ + if (target.isNew) { + if (!d->addTransition) + target.item->setY(y); + else + d->addActions << QDeclarativeAction(target.item, QLatin1String("y"), QVariant(y)); + } else if (y != target.item->y()) { + if (!d->moveTransition) + target.item->setY(y); + else + d->moveActions << QDeclarativeAction(target.item, QLatin1String("y"), QVariant(y)); + } + } +} + +void QDeclarativeBasePositioner::finishApplyTransitions() +{ + Q_D(QDeclarativeBasePositioner); + // Note that if a transition is not set the transition manager will + // apply the changes directly, in the case add/move aren't set + d->addTransitionManager.transition(d->addActions, d->addTransition); + d->moveTransitionManager.transition(d->moveActions, d->moveTransition); + d->addActions.clear(); + d->moveActions.clear(); +} + +/*! + \qmlclass Column QDeclarativeColumn + \ingroup qml-positioning-elements + \since 4.7 + \brief The Column item arranges its children vertically. + \inherits Item + + The Column item positions its child items so that they are vertically + aligned and not overlapping. + + Spacing between items can be added using the \l spacing property. + Transitions can be used for cases where items managed by a Column are + added or moved. These are stored in the \l add and \l move properties + respectively. + + See \l{Using QML Positioner and Repeater Items} for more details about this item and other + related items. + + \section1 Example Usage + + The following example positions differently shaped rectangles using a Column + item. + + \image verticalpositioner_example.png + + \snippet doc/src/snippets/declarative/column/vertical-positioner.qml document + + \section1 Using Transitions + + Transitions can be used to animate items that are added to, moved within, + or removed from a Column item. The \l add and \l move properties can be set to + the transitions that will be applied when items are added to, removed from, + or re-positioned within a Column item. + + The use of transitions with positioners is described in more detail in the + \l{Using QML Positioner and Repeater Items#Using Transitions}{Using QML + Positioner and Repeater Items} document. + + \image verticalpositioner_transition.gif + + \qml + Column { + spacing: 2 + add: Transition { + // Define an animation for adding a new item... + } + move: Transition { + // Define an animation for moving items within the column... + } + // ... + } + \endqml + + \section1 Limitations + + Note that the positioner assumes that the x and y positions of its children + will not change. If you manually change the x or y properties in script, bind + the x or y properties, use anchors on a child of a positioner, or have the + height of a child depend on the position of a child, then the + positioner may exhibit strange behavior. If you need to perform any of these + actions, consider positioning the items without the use of a Column. + + Items with a width or height of 0 will not be positioned. + + \sa Row, Grid, Flow, {declarative/positioners}{Positioners example} +*/ +/*! + \qmlproperty Transition Column::add + + This property holds the transition to be applied when adding an + item to the positioner. The transition will only be applied to the + added item(s). Positioner transitions will only affect the + position (x, y) of items. + + For a positioner, adding an item can mean that either the object + has been created or reparented, and thus is now a child or the + positioner, or that the object has had its opacity increased from + zero, and thus is now visible. + + \sa move +*/ +/*! + \qmlproperty Transition Column::move + + This property holds the transition to apply when moving an item + within the positioner. Positioner transitions will only affect + the position (x, y) of items. + + This transition can be performed when other items are added or removed + from the positioner, or when items resize themselves. + + \image positioner-move.gif + + \qml + Column { + move: Transition { + NumberAnimation { + properties: "y" + easing.type: Easing.OutBounce + } + } + } + \endqml + + \sa add, {declarative/positioners}{Positioners example} +*/ +/*! + \qmlproperty int Column::spacing + + The spacing is the amount in pixels left empty between adjacent + items. The default spacing is 0. + + \sa Grid::spacing +*/ +QDeclarativeColumn::QDeclarativeColumn(QDeclarativeItem *parent) +: QDeclarativeBasePositioner(Vertical, parent) +{ +} + +void QDeclarativeColumn::doPositioning(QSizeF *contentSize) +{ + int voffset = 0; + + for (int ii = 0; ii < positionedItems.count(); ++ii) { + const PositionedItem &child = positionedItems.at(ii); + if (!child.item || !child.isVisible) + continue; + + if(child.item->y() != voffset) + positionY(voffset, child); + + contentSize->setWidth(qMax(contentSize->width(), QGraphicsItemPrivate::get(child.item)->width())); + + voffset += QGraphicsItemPrivate::get(child.item)->height(); + voffset += spacing(); + } + + contentSize->setHeight(voffset - spacing()); +} + +void QDeclarativeColumn::reportConflictingAnchors() +{ + QDeclarativeBasePositionerPrivate *d = static_cast(QDeclarativeBasePositionerPrivate::get(this)); + for (int ii = 0; ii < positionedItems.count(); ++ii) { + const PositionedItem &child = positionedItems.at(ii); + if (child.item && QGraphicsItemPrivate::get(child.item)->isDeclarativeItem) { + QDeclarativeAnchors *anchors = QDeclarativeItemPrivate::get(static_cast(child.item))->_anchors; + if (anchors) { + QDeclarativeAnchors::Anchors usedAnchors = anchors->usedAnchors(); + if (usedAnchors & QDeclarativeAnchors::TopAnchor || + usedAnchors & QDeclarativeAnchors::BottomAnchor || + usedAnchors & QDeclarativeAnchors::VCenterAnchor || + anchors->fill() || anchors->centerIn()) { + d->anchorConflict = true; + break; + } + } + } + } + if (d->anchorConflict) { + qmlInfo(this) << "Cannot specify top, bottom, verticalCenter, fill or centerIn anchors for items inside Column"; + } +} + +/*! + \qmlclass Row QDeclarativeRow + \ingroup qml-positioning-elements + \since 4.7 + \brief The Row item arranges its children horizontally. + \inherits Item + + The Row item positions its child items so that they are horizontally + aligned and not overlapping. + + Use \l spacing to set the spacing between items in a Row, and use the + \l add and \l move properties to set the transitions that should be applied + when items are added to, removed from, or re-positioned within the Row. + + See \l{Using QML Positioner and Repeater Items} for more details about this item and other + related items. + + \section1 Example Usage + + The following example lays out differently shaped rectangles using a Row. + + \image horizontalpositioner_example.png + + \snippet doc/src/snippets/declarative/row/row.qml document + + \section1 Using Transitions + + Transitions can be used to animate items that are added to, moved within, + or removed from a Grid item. The \l add and \l move properties can be set to + the transitions that will be applied when items are added to, removed from, + or re-positioned within a Row item. + + \section1 Limitations + + Note that the positioner assumes that the x and y positions of its children + will not change. If you manually change the x or y properties in script, bind + the x or y properties, use anchors on a child of a positioner, or have the + width of a child depend on the position of a child, then the + positioner may exhibit strange behaviour. If you need to perform any of these + actions, consider positioning the items without the use of a Row. + + Items with a width or height of 0 will not be positioned. + + \sa Column, Grid, Flow, {declarative/positioners}{Positioners example} +*/ +/*! + \qmlproperty Transition Row::add + + This property holds the transition to be applied when adding an + item to the positioner. The transition will only be applied to the + added item(s). Positioner transitions will only affect the + position (x, y) of items. + + For a positioner, adding an item can mean that either the object + has been created or reparented, and thus is now a child or the + positioner, or that the object has had its opacity increased from + zero, and thus is now visible. + + \sa move +*/ +/*! + \qmlproperty Transition Row::move + + This property holds the transition to be applied when moving an + item within the positioner. Positioner transitions will only affect + the position (x, y) of items. + + This transition can be performed when other items are added or removed + from the positioner, or when items resize themselves. + + \qml + Row { + id: positioner + move: Transition { + NumberAnimation { + properties: "x" + ease: "easeOutBounce" + } + } + } + \endqml + + \sa add, {declarative/positioners}{Positioners example} +*/ +/*! + \qmlproperty int Row::spacing + + The spacing is the amount in pixels left empty between adjacent + items. The default spacing is 0. + + \sa Grid::spacing +*/ +QDeclarativeRow::QDeclarativeRow(QDeclarativeItem *parent) +: QDeclarativeBasePositioner(Horizontal, parent) +{ +} + +/*! + \qmlproperty enumeration Row::layoutDirection + \since QtQuick 1.1 + + This property holds the layoutDirection of the row. + + Possible values: + + \list + \o Qt.LeftToRight (default) - Items are laid out from left to right. If the width of the row is explicitly set, + the left anchor remains to the left of the row. + \o Qt.RightToLeft - Items are laid out from right to left. If the width of the row is explicitly set, + the right anchor remains to the right of the row. + \endlist + + When using the attached property \l {LayoutMirroring::enabled} for locale layouts, + the visual layout direction of the row positioner will be mirrored. However, the + property \c layoutDirection will remain unchanged. You can use the property + \l {LayoutMirroring::enabled} to determine whether the direction has been mirrored. + + \sa Grid::layoutDirection, Flow::layoutDirection, {declarative/righttoleft/layoutdirection}{Layout directions example}, {LayoutMirroring}{LayoutMirroring} +*/ +Qt::LayoutDirection QDeclarativeRow::layoutDirection() const +{ + return QDeclarativeBasePositionerPrivate::getLayoutDirection(this); +} + +void QDeclarativeRow::setLayoutDirection(Qt::LayoutDirection layoutDirection) +{ + QDeclarativeBasePositionerPrivate *d = static_cast(QDeclarativeBasePositionerPrivate::get(this)); + if (d->layoutDirection != layoutDirection) { + d->layoutDirection = layoutDirection; + // For RTL layout the positioning changes when the width changes. + if (d->layoutDirection == Qt::RightToLeft) + d->addItemChangeListener(d, QDeclarativeItemPrivate::Geometry); + else + d->removeItemChangeListener(d, QDeclarativeItemPrivate::Geometry); + prePositioning(); + emit layoutDirectionChanged(); + } +} + +Qt::LayoutDirection QDeclarativeRow::effectiveLayoutDirection() const +{ + return QDeclarativeBasePositionerPrivate::getEffectiveLayoutDirection(this); +} + +void QDeclarativeRow::doPositioning(QSizeF *contentSize) +{ + QDeclarativeBasePositionerPrivate *d = static_cast(QDeclarativeBasePositionerPrivate::get(this)); + int hoffset = 0; + + QList hoffsets; + for (int ii = 0; ii < positionedItems.count(); ++ii) { + const PositionedItem &child = positionedItems.at(ii); + if (!child.item || !child.isVisible) + continue; + + if(d->isLeftToRight()){ + if(child.item->x() != hoffset) + positionX(hoffset, child); + }else{ + hoffsets << hoffset; + } + + contentSize->setHeight(qMax(contentSize->height(), QGraphicsItemPrivate::get(child.item)->height())); + + hoffset += QGraphicsItemPrivate::get(child.item)->width(); + hoffset += spacing(); + } + + contentSize->setWidth(hoffset - spacing()); + + if(d->isLeftToRight()) + return; + + //Right to Left layout + int end = 0; + if(!widthValid()) + end = contentSize->width(); + else + end = width(); + + int acc = 0; + for (int ii = 0; ii < positionedItems.count(); ++ii) { + const PositionedItem &child = positionedItems.at(ii); + if (!child.item || !child.isVisible) + continue; + hoffset = end - hoffsets[acc++] - QGraphicsItemPrivate::get(child.item)->width(); + if(child.item->x() != hoffset) + positionX(hoffset, child); + } +} + +void QDeclarativeRow::reportConflictingAnchors() +{ + QDeclarativeBasePositionerPrivate *d = static_cast(QDeclarativeBasePositionerPrivate::get(this)); + for (int ii = 0; ii < positionedItems.count(); ++ii) { + const PositionedItem &child = positionedItems.at(ii); + if (child.item && QGraphicsItemPrivate::get(child.item)->isDeclarativeItem) { + QDeclarativeAnchors *anchors = QDeclarativeItemPrivate::get(static_cast(child.item))->_anchors; + if (anchors) { + QDeclarativeAnchors::Anchors usedAnchors = anchors->usedAnchors(); + if (usedAnchors & QDeclarativeAnchors::LeftAnchor || + usedAnchors & QDeclarativeAnchors::RightAnchor || + usedAnchors & QDeclarativeAnchors::HCenterAnchor || + anchors->fill() || anchors->centerIn()) { + d->anchorConflict = true; + break; + } + } + } + } + if (d->anchorConflict) + qmlInfo(this) << "Cannot specify left, right, horizontalCenter, fill or centerIn anchors for items inside Row"; +} + +/*! + \qmlclass Grid QDeclarativeGrid + \ingroup qml-positioning-elements + \since 4.7 + \brief The Grid item positions its children in a grid. + \inherits Item + + The Grid item positions its child items so that they are + aligned in a grid and are not overlapping. + + The grid positioner calculates a grid of rectangular cells of sufficient + size to hold all items, placing the items in the cells, from left to right + and top to bottom. Each item is positioned in the top-left corner of its + cell with position (0, 0). + + A Grid defaults to four columns, and as many rows as are necessary to + fit all child items. The number of rows and columns can be constrained + by setting the \l rows and \l columns properties. + + Spacing can be added between child items by setting the \l spacing + property. The amount of spacing applied will be the same in the + horizontal and vertical directions. + + See \l{Using QML Positioner and Repeater Items} for more details about this item and other + related items. + + \section1 Example Usage + + The following example demonstrates this. + + \image gridLayout_example.png + + \snippet doc/src/snippets/declarative/grid/grid.qml document + + \section1 Using Transitions + + Transitions can be used to animate items that are added to, moved within, + or removed from a Grid item. The \l add and \l move properties can be set to + the transitions that will be applied when items are added to, removed from, + or re-positioned within a Grid item. + + \section1 Limitations + + Note that the positioner assumes that the x and y positions of its children + will not change. If you manually change the x or y properties in script, bind + the x or y properties, use anchors on a child of a positioner, or have the + width or height of a child depend on the position of a child, then the + positioner may exhibit strange behaviour. If you need to perform any of these + actions, consider positioning the items without the use of a Grid. + + Items with a width or height of 0 will not be positioned. + + \sa Flow, Row, Column, {declarative/positioners}{Positioners example} +*/ +/*! + \qmlproperty Transition Grid::add + + This property holds the transition to be applied when adding an + item to the positioner. The transition will only be applied to the + added item(s). Positioner transitions will only affect the + position (x, y) of items. + + For a positioner, adding an item can mean that either the object + has been created or reparented, and thus is now a child or the + positioner, or that the object has had its opacity increased from + zero, and thus is now visible. + + \sa move +*/ +/*! + \qmlproperty Transition Grid::move + + This property holds the transition to be applied when moving an + item within the positioner. Positioner transitions will only affect + the position (x, y) of items. + + This transition can be performed when other items are added or removed + from the positioner, or when items resize themselves. + + \qml + Grid { + move: Transition { + NumberAnimation { + properties: "x,y" + ease: "easeOutBounce" + } + } + } + \endqml + + \sa add, {declarative/positioners}{Positioners example} +*/ +/*! + \qmlproperty int Grid::spacing + + The spacing is the amount in pixels left empty between adjacent + items. The default spacing is 0. + + The below example places a Grid containing a red, a blue and a + green rectangle on a gray background. The area the grid positioner + occupies is colored white. The positioner on the left has the + no spacing (the default), and the positioner on the right has + a spacing of 6. + + \inlineimage qml-grid-no-spacing.png + \inlineimage qml-grid-spacing.png + + \sa rows, columns +*/ +QDeclarativeGrid::QDeclarativeGrid(QDeclarativeItem *parent) : + QDeclarativeBasePositioner(Both, parent), m_rows(-1), m_columns(-1), m_flow(LeftToRight) +{ +} + +/*! + \qmlproperty int Grid::columns + + This property holds the number of columns in the grid. The default + number of columns is 4. + + If the grid does not have enough items to fill the specified + number of columns, some columns will be of zero width. +*/ + +/*! + \qmlproperty int Grid::rows + This property holds the number of rows in the grid. + + If the grid does not have enough items to fill the specified + number of rows, some rows will be of zero width. +*/ + +void QDeclarativeGrid::setColumns(const int columns) +{ + if (columns == m_columns) + return; + m_columns = columns; + prePositioning(); + emit columnsChanged(); +} + +void QDeclarativeGrid::setRows(const int rows) +{ + if (rows == m_rows) + return; + m_rows = rows; + prePositioning(); + emit rowsChanged(); +} + +/*! + \qmlproperty enumeration Grid::flow + This property holds the flow of the layout. + + Possible values are: + + \list + \o Grid.LeftToRight (default) - Items are positioned next to + each other in the \l layoutDirection, then wrapped to the next line. + \o Grid.TopToBottom - Items are positioned next to each + other from top to bottom, then wrapped to the next column. + \endlist +*/ +QDeclarativeGrid::Flow QDeclarativeGrid::flow() const +{ + return m_flow; +} + +void QDeclarativeGrid::setFlow(Flow flow) +{ + if (m_flow != flow) { + m_flow = flow; + prePositioning(); + emit flowChanged(); + } +} + +/*! + \qmlproperty enumeration Grid::layoutDirection + \since QtQuick 1.1 + + This property holds the layout direction of the layout. + + Possible values are: + + \list + \o Qt.LeftToRight (default) - Items are positioned from the top to bottom, + and left to right. The flow direction is dependent on the + \l Grid::flow property. + \o Qt.RightToLeft - Items are positioned from the top to bottom, + and right to left. The flow direction is dependent on the + \l Grid::flow property. + \endlist + + When using the attached property \l {LayoutMirroring::enabled} for locale layouts, + the visual layout direction of the grid positioner will be mirrored. However, the + property \c layoutDirection will remain unchanged. You can use the property + \l {LayoutMirroring::enabled} to determine whether the direction has been mirrored. + + \sa Flow::layoutDirection, Row::layoutDirection, {declarative/righttoleft/layoutdirection}{Layout directions example}, {LayoutMirroring}{LayoutMirroring} +*/ +Qt::LayoutDirection QDeclarativeGrid::layoutDirection() const +{ + return QDeclarativeBasePositionerPrivate::getLayoutDirection(this); +} + +void QDeclarativeGrid::setLayoutDirection(Qt::LayoutDirection layoutDirection) +{ + QDeclarativeBasePositionerPrivate *d = static_cast(QDeclarativeBasePositionerPrivate::get(this)); + if (d->layoutDirection != layoutDirection) { + d->layoutDirection = layoutDirection; + // For RTL layout the positioning changes when the width changes. + if (d->layoutDirection == Qt::RightToLeft) + d->addItemChangeListener(d, QDeclarativeItemPrivate::Geometry); + else + d->removeItemChangeListener(d, QDeclarativeItemPrivate::Geometry); + prePositioning(); + emit layoutDirectionChanged();; + } +} + +Qt::LayoutDirection QDeclarativeGrid::effectiveLayoutDirection() const +{ + return QDeclarativeBasePositionerPrivate::getEffectiveLayoutDirection(this); +} + +void QDeclarativeGrid::doPositioning(QSizeF *contentSize) +{ + QDeclarativeBasePositionerPrivate *d = static_cast(QDeclarativeBasePositionerPrivate::get(this)); + int c = m_columns; + int r = m_rows; + //Is allocating the extra QPODVector too much overhead? + QPODVector visibleItems;//we aren't concerned with invisible items + visibleItems.reserve(positionedItems.count()); + for(int i=0; i maxColWidth; + QList maxRowHeight; + int childIndex =0; + if (m_flow == LeftToRight) { + for (int i=0; i < r; i++){ + for (int j=0; j < c; j++){ + if (j==0) + maxRowHeight << 0; + if (i==0) + maxColWidth << 0; + + if (childIndex == visibleItems.count()) + break; + + const PositionedItem &child = visibleItems.at(childIndex++); + QGraphicsItemPrivate *childPrivate = QGraphicsItemPrivate::get(child.item); + if (childPrivate->width() > maxColWidth[j]) + maxColWidth[j] = childPrivate->width(); + if (childPrivate->height() > maxRowHeight[i]) + maxRowHeight[i] = childPrivate->height(); + } + } + } else { + for (int j=0; j < c; j++){ + for (int i=0; i < r; i++){ + if (j==0) + maxRowHeight << 0; + if (i==0) + maxColWidth << 0; + + if (childIndex == visibleItems.count()) + break; + + const PositionedItem &child = visibleItems.at(childIndex++); + QGraphicsItemPrivate *childPrivate = QGraphicsItemPrivate::get(child.item); + if (childPrivate->width() > maxColWidth[j]) + maxColWidth[j] = childPrivate->width(); + if (childPrivate->height() > maxRowHeight[i]) + maxRowHeight[i] = childPrivate->height(); + } + } + } + + int widthSum = 0; + for(int j=0; j < maxColWidth.size(); j++){ + if(j) + widthSum += spacing(); + widthSum += maxColWidth[j]; + } + + int heightSum = 0; + for(int i=0; i < maxRowHeight.size(); i++){ + if(i) + heightSum += spacing(); + heightSum += maxRowHeight[i]; + } + + contentSize->setHeight(heightSum); + contentSize->setWidth(widthSum); + + int end = 0; + if(widthValid()) + end = width(); + else + end = widthSum; + + int xoffset=0; + if(!d->isLeftToRight()) + xoffset=end; + int yoffset=0; + int curRow =0; + int curCol =0; + for (int i = 0; i < visibleItems.count(); ++i) { + const PositionedItem &child = visibleItems.at(i); + int childXOffset = xoffset; + if(!d->isLeftToRight()) + childXOffset -= QGraphicsItemPrivate::get(child.item)->width(); + if((child.item->x()!=childXOffset)||(child.item->y()!=yoffset)){ + positionX(childXOffset, child); + positionY(yoffset, child); + } + + if (m_flow == LeftToRight) { + if(d->isLeftToRight()) + xoffset+=maxColWidth[curCol]+spacing(); + else + xoffset-=maxColWidth[curCol]+spacing(); + curCol++; + curCol%=c; + if (!curCol){ + yoffset+=maxRowHeight[curRow]+spacing(); + if(d->isLeftToRight()) + xoffset=0; + else + xoffset=end; + curRow++; + if (curRow>=r) + break; + } + } else { + yoffset+=maxRowHeight[curRow]+spacing(); + curRow++; + curRow%=r; + if (!curRow){ + if(d->isLeftToRight()) + xoffset+=maxColWidth[curCol]+spacing(); + else + xoffset-=maxColWidth[curCol]+spacing(); + yoffset=0; + curCol++; + if (curCol>=c) + break; + } + } + } +} + +void QDeclarativeGrid::reportConflictingAnchors() +{ + QDeclarativeBasePositionerPrivate *d = static_cast(QDeclarativeBasePositionerPrivate::get(this)); + for (int ii = 0; ii < positionedItems.count(); ++ii) { + const PositionedItem &child = positionedItems.at(ii); + if (child.item && QGraphicsItemPrivate::get(child.item)->isDeclarativeItem) { + QDeclarativeAnchors *anchors = QDeclarativeItemPrivate::get(static_cast(child.item))->_anchors; + if (anchors && (anchors->usedAnchors() || anchors->fill() || anchors->centerIn())) { + d->anchorConflict = true; + break; + } + } + } + if (d->anchorConflict) + qmlInfo(this) << "Cannot specify anchors for items inside Grid"; +} + +/*! + \qmlclass Flow QDeclarativeFlow + \ingroup qml-positioning-elements + \since 4.7 + \brief The Flow item arranges its children side by side, wrapping as necessary. + \inherits Item + + The Flow item positions its child items like words on a page, wrapping them + to create rows or columns of items that do not overlap. + + Spacing between items can be added using the \l spacing property. + Transitions can be used for cases where items managed by a Column are + added or moved. These are stored in the \l add and \l move properties + respectively. + + See \l{Using QML Positioner and Repeater Items} for more details about this item and other + related items. + + \section1 Example Usage + + The following example positions \l Text items within a parent item using + a Flow item. + + \image qml-flow-snippet.png + + \snippet doc/src/snippets/declarative/flow.qml flow item + + \section1 Using Transitions + + Transitions can be used to animate items that are added to, moved within, + or removed from a Flow item. The \l add and \l move properties can be set to + the transitions that will be applied when items are added to, removed from, + or re-positioned within a Flow item. + + The use of transitions with positioners is described in more detail in the + \l{Using QML Positioner and Repeater Items#Using Transitions}{Using QML + Positioner and Repeater Items} document. + + \section1 Limitations + + Note that the positioner assumes that the x and y positions of its children + will not change. If you manually change the x or y properties in script, bind + the x or y properties, use anchors on a child of a positioner, or have the + width or height of a child depend on the position of a child, then the + positioner may exhibit strange behaviour. If you need to perform any of these + actions, consider positioning the items without the use of a Flow. + + Items with a width or height of 0 will not be positioned. + + \sa Column, Row, Grid, {declarative/positioners}{Positioners example} +*/ +/*! + \qmlproperty Transition Flow::add + + This property holds the transition to be applied when adding an + item to the positioner. The transition will only be applied to the + added item(s). Positioner transitions will only affect the + position (x, y) of items. + + For a positioner, adding an item can mean that either the object + has been created or reparented, and thus is now a child or the + positioner, or that the object has had its opacity increased from + zero, and thus is now visible. + + \sa move +*/ +/*! + \qmlproperty Transition Flow::move + + This property holds the transition to be applied when moving an + item within the positioner. Positioner transitions will only affect + the position (x, y) of items. + + This transition can be performed when other items are added or removed + from the positioner, or when items resize themselves. + + \qml + Flow { + id: positioner + move: Transition { + NumberAnimation { + properties: "x,y" + ease: "easeOutBounce" + } + } + } + \endqml + + \sa add, {declarative/positioners}{Positioners example} +*/ +/*! + \qmlproperty int Flow::spacing + + spacing is the amount in pixels left empty between each adjacent + item, and defaults to 0. + + \sa Grid::spacing +*/ + +class QDeclarativeFlowPrivate : public QDeclarativeBasePositionerPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeFlow) + +public: + QDeclarativeFlowPrivate() + : QDeclarativeBasePositionerPrivate(), flow(QDeclarativeFlow::LeftToRight) + {} + + QDeclarativeFlow::Flow flow; +}; + +QDeclarativeFlow::QDeclarativeFlow(QDeclarativeItem *parent) +: QDeclarativeBasePositioner(*(new QDeclarativeFlowPrivate), Both, parent) +{ + Q_D(QDeclarativeFlow); + // Flow layout requires relayout if its own size changes too. + d->addItemChangeListener(d, QDeclarativeItemPrivate::Geometry); +} + +/*! + \qmlproperty enumeration Flow::flow + This property holds the flow of the layout. + + Possible values are: + + \list + \o Flow.LeftToRight (default) - Items are positioned next to + to each other according to the \l layoutDirection until the width of the Flow + is exceeded, then wrapped to the next line. + \o Flow.TopToBottom - Items are positioned next to each + other from top to bottom until the height of the Flow is exceeded, + then wrapped to the next column. + \endlist +*/ +QDeclarativeFlow::Flow QDeclarativeFlow::flow() const +{ + Q_D(const QDeclarativeFlow); + return d->flow; +} + +void QDeclarativeFlow::setFlow(Flow flow) +{ + Q_D(QDeclarativeFlow); + if (d->flow != flow) { + d->flow = flow; + prePositioning(); + emit flowChanged(); + } +} + +/*! + \qmlproperty enumeration Flow::layoutDirection + \since QtQuick 1.1 + + This property holds the layout direction of the layout. + + Possible values are: + + \list + \o Qt.LeftToRight (default) - Items are positioned from the top to bottom, + and left to right. The flow direction is dependent on the + \l Flow::flow property. + \o Qt.RightToLeft - Items are positioned from the top to bottom, + and right to left. The flow direction is dependent on the + \l Flow::flow property. + \endlist + + When using the attached property \l {LayoutMirroring::enabled} for locale layouts, + the visual layout direction of the flow positioner will be mirrored. However, the + property \c layoutDirection will remain unchanged. You can use the property + \l {LayoutMirroring::enabled} to determine whether the direction has been mirrored. + + \sa Grid::layoutDirection, Row::layoutDirection, {declarative/righttoleft/layoutdirection}{Layout directions example}, {LayoutMirroring}{LayoutMirroring} +*/ + +Qt::LayoutDirection QDeclarativeFlow::layoutDirection() const +{ + Q_D(const QDeclarativeFlow); + return d->layoutDirection; +} + +void QDeclarativeFlow::setLayoutDirection(Qt::LayoutDirection layoutDirection) +{ + Q_D(QDeclarativeFlow); + if (d->layoutDirection != layoutDirection) { + d->layoutDirection = layoutDirection; + prePositioning(); + emit layoutDirectionChanged(); + } +} + +Qt::LayoutDirection QDeclarativeFlow::effectiveLayoutDirection() const +{ + return QDeclarativeBasePositionerPrivate::getEffectiveLayoutDirection(this); +} + +void QDeclarativeFlow::doPositioning(QSizeF *contentSize) +{ + Q_D(QDeclarativeFlow); + + int hoffset = 0; + int voffset = 0; + int linemax = 0; + QList hoffsets; + + for (int i = 0; i < positionedItems.count(); ++i) { + const PositionedItem &child = positionedItems.at(i); + if (!child.item || !child.isVisible) + continue; + + QGraphicsItemPrivate *childPrivate = QGraphicsItemPrivate::get(child.item); + if (d->flow == LeftToRight) { + if (widthValid() && hoffset && hoffset + childPrivate->width() > width()) { + hoffset = 0; + voffset += linemax + spacing(); + linemax = 0; + } + } else { + if (heightValid() && voffset && voffset + childPrivate->height() > height()) { + voffset = 0; + hoffset += linemax + spacing(); + linemax = 0; + } + } + + if(d->isLeftToRight()){ + if(child.item->x() != hoffset) + positionX(hoffset, child); + }else{ + hoffsets << hoffset; + } + if(child.item->y() != voffset) + positionY(voffset, child); + + contentSize->setWidth(qMax(contentSize->width(), hoffset + childPrivate->width())); + contentSize->setHeight(qMax(contentSize->height(), voffset + childPrivate->height())); + + if (d->flow == LeftToRight) { + hoffset += childPrivate->width(); + hoffset += spacing(); + linemax = qMax(linemax, qCeil(childPrivate->height())); + } else { + voffset += childPrivate->height(); + voffset += spacing(); + linemax = qMax(linemax, qCeil(childPrivate->width())); + } + } + + if(d->isLeftToRight()) + return; + + int end; + if(widthValid()) + end = width(); + else + end = contentSize->width(); + int acc = 0; + for (int i = 0; i < positionedItems.count(); ++i) { + const PositionedItem &child = positionedItems.at(i); + if (!child.item || !child.isVisible) + continue; + hoffset = end - hoffsets[acc++] - QGraphicsItemPrivate::get(child.item)->width(); + if(child.item->x() != hoffset) + positionX(hoffset, child); + } +} + +void QDeclarativeFlow::reportConflictingAnchors() +{ + Q_D(QDeclarativeFlow); + for (int ii = 0; ii < positionedItems.count(); ++ii) { + const PositionedItem &child = positionedItems.at(ii); + if (child.item && QGraphicsItemPrivate::get(child.item)->isDeclarativeItem) { + QDeclarativeAnchors *anchors = QDeclarativeItemPrivate::get(static_cast(child.item))->_anchors; + if (anchors && (anchors->usedAnchors() || anchors->fill() || anchors->centerIn())) { + d->anchorConflict = true; + break; + } + } + } + if (d->anchorConflict) + qmlInfo(this) << "Cannot specify anchors for items inside Flow"; +} + +QT_END_NAMESPACE diff --git a/src/declarative/graphicsitems/qdeclarativepositioners_p.h b/src/declarative/graphicsitems/qdeclarativepositioners_p.h new file mode 100644 index 00000000..2371ea49 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativepositioners_p.h @@ -0,0 +1,233 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVELAYOUTS_H +#define QDECLARATIVELAYOUTS_H + +#include "qdeclarativeimplicitsizeitem_p.h" + +#include +#include + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QDeclarativeBasePositionerPrivate; + +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeBasePositioner : public QDeclarativeImplicitSizeItem +{ + Q_OBJECT + + Q_PROPERTY(int spacing READ spacing WRITE setSpacing NOTIFY spacingChanged) + Q_PROPERTY(QDeclarativeTransition *move READ move WRITE setMove NOTIFY moveChanged) + Q_PROPERTY(QDeclarativeTransition *add READ add WRITE setAdd NOTIFY addChanged) +public: + enum PositionerType { None = 0x0, Horizontal = 0x1, Vertical = 0x2, Both = 0x3 }; + QDeclarativeBasePositioner(PositionerType, QDeclarativeItem *parent); + ~QDeclarativeBasePositioner(); + + int spacing() const; + void setSpacing(int); + + QDeclarativeTransition *move() const; + void setMove(QDeclarativeTransition *); + + QDeclarativeTransition *add() const; + void setAdd(QDeclarativeTransition *); + +protected: + QDeclarativeBasePositioner(QDeclarativeBasePositionerPrivate &dd, PositionerType at, QDeclarativeItem *parent); + virtual void componentComplete(); + virtual QVariant itemChange(GraphicsItemChange, const QVariant &); + void finishApplyTransitions(); + +Q_SIGNALS: + void spacingChanged(); + void moveChanged(); + void addChanged(); + +protected Q_SLOTS: + void prePositioning(); + void graphicsWidgetGeometryChanged(); + +protected: + virtual void doPositioning(QSizeF *contentSize)=0; + virtual void reportConflictingAnchors()=0; + class PositionedItem { + public : + PositionedItem(QGraphicsObject *i) : item(i), isNew(false), isVisible(true) {} + bool operator==(const PositionedItem &other) const { return other.item == item; } + QGraphicsObject *item; + bool isNew; + bool isVisible; + }; + + QPODVector positionedItems; + void positionX(int,const PositionedItem &target); + void positionY(int,const PositionedItem &target); + +private: + Q_DISABLE_COPY(QDeclarativeBasePositioner) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarativeBasePositioner) +}; + +class Q_AUTOTEST_EXPORT QDeclarativeColumn : public QDeclarativeBasePositioner +{ + Q_OBJECT +public: + QDeclarativeColumn(QDeclarativeItem *parent=0); +protected: + virtual void doPositioning(QSizeF *contentSize); + virtual void reportConflictingAnchors(); +private: + Q_DISABLE_COPY(QDeclarativeColumn) +}; + +class Q_AUTOTEST_EXPORT QDeclarativeRow: public QDeclarativeBasePositioner +{ + Q_OBJECT + Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection NOTIFY layoutDirectionChanged REVISION 1) +public: + QDeclarativeRow(QDeclarativeItem *parent=0); + + Qt::LayoutDirection layoutDirection() const; + void setLayoutDirection (Qt::LayoutDirection); + Qt::LayoutDirection effectiveLayoutDirection() const; + +Q_SIGNALS: + Q_REVISION(1) void layoutDirectionChanged(); + +protected: + virtual void doPositioning(QSizeF *contentSize); + virtual void reportConflictingAnchors(); +private: + Q_DISABLE_COPY(QDeclarativeRow) +}; + +class Q_AUTOTEST_EXPORT QDeclarativeGrid : public QDeclarativeBasePositioner +{ + Q_OBJECT + Q_PROPERTY(int rows READ rows WRITE setRows NOTIFY rowsChanged) + Q_PROPERTY(int columns READ columns WRITE setColumns NOTIFY columnsChanged) + Q_PROPERTY(Flow flow READ flow WRITE setFlow NOTIFY flowChanged) + Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection NOTIFY layoutDirectionChanged REVISION 1) +public: + QDeclarativeGrid(QDeclarativeItem *parent=0); + + int rows() const {return m_rows;} + void setRows(const int rows); + + int columns() const {return m_columns;} + void setColumns(const int columns); + + Q_ENUMS(Flow) + enum Flow { LeftToRight, TopToBottom }; + Flow flow() const; + void setFlow(Flow); + + Qt::LayoutDirection layoutDirection() const; + void setLayoutDirection (Qt::LayoutDirection); + Qt::LayoutDirection effectiveLayoutDirection() const; + +Q_SIGNALS: + void rowsChanged(); + void columnsChanged(); + void flowChanged(); + Q_REVISION(1) void layoutDirectionChanged(); + +protected: + virtual void doPositioning(QSizeF *contentSize); + virtual void reportConflictingAnchors(); + +private: + int m_rows; + int m_columns; + Flow m_flow; + Q_DISABLE_COPY(QDeclarativeGrid) +}; + +class QDeclarativeFlowPrivate; +class Q_AUTOTEST_EXPORT QDeclarativeFlow: public QDeclarativeBasePositioner +{ + Q_OBJECT + Q_PROPERTY(Flow flow READ flow WRITE setFlow NOTIFY flowChanged) + Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection NOTIFY layoutDirectionChanged REVISION 1) +public: + QDeclarativeFlow(QDeclarativeItem *parent=0); + + Q_ENUMS(Flow) + enum Flow { LeftToRight, TopToBottom }; + Flow flow() const; + void setFlow(Flow); + + Qt::LayoutDirection layoutDirection() const; + void setLayoutDirection (Qt::LayoutDirection); + Qt::LayoutDirection effectiveLayoutDirection() const; +Q_SIGNALS: + void flowChanged(); + Q_REVISION(1) void layoutDirectionChanged(); + +protected: + virtual void doPositioning(QSizeF *contentSize); + virtual void reportConflictingAnchors(); +protected: + QDeclarativeFlow(QDeclarativeFlowPrivate &dd, QDeclarativeItem *parent); +private: + Q_DISABLE_COPY(QDeclarativeFlow) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarativeFlow) +}; + + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeColumn) +QML_DECLARE_TYPE(QDeclarativeRow) +QML_DECLARE_TYPE(QDeclarativeGrid) +QML_DECLARE_TYPE(QDeclarativeFlow) + +QT_END_HEADER + +#endif diff --git a/src/declarative/graphicsitems/qdeclarativepositioners_p_p.h b/src/declarative/graphicsitems/qdeclarativepositioners_p_p.h new file mode 100644 index 00000000..2773a1d8 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativepositioners_p_p.h @@ -0,0 +1,173 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVELAYOUTS_P_H +#define QDECLARATIVELAYOUTS_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 "private/qdeclarativepositioners_p.h" + +#include "private/qdeclarativeimplicitsizeitem_p_p.h" + +#include +#include +#include + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE +class QDeclarativeBasePositionerPrivate : public QDeclarativeImplicitSizeItemPrivate, public QDeclarativeItemChangeListener +{ + Q_DECLARE_PUBLIC(QDeclarativeBasePositioner) + +public: + QDeclarativeBasePositionerPrivate() + : spacing(0), type(QDeclarativeBasePositioner::None) + , moveTransition(0), addTransition(0), queuedPositioning(false) + , doingPositioning(false), anchorConflict(false), layoutDirection(Qt::LeftToRight) + { + } + + void init(QDeclarativeBasePositioner::PositionerType at) + { + type = at; + } + + int spacing; + + QDeclarativeBasePositioner::PositionerType type; + QDeclarativeTransition *moveTransition; + QDeclarativeTransition *addTransition; + QDeclarativeStateOperation::ActionList addActions; + QDeclarativeStateOperation::ActionList moveActions; + QDeclarativeTransitionManager addTransitionManager; + QDeclarativeTransitionManager moveTransitionManager; + + void watchChanges(QGraphicsObject *other); + void unwatchChanges(QGraphicsObject* other); + bool queuedPositioning : 1; + bool doingPositioning : 1; + bool anchorConflict : 1; + + Qt::LayoutDirection layoutDirection; + + + void schedulePositioning() + { + Q_Q(QDeclarativeBasePositioner); + if(!queuedPositioning){ + QTimer::singleShot(0,q,SLOT(prePositioning())); + queuedPositioning = true; + } + } + + void mirrorChange() { + Q_Q(QDeclarativeBasePositioner); + if (type != QDeclarativeBasePositioner::Vertical) + q->prePositioning(); + } + bool isLeftToRight() const { + if (type == QDeclarativeBasePositioner::Vertical) + return true; + else + return effectiveLayoutMirror ? layoutDirection == Qt::RightToLeft : layoutDirection == Qt::LeftToRight; + } + + virtual void itemSiblingOrderChanged(QDeclarativeItem* other) + { + Q_UNUSED(other); + //Delay is due to many children often being reordered at once + //And we only want to reposition them all once + schedulePositioning(); + } + + void itemGeometryChanged(QDeclarativeItem *, const QRectF &newGeometry, const QRectF &oldGeometry) + { + Q_Q(QDeclarativeBasePositioner); + if (newGeometry.size() != oldGeometry.size()) + q->prePositioning(); + } + + virtual void itemVisibilityChanged(QDeclarativeItem *) + { + schedulePositioning(); + } + virtual void itemOpacityChanged(QDeclarativeItem *) + { + Q_Q(QDeclarativeBasePositioner); + q->prePositioning(); + } + + void itemDestroyed(QDeclarativeItem *item) + { + Q_Q(QDeclarativeBasePositioner); + q->positionedItems.removeOne(QDeclarativeBasePositioner::PositionedItem(item)); + } + + static Qt::LayoutDirection getLayoutDirection(const QDeclarativeBasePositioner *positioner) + { + return positioner->d_func()->layoutDirection; + } + + static Qt::LayoutDirection getEffectiveLayoutDirection(const QDeclarativeBasePositioner *positioner) + { + if (positioner->d_func()->effectiveLayoutMirror) + return positioner->d_func()->layoutDirection == Qt::RightToLeft ? Qt::LeftToRight : Qt::RightToLeft; + else + return positioner->d_func()->layoutDirection; + } +}; + +QT_END_NAMESPACE +#endif diff --git a/src/declarative/graphicsitems/qdeclarativerectangle.cpp b/src/declarative/graphicsitems/qdeclarativerectangle.cpp new file mode 100644 index 00000000..e4389ec1 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativerectangle.cpp @@ -0,0 +1,587 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativerectangle_p.h" +#include "private/qdeclarativerectangle_p_p.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \internal + \class QDeclarativePen + \brief The QDeclarativePen class provides a pen used for drawing rectangle borders on a QDeclarativeView. + + By default, the pen is invalid and nothing is drawn. You must either set a color (then the default + width is 1) or a width (then the default color is black). + + A width of 1 indicates is a single-pixel line on the border of the item being painted. + + Example: + \qml + Rectangle { + border.width: 2 + border.color: "red" + } + \endqml +*/ + +void QDeclarativePen::setColor(const QColor &c) +{ + _color = c; + _valid = (_color.alpha() && _width >= 1) ? true : false; + emit penChanged(); +} + +void QDeclarativePen::setWidth(int w) +{ + if (_width == w && _valid) + return; + + _width = w; + _valid = (_color.alpha() && _width >= 1) ? true : false; + emit penChanged(); +} + + +/*! + \qmlclass GradientStop QDeclarativeGradientStop + \ingroup qml-basic-visual-elements + \since 4.7 + \brief The GradientStop item defines the color at a position in a Gradient. + + \sa Gradient +*/ + +/*! + \qmlproperty real GradientStop::position + \qmlproperty color GradientStop::color + + The position and color properties describe the color used at a given + position in a gradient, as represented by a gradient stop. + + The default position is 0.0; the default color is black. + + \sa Gradient +*/ + +void QDeclarativeGradientStop::updateGradient() +{ + if (QDeclarativeGradient *grad = qobject_cast(parent())) + grad->doUpdate(); +} + +/*! + \qmlclass Gradient QDeclarativeGradient + \ingroup qml-basic-visual-elements + \since 4.7 + \brief The Gradient item defines a gradient fill. + + A gradient is defined by two or more colors, which will be blended seamlessly. + + The colors are specified as a set of GradientStop child items, each of + which defines a position on the gradient from 0.0 to 1.0 and a color. + The position of each GradientStop is defined by setting its + \l{GradientStop::}{position} property; its color is defined using its + \l{GradientStop::}{color} property. + + A gradient without any gradient stops is rendered as a solid white fill. + + Note that this item is not a visual representation of a gradient. To display a + gradient, use a visual element (like \l Rectangle) which supports the use + of gradients. + + \section1 Example Usage + + \div {class="float-right"} + \inlineimage qml-gradient.png + \enddiv + + The following example declares a \l Rectangle item with a gradient starting + with red, blending to yellow at one third of the height of the rectangle, + and ending with green: + + \snippet doc/src/snippets/declarative/gradient.qml code + + \clearfloat + \section1 Performance and Limitations + + Calculating gradients can be computationally expensive compared to the use + of solid color fills or images. Consider using gradients for static items + in a user interface. + + In Qt 4.7, only vertical, linear gradients can be applied to items. If you + need to apply different orientations of gradients, a combination of rotation + and clipping will need to be applied to the relevant items. This can + introduce additional performance requirements for your application. + + The use of animations involving gradient stops may not give the desired + result. An alternative way to animate gradients is to use pre-generated + images or SVG drawings containing gradients. + + \sa GradientStop +*/ + +/*! + \qmlproperty list Gradient::stops + This property holds the gradient stops describing the gradient. + + By default, this property contains an empty list. + + To set the gradient stops, define them as children of the Gradient element. +*/ + +const QGradient *QDeclarativeGradient::gradient() const +{ + if (!m_gradient && !m_stops.isEmpty()) { + m_gradient = new QLinearGradient(0,0,0,1.0); + for (int i = 0; i < m_stops.count(); ++i) { + const QDeclarativeGradientStop *stop = m_stops.at(i); + m_gradient->setCoordinateMode(QGradient::ObjectBoundingMode); + m_gradient->setColorAt(stop->position(), stop->color()); + } + } + + return m_gradient; +} + +void QDeclarativeGradient::doUpdate() +{ + delete m_gradient; + m_gradient = 0; + emit updated(); +} + + +/*! + \qmlclass Rectangle QDeclarativeRectangle + \ingroup qml-basic-visual-elements + \since 4.7 + \brief The Rectangle item provides a filled rectangle with an optional border. + \inherits Item + + Rectangle items are used to fill areas with solid color or gradients, and are + often used to hold other items. + + \section1 Appearance + + Each Rectangle item is painted using either a solid fill color, specified using + the \l color property, or a gradient, defined using a Gradient element and set + using the \l gradient property. If both a color and a gradient are specified, + the gradient is used. + + You can add an optional border to a rectangle with its own color and thickness + by settting the \l border.color and \l border.width properties. + + You can also create rounded rectangles using the \l radius property. Since this + introduces curved edges to the corners of a rectangle, it may be appropriate to + set the \l smooth property to improve its appearance. + + \section1 Example Usage + + \div {class="float-right"} + \inlineimage declarative-rect.png + \enddiv + + The following example shows the effects of some of the common properties on a + Rectangle item, which in this case is used to create a square: + + \snippet doc/src/snippets/declarative/rectangle/rectangle.qml document + + \clearfloat + \section1 Performance + + Using the \l smooth property improves the appearance of a rounded rectangle at + the cost of rendering performance. You should consider unsetting this property + for rectangles in motion, and only set it when they are stationary. + + \sa Image +*/ + +int QDeclarativeRectanglePrivate::doUpdateSlotIdx = -1; + +QDeclarativeRectangle::QDeclarativeRectangle(QDeclarativeItem *parent) + : QDeclarativeItem(*(new QDeclarativeRectanglePrivate), parent) +{ +} + +void QDeclarativeRectangle::doUpdate() +{ + Q_D(QDeclarativeRectangle); + d->rectImage = QPixmap(); + const int pw = d->pen && d->pen->isValid() ? d->pen->width() : 0; + d->setPaintMargin((pw+1)/2); + update(); +} + +/*! + \qmlproperty int Rectangle::border.width + \qmlproperty color Rectangle::border.color + + The width and color used to draw the border of the rectangle. + + A width of 1 creates a thin line. For no line, use a width of 0 or a transparent color. + + \note The width of the rectangle's border does not affect the geometry of the + rectangle itself or its position relative to other items if anchors are used. + + If \c border.width is an odd number, the rectangle is painted at a half-pixel offset to retain + border smoothness. Also, the border is rendered evenly on either side of the + rectangle's boundaries, and the spare pixel is rendered to the right and below the + rectangle (as documented for QRect rendering). This can cause unintended effects if + \c border.width is 1 and the rectangle is \l{Item::clip}{clipped} by a parent item: + + \div {class="float-right"} + \inlineimage rect-border-width.png + \enddiv + + \snippet doc/src/snippets/declarative/rectangle/rect-border-width.qml 0 + + \clearfloat + Here, the innermost rectangle's border is clipped on the bottom and right edges by its + parent. To avoid this, the border width can be set to two instead of one. +*/ +QDeclarativePen *QDeclarativeRectangle::border() +{ + Q_D(QDeclarativeRectangle); + return d->getPen(); +} + +/*! + \qmlproperty Gradient Rectangle::gradient + + The gradient to use to fill the rectangle. + + This property allows for the construction of simple vertical gradients. + Other gradients may by formed by adding rotation to the rectangle. + + \div {class="float-left"} + \inlineimage declarative-rect_gradient.png + \enddiv + + \snippet doc/src/snippets/declarative/rectangle/rectangle-gradient.qml rectangles + \clearfloat + + If both a gradient and a color are specified, the gradient will be used. + + \sa Gradient, color +*/ +QDeclarativeGradient *QDeclarativeRectangle::gradient() const +{ + Q_D(const QDeclarativeRectangle); + return d->gradient; +} + +void QDeclarativeRectangle::setGradient(QDeclarativeGradient *gradient) +{ + Q_D(QDeclarativeRectangle); + if (d->gradient == gradient) + return; + static int updatedSignalIdx = -1; + if (updatedSignalIdx < 0) + updatedSignalIdx = QDeclarativeGradient::staticMetaObject.indexOfSignal("updated()"); + if (d->doUpdateSlotIdx < 0) + d->doUpdateSlotIdx = QDeclarativeRectangle::staticMetaObject.indexOfSlot("doUpdate()"); + if (d->gradient) + QMetaObject::disconnect(d->gradient, updatedSignalIdx, this, d->doUpdateSlotIdx); + d->gradient = gradient; + if (d->gradient) + QMetaObject::connect(d->gradient, updatedSignalIdx, this, d->doUpdateSlotIdx); + update(); +} + + +/*! + \qmlproperty real Rectangle::radius + This property holds the corner radius used to draw a rounded rectangle. + + If radius is non-zero, the rectangle will be painted as a rounded rectangle, otherwise it will be + painted as a normal rectangle. The same radius is used by all 4 corners; there is currently + no way to specify different radii for different corners. +*/ +qreal QDeclarativeRectangle::radius() const +{ + Q_D(const QDeclarativeRectangle); + return d->radius; +} + +void QDeclarativeRectangle::setRadius(qreal radius) +{ + Q_D(QDeclarativeRectangle); + if (d->radius == radius) + return; + + d->radius = radius; + d->rectImage = QPixmap(); + update(); + emit radiusChanged(); +} + +/*! + \qmlproperty color Rectangle::color + This property holds the color used to fill the rectangle. + + The default color is white. + + \div {class="float-right"} + \inlineimage rect-color.png + \enddiv + + The following example shows rectangles with colors specified + using hexadecimal and named color notation: + + \snippet doc/src/snippets/declarative/rectangle/rectangle-colors.qml rectangles + + \clearfloat + If both a gradient and a color are specified, the gradient will be used. + + \sa gradient +*/ +QColor QDeclarativeRectangle::color() const +{ + Q_D(const QDeclarativeRectangle); + return d->color; +} + +void QDeclarativeRectangle::setColor(const QColor &c) +{ + Q_D(QDeclarativeRectangle); + if (d->color == c) + return; + + d->color = c; + d->rectImage = QPixmap(); + update(); + emit colorChanged(); +} + +void QDeclarativeRectangle::generateRoundedRect() +{ + Q_D(QDeclarativeRectangle); + if (d->rectImage.isNull()) { + const int pw = d->pen && d->pen->isValid() ? d->pen->width() : 0; + const int radius = qCeil(d->radius); //ensure odd numbered width/height so we get 1-pixel center + + QString key = QLatin1String("q_") % QString::number(pw) % d->color.name() % QString::number(d->color.alpha(), 16) % QLatin1Char('_') % QString::number(radius); + if (d->pen && d->pen->isValid()) + key += d->pen->color().name() % QString::number(d->pen->color().alpha(), 16); + + if (!QPixmapCache::find(key, &d->rectImage)) { + d->rectImage = QPixmap(radius*2 + 3 + pw*2, radius*2 + 3 + pw*2); + d->rectImage.fill(Qt::transparent); + QPainter p(&(d->rectImage)); + p.setRenderHint(QPainter::Antialiasing); + if (d->pen && d->pen->isValid()) { + QPen pn(QColor(d->pen->color()), d->pen->width()); + p.setPen(pn); + } else { + p.setPen(Qt::NoPen); + } + p.setBrush(d->color); + if (pw%2) + p.drawRoundedRect(QRectF(qreal(pw)/2+1, qreal(pw)/2+1, d->rectImage.width()-(pw+1), d->rectImage.height()-(pw+1)), d->radius, d->radius); + else + p.drawRoundedRect(QRectF(qreal(pw)/2, qreal(pw)/2, d->rectImage.width()-pw, d->rectImage.height()-pw), d->radius, d->radius); + + // end painting before inserting pixmap + // to pixmap cache to avoid a deep copy + p.end(); + QPixmapCache::insert(key, d->rectImage); + } + } +} + +void QDeclarativeRectangle::generateBorderedRect() +{ + Q_D(QDeclarativeRectangle); + if (d->rectImage.isNull()) { + const int pw = d->pen && d->pen->isValid() ? d->pen->width() : 0; + + QString key = QLatin1String("q_") % QString::number(pw) % d->color.name() % QString::number(d->color.alpha(), 16); + if (d->pen && d->pen->isValid()) + key += d->pen->color().name() % QString::number(d->pen->color().alpha(), 16); + + if (!QPixmapCache::find(key, &d->rectImage)) { + // Adding 5 here makes qDrawBorderPixmap() paint correctly with smooth: true + // See QTBUG-7999 and QTBUG-10765 for more details. + d->rectImage = QPixmap(pw*2 + 5, pw*2 + 5); + d->rectImage.fill(Qt::transparent); + QPainter p(&(d->rectImage)); + p.setRenderHint(QPainter::Antialiasing); + if (d->pen && d->pen->isValid()) { + QPen pn(QColor(d->pen->color()), d->pen->width()); + pn.setJoinStyle(Qt::MiterJoin); + p.setPen(pn); + } else { + p.setPen(Qt::NoPen); + } + p.setBrush(d->color); + if (pw%2) + p.drawRect(QRectF(qreal(pw)/2+1, qreal(pw)/2+1, d->rectImage.width()-(pw+1), d->rectImage.height()-(pw+1))); + else + p.drawRect(QRectF(qreal(pw)/2, qreal(pw)/2, d->rectImage.width()-pw, d->rectImage.height()-pw)); + + // end painting before inserting pixmap + // to pixmap cache to avoid a deep copy + p.end(); + QPixmapCache::insert(key, d->rectImage); + } + } +} + +void QDeclarativeRectangle::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *) +{ + Q_D(QDeclarativeRectangle); + if (width() <= 0 || height() <= 0) + return; + if (d->radius > 0 || (d->pen && d->pen->isValid()) + || (d->gradient && d->gradient->gradient()) ) { + drawRect(*p); + } + else { + bool oldAA = p->testRenderHint(QPainter::Antialiasing); + if (d->smooth) + p->setRenderHints(QPainter::Antialiasing, true); + p->fillRect(QRectF(0, 0, width(), height()), d->color); + if (d->smooth) + p->setRenderHint(QPainter::Antialiasing, oldAA); + } +} + +void QDeclarativeRectangle::drawRect(QPainter &p) +{ + Q_D(QDeclarativeRectangle); + if ((d->gradient && d->gradient->gradient()) + || d->radius > width()/2 || d->radius > height()/2 + || width() < 3 || height() < 3) { + // XXX This path is still slower than the image path + // Image path won't work for gradients or invalid radius though + bool oldAA = p.testRenderHint(QPainter::Antialiasing); + if (d->smooth) + p.setRenderHint(QPainter::Antialiasing); + if (d->pen && d->pen->isValid()) { + QPen pn(QColor(d->pen->color()), d->pen->width()); + pn.setJoinStyle(Qt::MiterJoin); + p.setPen(pn); + } else { + p.setPen(Qt::NoPen); + } + if (d->gradient && d->gradient->gradient()) + p.setBrush(*d->gradient->gradient()); + else + p.setBrush(d->color); + const int pw = d->pen && d->pen->isValid() ? d->pen->width() : 0; + QRectF rect; + if (pw%2) + rect = QRectF(0.5, 0.5, width()-1, height()-1); + else + rect = QRectF(0, 0, width(), height()); + qreal radius = d->radius; + if (radius > width()/2 || radius > height()/2) + radius = qMin(width()/2, height()/2); + if (radius > 0.) + p.drawRoundedRect(rect, radius, radius); + else + p.drawRect(rect); + if (d->smooth) + p.setRenderHint(QPainter::Antialiasing, oldAA); + } else { + bool oldAA = p.testRenderHint(QPainter::Antialiasing); + bool oldSmooth = p.testRenderHint(QPainter::SmoothPixmapTransform); + if (d->smooth) + p.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform, d->smooth); + + const int pw = d->pen && d->pen->isValid() ? (d->pen->width()+1)/2*2 : 0; + + if (d->radius > 0) + generateRoundedRect(); + else + generateBorderedRect(); + + int xOffset = (d->rectImage.width()-1)/2; + int yOffset = (d->rectImage.height()-1)/2; + Q_ASSERT(d->rectImage.width() == 2*xOffset + 1); + Q_ASSERT(d->rectImage.height() == 2*yOffset + 1); + + // check whether we've eliminated the center completely + if (2*xOffset > width()+pw) + xOffset = (width()+pw)/2; + if (2*yOffset > height()+pw) + yOffset = (height()+pw)/2; + + QMargins margins(xOffset, yOffset, xOffset, yOffset); + QTileRules rules(Qt::StretchTile, Qt::StretchTile); + //NOTE: even though our item may have qreal-based width and height, qDrawBorderPixmap only supports QRects + qDrawBorderPixmap(&p, QRect(-pw/2, -pw/2, width()+pw, height()+pw), margins, d->rectImage, d->rectImage.rect(), margins, rules); + + if (d->smooth) { + p.setRenderHint(QPainter::Antialiasing, oldAA); + p.setRenderHint(QPainter::SmoothPixmapTransform, oldSmooth); + } + } +} + +/*! + \qmlproperty bool Rectangle::smooth + + Set this property if you want the item to be smoothly scaled or + transformed. Smooth filtering gives better visual quality, but is slower. If + the item is displayed at its natural size, this property has no visual or + performance effect. + + \note Generally scaling artifacts are only visible if the item is stationary on + the screen. A common pattern when animating an item is to disable smooth + filtering at the beginning of the animation and reenable it at the conclusion. + + \image rect-smooth.png + On this image, smooth is turned off on the top half and on on the bottom half. +*/ + +QRectF QDeclarativeRectangle::boundingRect() const +{ + Q_D(const QDeclarativeRectangle); + return QRectF(-d->paintmargin, -d->paintmargin, d->width()+d->paintmargin*2, d->height()+d->paintmargin*2); +} + +QT_END_NAMESPACE diff --git a/src/declarative/graphicsitems/qdeclarativerectangle_p.h b/src/declarative/graphicsitems/qdeclarativerectangle_p.h new file mode 100644 index 00000000..450e9bf9 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativerectangle_p.h @@ -0,0 +1,188 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVERECT_H +#define QDECLARATIVERECT_H + +#include "qdeclarativeitem.h" + +#include + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativePen : public QObject +{ + Q_OBJECT + + Q_PROPERTY(int width READ width WRITE setWidth NOTIFY penChanged) + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY penChanged) +public: + QDeclarativePen(QObject *parent=0) + : QObject(parent), _width(1), _color("#000000"), _valid(false) + {} + + int width() const { return _width; } + void setWidth(int w); + + QColor color() const { return _color; } + void setColor(const QColor &c); + + bool isValid() { return _valid; } + +Q_SIGNALS: + void penChanged(); + +private: + int _width; + QColor _color; + bool _valid; +}; + +class Q_AUTOTEST_EXPORT QDeclarativeGradientStop : public QObject +{ + Q_OBJECT + + Q_PROPERTY(qreal position READ position WRITE setPosition) + Q_PROPERTY(QColor color READ color WRITE setColor) + +public: + QDeclarativeGradientStop(QObject *parent=0) : QObject(parent) {} + + qreal position() const { return m_position; } + void setPosition(qreal position) { m_position = position; updateGradient(); } + + QColor color() const { return m_color; } + void setColor(const QColor &color) { m_color = color; updateGradient(); } + +private: + void updateGradient(); + +private: + qreal m_position; + QColor m_color; +}; + +class Q_AUTOTEST_EXPORT QDeclarativeGradient : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QDeclarativeListProperty stops READ stops) + Q_CLASSINFO("DefaultProperty", "stops") + +public: + QDeclarativeGradient(QObject *parent=0) : QObject(parent), m_gradient(0) {} + ~QDeclarativeGradient() { delete m_gradient; } + + QDeclarativeListProperty stops() { return QDeclarativeListProperty(this, m_stops); } + + const QGradient *gradient() const; + +Q_SIGNALS: + void updated(); + +private: + void doUpdate(); + +private: + QList m_stops; + mutable QGradient *m_gradient; + friend class QDeclarativeGradientStop; +}; + +class QDeclarativeRectanglePrivate; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeRectangle : public QDeclarativeItem +{ + Q_OBJECT + + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) + Q_PROPERTY(QDeclarativeGradient *gradient READ gradient WRITE setGradient) + Q_PROPERTY(QDeclarativePen * border READ border CONSTANT) + Q_PROPERTY(qreal radius READ radius WRITE setRadius NOTIFY radiusChanged) +public: + QDeclarativeRectangle(QDeclarativeItem *parent=0); + + QColor color() const; + void setColor(const QColor &); + + QDeclarativePen *border(); + + QDeclarativeGradient *gradient() const; + void setGradient(QDeclarativeGradient *gradient); + + qreal radius() const; + void setRadius(qreal radius); + + QRectF boundingRect() const; + + void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *); + +Q_SIGNALS: + void colorChanged(); + void radiusChanged(); + +private Q_SLOTS: + void doUpdate(); + +private: + void generateRoundedRect(); + void generateBorderedRect(); + void drawRect(QPainter &painter); + +private: + Q_DISABLE_COPY(QDeclarativeRectangle) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarativeRectangle) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativePen) +QML_DECLARE_TYPE(QDeclarativeGradientStop) +QML_DECLARE_TYPE(QDeclarativeGradient) +QML_DECLARE_TYPE(QDeclarativeRectangle) + +QT_END_HEADER + +#endif // QDECLARATIVERECT_H diff --git a/src/declarative/graphicsitems/qdeclarativerectangle_p_p.h b/src/declarative/graphicsitems/qdeclarativerectangle_p_p.h new file mode 100644 index 00000000..0f1fb0f5 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativerectangle_p_p.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVERECT_P_H +#define QDECLARATIVERECT_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 "private/qdeclarativeitem_p.h" + +QT_BEGIN_NAMESPACE + +class QDeclarativeGradient; +class QDeclarativeRectangle; +class QDeclarativeRectanglePrivate : public QDeclarativeItemPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeRectangle) + +public: + QDeclarativeRectanglePrivate() : + color(Qt::white), gradient(0), pen(0), radius(0), paintmargin(0) + { + QGraphicsItemPrivate::flags = QGraphicsItemPrivate::flags & ~QGraphicsItem::ItemHasNoContents; + } + + ~QDeclarativeRectanglePrivate() + { + delete pen; + } + + QColor color; + QDeclarativeGradient *gradient; + QDeclarativePen *pen; + qreal radius; + qreal paintmargin; + QPixmap rectImage; + static int doUpdateSlotIdx; + + QDeclarativePen *getPen() { + if (!pen) { + Q_Q(QDeclarativeRectangle); + pen = new QDeclarativePen; + static int penChangedSignalIdx = -1; + if (penChangedSignalIdx < 0) + penChangedSignalIdx = QDeclarativePen::staticMetaObject.indexOfSignal("penChanged()"); + if (doUpdateSlotIdx < 0) + doUpdateSlotIdx = QDeclarativeRectangle::staticMetaObject.indexOfSlot("doUpdate()"); + QMetaObject::connect(pen, penChangedSignalIdx, q, doUpdateSlotIdx); + } + return pen; + } + + void setPaintMargin(qreal margin) + { + Q_Q(QDeclarativeRectangle); + if (margin == paintmargin) + return; + q->prepareGeometryChange(); + paintmargin = margin; + } +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVERECT_P_H diff --git a/src/declarative/graphicsitems/qdeclarativerepeater.cpp b/src/declarative/graphicsitems/qdeclarativerepeater.cpp new file mode 100644 index 00000000..9250c783 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativerepeater.cpp @@ -0,0 +1,447 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativerepeater_p.h" +#include "private/qdeclarativerepeater_p_p.h" + +#include "private/qdeclarativevisualitemmodel_p.h" +#include +#include + +#include + +QT_BEGIN_NAMESPACE +QDeclarativeRepeaterPrivate::QDeclarativeRepeaterPrivate() +: model(0), ownModel(false) +{ +} + +QDeclarativeRepeaterPrivate::~QDeclarativeRepeaterPrivate() +{ + if (ownModel) + delete model; +} + +/*! + \qmlclass Repeater QDeclarativeRepeater + \ingroup qml-utility-elements + \since 4.7 + \inherits Item + + \brief The Repeater element allows you to repeat an Item-based component using a model. + + The Repeater element is used to create a large number of + similar items. Like other view elements, a Repeater has a \l model and a \l delegate: + for each entry in the model, the delegate is instantiated + in a context seeded with data from the model. A Repeater item is usually + enclosed in a positioner element such as \l Row or \l Column to visually + position the multiple delegate items created by the Repeater. + + The following Repeater creates three instances of a \l Rectangle item within + a \l Row: + + \snippet doc/src/snippets/declarative/repeaters/repeater.qml import + \codeline + \snippet doc/src/snippets/declarative/repeaters/repeater.qml simple + + \image repeater-simple.png + + A Repeater's \l model can be any of the supported \l {qmlmodels}{data models}. + Additionally, like delegates for other views, a Repeater delegate can access + its index within the repeater, as well as the model data relevant to the + delegate. See the \l delegate property documentation for details. + + Items instantiated by the Repeater are inserted, in order, as + children of the Repeater's parent. The insertion starts immediately after + the repeater's position in its parent stacking list. This allows + a Repeater to be used inside a layout. For example, the following Repeater's + items are stacked between a red rectangle and a blue rectangle: + + \snippet doc/src/snippets/declarative/repeaters/repeater.qml layout + + \image repeater.png + + + \note A Repeater item owns all items it instantiates. Removing or dynamically destroying + an item created by a Repeater results in unpredictable behavior. + + + \section2 Considerations when using Repeater + + The Repeater element creates all of its delegate items when the repeater is first + created. This can be inefficient if there are a large number of delegate items and + not all of the items are required to be visible at the same time. If this is the case, + consider using other view elements like ListView (which only creates delegate items + when they are scrolled into view) or use the \l {Dynamic Object Creation} methods to + create items as they are required. + + Also, note that Repeater is \l {Item}-based, and can only repeat \l {Item}-derived objects. + For example, it cannot be used to repeat QtObjects: + \badcode + Item { + //XXX does not work! Can't repeat QtObject as it doesn't derive from Item. + Repeater { + model: 10 + QtObject {} + } + } + \endcode + */ + +/*! + \qmlsignal Repeater::onItemAdded(int index, Item item) + \since QtQuick 1.1 + + This handler is called when an item is added to the repeater. The \a index + parameter holds the index at which the item has been inserted within the + repeater, and the \a item parameter holds the \l Item that has been added. +*/ + +/*! + \qmlsignal Repeater::onItemRemoved(int index, Item item) + \since QtQuick 1.1 + + This handler is called when an item is removed from the repeater. The \a index + parameter holds the index at which the item was removed from the repeater, + and the \a item parameter holds the \l Item that was removed. + + Do not keep a reference to \a item if it was created by this repeater, as + in these cases it will be deleted shortly after the handler is called. +*/ + +QDeclarativeRepeater::QDeclarativeRepeater(QDeclarativeItem *parent) + : QDeclarativeItem(*(new QDeclarativeRepeaterPrivate), parent) +{ +} + +QDeclarativeRepeater::~QDeclarativeRepeater() +{ +} + +/*! + \qmlproperty any Repeater::model + + The model providing data for the repeater. + + This property can be set to any of the supported \l {qmlmodels}{data models}: + + \list + \o A number that indicates the number of delegates to be created by the repeater + \o A model (e.g. a ListModel item, or a QAbstractItemModel subclass) + \o A string list + \o An object list + \endlist + + The type of model affects the properties that are exposed to the \l delegate. + + \sa {qmlmodels}{Data Models} +*/ +QVariant QDeclarativeRepeater::model() const +{ + Q_D(const QDeclarativeRepeater); + return d->dataSource; +} + +void QDeclarativeRepeater::setModel(const QVariant &model) +{ + Q_D(QDeclarativeRepeater); + if (d->dataSource == model) + return; + + clear(); + if (d->model) { + disconnect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int))); + disconnect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int))); + disconnect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int))); + disconnect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset())); + /* + disconnect(d->model, SIGNAL(createdItem(int,QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*))); + disconnect(d->model, SIGNAL(destroyingItem(QDeclarativeItem*)), this, SLOT(destroyingItem(QDeclarativeItem*))); + */ + } + d->dataSource = model; + QObject *object = qvariant_cast(model); + QDeclarativeVisualModel *vim = 0; + if (object && (vim = qobject_cast(object))) { + if (d->ownModel) { + delete d->model; + d->ownModel = false; + } + d->model = vim; + } else { + if (!d->ownModel) { + d->model = new QDeclarativeVisualDataModel(qmlContext(this), this); + d->ownModel = true; + } + if (QDeclarativeVisualDataModel *dataModel = qobject_cast(d->model)) + dataModel->setModel(model); + } + if (d->model) { + connect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int))); + connect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int))); + connect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int))); + connect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset())); + /* + connect(d->model, SIGNAL(createdItem(int,QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*))); + connect(d->model, SIGNAL(destroyingItem(QDeclarativeItem*)), this, SLOT(destroyingItem(QDeclarativeItem*))); + */ + regenerate(); + } + emit modelChanged(); + emit countChanged(); +} + +/*! + \qmlproperty Component Repeater::delegate + \default + + The delegate provides a template defining each item instantiated by the repeater. + + Delegates are exposed to a read-only \c index property that indicates the index + of the delegate within the repeater. For example, the following \l Text delegate + displays the index of each repeated item: + + \table + \row + \o \snippet doc/src/snippets/declarative/repeaters/repeater.qml index + \o \image repeater-index.png + \endtable + + If the \l model is a \l{QStringList-based model}{string list} or + \l{QObjectList-based model}{object list}, the delegate is also exposed to + a read-only \c modelData property that holds the string or object data. For + example: + + \table + \row + \o \snippet doc/src/snippets/declarative/repeaters/repeater.qml modeldata + \o \image repeater-modeldata.png + \endtable + + If the \l model is a model object (such as a \l ListModel) the delegate + can access all model roles as named properties, in the same way that delegates + do for view classes like ListView. + + \sa {QML Data Models} + */ +QDeclarativeComponent *QDeclarativeRepeater::delegate() const +{ + Q_D(const QDeclarativeRepeater); + if (d->model) { + if (QDeclarativeVisualDataModel *dataModel = qobject_cast(d->model)) + return dataModel->delegate(); + } + + return 0; +} + +void QDeclarativeRepeater::setDelegate(QDeclarativeComponent *delegate) +{ + Q_D(QDeclarativeRepeater); + if (QDeclarativeVisualDataModel *dataModel = qobject_cast(d->model)) + if (delegate == dataModel->delegate()) + return; + + if (!d->ownModel) { + d->model = new QDeclarativeVisualDataModel(qmlContext(this)); + d->ownModel = true; + } + if (QDeclarativeVisualDataModel *dataModel = qobject_cast(d->model)) { + dataModel->setDelegate(delegate); + regenerate(); + emit delegateChanged(); + } +} + +/*! + \qmlproperty int Repeater::count + + This property holds the number of items in the repeater. +*/ +int QDeclarativeRepeater::count() const +{ + Q_D(const QDeclarativeRepeater); + if (d->model) + return d->model->count(); + return 0; +} + +/*! + \qmlmethod Item Repeater::itemAt(index) + \since QtQuick 1.1 + + Returns the \l Item that has been created at the given \a index, or \c null + if no item exists at \a index. +*/ +QDeclarativeItem *QDeclarativeRepeater::itemAt(int index) const +{ + Q_D(const QDeclarativeRepeater); + if (index >= 0 && index < d->deletables.count()) + return d->deletables[index]; + return 0; + +} + +void QDeclarativeRepeater::componentComplete() +{ + QDeclarativeItem::componentComplete(); + regenerate(); +} + +QVariant QDeclarativeRepeater::itemChange(GraphicsItemChange change, + const QVariant &value) +{ + QVariant rv = QDeclarativeItem::itemChange(change, value); + if (change == ItemParentHasChanged) { + regenerate(); + } + + return rv; +} + +void QDeclarativeRepeater::clear() +{ + Q_D(QDeclarativeRepeater); + bool complete = isComponentComplete(); + + if (d->model) { + while (d->deletables.count() > 0) { + QDeclarativeItem *item = d->deletables.takeLast(); + if (complete) + emit itemRemoved(d->deletables.count()-1, item); + d->model->release(item); + } + } + d->deletables.clear(); +} + +void QDeclarativeRepeater::regenerate() +{ + Q_D(QDeclarativeRepeater); + if (!isComponentComplete()) + return; + + clear(); + + if (!d->model || !d->model->count() || !d->model->isValid() || !parentItem() || !isComponentComplete()) + return; + + for (int ii = 0; ii < count(); ++ii) { + QDeclarativeItem *item = d->model->item(ii); + if (item) { + QDeclarative_setParent_noEvent(item, parentItem()); + item->setParentItem(parentItem()); + item->stackBefore(this); + d->deletables << item; + emit itemAdded(ii, item); + } + } +} + +void QDeclarativeRepeater::itemsInserted(int index, int count) +{ + Q_D(QDeclarativeRepeater); + if (!isComponentComplete()) + return; + for (int i = 0; i < count; ++i) { + int modelIndex = index + i; + QDeclarativeItem *item = d->model->item(modelIndex); + if (item) { + QDeclarative_setParent_noEvent(item, parentItem()); + item->setParentItem(parentItem()); + if (modelIndex < d->deletables.count()) + item->stackBefore(d->deletables.at(modelIndex)); + else + item->stackBefore(this); + d->deletables.insert(modelIndex, item); + emit itemAdded(modelIndex, item); + } + } + emit countChanged(); +} + +void QDeclarativeRepeater::itemsRemoved(int index, int count) +{ + Q_D(QDeclarativeRepeater); + if (!isComponentComplete() || count <= 0) + return; + while (count--) { + QDeclarativeItem *item = d->deletables.takeAt(index); + emit itemRemoved(index, item); + if (item) + d->model->release(item); + else + break; + } + emit countChanged(); +} + +void QDeclarativeRepeater::itemsMoved(int from, int to, int count) +{ + Q_D(QDeclarativeRepeater); + if (!isComponentComplete() || count <= 0) + return; + if (from + count > d->deletables.count()) { + regenerate(); + return; + } + QList removed; + int removedCount = count; + while (removedCount--) + removed << d->deletables.takeAt(from); + for (int i = 0; i < count; ++i) + d->deletables.insert(to + i, removed.at(i)); + d->deletables.last()->stackBefore(this); + for (int i = d->model->count()-1; i > 0; --i) { + QDeclarativeItem *item = d->deletables.at(i-1); + item->stackBefore(d->deletables.at(i)); + } +} + +void QDeclarativeRepeater::modelReset() +{ + if (!isComponentComplete()) + return; + regenerate(); + emit countChanged(); +} + +QT_END_NAMESPACE diff --git a/src/declarative/graphicsitems/qdeclarativerepeater_p.h b/src/declarative/graphicsitems/qdeclarativerepeater_p.h new file mode 100644 index 00000000..ee1aa748 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativerepeater_p.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEREPEATER_H +#define QDECLARATIVEREPEATER_H + +#include "qdeclarativeitem.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeRepeaterPrivate; +class Q_AUTOTEST_EXPORT QDeclarativeRepeater : public QDeclarativeItem +{ + Q_OBJECT + + Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged) + Q_PROPERTY(QDeclarativeComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged) + Q_PROPERTY(int count READ count NOTIFY countChanged) + Q_CLASSINFO("DefaultProperty", "delegate") + +public: + QDeclarativeRepeater(QDeclarativeItem *parent=0); + virtual ~QDeclarativeRepeater(); + + QVariant model() const; + void setModel(const QVariant &); + + QDeclarativeComponent *delegate() const; + void setDelegate(QDeclarativeComponent *); + + int count() const; + + Q_INVOKABLE Q_REVISION(1) QDeclarativeItem *itemAt(int index) const; + +Q_SIGNALS: + void modelChanged(); + void delegateChanged(); + void countChanged(); + + Q_REVISION(1) void itemAdded(int index, QDeclarativeItem *item); + Q_REVISION(1) void itemRemoved(int index, QDeclarativeItem *item); + +private: + void clear(); + void regenerate(); + +protected: + virtual void componentComplete(); + QVariant itemChange(GraphicsItemChange change, const QVariant &value); + +private Q_SLOTS: + void itemsInserted(int,int); + void itemsRemoved(int,int); + void itemsMoved(int,int,int); + void modelReset(); + +private: + Q_DISABLE_COPY(QDeclarativeRepeater) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarativeRepeater) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeRepeater) + +QT_END_HEADER + +#endif // QDECLARATIVEREPEATER_H diff --git a/src/declarative/graphicsitems/qdeclarativerepeater_p_p.h b/src/declarative/graphicsitems/qdeclarativerepeater_p_p.h new file mode 100644 index 00000000..0ba14b58 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativerepeater_p_p.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEREPEATER_P_H +#define QDECLARATIVEREPEATER_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 "private/qdeclarativerepeater_p.h" + +#include "private/qdeclarativeitem_p.h" + +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeContext; +class QDeclarativeVisualModel; +class QDeclarativeRepeaterPrivate : public QDeclarativeItemPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeRepeater) + +public: + QDeclarativeRepeaterPrivate(); + ~QDeclarativeRepeaterPrivate(); + + QDeclarativeVisualModel *model; + QVariant dataSource; + bool ownModel; + + QList > deletables; +}; + +QT_END_NAMESPACE +#endif // QDECLARATIVEREPEATER_P_H diff --git a/src/declarative/graphicsitems/qdeclarativescalegrid.cpp b/src/declarative/graphicsitems/qdeclarativescalegrid.cpp new file mode 100644 index 00000000..44f50fd5 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativescalegrid.cpp @@ -0,0 +1,213 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativescalegrid_p_p.h" + +#include + +#include +#include + +QT_BEGIN_NAMESPACE +/*! + \internal + \class QDeclarativeScaleGrid + \brief The QDeclarativeScaleGrid class allows you to specify a 3x3 grid to use in scaling an image. +*/ + +QDeclarativeScaleGrid::QDeclarativeScaleGrid(QObject *parent) : QObject(parent), _left(0), _top(0), _right(0), _bottom(0) +{ +} + +QDeclarativeScaleGrid::~QDeclarativeScaleGrid() +{ +} + +bool QDeclarativeScaleGrid::isNull() const +{ + return !_left && !_top && !_right && !_bottom; +} + +void QDeclarativeScaleGrid::setLeft(int pos) +{ + if (_left != pos) { + _left = pos; + emit borderChanged(); + } +} + +void QDeclarativeScaleGrid::setTop(int pos) +{ + if (_top != pos) { + _top = pos; + emit borderChanged(); + } +} + +void QDeclarativeScaleGrid::setRight(int pos) +{ + if (_right != pos) { + _right = pos; + emit borderChanged(); + } +} + +void QDeclarativeScaleGrid::setBottom(int pos) +{ + if (_bottom != pos) { + _bottom = pos; + emit borderChanged(); + } +} + +QDeclarativeGridScaledImage::QDeclarativeGridScaledImage() +: _l(-1), _r(-1), _t(-1), _b(-1), + _h(QDeclarativeBorderImage::Stretch), _v(QDeclarativeBorderImage::Stretch) +{ +} + +QDeclarativeGridScaledImage::QDeclarativeGridScaledImage(const QDeclarativeGridScaledImage &o) +: _l(o._l), _r(o._r), _t(o._t), _b(o._b), _h(o._h), _v(o._v), _pix(o._pix) +{ +} + +QDeclarativeGridScaledImage &QDeclarativeGridScaledImage::operator=(const QDeclarativeGridScaledImage &o) +{ + _l = o._l; + _r = o._r; + _t = o._t; + _b = o._b; + _h = o._h; + _v = o._v; + _pix = o._pix; + return *this; +} + +QDeclarativeGridScaledImage::QDeclarativeGridScaledImage(QIODevice *data) +: _l(-1), _r(-1), _t(-1), _b(-1), _h(QDeclarativeBorderImage::Stretch), _v(QDeclarativeBorderImage::Stretch) +{ + int l = -1; + int r = -1; + int t = -1; + int b = -1; + QString imgFile; + + QByteArray raw; + while(raw = data->readLine(), !raw.isEmpty()) { + QString line = QString::fromUtf8(raw.trimmed()); + if (line.isEmpty() || line.startsWith(QLatin1Char('#'))) + continue; + + int colonId = line.indexOf(QLatin1Char(':')); + if (colonId <= 0) + return; + QStringList list; + list.append(line.left(colonId).trimmed()); + list.append(line.mid(colonId+1).trimmed()); + + if (list[0] == QLatin1String("border.left")) + l = list[1].toInt(); + else if (list[0] == QLatin1String("border.right")) + r = list[1].toInt(); + else if (list[0] == QLatin1String("border.top")) + t = list[1].toInt(); + else if (list[0] == QLatin1String("border.bottom")) + b = list[1].toInt(); + else if (list[0] == QLatin1String("source")) + imgFile = list[1]; + else if (list[0] == QLatin1String("horizontalTileRule")) + _h = stringToRule(list[1]); + else if (list[0] == QLatin1String("verticalTileRule")) + _v = stringToRule(list[1]); + } + + if (l < 0 || r < 0 || t < 0 || b < 0 || imgFile.isEmpty()) + return; + + _l = l; _r = r; _t = t; _b = b; + + _pix = imgFile; +} + +QDeclarativeBorderImage::TileMode QDeclarativeGridScaledImage::stringToRule(const QString &s) +{ + if (s == QLatin1String("Stretch")) + return QDeclarativeBorderImage::Stretch; + if (s == QLatin1String("Repeat")) + return QDeclarativeBorderImage::Repeat; + if (s == QLatin1String("Round")) + return QDeclarativeBorderImage::Round; + + qWarning("QDeclarativeGridScaledImage: Invalid tile rule specified. Using Stretch."); + return QDeclarativeBorderImage::Stretch; +} + +bool QDeclarativeGridScaledImage::isValid() const +{ + return _l >= 0; +} + +int QDeclarativeGridScaledImage::gridLeft() const +{ + return _l; +} + +int QDeclarativeGridScaledImage::gridRight() const +{ + return _r; +} + +int QDeclarativeGridScaledImage::gridTop() const +{ + return _t; +} + +int QDeclarativeGridScaledImage::gridBottom() const +{ + return _b; +} + +QString QDeclarativeGridScaledImage::pixmapUrl() const +{ + return _pix; +} + +QT_END_NAMESPACE diff --git a/src/declarative/graphicsitems/qdeclarativescalegrid_p_p.h b/src/declarative/graphicsitems/qdeclarativescalegrid_p_p.h new file mode 100644 index 00000000..90f69d90 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativescalegrid_p_p.h @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESCALEGRID_H +#define QDECLARATIVESCALEGRID_H + +#include + +#include +#include + +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeScaleGrid : public QObject +{ + Q_OBJECT + Q_ENUMS(TileRule) + + Q_PROPERTY(int left READ left WRITE setLeft NOTIFY borderChanged) + Q_PROPERTY(int top READ top WRITE setTop NOTIFY borderChanged) + Q_PROPERTY(int right READ right WRITE setRight NOTIFY borderChanged) + Q_PROPERTY(int bottom READ bottom WRITE setBottom NOTIFY borderChanged) + +public: + QDeclarativeScaleGrid(QObject *parent=0); + ~QDeclarativeScaleGrid(); + + bool isNull() const; + + int left() const { return _left; } + void setLeft(int); + + int top() const { return _top; } + void setTop(int); + + int right() const { return _right; } + void setRight(int); + + int bottom() const { return _bottom; } + void setBottom(int); + +Q_SIGNALS: + void borderChanged(); + +private: + int _left; + int _top; + int _right; + int _bottom; +}; + +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeGridScaledImage +{ +public: + QDeclarativeGridScaledImage(); + QDeclarativeGridScaledImage(const QDeclarativeGridScaledImage &); + QDeclarativeGridScaledImage(QIODevice*); + QDeclarativeGridScaledImage &operator=(const QDeclarativeGridScaledImage &); + bool isValid() const; + int gridLeft() const; + int gridRight() const; + int gridTop() const; + int gridBottom() const; + QDeclarativeBorderImage::TileMode horizontalTileRule() const { return _h; } + QDeclarativeBorderImage::TileMode verticalTileRule() const { return _v; } + + QString pixmapUrl() const; + +private: + static QDeclarativeBorderImage::TileMode stringToRule(const QString &); + +private: + int _l; + int _r; + int _t; + int _b; + QDeclarativeBorderImage::TileMode _h; + QDeclarativeBorderImage::TileMode _v; + QString _pix; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeScaleGrid) + +QT_END_HEADER + +#endif // QDECLARATIVESCALEGRID_H diff --git a/src/declarative/graphicsitems/qdeclarativetext.cpp b/src/declarative/graphicsitems/qdeclarativetext.cpp new file mode 100644 index 00000000..4d0e5829 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativetext.cpp @@ -0,0 +1,1635 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativetext_p.h" +#include "private/qdeclarativetext_p_p.h" +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +extern Q_GUI_EXPORT bool qt_applefontsmoothing_enabled; + +class QTextDocumentWithImageResources : public QTextDocument { + Q_OBJECT + +public: + QTextDocumentWithImageResources(QDeclarativeText *parent); + virtual ~QTextDocumentWithImageResources(); + + void setText(const QString &); + int resourcesLoading() const { return outstanding; } + +protected: + QVariant loadResource(int type, const QUrl &name); + +private slots: + void requestFinished(); + +private: + QHash m_resources; + + int outstanding; + static QSet errors; +}; + +DEFINE_BOOL_CONFIG_OPTION(enableImageCache, QML_ENABLE_TEXT_IMAGE_CACHE); + +QString QDeclarativeTextPrivate::elideChar = QString(0x2026); + +QDeclarativeTextPrivate::QDeclarativeTextPrivate() +: color((QRgb)0), style(QDeclarativeText::Normal), hAlign(QDeclarativeText::AlignLeft), + vAlign(QDeclarativeText::AlignTop), elideMode(QDeclarativeText::ElideNone), + format(QDeclarativeText::AutoText), wrapMode(QDeclarativeText::NoWrap), lineHeight(1), + lineHeightMode(QDeclarativeText::ProportionalHeight), lineCount(1), truncated(false), maximumLineCount(INT_MAX), + maximumLineCountValid(false), imageCacheDirty(true), updateOnComponentComplete(true), richText(false), singleline(false), + cacheAllTextAsImage(true), internalWidthUpdate(false), requireImplicitWidth(false), hAlignImplicit(true), + rightToLeftText(false), layoutTextElided(false), naturalWidth(0), doc(0) +{ + cacheAllTextAsImage = enableImageCache(); + QGraphicsItemPrivate::acceptedMouseButtons = Qt::LeftButton; + QGraphicsItemPrivate::flags = QGraphicsItemPrivate::flags & ~QGraphicsItem::ItemHasNoContents; +} + +QTextDocumentWithImageResources::QTextDocumentWithImageResources(QDeclarativeText *parent) +: QTextDocument(parent), outstanding(0) +{ +} + +QTextDocumentWithImageResources::~QTextDocumentWithImageResources() +{ + if (!m_resources.isEmpty()) + qDeleteAll(m_resources); +} + +QVariant QTextDocumentWithImageResources::loadResource(int type, const QUrl &name) +{ + QDeclarativeContext *context = qmlContext(parent()); + QUrl url = context->resolvedUrl(name); + + if (type == QTextDocument::ImageResource) { + QHash::Iterator iter = m_resources.find(url); + + if (iter == m_resources.end()) { + QDeclarativePixmap *p = new QDeclarativePixmap(context->engine(), url); + iter = m_resources.insert(name, p); + + if (p->isLoading()) { + p->connectFinished(this, SLOT(requestFinished())); + outstanding++; + } + } + + QDeclarativePixmap *p = *iter; + if (p->isReady()) { + return p->pixmap(); + } else if (p->isError()) { + if (!errors.contains(url)) { + errors.insert(url); + qmlInfo(parent()) << p->error(); + } + } + } + + return QTextDocument::loadResource(type,url); // The *resolved* URL +} + +void QTextDocumentWithImageResources::requestFinished() +{ + outstanding--; + if (outstanding == 0) { + QDeclarativeText *textItem = static_cast(parent()); + QString text = textItem->text(); +#ifndef QT_NO_TEXTHTMLPARSER + setHtml(text); +#else + setPlainText(text); +#endif + QDeclarativeTextPrivate *d = QDeclarativeTextPrivate::get(textItem); + d->updateLayout(); + } +} + +void QTextDocumentWithImageResources::setText(const QString &text) +{ + if (!m_resources.isEmpty()) { + qDeleteAll(m_resources); + m_resources.clear(); + outstanding = 0; + } + +#ifndef QT_NO_TEXTHTMLPARSER + setHtml(text); +#else + setPlainText(text); +#endif +} + +QSet QTextDocumentWithImageResources::errors; + +QDeclarativeTextPrivate::~QDeclarativeTextPrivate() +{ +} + +qreal QDeclarativeTextPrivate::implicitWidth() const +{ + if (!requireImplicitWidth) { + // We don't calculate implicitWidth unless it is required. + // We need to force a size update now to ensure implicitWidth is calculated + QDeclarativeTextPrivate *me = const_cast(this); + me->requireImplicitWidth = true; + me->updateSize(); + } + return mImplicitWidth; +} + +void QDeclarativeTextPrivate::updateLayout() +{ + Q_Q(QDeclarativeText); + if (!q->isComponentComplete()) { + updateOnComponentComplete = true; + return; + } + + layoutTextElided = false; + // Setup instance of QTextLayout for all cases other than richtext + if (!richText) { + layout.clearLayout(); + layout.setFont(font); + if (format != QDeclarativeText::StyledText) { + QString tmp = text; + tmp.replace(QLatin1Char('\n'), QChar::LineSeparator); + singleline = !tmp.contains(QChar::LineSeparator); + if (singleline && !maximumLineCountValid && elideMode != QDeclarativeText::ElideNone && q->widthValid()) { + QFontMetrics fm(font); + tmp = fm.elidedText(tmp,(Qt::TextElideMode)elideMode,q->width()); + if (tmp != text) { + layoutTextElided = true; + if (!truncated) { + truncated = true; + emit q->truncatedChanged(); + } + } + } + layout.setText(tmp); + } else { + singleline = false; + QDeclarativeStyledText::parse(text, layout); + } + } + + updateSize(); +} + +void QDeclarativeTextPrivate::updateSize() +{ + Q_Q(QDeclarativeText); + + if (!q->isComponentComplete()) { + updateOnComponentComplete = true; + return; + } + + if (!requireImplicitWidth) { + emit q->implicitWidthChanged(); + // if the implicitWidth is used, then updateSize() has already been called (recursively) + if (requireImplicitWidth) + return; + } + + invalidateImageCache(); + + QFontMetrics fm(font); + if (text.isEmpty()) { + q->setImplicitWidth(0); + q->setImplicitHeight(fm.height()); + paintedSize = QSize(0, fm.height()); + emit q->paintedSizeChanged(); + q->update(); + return; + } + + int dy = q->height(); + QSize size(0, 0); + + //setup instance of QTextLayout for all cases other than richtext + if (!richText) { + QRect textRect = setupTextLayout(); + if (layedOutTextRect.size() != textRect.size()) + q->prepareGeometryChange(); + layedOutTextRect = textRect; + size = textRect.size(); + dy -= size.height(); + } else { + singleline = false; // richtext can't elide or be optimized for single-line case + ensureDoc(); + doc->setDefaultFont(font); + + QDeclarativeText::HAlignment horizontalAlignment = q->effectiveHAlign(); + if (rightToLeftText) { + if (horizontalAlignment == QDeclarativeText::AlignLeft) + horizontalAlignment = QDeclarativeText::AlignRight; + else if (horizontalAlignment == QDeclarativeText::AlignRight) + horizontalAlignment = QDeclarativeText::AlignLeft; + } + QTextOption option; + option.setAlignment((Qt::Alignment)int(horizontalAlignment | vAlign)); + option.setWrapMode(QTextOption::WrapMode(wrapMode)); + doc->setDefaultTextOption(option); + if (requireImplicitWidth && q->widthValid()) { + doc->setTextWidth(-1); + naturalWidth = doc->idealWidth(); + } + if (q->widthValid()) + doc->setTextWidth(q->width()); + else + doc->setTextWidth(doc->idealWidth()); // ### Text does not align if width is not set (QTextDoc bug) + dy -= (int)doc->size().height(); + QSize dsize = doc->size().toSize(); + if (dsize != layedOutTextRect.size()) { + q->prepareGeometryChange(); + layedOutTextRect = QRect(QPoint(0,0), dsize); + } + size = QSize(int(doc->idealWidth()),dsize.height()); + } + int yoff = 0; + + if (q->heightValid()) { + if (vAlign == QDeclarativeText::AlignBottom) + yoff = dy; + else if (vAlign == QDeclarativeText::AlignVCenter) + yoff = dy/2; + } + q->setBaselineOffset(fm.ascent() + yoff); + + //### need to comfirm cost of always setting these for richText + internalWidthUpdate = true; + if (!q->widthValid()) + q->setImplicitWidth(size.width()); + else if (requireImplicitWidth) + q->setImplicitWidth(naturalWidth); + internalWidthUpdate = false; + q->setImplicitHeight(size.height()); + if (paintedSize != size) { + paintedSize = size; + emit q->paintedSizeChanged(); + } + q->update(); +} + +/*! + Lays out the QDeclarativeTextPrivate::layout QTextLayout in the constraints of the QDeclarativeText. + + Returns the size of the final text. This can be used to position the text vertically (the text is + already absolutely positioned horizontally). +*/ +QRect QDeclarativeTextPrivate::setupTextLayout() +{ + // ### text layout handling should be profiled and optimized as needed + // what about QStackTextEngine engine(tmp, d->font.font()); QTextLayout textLayout(&engine); + Q_Q(QDeclarativeText); + layout.setCacheEnabled(true); + + qreal lineWidth = 0; + int visibleCount = 0; + + //set manual width + if (q->widthValid()) + lineWidth = q->width(); + + QTextOption textOption = layout.textOption(); + textOption.setAlignment(Qt::Alignment(q->effectiveHAlign())); + textOption.setWrapMode(QTextOption::WrapMode(wrapMode)); + layout.setTextOption(textOption); + + bool elideText = false; + bool truncate = false; + + QFontMetrics fm(layout.font()); + elidePos = QPointF(); + + if (requireImplicitWidth && q->widthValid()) { + // requires an extra layout + QString elidedText; + if (layoutTextElided) { + // We have provided elided text to the layout, but we must calculate unelided width. + elidedText = layout.text(); + layout.setText(text); + } + layout.beginLayout(); + forever { + QTextLine line = layout.createLine(); + if (!line.isValid()) + break; + } + layout.endLayout(); + QRectF br; + for (int i = 0; i < layout.lineCount(); ++i) { + QTextLine line = layout.lineAt(i); + br = br.united(line.naturalTextRect()); + } + naturalWidth = br.width(); + if (layoutTextElided) + layout.setText(elidedText); + } + + if (maximumLineCountValid) { + layout.beginLayout(); + if (!lineWidth) + lineWidth = INT_MAX; + int linesLeft = maximumLineCount; + int visibleTextLength = 0; + while (linesLeft > 0) { + QTextLine line = layout.createLine(); + if (!line.isValid()) + break; + + visibleCount++; + if (lineWidth) + line.setLineWidth(lineWidth); + visibleTextLength += line.textLength(); + + if (--linesLeft == 0) { + if (visibleTextLength < text.length()) { + truncate = true; + if (elideMode==QDeclarativeText::ElideRight && q->widthValid()) { + qreal elideWidth = fm.width(elideChar); + // Need to correct for alignment + line.setLineWidth(lineWidth-elideWidth); + if (layout.text().mid(line.textStart(), line.textLength()).isRightToLeft()) { + line.setPosition(QPointF(line.position().x() + elideWidth, line.position().y())); + elidePos.setX(line.naturalTextRect().left() - elideWidth); + } else { + elidePos.setX(line.naturalTextRect().right()); + } + elideText = true; + } + } + } + } + layout.endLayout(); + + //Update truncated + if (truncated != truncate) { + truncated = truncate; + emit q->truncatedChanged(); + } + } else { + layout.beginLayout(); + forever { + QTextLine line = layout.createLine(); + if (!line.isValid()) + break; + visibleCount++; + if (lineWidth) + line.setLineWidth(lineWidth); + } + layout.endLayout(); + } + + qreal height = 0; + QRectF br; + for (int i = 0; i < layout.lineCount(); ++i) { + QTextLine line = layout.lineAt(i); + // set line spacing + line.setPosition(QPointF(line.position().x(), height)); + if (elideText && i == layout.lineCount()-1) { + elidePos.setY(height + fm.ascent()); + br = br.united(QRectF(elidePos, QSizeF(fm.width(elideChar), fm.ascent()))); + } + br = br.united(line.naturalTextRect()); + height += (lineHeightMode == QDeclarativeText::FixedHeight) ? lineHeight : line.height() * lineHeight; + } + br.setHeight(height); + + if (!q->widthValid()) + naturalWidth = br.width(); + + //Update the number of visible lines + if (lineCount != visibleCount) { + lineCount = visibleCount; + emit q->lineCountChanged(); + } + + return QRect(qRound(br.x()), qRound(br.y()), qCeil(br.width()), qCeil(br.height())); +} + +/*! + Returns a painted version of the QDeclarativeTextPrivate::layout QTextLayout. + If \a drawStyle is true, the style color overrides all colors in the document. +*/ +QPixmap QDeclarativeTextPrivate::textLayoutImage(bool drawStyle) +{ + //do layout + QSize size = layedOutTextRect.size(); + //paint text + QPixmap img(size); + if (!size.isEmpty()) { + img.fill(Qt::transparent); +#ifdef Q_WS_MAC + bool oldSmooth = qt_applefontsmoothing_enabled; + qt_applefontsmoothing_enabled = false; +#endif + QPainter p(&img); +#ifdef Q_WS_MAC + qt_applefontsmoothing_enabled = oldSmooth; +#endif + drawTextLayout(&p, QPointF(-layedOutTextRect.x(),0), drawStyle); + } + return img; +} + +/*! + Paints the QDeclarativeTextPrivate::layout QTextLayout into \a painter at \a pos. If + \a drawStyle is true, the style color overrides all colors in the document. +*/ +void QDeclarativeTextPrivate::drawTextLayout(QPainter *painter, const QPointF &pos, bool drawStyle) +{ + if (drawStyle) + painter->setPen(styleColor); + else + painter->setPen(color); + painter->setFont(font); + layout.draw(painter, pos); + if (!elidePos.isNull()) + painter->drawText(pos + elidePos, elideChar); +} + +/*! + Returns a painted version of the QDeclarativeTextPrivate::doc QTextDocument. + If \a drawStyle is true, the style color overrides all colors in the document. +*/ +QPixmap QDeclarativeTextPrivate::textDocumentImage(bool drawStyle) +{ + QSize size = doc->size().toSize(); + + //paint text + QPixmap img(size); + img.fill(Qt::transparent); +#ifdef Q_WS_MAC + bool oldSmooth = qt_applefontsmoothing_enabled; + qt_applefontsmoothing_enabled = false; +#endif + QPainter p(&img); +#ifdef Q_WS_MAC + qt_applefontsmoothing_enabled = oldSmooth; +#endif + + QAbstractTextDocumentLayout::PaintContext context; + + QTextOption oldOption(doc->defaultTextOption()); + if (drawStyle) { + context.palette.setColor(QPalette::Text, styleColor); + QTextOption colorOption(doc->defaultTextOption()); + colorOption.setFlags(QTextOption::SuppressColors); + doc->setDefaultTextOption(colorOption); + } else { + context.palette.setColor(QPalette::Text, color); + } + doc->documentLayout()->draw(&p, context); + if (drawStyle) + doc->setDefaultTextOption(oldOption); + return img; +} + +/*! + Mark the image cache as dirty. +*/ +void QDeclarativeTextPrivate::invalidateImageCache() +{ + Q_Q(QDeclarativeText); + + if(cacheAllTextAsImage || style != QDeclarativeText::Normal){//If actually using the image cache + if (imageCacheDirty) + return; + + imageCacheDirty = true; + imageCache = QPixmap(); + } + if (q->isComponentComplete()) + q->update(); +} + +/*! + Tests if the image cache is dirty, and repaints it if it is. +*/ +void QDeclarativeTextPrivate::checkImageCache() +{ + if (!imageCacheDirty) + return; + + if (text.isEmpty()) { + + imageCache = QPixmap(); + + } else { + + QPixmap textImage; + QPixmap styledImage; + + if (richText) { + textImage = textDocumentImage(false); + if (style != QDeclarativeText::Normal) + styledImage = textDocumentImage(true); //### should use styleColor + } else { + textImage = textLayoutImage(false); + if (style != QDeclarativeText::Normal) + styledImage = textLayoutImage(true); //### should use styleColor + } + + switch (style) { + case QDeclarativeText::Outline: + imageCache = drawOutline(textImage, styledImage); + break; + case QDeclarativeText::Sunken: + imageCache = drawOutline(textImage, styledImage, -1); + break; + case QDeclarativeText::Raised: + imageCache = drawOutline(textImage, styledImage, 1); + break; + default: + imageCache = textImage; + break; + } + + } + + imageCacheDirty = false; +} + +/*! + Ensures the QDeclarativeTextPrivate::doc variable is set to a valid text document +*/ +void QDeclarativeTextPrivate::ensureDoc() +{ + if (!doc) { + Q_Q(QDeclarativeText); + doc = new QTextDocumentWithImageResources(q); + doc->setDocumentMargin(0); + } +} + +/*! + Draw \a styleSource as an outline around \a source and return the new image. +*/ +QPixmap QDeclarativeTextPrivate::drawOutline(const QPixmap &source, const QPixmap &styleSource) +{ + QPixmap img = QPixmap(styleSource.width() + 2, styleSource.height() + 2); + img.fill(Qt::transparent); + + QPainter ppm(&img); + + QPoint pos(0, 0); + pos += QPoint(-1, 0); + ppm.drawPixmap(pos, styleSource); + pos += QPoint(2, 0); + ppm.drawPixmap(pos, styleSource); + pos += QPoint(-1, -1); + ppm.drawPixmap(pos, styleSource); + pos += QPoint(0, 2); + ppm.drawPixmap(pos, styleSource); + + pos += QPoint(0, -1); + ppm.drawPixmap(pos, source); + ppm.end(); + + return img; +} + +/*! + Draw \a styleSource below \a source at \a yOffset and return the new image. +*/ +QPixmap QDeclarativeTextPrivate::drawOutline(const QPixmap &source, const QPixmap &styleSource, int yOffset) +{ + QPixmap img = QPixmap(styleSource.width() + 2, styleSource.height() + 2); + img.fill(Qt::transparent); + + QPainter ppm(&img); + + ppm.drawPixmap(QPoint(0, yOffset), styleSource); + ppm.drawPixmap(0, 0, source); + + ppm.end(); + + return img; +} + +/*! + \qmlclass Text QDeclarativeText + \ingroup qml-basic-visual-elements + \since 4.7 + \brief The Text item allows you to add formatted text to a scene. + \inherits Item + + Text items can display both plain and rich text. For example, red text with + a specific font and size can be defined like this: + + \qml + Text { + text: "Hello World!" + font.family: "Helvetica" + font.pointSize: 24 + color: "red" + } + \endqml + + Rich text is defined using HTML-style markup: + + \qml + Text { + text: "Hello World!" + } + \endqml + + \image declarative-text.png + + If height and width are not explicitly set, Text will attempt to determine how + much room is needed and set it accordingly. Unless \l wrapMode is set, it will always + prefer width to height (all text will be placed on a single line). + + The \l elide property can alternatively be used to fit a single line of + plain text to a set width. + + Note that the \l{Supported HTML Subset} is limited. Also, if the text contains + HTML img tags that load remote images, the text is reloaded. + + Text provides read-only text. For editable text, see \l TextEdit. + + \sa {declarative/text/fonts}{Fonts example} +*/ +QDeclarativeText::QDeclarativeText(QDeclarativeItem *parent) + : QDeclarativeImplicitSizeItem(*(new QDeclarativeTextPrivate), parent) +{ +} + +QDeclarativeText::~QDeclarativeText() +{ +} + +/*! + \qmlproperty bool Text::clip + This property holds whether the text is clipped. + + Note that if the text does not fit in the bounding rectangle it will be abruptly chopped. + + If you want to display potentially long text in a limited space, you probably want to use \c elide instead. +*/ + +/*! + \qmlproperty bool Text::smooth + + This property holds whether the text is smoothly scaled or transformed. + + Smooth filtering gives better visual quality, but is slower. If + the item is displayed at its natural size, this property has no visual or + performance effect. + + \note Generally scaling artifacts are only visible if the item is stationary on + the screen. A common pattern when animating an item is to disable smooth + filtering at the beginning of the animation and reenable it at the conclusion. +*/ + +/*! + \qmlsignal Text::onLinkActivated(string link) + + This handler is called when the user clicks on a link embedded in the text. + The link must be in rich text or HTML format and the + \a link string provides access to the particular link. + + \snippet doc/src/snippets/declarative/text/onLinkActivated.qml 0 + + The example code will display the text + "The main website is at \l{http://qt.nokia.com}{Nokia Qt DF}." + + Clicking on the highlighted link will output + \tt{http://qt.nokia.com link activated} to the console. +*/ + +/*! + \qmlproperty string Text::font.family + + Sets the family name of the font. + + The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]". + If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen. + If the family isn't available a family will be set using the font matching algorithm. +*/ + +/*! + \qmlproperty bool Text::font.bold + + Sets whether the font weight is bold. +*/ + +/*! + \qmlproperty enumeration Text::font.weight + + Sets the font's weight. + + The weight can be one of: + \list + \o Font.Light + \o Font.Normal - the default + \o Font.DemiBold + \o Font.Bold + \o Font.Black + \endlist + + \qml + Text { text: "Hello"; font.weight: Font.DemiBold } + \endqml +*/ + +/*! + \qmlproperty bool Text::font.italic + + Sets whether the font has an italic style. +*/ + +/*! + \qmlproperty bool Text::font.underline + + Sets whether the text is underlined. +*/ + +/*! + \qmlproperty bool Text::font.strikeout + + Sets whether the font has a strikeout style. +*/ + +/*! + \qmlproperty real Text::font.pointSize + + Sets the font size in points. The point size must be greater than zero. +*/ + +/*! + \qmlproperty int Text::font.pixelSize + + Sets the font size in pixels. + + Using this function makes the font device dependent. + Use \c pointSize to set the size of the font in a device independent manner. +*/ + +/*! + \qmlproperty real Text::font.letterSpacing + + Sets the letter spacing for the font. + + Letter spacing changes the default spacing between individual letters in the font. + A positive value increases the letter spacing by the corresponding pixels; a negative value decreases the spacing. +*/ + +/*! + \qmlproperty real Text::font.wordSpacing + + Sets the word spacing for the font. + + Word spacing changes the default spacing between individual words. + A positive value increases the word spacing by a corresponding amount of pixels, + while a negative value decreases the inter-word spacing accordingly. +*/ + +/*! + \qmlproperty enumeration Text::font.capitalization + + Sets the capitalization for the text. + + \list + \o Font.MixedCase - This is the normal text rendering option where no capitalization change is applied. + \o Font.AllUppercase - This alters the text to be rendered in all uppercase type. + \o Font.AllLowercase - This alters the text to be rendered in all lowercase type. + \o Font.SmallCaps - This alters the text to be rendered in small-caps type. + \o Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character. + \endlist + + \qml + Text { text: "Hello"; font.capitalization: Font.AllLowercase } + \endqml +*/ +QFont QDeclarativeText::font() const +{ + Q_D(const QDeclarativeText); + return d->sourceFont; +} + +void QDeclarativeText::setFont(const QFont &font) +{ + Q_D(QDeclarativeText); + if (d->sourceFont == font) + return; + + d->sourceFont = font; + QFont oldFont = d->font; + d->font = font; + if (d->font.pointSizeF() != -1) { + // 0.5pt resolution + qreal size = qRound(d->font.pointSizeF()*2.0); + d->font.setPointSizeF(size/2.0); + } + + if (oldFont != d->font) + d->updateLayout(); + + emit fontChanged(d->sourceFont); +} + +/*! + \qmlproperty string Text::text + + The text to display. Text supports both plain and rich text strings. + + The item will try to automatically determine whether the text should + be treated as rich text. This determination is made using Qt::mightBeRichText(). +*/ +QString QDeclarativeText::text() const +{ + Q_D(const QDeclarativeText); + return d->text; +} + +void QDeclarativeText::setText(const QString &n) +{ + Q_D(QDeclarativeText); + if (d->text == n) + return; + + d->richText = d->format == RichText || (d->format == AutoText && Qt::mightBeRichText(n)); + d->text = n; + if (isComponentComplete()) { + if (d->richText) { + d->ensureDoc(); + d->doc->setText(n); + d->rightToLeftText = d->doc->toPlainText().isRightToLeft(); + } else { + d->rightToLeftText = d->text.isRightToLeft(); + } + d->determineHorizontalAlignment(); + } + d->updateLayout(); + emit textChanged(d->text); +} + + +/*! + \qmlproperty color Text::color + + The text color. + + An example of green text defined using hexadecimal notation: + \qml + Text { + color: "#00FF00" + text: "green text" + } + \endqml + + An example of steel blue text defined using an SVG color name: + \qml + Text { + color: "steelblue" + text: "blue text" + } + \endqml +*/ +QColor QDeclarativeText::color() const +{ + Q_D(const QDeclarativeText); + return d->color; +} + +void QDeclarativeText::setColor(const QColor &color) +{ + Q_D(QDeclarativeText); + if (d->color == color) + return; + + d->color = color; + d->invalidateImageCache(); + emit colorChanged(d->color); +} + +/*! + \qmlproperty enumeration Text::style + + Set an additional text style. + + Supported text styles are: + \list + \o Text.Normal - the default + \o Text.Outline + \o Text.Raised + \o Text.Sunken + \endlist + + \qml + Row { + Text { font.pointSize: 24; text: "Normal" } + Text { font.pointSize: 24; text: "Raised"; style: Text.Raised; styleColor: "#AAAAAA" } + Text { font.pointSize: 24; text: "Outline";style: Text.Outline; styleColor: "red" } + Text { font.pointSize: 24; text: "Sunken"; style: Text.Sunken; styleColor: "#AAAAAA" } + } + \endqml + + \image declarative-textstyle.png +*/ +QDeclarativeText::TextStyle QDeclarativeText::style() const +{ + Q_D(const QDeclarativeText); + return d->style; +} + +void QDeclarativeText::setStyle(QDeclarativeText::TextStyle style) +{ + Q_D(QDeclarativeText); + if (d->style == style) + return; + + // changing to/from Normal requires the boundingRect() to change + if (isComponentComplete() && (d->style == Normal || style == Normal)) + prepareGeometryChange(); + d->style = style; + d->invalidateImageCache(); + emit styleChanged(d->style); +} + +/*! + \qmlproperty color Text::styleColor + + Defines the secondary color used by text styles. + + \c styleColor is used as the outline color for outlined text, and as the + shadow color for raised or sunken text. If no style has been set, it is not + used at all. + + \qml + Text { font.pointSize: 18; text: "hello"; style: Text.Raised; styleColor: "gray" } + \endqml + + \sa style + */ +QColor QDeclarativeText::styleColor() const +{ + Q_D(const QDeclarativeText); + return d->styleColor; +} + +void QDeclarativeText::setStyleColor(const QColor &color) +{ + Q_D(QDeclarativeText); + if (d->styleColor == color) + return; + + d->styleColor = color; + d->invalidateImageCache(); + emit styleColorChanged(d->styleColor); +} + + +/*! + \qmlproperty enumeration Text::horizontalAlignment + \qmlproperty enumeration Text::verticalAlignment + + Sets the horizontal and vertical alignment of the text within the Text items + width and height. By default, the text is vertically aligned to the top. Horizontal + alignment follows the natural alignment of the text, for example text that is read + from left to right will be aligned to the left. + + The valid values for \c horizontalAlignment are \c Text.AlignLeft, \c Text.AlignRight, \c Text.AlignHCenter and + \c Text.AlignJustify. The valid values for \c verticalAlignment are \c Text.AlignTop, \c Text.AlignBottom + and \c Text.AlignVCenter. + + Note that for a single line of text, the size of the text is the area of the text. In this common case, + all alignments are equivalent. If you want the text to be, say, centered in its parent, then you will + need to either modify the Item::anchors, or set horizontalAlignment to Text.AlignHCenter and bind the width to + that of the parent. + + When using the attached property \l {LayoutMirroring::enabled} to mirror application + layouts, the horizontal alignment of text will also be mirrored. However, the property + \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment + of Text, use the property \l {LayoutMirroring::enabled}. +*/ +QDeclarativeText::HAlignment QDeclarativeText::hAlign() const +{ + Q_D(const QDeclarativeText); + return d->hAlign; +} + +void QDeclarativeText::setHAlign(HAlignment align) +{ + Q_D(QDeclarativeText); + bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror; + d->hAlignImplicit = false; + if (d->setHAlign(align, forceAlign) && isComponentComplete()) + d->updateLayout(); +} + +void QDeclarativeText::resetHAlign() +{ + Q_D(QDeclarativeText); + d->hAlignImplicit = true; + if (d->determineHorizontalAlignment() && isComponentComplete()) + d->updateLayout(); +} + +QDeclarativeText::HAlignment QDeclarativeText::effectiveHAlign() const +{ + Q_D(const QDeclarativeText); + QDeclarativeText::HAlignment effectiveAlignment = d->hAlign; + if (!d->hAlignImplicit && d->effectiveLayoutMirror) { + switch (d->hAlign) { + case QDeclarativeText::AlignLeft: + effectiveAlignment = QDeclarativeText::AlignRight; + break; + case QDeclarativeText::AlignRight: + effectiveAlignment = QDeclarativeText::AlignLeft; + break; + default: + break; + } + } + return effectiveAlignment; +} + +bool QDeclarativeTextPrivate::setHAlign(QDeclarativeText::HAlignment alignment, bool forceAlign) +{ + Q_Q(QDeclarativeText); + if (hAlign != alignment || forceAlign) { + QDeclarativeText::HAlignment oldEffectiveHAlign = q->effectiveHAlign(); + hAlign = alignment; + emit q->horizontalAlignmentChanged(hAlign); + return true; + } + return false; +} + +bool QDeclarativeTextPrivate::determineHorizontalAlignment() +{ + Q_Q(QDeclarativeText); + if (hAlignImplicit && q->isComponentComplete()) { + bool alignToRight = text.isEmpty() ? QApplication::keyboardInputDirection() == Qt::RightToLeft : rightToLeftText; + return setHAlign(alignToRight ? QDeclarativeText::AlignRight : QDeclarativeText::AlignLeft); + } + return false; +} + +void QDeclarativeTextPrivate::mirrorChange() +{ + Q_Q(QDeclarativeText); + if (q->isComponentComplete()) { + if (!hAlignImplicit && (hAlign == QDeclarativeText::AlignRight || hAlign == QDeclarativeText::AlignLeft)) { + updateLayout(); + } + } +} + +QTextDocument *QDeclarativeTextPrivate::textDocument() +{ + return doc; +} + +QDeclarativeText::VAlignment QDeclarativeText::vAlign() const +{ + Q_D(const QDeclarativeText); + return d->vAlign; +} + +void QDeclarativeText::setVAlign(VAlignment align) +{ + Q_D(QDeclarativeText); + if (d->vAlign == align) + return; + + if (isComponentComplete()) + prepareGeometryChange(); + d->vAlign = align; + emit verticalAlignmentChanged(align); +} + +/*! + \qmlproperty enumeration Text::wrapMode + + Set this property to wrap the text to the Text item's width. The text will only + wrap if an explicit width has been set. wrapMode can be one of: + + \list + \o Text.NoWrap (default) - no wrapping will be performed. If the text contains insufficient newlines, then \l paintedWidth will exceed a set width. + \o Text.WordWrap - wrapping is done on word boundaries only. If a word is too long, \l paintedWidth will exceed a set width. + \o Text.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word. + \o Text.Wrap - if possible, wrapping occurs at a word boundary; otherwise it will occur at the appropriate point on the line, even in the middle of a word. + \endlist +*/ +QDeclarativeText::WrapMode QDeclarativeText::wrapMode() const +{ + Q_D(const QDeclarativeText); + return d->wrapMode; +} + +void QDeclarativeText::setWrapMode(WrapMode mode) +{ + Q_D(QDeclarativeText); + if (mode == d->wrapMode) + return; + + d->wrapMode = mode; + d->updateLayout(); + + emit wrapModeChanged(); +} + +/*! + \qmlproperty int Text::lineCount + \since QtQuick 1.1 + + Returns the number of lines visible in the text item. + + This property is not supported for rich text. + + \sa maximumLineCount +*/ +int QDeclarativeText::lineCount() const +{ + Q_D(const QDeclarativeText); + return d->lineCount; +} + +/*! + \qmlproperty bool Text::truncated + \since QtQuick 1.1 + + Returns true if the text has been truncated due to \l maximumLineCount + or \l elide. + + This property is not supported for rich text. + + \sa maximumLineCount, elide +*/ +bool QDeclarativeText::truncated() const +{ + Q_D(const QDeclarativeText); + return d->truncated; +} + +/*! + \qmlproperty int Text::maximumLineCount + \since QtQuick 1.1 + + Set this property to limit the number of lines that the text item will show. + If elide is set to Text.ElideRight, the text will be elided appropriately. + By default, this is the value of the largest possible integer. + + This property is not supported for rich text. + + \sa lineCount, elide +*/ +int QDeclarativeText::maximumLineCount() const +{ + Q_D(const QDeclarativeText); + return d->maximumLineCount; +} + +void QDeclarativeText::setMaximumLineCount(int lines) +{ + Q_D(QDeclarativeText); + + d->maximumLineCountValid = lines==INT_MAX ? false : true; + if (d->maximumLineCount != lines) { + d->maximumLineCount = lines; + d->updateLayout(); + emit maximumLineCountChanged(); + } +} + +void QDeclarativeText::resetMaximumLineCount() +{ + Q_D(QDeclarativeText); + setMaximumLineCount(INT_MAX); + d->elidePos = QPointF(); + if (d->truncated != false) { + d->truncated = false; + emit truncatedChanged(); + } +} + +/*! + \qmlproperty enumeration Text::textFormat + + The way the text property should be displayed. + + Supported text formats are: + + \list + \o Text.AutoText (default) + \o Text.PlainText + \o Text.RichText + \o Text.StyledText + \endlist + + If the text format is \c Text.AutoText the text element + will automatically determine whether the text should be treated as + rich text. This determination is made using Qt::mightBeRichText(). + + Text.StyledText is an optimized format supporting some basic text + styling markup, in the style of html 3.2: + + \code + font size and color + bold + italic +
+ > < & + \endcode + + \c Text.StyledText parser is strict, requiring tags to be correctly nested. + + \table + \row + \o + \qml +Column { + Text { + font.pointSize: 24 + text: "Hello World!" + } + Text { + font.pointSize: 24 + textFormat: Text.RichText + text: "Hello World!" + } + Text { + font.pointSize: 24 + textFormat: Text.PlainText + text: "Hello World!" + } +} + \endqml + \o \image declarative-textformat.png + \endtable +*/ +QDeclarativeText::TextFormat QDeclarativeText::textFormat() const +{ + Q_D(const QDeclarativeText); + return d->format; +} + +void QDeclarativeText::setTextFormat(TextFormat format) +{ + Q_D(QDeclarativeText); + if (format == d->format) + return; + d->format = format; + bool wasRich = d->richText; + d->richText = format == RichText || (format == AutoText && Qt::mightBeRichText(d->text)); + + if (!wasRich && d->richText && isComponentComplete()) { + d->ensureDoc(); + d->doc->setText(d->text); + } + + d->updateLayout(); + + emit textFormatChanged(d->format); +} + +/*! + \qmlproperty enumeration Text::elide + + Set this property to elide parts of the text fit to the Text item's width. + The text will only elide if an explicit width has been set. + + This property cannot be used with rich text. + + Eliding can be: + \list + \o Text.ElideNone - the default + \o Text.ElideLeft + \o Text.ElideMiddle + \o Text.ElideRight + \endlist + + If this property is set to Text.ElideRight, it can be used with multiline + text. The text will only elide if maximumLineCount has been set. + + If the text is a multi-length string, and the mode is not \c Text.ElideNone, + the first string that fits will be used, otherwise the last will be elided. + + Multi-length strings are ordered from longest to shortest, separated by the + Unicode "String Terminator" character \c U009C (write this in QML with \c{"\u009C"} or \c{"\x9C"}). +*/ +QDeclarativeText::TextElideMode QDeclarativeText::elideMode() const +{ + Q_D(const QDeclarativeText); + return d->elideMode; +} + +void QDeclarativeText::setElideMode(QDeclarativeText::TextElideMode mode) +{ + Q_D(QDeclarativeText); + if (mode == d->elideMode) + return; + + d->elideMode = mode; + d->updateLayout(); + + emit elideModeChanged(d->elideMode); +} + +/*! \internal */ +QRectF QDeclarativeText::boundingRect() const +{ + Q_D(const QDeclarativeText); + + QRect rect = d->layedOutTextRect; + if (d->style != Normal) + rect.adjust(-1, 0, 1, 2); + + // Could include font max left/right bearings to either side of rectangle. + + int h = height(); + switch (d->vAlign) { + case AlignTop: + break; + case AlignBottom: + rect.moveTop(h - rect.height()); + break; + case AlignVCenter: + rect.moveTop((h - rect.height()) / 2); + break; + } + + return QRectF(rect); +} + +/*! \internal */ +void QDeclarativeText::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_D(QDeclarativeText); + if ((!d->internalWidthUpdate && newGeometry.width() != oldGeometry.width()) + && (d->wrapMode != QDeclarativeText::NoWrap + || d->elideMode != QDeclarativeText::ElideNone + || d->hAlign != QDeclarativeText::AlignLeft)) { + if ((d->singleline || d->maximumLineCountValid) && d->elideMode != QDeclarativeText::ElideNone && widthValid()) { + // We need to re-elide + d->updateLayout(); + } else { + // We just need to re-layout + d->updateSize(); + } + } + + QDeclarativeItem::geometryChanged(newGeometry, oldGeometry); +} + +/*! + \qmlproperty real Text::paintedWidth + + Returns the width of the text, including width past the width + which is covered due to insufficient wrapping if WrapMode is set. +*/ +qreal QDeclarativeText::paintedWidth() const +{ + Q_D(const QDeclarativeText); + return d->paintedSize.width(); +} + +/*! + \qmlproperty real Text::paintedHeight + + Returns the height of the text, including height past the height + which is covered due to there being more text than fits in the set height. +*/ +qreal QDeclarativeText::paintedHeight() const +{ + Q_D(const QDeclarativeText); + return d->paintedSize.height(); +} + +/*! + \qmlproperty real Text::lineHeight + \since QtQuick 1.1 + + Sets the line height for the text. + The value can be in pixels or a multiplier depending on lineHeightMode. + + The default value is a multiplier of 1.0. + The line height must be a positive value. +*/ +qreal QDeclarativeText::lineHeight() const +{ + Q_D(const QDeclarativeText); + return d->lineHeight; +} + +void QDeclarativeText::setLineHeight(qreal lineHeight) +{ + Q_D(QDeclarativeText); + + if ((d->lineHeight == lineHeight) || (lineHeight < 0.0)) + return; + + d->lineHeight = lineHeight; + d->updateLayout(); + emit lineHeightChanged(lineHeight); +} + +/*! + \qmlproperty enumeration Text::lineHeightMode + + This property determines how the line height is specified. + The possible values are: + + \list + \o Text.ProportionalHeight (default) - this sets the spacing proportional to the + line (as a multiplier). For example, set to 2 for double spacing. + \o Text.FixedHeight - this sets the line height to a fixed line height (in pixels). + \endlist +*/ +QDeclarativeText::LineHeightMode QDeclarativeText::lineHeightMode() const +{ + Q_D(const QDeclarativeText); + return d->lineHeightMode; +} + +void QDeclarativeText::setLineHeightMode(LineHeightMode mode) +{ + Q_D(QDeclarativeText); + if (mode == d->lineHeightMode) + return; + + d->lineHeightMode = mode; + d->updateLayout(); + + emit lineHeightModeChanged(mode); +} + +/*! + Returns the number of resources (images) that are being loaded asynchronously. +*/ +int QDeclarativeText::resourcesLoading() const +{ + Q_D(const QDeclarativeText); + return d->doc ? d->doc->resourcesLoading() : 0; +} + +/*! \internal */ +void QDeclarativeText::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *) +{ + Q_D(QDeclarativeText); + + if (d->cacheAllTextAsImage || d->style != Normal) { + d->checkImageCache(); + if (d->imageCache.isNull()) + return; + + bool oldAA = p->testRenderHint(QPainter::Antialiasing); + bool oldSmooth = p->testRenderHint(QPainter::SmoothPixmapTransform); + if (d->smooth) + p->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform, d->smooth); + + QRect br = boundingRect().toRect(); + + bool needClip = clip() && (d->imageCache.width() > width() || + d->imageCache.height() > height()); + + if (needClip) + p->drawPixmap(0, 0, width(), height(), d->imageCache, -br.x(), -br.y(), width(), height()); + else + p->drawPixmap(br.x(), br.y(), d->imageCache); + + if (d->smooth) { + p->setRenderHint(QPainter::Antialiasing, oldAA); + p->setRenderHint(QPainter::SmoothPixmapTransform, oldSmooth); + } + } else { + QRectF bounds = boundingRect(); + + bool needClip = clip() && (d->layedOutTextRect.width() > width() || + d->layedOutTextRect.height() > height()); + + if (needClip) { + p->save(); + p->setClipRect(0, 0, width(), height(), Qt::IntersectClip); + } + if (d->richText) { + QAbstractTextDocumentLayout::PaintContext context; + context.palette.setColor(QPalette::Text, d->color); + p->translate(bounds.x(), bounds.y()); + d->doc->documentLayout()->draw(p, context); + p->translate(-bounds.x(), -bounds.y()); + } else { + d->drawTextLayout(p, QPointF(0, bounds.y()), false); + } + + if (needClip) { + p->restore(); + } + } +} + +/*! \internal */ +void QDeclarativeText::componentComplete() +{ + Q_D(QDeclarativeText); + QDeclarativeItem::componentComplete(); + if (d->updateOnComponentComplete) { + d->updateOnComponentComplete = false; + if (d->richText) { + d->ensureDoc(); + d->doc->setText(d->text); + d->rightToLeftText = d->doc->toPlainText().isRightToLeft(); + } else { + d->rightToLeftText = d->text.isRightToLeft(); + } + d->determineHorizontalAlignment(); + d->updateLayout(); + } +} + +/*! \internal */ +void QDeclarativeText::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarativeText); + + if (!d->richText || !d->doc || d->doc->documentLayout()->anchorAt(event->pos()).isEmpty()) { + event->setAccepted(false); + d->activeLink.clear(); + } else { + d->activeLink = d->doc->documentLayout()->anchorAt(event->pos()); + } + + // ### may malfunction if two of the same links are clicked & dragged onto each other) + + if (!event->isAccepted()) + QDeclarativeItem::mousePressEvent(event); + +} + +/*! \internal */ +void QDeclarativeText::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarativeText); + + // ### confirm the link, and send a signal out + if (d->richText && d->doc && d->activeLink == d->doc->documentLayout()->anchorAt(event->pos())) + emit linkActivated(d->activeLink); + else + event->setAccepted(false); + + if (!event->isAccepted()) + QDeclarativeItem::mouseReleaseEvent(event); +} + +QT_END_NAMESPACE + +#include "qdeclarativetext.moc" diff --git a/src/declarative/graphicsitems/qdeclarativetext_p.h b/src/declarative/graphicsitems/qdeclarativetext_p.h new file mode 100644 index 00000000..58d830e4 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativetext_p.h @@ -0,0 +1,211 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVETEXT_H +#define QDECLARATIVETEXT_H + +#include +#include "qdeclarativeimplicitsizeitem_p.h" + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QDeclarativeTextPrivate; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeText : public QDeclarativeImplicitSizeItem +{ + Q_OBJECT + Q_ENUMS(HAlignment) + Q_ENUMS(VAlignment) + Q_ENUMS(TextStyle) + Q_ENUMS(TextFormat) + Q_ENUMS(TextElideMode) + Q_ENUMS(WrapMode) + Q_ENUMS(LineHeightMode) + + Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) + Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY fontChanged) + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) + Q_PROPERTY(TextStyle style READ style WRITE setStyle NOTIFY styleChanged) + Q_PROPERTY(QColor styleColor READ styleColor WRITE setStyleColor NOTIFY styleColorChanged) + Q_PROPERTY(HAlignment horizontalAlignment READ hAlign WRITE setHAlign RESET resetHAlign NOTIFY horizontalAlignmentChanged) + Q_PROPERTY(VAlignment verticalAlignment READ vAlign WRITE setVAlign NOTIFY verticalAlignmentChanged) + Q_PROPERTY(WrapMode wrapMode READ wrapMode WRITE setWrapMode NOTIFY wrapModeChanged) + Q_PROPERTY(int lineCount READ lineCount NOTIFY lineCountChanged REVISION 1) + Q_PROPERTY(bool truncated READ truncated NOTIFY truncatedChanged REVISION 1) + Q_PROPERTY(int maximumLineCount READ maximumLineCount WRITE setMaximumLineCount NOTIFY maximumLineCountChanged RESET resetMaximumLineCount REVISION 1) + + Q_PROPERTY(TextFormat textFormat READ textFormat WRITE setTextFormat NOTIFY textFormatChanged) + Q_PROPERTY(TextElideMode elide READ elideMode WRITE setElideMode NOTIFY elideModeChanged) //### elideMode? + Q_PROPERTY(qreal paintedWidth READ paintedWidth NOTIFY paintedSizeChanged) + Q_PROPERTY(qreal paintedHeight READ paintedHeight NOTIFY paintedSizeChanged) + Q_PROPERTY(qreal lineHeight READ lineHeight WRITE setLineHeight NOTIFY lineHeightChanged REVISION 1) + Q_PROPERTY(LineHeightMode lineHeightMode READ lineHeightMode WRITE setLineHeightMode NOTIFY lineHeightModeChanged REVISION 1) + +public: + QDeclarativeText(QDeclarativeItem *parent=0); + ~QDeclarativeText(); + + enum HAlignment { AlignLeft = Qt::AlignLeft, + AlignRight = Qt::AlignRight, + AlignHCenter = Qt::AlignHCenter, + AlignJustify = Qt::AlignJustify }; // ### VERSIONING: Only in QtQuick 1.1 + enum VAlignment { AlignTop = Qt::AlignTop, + AlignBottom = Qt::AlignBottom, + AlignVCenter = Qt::AlignVCenter }; + enum TextStyle { Normal, + Outline, + Raised, + Sunken }; + enum TextFormat { PlainText = Qt::PlainText, + RichText = Qt::RichText, + AutoText = Qt::AutoText, + StyledText = 4 }; + enum TextElideMode { ElideLeft = Qt::ElideLeft, + ElideRight = Qt::ElideRight, + ElideMiddle = Qt::ElideMiddle, + ElideNone = Qt::ElideNone }; + + enum WrapMode { NoWrap = QTextOption::NoWrap, + WordWrap = QTextOption::WordWrap, + WrapAnywhere = QTextOption::WrapAnywhere, + WrapAtWordBoundaryOrAnywhere = QTextOption::WrapAtWordBoundaryOrAnywhere, // COMPAT + Wrap = QTextOption::WrapAtWordBoundaryOrAnywhere + }; + + enum LineHeightMode { ProportionalHeight, FixedHeight }; + + QString text() const; + void setText(const QString &); + + QFont font() const; + void setFont(const QFont &font); + + QColor color() const; + void setColor(const QColor &c); + + TextStyle style() const; + void setStyle(TextStyle style); + + QColor styleColor() const; + void setStyleColor(const QColor &c); + + HAlignment hAlign() const; + void setHAlign(HAlignment align); + void resetHAlign(); + HAlignment effectiveHAlign() const; + + VAlignment vAlign() const; + void setVAlign(VAlignment align); + + WrapMode wrapMode() const; + void setWrapMode(WrapMode w); + + int lineCount() const; + bool truncated() const; + + int maximumLineCount() const; + void setMaximumLineCount(int lines); + void resetMaximumLineCount(); + + TextFormat textFormat() const; + void setTextFormat(TextFormat format); + + TextElideMode elideMode() const; + void setElideMode(TextElideMode); + + qreal lineHeight() const; + void setLineHeight(qreal lineHeight); + + LineHeightMode lineHeightMode() const; + void setLineHeightMode(LineHeightMode); + + void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *); + + virtual void componentComplete(); + + int resourcesLoading() const; // mainly for testing + + qreal paintedWidth() const; + qreal paintedHeight() const; + + QRectF boundingRect() const; + +Q_SIGNALS: + void textChanged(const QString &text); + void linkActivated(const QString &link); + void fontChanged(const QFont &font); + void colorChanged(const QColor &color); + void styleChanged(TextStyle style); + void styleColorChanged(const QColor &color); + void horizontalAlignmentChanged(HAlignment alignment); + void verticalAlignmentChanged(VAlignment alignment); + void wrapModeChanged(); + Q_REVISION(1) void lineCountChanged(); + Q_REVISION(1) void truncatedChanged(); + Q_REVISION(1) void maximumLineCountChanged(); + void textFormatChanged(TextFormat textFormat); + void elideModeChanged(TextElideMode mode); + void paintedSizeChanged(); + Q_REVISION(1) void lineHeightChanged(qreal lineHeight); + Q_REVISION(1) void lineHeightModeChanged(LineHeightMode mode); + +protected: + void mousePressEvent(QGraphicsSceneMouseEvent *event); + void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); + virtual void geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry); + +private: + Q_DISABLE_COPY(QDeclarativeText) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarativeText) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeText) + +QT_END_HEADER + +#endif diff --git a/src/declarative/graphicsitems/qdeclarativetext_p_p.h b/src/declarative/graphicsitems/qdeclarativetext_p_p.h new file mode 100644 index 00000000..55447630 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativetext_p_p.h @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVETEXT_P_H +#define QDECLARATIVETEXT_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 "qdeclarativeitem.h" +#include "private/qdeclarativeimplicitsizeitem_p_p.h" +#include "private/qdeclarativetextlayout_p.h" + +#include + +#include + +QT_BEGIN_NAMESPACE + +class QTextLayout; +class QTextDocumentWithImageResources; + +class Q_AUTOTEST_EXPORT QDeclarativeTextPrivate : public QDeclarativeImplicitSizeItemPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeText) +public: + QDeclarativeTextPrivate(); + + ~QDeclarativeTextPrivate(); + + void updateSize(); + void updateLayout(); + bool determineHorizontalAlignment(); + bool setHAlign(QDeclarativeText::HAlignment, bool forceAlign = false); + void mirrorChange(); + QTextDocument *textDocument(); + + QString text; + QFont font; + QFont sourceFont; + QColor color; + QDeclarativeText::TextStyle style; + QColor styleColor; + QString activeLink; + QDeclarativeText::HAlignment hAlign; + QDeclarativeText::VAlignment vAlign; + QDeclarativeText::TextElideMode elideMode; + QDeclarativeText::TextFormat format; + QDeclarativeText::WrapMode wrapMode; + qreal lineHeight; + QDeclarativeText::LineHeightMode lineHeightMode; + int lineCount; + bool truncated; + int maximumLineCount; + int maximumLineCountValid; + QPointF elidePos; + + static QString elideChar; + + void invalidateImageCache(); + void checkImageCache(); + QPixmap imageCache; + + bool imageCacheDirty:1; + bool updateOnComponentComplete:1; + bool richText:1; + bool singleline:1; + bool cacheAllTextAsImage:1; + bool internalWidthUpdate:1; + bool requireImplicitWidth:1; + bool hAlignImplicit:1; + bool rightToLeftText:1; + bool layoutTextElided:1; + + QRect layedOutTextRect; + QSize paintedSize; + qreal naturalWidth; + virtual qreal implicitWidth() const; + void ensureDoc(); + QPixmap textDocumentImage(bool drawStyle); + QTextDocumentWithImageResources *doc; + + QRect setupTextLayout(); + QPixmap textLayoutImage(bool drawStyle); + void drawTextLayout(QPainter *p, const QPointF &pos, bool drawStyle); + QDeclarativeTextLayout layout; + + static QPixmap drawOutline(const QPixmap &source, const QPixmap &styleSource); + static QPixmap drawOutline(const QPixmap &source, const QPixmap &styleSource, int yOffset); + + static inline QDeclarativeTextPrivate *get(QDeclarativeText *t) { + return t->d_func(); + } +}; + +QT_END_NAMESPACE +#endif diff --git a/src/declarative/graphicsitems/qdeclarativetextedit.cpp b/src/declarative/graphicsitems/qdeclarativetextedit.cpp new file mode 100644 index 00000000..c23718b6 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativetextedit.cpp @@ -0,0 +1,1895 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativetextedit_p.h" +#include "private/qdeclarativetextedit_p_p.h" + +#include "private/qdeclarativeevents_p_p.h" +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmlclass TextEdit QDeclarativeTextEdit + \ingroup qml-basic-visual-elements + \since 4.7 + \brief The TextEdit item displays multiple lines of editable formatted text. + \inherits Item + + The TextEdit item displays a block of editable, formatted text. + + It can display both plain and rich text. For example: + + \qml +TextEdit { + width: 240 + text: "Hello World!" + font.family: "Helvetica" + font.pointSize: 20 + color: "blue" + focus: true +} + \endqml + + \image declarative-textedit.gif + + Setting \l {Item::focus}{focus} to \c true enables the TextEdit item to receive keyboard focus. + + Note that the TextEdit does not implement scrolling, following the cursor, or other behaviors specific + to a look-and-feel. For example, to add flickable scrolling that follows the cursor: + + \snippet snippets/declarative/texteditor.qml 0 + + A particular look-and-feel might use smooth scrolling (eg. using SmoothedFollow), might have a visible + scrollbar, or a scrollbar that fades in to show location, etc. + + Clipboard support is provided by the cut(), copy(), and paste() functions, and the selection can + be handled in a traditional "mouse" mechanism by setting selectByMouse, or handled completely + from QML by manipulating selectionStart and selectionEnd, or using selectAll() or selectWord(). + + You can translate between cursor positions (characters from the start of the document) and pixel + points using positionAt() and positionToRectangle(). + + \sa Text, TextInput, {declarative/text/textselection}{Text Selection example} +*/ + +/*! + \qmlsignal TextEdit::onLinkActivated(string link) + \since QtQuick 1.1 + + This handler is called when the user clicks on a link embedded in the text. + The link must be in rich text or HTML format and the + \a link string provides access to the particular link. +*/ +QDeclarativeTextEdit::QDeclarativeTextEdit(QDeclarativeItem *parent) +: QDeclarativeImplicitSizePaintedItem(*(new QDeclarativeTextEditPrivate), parent) +{ + Q_D(QDeclarativeTextEdit); + d->init(); +} + +QString QDeclarativeTextEdit::text() const +{ + Q_D(const QDeclarativeTextEdit); + +#ifndef QT_NO_TEXTHTMLPARSER + if (d->richText) + return d->document->toHtml(); + else +#endif + return d->document->toPlainText(); +} + +/*! + \qmlproperty string TextEdit::font.family + + Sets the family name of the font. + + The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]". + If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen. + If the family isn't available a family will be set using the font matching algorithm. +*/ + +/*! + \qmlproperty bool TextEdit::font.bold + + Sets whether the font weight is bold. +*/ + +/*! + \qmlproperty enumeration TextEdit::font.weight + + Sets the font's weight. + + The weight can be one of: + \list + \o Font.Light + \o Font.Normal - the default + \o Font.DemiBold + \o Font.Bold + \o Font.Black + \endlist + + \qml + TextEdit { text: "Hello"; font.weight: Font.DemiBold } + \endqml +*/ + +/*! + \qmlproperty bool TextEdit::font.italic + + Sets whether the font has an italic style. +*/ + +/*! + \qmlproperty bool TextEdit::font.underline + + Sets whether the text is underlined. +*/ + +/*! + \qmlproperty bool TextEdit::font.strikeout + + Sets whether the font has a strikeout style. +*/ + +/*! + \qmlproperty real TextEdit::font.pointSize + + Sets the font size in points. The point size must be greater than zero. +*/ + +/*! + \qmlproperty int TextEdit::font.pixelSize + + Sets the font size in pixels. + + Using this function makes the font device dependent. Use + \l{TextEdit::font.pointSize} to set the size of the font in a + device independent manner. +*/ + +/*! + \qmlproperty real TextEdit::font.letterSpacing + + Sets the letter spacing for the font. + + Letter spacing changes the default spacing between individual letters in the font. + A positive value increases the letter spacing by the corresponding pixels; a negative value decreases the spacing. +*/ + +/*! + \qmlproperty real TextEdit::font.wordSpacing + + Sets the word spacing for the font. + + Word spacing changes the default spacing between individual words. + A positive value increases the word spacing by a corresponding amount of pixels, + while a negative value decreases the inter-word spacing accordingly. +*/ + +/*! + \qmlproperty enumeration TextEdit::font.capitalization + + Sets the capitalization for the text. + + \list + \o Font.MixedCase - This is the normal text rendering option where no capitalization change is applied. + \o Font.AllUppercase - This alters the text to be rendered in all uppercase type. + \o Font.AllLowercase - This alters the text to be rendered in all lowercase type. + \o Font.SmallCaps - This alters the text to be rendered in small-caps type. + \o Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character. + \endlist + + \qml + TextEdit { text: "Hello"; font.capitalization: Font.AllLowercase } + \endqml +*/ + +/*! + \qmlproperty string TextEdit::text + + The text to display. If the text format is AutoText the text edit will + automatically determine whether the text should be treated as + rich text. This determination is made using Qt::mightBeRichText(). +*/ +void QDeclarativeTextEdit::setText(const QString &text) +{ + Q_D(QDeclarativeTextEdit); + if (QDeclarativeTextEdit::text() == text) + return; + + d->richText = d->format == RichText || (d->format == AutoText && Qt::mightBeRichText(text)); + if (d->richText) { +#ifndef QT_NO_TEXTHTMLPARSER + d->control->setHtml(text); +#else + d->control->setPlainText(text); +#endif + } else { + d->control->setPlainText(text); + } + q_textChanged(); +} + +/*! + \qmlproperty enumeration TextEdit::textFormat + + The way the text property should be displayed. + + \list + \o TextEdit.AutoText + \o TextEdit.PlainText + \o TextEdit.RichText + \endlist + + The default is TextEdit.AutoText. If the text format is TextEdit.AutoText the text edit + will automatically determine whether the text should be treated as + rich text. This determination is made using Qt::mightBeRichText(). + + \table + \row + \o + \qml +Column { + TextEdit { + font.pointSize: 24 + text: "Hello World!" + } + TextEdit { + font.pointSize: 24 + textFormat: TextEdit.RichText + text: "Hello World!" + } + TextEdit { + font.pointSize: 24 + textFormat: TextEdit.PlainText + text: "Hello World!" + } +} + \endqml + \o \image declarative-textformat.png + \endtable +*/ +QDeclarativeTextEdit::TextFormat QDeclarativeTextEdit::textFormat() const +{ + Q_D(const QDeclarativeTextEdit); + return d->format; +} + +void QDeclarativeTextEdit::setTextFormat(TextFormat format) +{ + Q_D(QDeclarativeTextEdit); + if (format == d->format) + return; + bool wasRich = d->richText; + d->richText = format == RichText || (format == AutoText && Qt::mightBeRichText(d->text)); + + if (wasRich && !d->richText) { + d->control->setPlainText(d->text); + updateSize(); + } else if (!wasRich && d->richText) { +#ifndef QT_NO_TEXTHTMLPARSER + d->control->setHtml(d->text); +#else + d->control->setPlainText(d->text); +#endif + updateSize(); + } + d->format = format; + d->control->setAcceptRichText(d->format != PlainText); + emit textFormatChanged(d->format); +} + +QFont QDeclarativeTextEdit::font() const +{ + Q_D(const QDeclarativeTextEdit); + return d->sourceFont; +} + +void QDeclarativeTextEdit::setFont(const QFont &font) +{ + Q_D(QDeclarativeTextEdit); + if (d->sourceFont == font) + return; + + d->sourceFont = font; + QFont oldFont = d->font; + d->font = font; + if (d->font.pointSizeF() != -1) { + // 0.5pt resolution + qreal size = qRound(d->font.pointSizeF()*2.0); + d->font.setPointSizeF(size/2.0); + } + + if (oldFont != d->font) { + clearCache(); + d->document->setDefaultFont(d->font); + if(d->cursor){ + d->cursor->setHeight(QFontMetrics(d->font).height()); + moveCursorDelegate(); + } + updateSize(); + update(); + } + emit fontChanged(d->sourceFont); +} + +/*! + \qmlproperty color TextEdit::color + + The text color. + + \qml + // green text using hexadecimal notation + TextEdit { color: "#00FF00" } + \endqml + + \qml + // steelblue text using SVG color name + TextEdit { color: "steelblue" } + \endqml +*/ +QColor QDeclarativeTextEdit::color() const +{ + Q_D(const QDeclarativeTextEdit); + return d->color; +} + +void QDeclarativeTextEdit::setColor(const QColor &color) +{ + Q_D(QDeclarativeTextEdit); + if (d->color == color) + return; + + clearCache(); + d->color = color; + QPalette pal = d->control->palette(); + pal.setColor(QPalette::Text, color); + d->control->setPalette(pal); + update(); + emit colorChanged(d->color); +} + +/*! + \qmlproperty color TextEdit::selectionColor + + The text highlight color, used behind selections. +*/ +QColor QDeclarativeTextEdit::selectionColor() const +{ + Q_D(const QDeclarativeTextEdit); + return d->selectionColor; +} + +void QDeclarativeTextEdit::setSelectionColor(const QColor &color) +{ + Q_D(QDeclarativeTextEdit); + if (d->selectionColor == color) + return; + + clearCache(); + d->selectionColor = color; + QPalette pal = d->control->palette(); + pal.setColor(QPalette::Highlight, color); + d->control->setPalette(pal); + update(); + emit selectionColorChanged(d->selectionColor); +} + +/*! + \qmlproperty color TextEdit::selectedTextColor + + The selected text color, used in selections. +*/ +QColor QDeclarativeTextEdit::selectedTextColor() const +{ + Q_D(const QDeclarativeTextEdit); + return d->selectedTextColor; +} + +void QDeclarativeTextEdit::setSelectedTextColor(const QColor &color) +{ + Q_D(QDeclarativeTextEdit); + if (d->selectedTextColor == color) + return; + + clearCache(); + d->selectedTextColor = color; + QPalette pal = d->control->palette(); + pal.setColor(QPalette::HighlightedText, color); + d->control->setPalette(pal); + update(); + emit selectedTextColorChanged(d->selectedTextColor); +} + +/*! + \qmlproperty enumeration TextEdit::horizontalAlignment + \qmlproperty enumeration TextEdit::verticalAlignment + + Sets the horizontal and vertical alignment of the text within the TextEdit item's + width and height. By default, the text alignment follows the natural alignment + of the text, for example text that is read from left to right will be aligned to + the left. + + Valid values for \c horizontalAlignment are: + \list + \o TextEdit.AlignLeft (default) + \o TextEdit.AlignRight + \o TextEdit.AlignHCenter + \o TextEdit.AlignJustify + \endlist + + Valid values for \c verticalAlignment are: + \list + \o TextEdit.AlignTop (default) + \o TextEdit.AlignBottom + \o TextEdit.AlignVCenter + \endlist + + When using the attached property \l {LayoutMirroring::enabled} to mirror application + layouts, the horizontal alignment of text will also be mirrored. However, the property + \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment + of TextEdit, use the property \l {LayoutMirroring::enabled}. +*/ +QDeclarativeTextEdit::HAlignment QDeclarativeTextEdit::hAlign() const +{ + Q_D(const QDeclarativeTextEdit); + return d->hAlign; +} + +void QDeclarativeTextEdit::setHAlign(HAlignment align) +{ + Q_D(QDeclarativeTextEdit); + bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror; + d->hAlignImplicit = false; + if (d->setHAlign(align, forceAlign) && isComponentComplete()) { + d->updateDefaultTextOption(); + updateSize(); + } +} + +void QDeclarativeTextEdit::resetHAlign() +{ + Q_D(QDeclarativeTextEdit); + d->hAlignImplicit = true; + if (d->determineHorizontalAlignment() && isComponentComplete()) { + d->updateDefaultTextOption(); + updateSize(); + } +} + +QDeclarativeTextEdit::HAlignment QDeclarativeTextEdit::effectiveHAlign() const +{ + Q_D(const QDeclarativeTextEdit); + QDeclarativeTextEdit::HAlignment effectiveAlignment = d->hAlign; + if (!d->hAlignImplicit && d->effectiveLayoutMirror) { + switch (d->hAlign) { + case QDeclarativeTextEdit::AlignLeft: + effectiveAlignment = QDeclarativeTextEdit::AlignRight; + break; + case QDeclarativeTextEdit::AlignRight: + effectiveAlignment = QDeclarativeTextEdit::AlignLeft; + break; + default: + break; + } + } + return effectiveAlignment; +} + +bool QDeclarativeTextEditPrivate::setHAlign(QDeclarativeTextEdit::HAlignment alignment, bool forceAlign) +{ + Q_Q(QDeclarativeTextEdit); + if (hAlign != alignment || forceAlign) { + QDeclarativeTextEdit::HAlignment oldEffectiveHAlign = q->effectiveHAlign(); + hAlign = alignment; + emit q->horizontalAlignmentChanged(alignment); + return true; + } + return false; +} + +bool QDeclarativeTextEditPrivate::determineHorizontalAlignment() +{ + Q_Q(QDeclarativeTextEdit); + if (hAlignImplicit && q->isComponentComplete()) { + bool alignToRight; + if (text.isEmpty()) { + const QString preeditText = control->textCursor().block().layout()->preeditAreaText(); + alignToRight = preeditText.isEmpty() + ? QApplication::keyboardInputDirection() == Qt::RightToLeft + : preeditText.isRightToLeft(); + } else { + alignToRight = rightToLeftText; + } + return setHAlign(alignToRight ? QDeclarativeTextEdit::AlignRight : QDeclarativeTextEdit::AlignLeft); + } + return false; +} + +void QDeclarativeTextEditPrivate::mirrorChange() +{ + Q_Q(QDeclarativeTextEdit); + if (q->isComponentComplete()) { + if (!hAlignImplicit && (hAlign == QDeclarativeTextEdit::AlignRight || hAlign == QDeclarativeTextEdit::AlignLeft)) { + updateDefaultTextOption(); + q->updateSize(); + } + } +} + +QDeclarativeTextEdit::VAlignment QDeclarativeTextEdit::vAlign() const +{ + Q_D(const QDeclarativeTextEdit); + return d->vAlign; +} + +void QDeclarativeTextEdit::setVAlign(QDeclarativeTextEdit::VAlignment alignment) +{ + Q_D(QDeclarativeTextEdit); + if (alignment == d->vAlign) + return; + d->vAlign = alignment; + d->updateDefaultTextOption(); + updateSize(); + moveCursorDelegate(); + emit verticalAlignmentChanged(d->vAlign); +} + +/*! + \qmlproperty enumeration TextEdit::wrapMode + + Set this property to wrap the text to the TextEdit item's width. + The text will only wrap if an explicit width has been set. + + \list + \o TextEdit.NoWrap - no wrapping will be performed. If the text contains insufficient newlines, then implicitWidth will exceed a set width. + \o TextEdit.WordWrap - wrapping is done on word boundaries only. If a word is too long, implicitWidth will exceed a set width. + \o TextEdit.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word. + \o TextEdit.Wrap - if possible, wrapping occurs at a word boundary; otherwise it will occur at the appropriate point on the line, even in the middle of a word. + \endlist + + The default is TextEdit.NoWrap. If you set a width, consider using TextEdit.Wrap. +*/ +QDeclarativeTextEdit::WrapMode QDeclarativeTextEdit::wrapMode() const +{ + Q_D(const QDeclarativeTextEdit); + return d->wrapMode; +} + +void QDeclarativeTextEdit::setWrapMode(WrapMode mode) +{ + Q_D(QDeclarativeTextEdit); + if (mode == d->wrapMode) + return; + d->wrapMode = mode; + d->updateDefaultTextOption(); + updateSize(); + emit wrapModeChanged(); +} + +/*! + \qmlproperty int TextEdit::lineCount + \since QtQuick 1.1 + + Returns the total number of lines in the textEdit item. +*/ +int QDeclarativeTextEdit::lineCount() const +{ + Q_D(const QDeclarativeTextEdit); + return d->lineCount; +} + +/*! + \qmlproperty real TextEdit::paintedWidth + + Returns the width of the text, including the width past the width + which is covered due to insufficient wrapping if \l wrapMode is set. +*/ +qreal QDeclarativeTextEdit::paintedWidth() const +{ + Q_D(const QDeclarativeTextEdit); + return d->paintedSize.width(); +} + +/*! + \qmlproperty real TextEdit::paintedHeight + + Returns the height of the text, including the height past the height + that is covered if the text does not fit within the set height. +*/ +qreal QDeclarativeTextEdit::paintedHeight() const +{ + Q_D(const QDeclarativeTextEdit); + return d->paintedSize.height(); +} + +/*! + \qmlmethod rectangle TextEdit::positionToRectangle(position) + + Returns the rectangle at the given \a position in the text. The x, y, + and height properties correspond to the cursor that would describe + that position. +*/ +QRectF QDeclarativeTextEdit::positionToRectangle(int pos) const +{ + Q_D(const QDeclarativeTextEdit); + QTextCursor c(d->document); + c.setPosition(pos); + return d->control->cursorRect(c); + +} + +/*! + \qmlmethod int TextEdit::positionAt(int x, int y) + + Returns the text position closest to pixel position (\a x, \a y). + + Position 0 is before the first character, position 1 is after the first character + but before the second, and so on until position \l {text}.length, which is after all characters. +*/ +int QDeclarativeTextEdit::positionAt(int x, int y) const +{ + Q_D(const QDeclarativeTextEdit); + int r = d->document->documentLayout()->hitTest(QPoint(x,y-d->yoff), Qt::FuzzyHit); + QTextCursor cursor = d->control->textCursor(); + if (r > cursor.position()) { + // The cursor position includes positions within the preedit text, but only positions in the + // same text block are offset so it is possible to get a position that is either part of the + // preedit or the next text block. + QTextLayout *layout = cursor.block().layout(); + const int preeditLength = layout + ? layout->preeditAreaText().length() + : 0; + if (preeditLength > 0 + && d->document->documentLayout()->blockBoundingRect(cursor.block()).contains(x,y-d->yoff)) { + r = r > cursor.position() + preeditLength + ? r - preeditLength + : cursor.position(); + } + } + return r; +} + +void QDeclarativeTextEdit::moveCursorSelection(int pos) +{ + //Note that this is the same as setCursorPosition but with the KeepAnchor flag set + Q_D(QDeclarativeTextEdit); + QTextCursor cursor = d->control->textCursor(); + if (cursor.position() == pos) + return; + cursor.setPosition(pos, QTextCursor::KeepAnchor); + d->control->setTextCursor(cursor); +} + +/*! + \qmlmethod void TextEdit::moveCursorSelection(int position, SelectionMode mode = TextEdit.SelectCharacters) + \since QtQuick 1.1 + + Moves the cursor to \a position and updates the selection according to the optional \a mode + parameter. (To only move the cursor, set the \l cursorPosition property.) + + When this method is called it additionally sets either the + selectionStart or the selectionEnd (whichever was at the previous cursor position) + to the specified position. This allows you to easily extend and contract the selected + text range. + + The selection mode specifies whether the selection is updated on a per character or a per word + basis. If not specified the selection mode will default to TextEdit.SelectCharacters. + + \list + \o TextEdit.SelectCharacters - Sets either the selectionStart or selectionEnd (whichever was at + the previous cursor position) to the specified position. + \o TextEdit.SelectWords - Sets the selectionStart and selectionEnd to include all + words between the specified postion and the previous cursor position. Words partially in the + range are included. + \endlist + + For example, take this sequence of calls: + + \code + cursorPosition = 5 + moveCursorSelection(9, TextEdit.SelectCharacters) + moveCursorSelection(7, TextEdit.SelectCharacters) + \endcode + + This moves the cursor to position 5, extend the selection end from 5 to 9 + and then retract the selection end from 9 to 7, leaving the text from position 5 to 7 + selected (the 6th and 7th characters). + + The same sequence with TextEdit.SelectWords will extend the selection start to a word boundary + before or on position 5 and extend the selection end to a word boundary on or past position 9. +*/ +void QDeclarativeTextEdit::moveCursorSelection(int pos, SelectionMode mode) +{ + Q_D(QDeclarativeTextEdit); + QTextCursor cursor = d->control->textCursor(); + if (cursor.position() == pos) + return; + if (mode == SelectCharacters) { + cursor.setPosition(pos, QTextCursor::KeepAnchor); + } else if (cursor.anchor() < pos || (cursor.anchor() == pos && cursor.position() < pos)) { + if (cursor.anchor() > cursor.position()) { + cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor); + cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor); + if (cursor.position() == cursor.anchor()) + cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::MoveAnchor); + else + cursor.setPosition(cursor.position(), QTextCursor::MoveAnchor); + } else { + cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor); + cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::MoveAnchor); + } + + cursor.setPosition(pos, QTextCursor::KeepAnchor); + cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor); + if (cursor.position() != pos) + cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor); + } else if (cursor.anchor() > pos || (cursor.anchor() == pos && cursor.position() > pos)) { + if (cursor.anchor() < cursor.position()) { + cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor); + cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::MoveAnchor); + } else { + cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor); + cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor); + cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor); + if (cursor.position() != cursor.anchor()) { + cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor); + cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::MoveAnchor); + } + } + + cursor.setPosition(pos, QTextCursor::KeepAnchor); + cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor); + if (cursor.position() != pos) { + cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor); + cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor); + } + } + d->control->setTextCursor(cursor); +} + +/*! + \qmlproperty bool TextEdit::cursorVisible + If true the text edit shows a cursor. + + This property is set and unset when the text edit gets active focus, but it can also + be set directly (useful, for example, if a KeyProxy might forward keys to it). +*/ +bool QDeclarativeTextEdit::isCursorVisible() const +{ + Q_D(const QDeclarativeTextEdit); + return d->cursorVisible; +} + +void QDeclarativeTextEdit::setCursorVisible(bool on) +{ + Q_D(QDeclarativeTextEdit); + if (d->cursorVisible == on) + return; + d->cursorVisible = on; + QFocusEvent focusEvent(on ? QEvent::FocusIn : QEvent::FocusOut); + if (!on && !d->persistentSelection) + d->control->setCursorIsFocusIndicator(true); + d->control->processEvent(&focusEvent, QPointF(0, -d->yoff)); + emit cursorVisibleChanged(d->cursorVisible); +} + +/*! + \qmlproperty int TextEdit::cursorPosition + The position of the cursor in the TextEdit. +*/ +int QDeclarativeTextEdit::cursorPosition() const +{ + Q_D(const QDeclarativeTextEdit); + return d->control->textCursor().position(); +} + +void QDeclarativeTextEdit::setCursorPosition(int pos) +{ + Q_D(QDeclarativeTextEdit); + if (pos < 0 || pos > d->text.length()) + return; + QTextCursor cursor = d->control->textCursor(); + if (cursor.position() == pos && cursor.anchor() == pos) + return; + cursor.setPosition(pos); + d->control->setTextCursor(cursor); +} + +/*! + \qmlproperty Component TextEdit::cursorDelegate + The delegate for the cursor in the TextEdit. + + If you set a cursorDelegate for a TextEdit, this delegate will be used for + drawing the cursor instead of the standard cursor. An instance of the + delegate will be created and managed by the text edit when a cursor is + needed, and the x and y properties of delegate instance will be set so as + to be one pixel before the top left of the current character. + + Note that the root item of the delegate component must be a QDeclarativeItem or + QDeclarativeItem derived item. +*/ +QDeclarativeComponent* QDeclarativeTextEdit::cursorDelegate() const +{ + Q_D(const QDeclarativeTextEdit); + return d->cursorComponent; +} + +void QDeclarativeTextEdit::setCursorDelegate(QDeclarativeComponent* c) +{ + Q_D(QDeclarativeTextEdit); + if(d->cursorComponent){ + if(d->cursor){ + d->control->setCursorWidth(-1); + dirtyCache(cursorRectangle()); + delete d->cursor; + d->cursor = 0; + } + } + d->cursorComponent = c; + if(c && c->isReady()){ + loadCursorDelegate(); + }else{ + if(c) + connect(c, SIGNAL(statusChanged()), + this, SLOT(loadCursorDelegate())); + } + + emit cursorDelegateChanged(); +} + +void QDeclarativeTextEdit::loadCursorDelegate() +{ + Q_D(QDeclarativeTextEdit); + if(d->cursorComponent->isLoading()) + return; + d->cursor = qobject_cast(d->cursorComponent->create(qmlContext(this))); + if(d->cursor){ + d->control->setCursorWidth(0); + dirtyCache(cursorRectangle()); + QDeclarative_setParent_noEvent(d->cursor, this); + d->cursor->setParentItem(this); + d->cursor->setHeight(QFontMetrics(d->font).height()); + moveCursorDelegate(); + }else{ + qmlInfo(this) << "Error loading cursor delegate."; + } +} + +/*! + \qmlproperty int TextEdit::selectionStart + + The cursor position before the first character in the current selection. + + This property is read-only. To change the selection, use select(start,end), + selectAll(), or selectWord(). + + \sa selectionEnd, cursorPosition, selectedText +*/ +int QDeclarativeTextEdit::selectionStart() const +{ + Q_D(const QDeclarativeTextEdit); + return d->control->textCursor().selectionStart(); +} + +/*! + \qmlproperty int TextEdit::selectionEnd + + The cursor position after the last character in the current selection. + + This property is read-only. To change the selection, use select(start,end), + selectAll(), or selectWord(). + + \sa selectionStart, cursorPosition, selectedText +*/ +int QDeclarativeTextEdit::selectionEnd() const +{ + Q_D(const QDeclarativeTextEdit); + return d->control->textCursor().selectionEnd(); +} + +/*! + \qmlproperty string TextEdit::selectedText + + This read-only property provides the text currently selected in the + text edit. + + It is equivalent to the following snippet, but is faster and easier + to use. + \code + //myTextEdit is the id of the TextEdit + myTextEdit.text.toString().substring(myTextEdit.selectionStart, + myTextEdit.selectionEnd); + \endcode +*/ +QString QDeclarativeTextEdit::selectedText() const +{ + Q_D(const QDeclarativeTextEdit); + return d->control->textCursor().selectedText(); +} + +/*! + \qmlproperty bool TextEdit::activeFocusOnPress + + Whether the TextEdit should gain active focus on a mouse press. By default this is + set to true. +*/ +bool QDeclarativeTextEdit::focusOnPress() const +{ + Q_D(const QDeclarativeTextEdit); + return d->focusOnPress; +} + +void QDeclarativeTextEdit::setFocusOnPress(bool on) +{ + Q_D(QDeclarativeTextEdit); + if (d->focusOnPress == on) + return; + d->focusOnPress = on; + emit activeFocusOnPressChanged(d->focusOnPress); +} + +/*! + \qmlproperty bool TextEdit::persistentSelection + + Whether the TextEdit should keep the selection visible when it loses active focus to another + item in the scene. By default this is set to true; +*/ +bool QDeclarativeTextEdit::persistentSelection() const +{ + Q_D(const QDeclarativeTextEdit); + return d->persistentSelection; +} + +void QDeclarativeTextEdit::setPersistentSelection(bool on) +{ + Q_D(QDeclarativeTextEdit); + if (d->persistentSelection == on) + return; + d->persistentSelection = on; + emit persistentSelectionChanged(d->persistentSelection); +} + +/* + \qmlproperty real TextEdit::textMargin + + The margin, in pixels, around the text in the TextEdit. +*/ +qreal QDeclarativeTextEdit::textMargin() const +{ + Q_D(const QDeclarativeTextEdit); + return d->textMargin; +} + +void QDeclarativeTextEdit::setTextMargin(qreal margin) +{ + Q_D(QDeclarativeTextEdit); + if (d->textMargin == margin) + return; + d->textMargin = margin; + d->document->setDocumentMargin(d->textMargin); + emit textMarginChanged(d->textMargin); +} + +void QDeclarativeTextEdit::geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry) +{ + if (newGeometry.width() != oldGeometry.width()) + updateSize(); + QDeclarativePaintedItem::geometryChanged(newGeometry, oldGeometry); +} + +/*! + Ensures any delayed caching or data loading the class + needs to performed is complete. +*/ +void QDeclarativeTextEdit::componentComplete() +{ + Q_D(QDeclarativeTextEdit); + QDeclarativePaintedItem::componentComplete(); + if (d->dirty) { + d->determineHorizontalAlignment(); + d->updateDefaultTextOption(); + updateSize(); + d->dirty = false; + } +} + +/*! + \qmlproperty bool TextEdit::selectByMouse + + Defaults to false. + + If true, the user can use the mouse to select text in some + platform-specific way. Note that for some platforms this may + not be an appropriate interaction (eg. may conflict with how + the text needs to behave inside a Flickable. +*/ +bool QDeclarativeTextEdit::selectByMouse() const +{ + Q_D(const QDeclarativeTextEdit); + return d->selectByMouse; +} + +void QDeclarativeTextEdit::setSelectByMouse(bool on) +{ + Q_D(QDeclarativeTextEdit); + if (d->selectByMouse != on) { + d->selectByMouse = on; + setKeepMouseGrab(on); + if (on) + setTextInteractionFlags(d->control->textInteractionFlags() | Qt::TextSelectableByMouse); + else + setTextInteractionFlags(d->control->textInteractionFlags() & ~Qt::TextSelectableByMouse); + emit selectByMouseChanged(on); + } +} + + +/*! + \qmlproperty enum TextEdit::mouseSelectionMode + \since QtQuick 1.1 + + Specifies how text should be selected using a mouse. + + \list + \o TextEdit.SelectCharacters - The selection is updated with individual characters. (Default) + \o TextEdit.SelectWords - The selection is updated with whole words. + \endlist + + This property only applies when \l selectByMouse is true. +*/ + +QDeclarativeTextEdit::SelectionMode QDeclarativeTextEdit::mouseSelectionMode() const +{ + Q_D(const QDeclarativeTextEdit); + return d->mouseSelectionMode; +} + +void QDeclarativeTextEdit::setMouseSelectionMode(SelectionMode mode) +{ + Q_D(QDeclarativeTextEdit); + if (d->mouseSelectionMode != mode) { + d->mouseSelectionMode = mode; + d->control->setWordSelectionEnabled(mode == SelectWords); + emit mouseSelectionModeChanged(mode); + } +} + +/*! + \qmlproperty bool TextEdit::readOnly + + Whether the user an interact with the TextEdit item. If this + property is set to true the text cannot be edited by user interaction. + + By default this property is false. +*/ +void QDeclarativeTextEdit::setReadOnly(bool r) +{ + Q_D(QDeclarativeTextEdit); + if (r == isReadOnly()) + return; + + setFlag(QGraphicsItem::ItemAcceptsInputMethod, !r); + + Qt::TextInteractionFlags flags = Qt::LinksAccessibleByMouse; + if (d->selectByMouse) + flags = flags | Qt::TextSelectableByMouse; + if (!r) + flags = flags | Qt::TextSelectableByKeyboard | Qt::TextEditable; + d->control->setTextInteractionFlags(flags); + if (!r) + d->control->moveCursor(QTextCursor::End); + + emit readOnlyChanged(r); +} + +bool QDeclarativeTextEdit::isReadOnly() const +{ + Q_D(const QDeclarativeTextEdit); + return !(d->control->textInteractionFlags() & Qt::TextEditable); +} + +/*! + Sets how the text edit should interact with user input to the given + \a flags. +*/ +void QDeclarativeTextEdit::setTextInteractionFlags(Qt::TextInteractionFlags flags) +{ + Q_D(QDeclarativeTextEdit); + d->control->setTextInteractionFlags(flags); +} + +/*! + Returns the flags specifying how the text edit should interact + with user input. +*/ +Qt::TextInteractionFlags QDeclarativeTextEdit::textInteractionFlags() const +{ + Q_D(const QDeclarativeTextEdit); + return d->control->textInteractionFlags(); +} + +/*! + \qmlproperty rectangle TextEdit::cursorRectangle + + The rectangle where the text cursor is rendered + within the text edit. Read-only. +*/ +QRect QDeclarativeTextEdit::cursorRectangle() const +{ + Q_D(const QDeclarativeTextEdit); + return d->control->cursorRect().toRect().translated(0,d->yoff); +} + + +/*! +\overload +Handles the given \a event. +*/ +bool QDeclarativeTextEdit::event(QEvent *event) +{ + Q_D(QDeclarativeTextEdit); + if (event->type() == QEvent::ShortcutOverride) { + d->control->processEvent(event, QPointF(0, -d->yoff)); + return event->isAccepted(); + } + return QDeclarativePaintedItem::event(event); +} + +/*! +\overload +Handles the given key \a event. +*/ +void QDeclarativeTextEdit::keyPressEvent(QKeyEvent *event) +{ + Q_D(QDeclarativeTextEdit); + keyPressPreHandler(event); + if (!event->isAccepted()) + d->control->processEvent(event, QPointF(0, -d->yoff)); + if (!event->isAccepted()) + QDeclarativePaintedItem::keyPressEvent(event); +} + +/*! +\overload +Handles the given key \a event. +*/ +void QDeclarativeTextEdit::keyReleaseEvent(QKeyEvent *event) +{ + Q_D(QDeclarativeTextEdit); + keyReleasePreHandler(event); + if (!event->isAccepted()) + d->control->processEvent(event, QPointF(0, -d->yoff)); + if (!event->isAccepted()) + QDeclarativePaintedItem::keyReleaseEvent(event); +} + +void QDeclarativeTextEditPrivate::focusChanged(bool hasFocus) +{ + Q_Q(QDeclarativeTextEdit); + q->setCursorVisible(hasFocus && scene && scene->hasFocus()); + QDeclarativeItemPrivate::focusChanged(hasFocus); +} + +/*! + \qmlmethod void TextEdit::deselect() + \since QtQuick 1.1 + + Removes active text selection. +*/ +void QDeclarativeTextEdit::deselect() +{ + Q_D(QDeclarativeTextEdit); + QTextCursor c = d->control->textCursor(); + c.clearSelection(); + d->control->setTextCursor(c); +} + +/*! + \qmlmethod void TextEdit::selectAll() + + Causes all text to be selected. +*/ +void QDeclarativeTextEdit::selectAll() +{ + Q_D(QDeclarativeTextEdit); + d->control->selectAll(); +} + +/*! + \qmlmethod void TextEdit::selectWord() + + Causes the word closest to the current cursor position to be selected. +*/ +void QDeclarativeTextEdit::selectWord() +{ + Q_D(QDeclarativeTextEdit); + QTextCursor c = d->control->textCursor(); + c.select(QTextCursor::WordUnderCursor); + d->control->setTextCursor(c); +} + +/*! + \qmlmethod void TextEdit::select(int start, int end) + + Causes the text from \a start to \a end to be selected. + + If either start or end is out of range, the selection is not changed. + + After calling this, selectionStart will become the lesser + and selectionEnd will become the greater (regardless of the order passed + to this method). + + \sa selectionStart, selectionEnd +*/ +void QDeclarativeTextEdit::select(int start, int end) +{ + Q_D(QDeclarativeTextEdit); + if (start < 0 || end < 0 || start > d->text.length() || end > d->text.length()) + return; + QTextCursor cursor = d->control->textCursor(); + cursor.beginEditBlock(); + cursor.setPosition(start, QTextCursor::MoveAnchor); + cursor.setPosition(end, QTextCursor::KeepAnchor); + cursor.endEditBlock(); + d->control->setTextCursor(cursor); + + // QTBUG-11100 + updateSelectionMarkers(); +} + +/*! + \qmlmethod void TextEdit::isRightToLeft(int start, int end) + + Returns true if the natural reading direction of the editor text + found between positions \a start and \a end is right to left. +*/ +bool QDeclarativeTextEdit::isRightToLeft(int start, int end) +{ + Q_D(QDeclarativeTextEdit); + if (start > end) { + qmlInfo(this) << "isRightToLeft(start, end) called with the end property being smaller than the start."; + return false; + } else { + return d->text.mid(start, end - start).isRightToLeft(); + } +} + +#ifndef QT_NO_CLIPBOARD +/*! + \qmlmethod TextEdit::cut() + + Moves the currently selected text to the system clipboard. +*/ +void QDeclarativeTextEdit::cut() +{ + Q_D(QDeclarativeTextEdit); + d->control->cut(); +} + +/*! + \qmlmethod TextEdit::copy() + + Copies the currently selected text to the system clipboard. +*/ +void QDeclarativeTextEdit::copy() +{ + Q_D(QDeclarativeTextEdit); + d->control->copy(); +} + +/*! + \qmlmethod TextEdit::paste() + + Replaces the currently selected text by the contents of the system clipboard. +*/ +void QDeclarativeTextEdit::paste() +{ + Q_D(QDeclarativeTextEdit); + d->control->paste(); +} +#endif // QT_NO_CLIPBOARD + +/*! +\overload +Handles the given mouse \a event. +*/ +void QDeclarativeTextEdit::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarativeTextEdit); + if (d->focusOnPress){ + bool hadActiveFocus = hasActiveFocus(); + forceActiveFocus(); + if (d->showInputPanelOnFocus) { + if (hasActiveFocus() && hadActiveFocus && !isReadOnly()) { + // re-open input panel on press if already focused + openSoftwareInputPanel(); + } + } else { // show input panel on click + if (hasActiveFocus() && !hadActiveFocus) { + d->clickCausedFocus = true; + } + } + } + + d->control->processEvent(event, QPointF(0, -d->yoff)); + if (!event->isAccepted()) + QDeclarativePaintedItem::mousePressEvent(event); +} + +/*! +\overload +Handles the given mouse \a event. +*/ +void QDeclarativeTextEdit::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarativeTextEdit); + d->control->processEvent(event, QPointF(0, -d->yoff)); + if (!d->showInputPanelOnFocus) { // input panel on click + if (d->focusOnPress && !isReadOnly() && boundingRect().contains(event->pos())) { + if (QGraphicsView * view = qobject_cast(qApp->focusWidget())) { + if (view->scene() && view->scene() == scene()) { + qt_widget_private(view)->handleSoftwareInputPanel(event->button(), d->clickCausedFocus); + } + } + } + } + d->clickCausedFocus = false; + + if (!event->isAccepted()) + QDeclarativePaintedItem::mouseReleaseEvent(event); +} + +/*! +\overload +Handles the given mouse \a event. +*/ +void QDeclarativeTextEdit::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarativeTextEdit); + + d->control->processEvent(event, QPointF(0, -d->yoff)); + if (!event->isAccepted()) + QDeclarativePaintedItem::mouseDoubleClickEvent(event); + +} + +/*! +\overload +Handles the given mouse \a event. +*/ +void QDeclarativeTextEdit::mouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarativeTextEdit); + d->control->processEvent(event, QPointF(0, -d->yoff)); + if (!event->isAccepted()) + QDeclarativePaintedItem::mouseMoveEvent(event); +} + +/*! +\overload +Handles the given input method \a event. +*/ +void QDeclarativeTextEdit::inputMethodEvent(QInputMethodEvent *event) +{ + Q_D(QDeclarativeTextEdit); + const bool wasComposing = isInputMethodComposing(); + d->control->processEvent(event, QPointF(0, -d->yoff)); + if (wasComposing != isInputMethodComposing()) + emit inputMethodComposingChanged(); +} + +/*! +\overload +Returns the value of the given \a property. +*/ +QVariant QDeclarativeTextEdit::inputMethodQuery(Qt::InputMethodQuery property) const +{ + Q_D(const QDeclarativeTextEdit); + return d->control->inputMethodQuery(property); +} + +/*! +Draws the contents of the text edit using the given \a painter within +the given \a bounds. +*/ +void QDeclarativeTextEdit::drawContents(QPainter *painter, const QRect &bounds) +{ + Q_D(QDeclarativeTextEdit); + + painter->setRenderHint(QPainter::TextAntialiasing, true); + painter->translate(0,d->yoff); + + d->control->drawContents(painter, bounds.translated(0,-d->yoff)); + + painter->translate(0,-d->yoff); +} + +void QDeclarativeTextEdit::updateImgCache(const QRectF &rf) +{ + Q_D(const QDeclarativeTextEdit); + QRect r; + if (!rf.isValid()) { + r = QRect(0,0,INT_MAX,INT_MAX); + } else { + r = rf.toRect(); + if (r.height() > INT_MAX/2) { + // Take care of overflow when translating "everything" + r.setTop(r.y() + d->yoff); + r.setBottom(INT_MAX/2); + } else { + r = r.translated(0,d->yoff); + } + } + dirtyCache(r); + emit update(); +} + +/*! + \qmlproperty bool TextEdit::smooth + + This property holds whether the text is smoothly scaled or transformed. + + Smooth filtering gives better visual quality, but is slower. If + the item is displayed at its natural size, this property has no visual or + performance effect. + + \note Generally scaling artifacts are only visible if the item is stationary on + the screen. A common pattern when animating an item is to disable smooth + filtering at the beginning of the animation and reenable it at the conclusion. +*/ + +/*! + \qmlproperty bool TextEdit::canPaste + \since QtQuick 1.1 + + Returns true if the TextEdit is writable and the content of the clipboard is + suitable for pasting into the TextEdit. +*/ +bool QDeclarativeTextEdit::canPaste() const +{ + Q_D(const QDeclarativeTextEdit); + return d->canPaste; +} + +/*! + \qmlproperty bool TextEdit::inputMethodComposing + + \since QtQuick 1.1 + + This property holds whether the TextEdit has partial text input from an + input method. + + While it is composing an input method may rely on mouse or key events from + the TextEdit to edit or commit the partial text. This property can be used + to determine when to disable events handlers that may interfere with the + correct operation of an input method. +*/ +bool QDeclarativeTextEdit::isInputMethodComposing() const +{ + Q_D(const QDeclarativeTextEdit); + if (QTextLayout *layout = d->control->textCursor().block().layout()) + return layout->preeditAreaText().length() > 0; + return false; +} + +void QDeclarativeTextEditPrivate::init() +{ + Q_Q(QDeclarativeTextEdit); + + q->setSmooth(smooth); + q->setAcceptedMouseButtons(Qt::LeftButton); + q->setFlag(QGraphicsItem::ItemHasNoContents, false); + q->setFlag(QGraphicsItem::ItemAcceptsInputMethod); + + control = new QTextControl(q); + control->setIgnoreUnusedNavigationEvents(true); + control->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::TextSelectableByKeyboard | Qt::TextEditable); + control->setDragEnabled(false); + + // QTextControl follows the default text color + // defined by the platform, declarative text + // should be black by default + QPalette pal = control->palette(); + if (pal.color(QPalette::Text) != color) { + pal.setColor(QPalette::Text, color); + control->setPalette(pal); + } + + QObject::connect(control, SIGNAL(updateRequest(QRectF)), q, SLOT(updateImgCache(QRectF))); + + QObject::connect(control, SIGNAL(textChanged()), q, SLOT(q_textChanged())); + QObject::connect(control, SIGNAL(selectionChanged()), q, SIGNAL(selectionChanged())); + QObject::connect(control, SIGNAL(selectionChanged()), q, SLOT(updateSelectionMarkers())); + QObject::connect(control, SIGNAL(cursorPositionChanged()), q, SLOT(updateSelectionMarkers())); + QObject::connect(control, SIGNAL(cursorPositionChanged()), q, SIGNAL(cursorPositionChanged())); + QObject::connect(control, SIGNAL(microFocusChanged()), q, SLOT(moveCursorDelegate())); + QObject::connect(control, SIGNAL(linkActivated(QString)), q, SIGNAL(linkActivated(QString))); +#ifndef QT_NO_CLIPBOARD + QObject::connect(q, SIGNAL(readOnlyChanged(bool)), q, SLOT(q_canPasteChanged())); + QObject::connect(QApplication::clipboard(), SIGNAL(dataChanged()), q, SLOT(q_canPasteChanged())); + canPaste = control->canPaste(); +#endif + + document = control->document(); + document->setDefaultFont(font); + document->setDocumentMargin(textMargin); + document->setUndoRedoEnabled(false); // flush undo buffer. + document->setUndoRedoEnabled(true); + updateDefaultTextOption(); +} + +void QDeclarativeTextEdit::q_textChanged() +{ + Q_D(QDeclarativeTextEdit); + d->text = text(); + d->rightToLeftText = d->document->begin().layout()->engine()->isRightToLeft(); + d->determineHorizontalAlignment(); + d->updateDefaultTextOption(); + updateSize(); + updateTotalLines(); + emit textChanged(d->text); +} + +void QDeclarativeTextEdit::moveCursorDelegate() +{ + Q_D(QDeclarativeTextEdit); + d->determineHorizontalAlignment(); + updateMicroFocus(); + emit cursorRectangleChanged(); + if(!d->cursor) + return; + QRectF cursorRect = cursorRectangle(); + d->cursor->setX(cursorRect.x()); + d->cursor->setY(cursorRect.y()); +} + +void QDeclarativeTextEditPrivate::updateSelection() +{ + Q_Q(QDeclarativeTextEdit); + QTextCursor cursor = control->textCursor(); + bool startChange = (lastSelectionStart != cursor.selectionStart()); + bool endChange = (lastSelectionEnd != cursor.selectionEnd()); + cursor.beginEditBlock(); + cursor.setPosition(lastSelectionStart, QTextCursor::MoveAnchor); + cursor.setPosition(lastSelectionEnd, QTextCursor::KeepAnchor); + cursor.endEditBlock(); + control->setTextCursor(cursor); + if(startChange) + q->selectionStartChanged(); + if(endChange) + q->selectionEndChanged(); +} + +void QDeclarativeTextEdit::updateSelectionMarkers() +{ + Q_D(QDeclarativeTextEdit); + if(d->lastSelectionStart != d->control->textCursor().selectionStart()){ + d->lastSelectionStart = d->control->textCursor().selectionStart(); + emit selectionStartChanged(); + } + if(d->lastSelectionEnd != d->control->textCursor().selectionEnd()){ + d->lastSelectionEnd = d->control->textCursor().selectionEnd(); + emit selectionEndChanged(); + } +} + +QRectF QDeclarativeTextEdit::boundingRect() const +{ + Q_D(const QDeclarativeTextEdit); + QRectF r = QDeclarativePaintedItem::boundingRect(); + int cursorWidth = 1; + if(d->cursor) + cursorWidth = d->cursor->width(); + if(!d->document->isEmpty()) + cursorWidth += 3;// ### Need a better way of accounting for space between char and cursor + + // Could include font max left/right bearings to either side of rectangle. + + r.setRight(r.right() + cursorWidth); + return r.translated(0,d->yoff); +} + +qreal QDeclarativeTextEditPrivate::implicitWidth() const +{ + Q_Q(const QDeclarativeTextEdit); + if (!requireImplicitWidth) { + // We don't calculate implicitWidth unless it is required. + // We need to force a size update now to ensure implicitWidth is calculated + const_cast(this)->requireImplicitWidth = true; + const_cast(q)->updateSize(); + } + return mImplicitWidth; +} + +//### we should perhaps be a bit smarter here -- depending on what has changed, we shouldn't +// need to do all the calculations each time +void QDeclarativeTextEdit::updateSize() +{ + Q_D(QDeclarativeTextEdit); + if (isComponentComplete()) { + qreal naturalWidth = d->mImplicitWidth; + // ### assumes that if the width is set, the text will fill to edges + // ### (unless wrap is false, then clipping will occur) + if (widthValid()) { + if (!d->requireImplicitWidth) { + emit implicitWidthChanged(); + // if the implicitWidth is used, then updateSize() has already been called (recursively) + if (d->requireImplicitWidth) + return; + } + if (d->requireImplicitWidth) { + d->document->setTextWidth(-1); + naturalWidth = d->document->idealWidth(); + } + if (d->document->textWidth() != width()) + d->document->setTextWidth(width()); + } else { + d->document->setTextWidth(-1); + } + QFontMetrics fm = QFontMetrics(d->font); + int dy = height(); + dy -= (int)d->document->size().height(); + + int nyoff; + if (heightValid()) { + if (d->vAlign == AlignBottom) + nyoff = dy; + else if (d->vAlign == AlignVCenter) + nyoff = dy/2; + else + nyoff = 0; + } else { + nyoff = 0; + } + if (nyoff != d->yoff) { + prepareGeometryChange(); + d->yoff = nyoff; + } + setBaselineOffset(fm.ascent() + d->yoff + d->textMargin); + + //### need to comfirm cost of always setting these + int newWidth = qCeil(d->document->idealWidth()); + if (!widthValid() && d->document->textWidth() != newWidth) + d->document->setTextWidth(newWidth); // ### Text does not align if width is not set (QTextDoc bug) + // ### Setting the implicitWidth triggers another updateSize(), and unless there are bindings nothing has changed. + if (!widthValid()) + setImplicitWidth(newWidth); + else if (d->requireImplicitWidth) + setImplicitWidth(naturalWidth); + qreal newHeight = d->document->size().height(); + if (newHeight == 0) + newHeight = fm.height(); + setImplicitHeight(newHeight); + + d->paintedSize = QSize(newWidth, newHeight); + setContentsSize(d->paintedSize); + emit paintedSizeChanged(); + } else { + d->dirty = true; + } + emit update(); +} + +void QDeclarativeTextEdit::updateTotalLines() +{ + Q_D(QDeclarativeTextEdit); + + int subLines = 0; + + for (QTextBlock it = d->document->begin(); it != d->document->end(); it = it.next()) { + QTextLayout *layout = it.layout(); + if (!layout) + continue; + subLines += layout->lineCount()-1; + } + + int newTotalLines = d->document->lineCount() + subLines; + if (d->lineCount != newTotalLines) { + d->lineCount = newTotalLines; + emit lineCountChanged(); + } +} + +void QDeclarativeTextEditPrivate::updateDefaultTextOption() +{ + Q_Q(QDeclarativeTextEdit); + QTextOption opt = document->defaultTextOption(); + int oldAlignment = opt.alignment(); + + QDeclarativeTextEdit::HAlignment horizontalAlignment = q->effectiveHAlign(); + if (rightToLeftText) { + if (horizontalAlignment == QDeclarativeTextEdit::AlignLeft) + horizontalAlignment = QDeclarativeTextEdit::AlignRight; + else if (horizontalAlignment == QDeclarativeTextEdit::AlignRight) + horizontalAlignment = QDeclarativeTextEdit::AlignLeft; + } + opt.setAlignment((Qt::Alignment)(int)(horizontalAlignment | vAlign)); + + QTextOption::WrapMode oldWrapMode = opt.wrapMode(); + opt.setWrapMode(QTextOption::WrapMode(wrapMode)); + + if (oldWrapMode == opt.wrapMode() && oldAlignment == opt.alignment()) + return; + document->setDefaultTextOption(opt); +} + + +/*! + \qmlmethod void TextEdit::openSoftwareInputPanel() + + Opens software input panels like virtual keyboards for typing, useful for + customizing when you want the input keyboard to be shown and hidden in + your application. + + By default the opening of input panels follows the platform style. On Symbian^1 and + Symbian^3 -based devices the panels are opened by clicking TextEdit. On other platforms + the panels are automatically opened when TextEdit element gains active focus. Input panels are + always closed if no editor has active focus. + + You can disable the automatic behavior by setting the property \c activeFocusOnPress to false + and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement + the behavior you want. + + Only relevant on platforms, which provide virtual keyboards. + + \code + import QtQuick 1.0 + TextEdit { + id: textEdit + text: "Hello world!" + activeFocusOnPress: false + MouseArea { + anchors.fill: parent + onClicked: { + if (!textEdit.activeFocus) { + textEdit.forceActiveFocus(); + textEdit.openSoftwareInputPanel(); + } else { + textEdit.focus = false; + } + } + onPressAndHold: textEdit.closeSoftwareInputPanel(); + } + } + \endcode +*/ +void QDeclarativeTextEdit::openSoftwareInputPanel() +{ + QEvent event(QEvent::RequestSoftwareInputPanel); + if (qApp) { + if (QGraphicsView * view = qobject_cast(qApp->focusWidget())) { + if (view->scene() && view->scene() == scene()) { + QApplication::sendEvent(view, &event); + } + } + } +} + +/*! + \qmlmethod void TextEdit::closeSoftwareInputPanel() + + Closes a software input panel like a virtual keyboard shown on the screen, useful + for customizing when you want the input keyboard to be shown and hidden in + your application. + + By default the opening of input panels follows the platform style. On Symbian^1 and + Symbian^3 -based devices the panels are opened by clicking TextEdit. On other platforms + the panels are automatically opened when TextEdit element gains active focus. Input panels are + always closed if no editor has active focus. + + You can disable the automatic behavior by setting the property \c activeFocusOnPress to false + and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement + the behavior you want. + + Only relevant on platforms, which provide virtual keyboards. + + \code + import QtQuick 1.0 + TextEdit { + id: textEdit + text: "Hello world!" + activeFocusOnPress: false + MouseArea { + anchors.fill: parent + onClicked: { + if (!textEdit.activeFocus) { + textEdit.forceActiveFocus(); + textEdit.openSoftwareInputPanel(); + } else { + textEdit.focus = false; + } + } + onPressAndHold: textEdit.closeSoftwareInputPanel(); + } + } + \endcode +*/ +void QDeclarativeTextEdit::closeSoftwareInputPanel() +{ + QEvent event(QEvent::CloseSoftwareInputPanel); + if (qApp) { + if (QGraphicsView * view = qobject_cast(qApp->focusWidget())) { + if (view->scene() && view->scene() == scene()) { + QApplication::sendEvent(view, &event); + } + } + } +} + +void QDeclarativeTextEdit::focusInEvent(QFocusEvent *event) +{ + Q_D(const QDeclarativeTextEdit); + if (d->showInputPanelOnFocus) { + if (d->focusOnPress && !isReadOnly()) { + openSoftwareInputPanel(); + } + } + QDeclarativePaintedItem::focusInEvent(event); +} + +void QDeclarativeTextEdit::q_canPasteChanged() +{ + Q_D(QDeclarativeTextEdit); + bool old = d->canPaste; + d->canPaste = d->control->canPaste(); + if(old!=d->canPaste) + emit canPasteChanged(); +} + +QT_END_NAMESPACE diff --git a/src/declarative/graphicsitems/qdeclarativetextedit_p.h b/src/declarative/graphicsitems/qdeclarativetextedit_p.h new file mode 100644 index 00000000..68432b45 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativetextedit_p.h @@ -0,0 +1,305 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVETEXTEDIT_H +#define QDECLARATIVETEXTEDIT_H + +#include "private/qdeclarativetext_p.h" +#include "private/qdeclarativeimplicitsizeitem_p.h" + +#include +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + + +class QDeclarativeTextEditPrivate; +class Q_AUTOTEST_EXPORT QDeclarativeTextEdit : public QDeclarativeImplicitSizePaintedItem +{ + Q_OBJECT + Q_ENUMS(VAlignment) + Q_ENUMS(HAlignment) + Q_ENUMS(TextFormat) + Q_ENUMS(WrapMode) + Q_ENUMS(SelectionMode) + + Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) + Q_PROPERTY(QColor selectionColor READ selectionColor WRITE setSelectionColor NOTIFY selectionColorChanged) + Q_PROPERTY(QColor selectedTextColor READ selectedTextColor WRITE setSelectedTextColor NOTIFY selectedTextColorChanged) + Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY fontChanged) + Q_PROPERTY(HAlignment horizontalAlignment READ hAlign WRITE setHAlign RESET resetHAlign NOTIFY horizontalAlignmentChanged) + Q_PROPERTY(VAlignment verticalAlignment READ vAlign WRITE setVAlign NOTIFY verticalAlignmentChanged) + Q_PROPERTY(WrapMode wrapMode READ wrapMode WRITE setWrapMode NOTIFY wrapModeChanged) + Q_PROPERTY(int lineCount READ lineCount NOTIFY lineCountChanged REVISION 1) + Q_PROPERTY(qreal paintedWidth READ paintedWidth NOTIFY paintedSizeChanged) + Q_PROPERTY(qreal paintedHeight READ paintedHeight NOTIFY paintedSizeChanged) + Q_PROPERTY(TextFormat textFormat READ textFormat WRITE setTextFormat NOTIFY textFormatChanged) + Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly NOTIFY readOnlyChanged) + Q_PROPERTY(bool cursorVisible READ isCursorVisible WRITE setCursorVisible NOTIFY cursorVisibleChanged) + Q_PROPERTY(int cursorPosition READ cursorPosition WRITE setCursorPosition NOTIFY cursorPositionChanged) + Q_PROPERTY(QRect cursorRectangle READ cursorRectangle NOTIFY cursorRectangleChanged) + Q_PROPERTY(QDeclarativeComponent* cursorDelegate READ cursorDelegate WRITE setCursorDelegate NOTIFY cursorDelegateChanged) + Q_PROPERTY(int selectionStart READ selectionStart NOTIFY selectionStartChanged) + Q_PROPERTY(int selectionEnd READ selectionEnd NOTIFY selectionEndChanged) + Q_PROPERTY(QString selectedText READ selectedText NOTIFY selectionChanged) + Q_PROPERTY(bool activeFocusOnPress READ focusOnPress WRITE setFocusOnPress NOTIFY activeFocusOnPressChanged) + Q_PROPERTY(bool persistentSelection READ persistentSelection WRITE setPersistentSelection NOTIFY persistentSelectionChanged) + Q_PROPERTY(qreal textMargin READ textMargin WRITE setTextMargin NOTIFY textMarginChanged) + Q_PROPERTY(Qt::InputMethodHints inputMethodHints READ inputMethodHints WRITE setInputMethodHints) + Q_PROPERTY(bool selectByMouse READ selectByMouse WRITE setSelectByMouse NOTIFY selectByMouseChanged) + Q_PROPERTY(SelectionMode mouseSelectionMode READ mouseSelectionMode WRITE setMouseSelectionMode NOTIFY mouseSelectionModeChanged REVISION 1) + Q_PROPERTY(bool canPaste READ canPaste NOTIFY canPasteChanged REVISION 1) + Q_PROPERTY(bool inputMethodComposing READ isInputMethodComposing NOTIFY inputMethodComposingChanged REVISION 1) + +public: + QDeclarativeTextEdit(QDeclarativeItem *parent=0); + + enum HAlignment { + AlignLeft = Qt::AlignLeft, + AlignRight = Qt::AlignRight, + AlignHCenter = Qt::AlignHCenter, + AlignJustify = Qt::AlignJustify // ### VERSIONING: Only in QtQuick 1.1 + }; + + enum VAlignment { + AlignTop = Qt::AlignTop, + AlignBottom = Qt::AlignBottom, + AlignVCenter = Qt::AlignVCenter + }; + + enum TextFormat { + PlainText = Qt::PlainText, + RichText = Qt::RichText, + AutoText = Qt::AutoText + }; + + enum WrapMode { NoWrap = QTextOption::NoWrap, + WordWrap = QTextOption::WordWrap, + WrapAnywhere = QTextOption::WrapAnywhere, + WrapAtWordBoundaryOrAnywhere = QTextOption::WrapAtWordBoundaryOrAnywhere, // COMPAT + Wrap = QTextOption::WrapAtWordBoundaryOrAnywhere + }; + + enum SelectionMode { + SelectCharacters, + SelectWords + }; + + Q_INVOKABLE void openSoftwareInputPanel(); + Q_INVOKABLE void closeSoftwareInputPanel(); + + QString text() const; + void setText(const QString &); + + TextFormat textFormat() const; + void setTextFormat(TextFormat format); + + QFont font() const; + void setFont(const QFont &font); + + QColor color() const; + void setColor(const QColor &c); + + QColor selectionColor() const; + void setSelectionColor(const QColor &c); + + QColor selectedTextColor() const; + void setSelectedTextColor(const QColor &c); + + HAlignment hAlign() const; + void setHAlign(HAlignment align); + void resetHAlign(); + HAlignment effectiveHAlign() const; + + VAlignment vAlign() const; + void setVAlign(VAlignment align); + + WrapMode wrapMode() const; + void setWrapMode(WrapMode w); + + int lineCount() const; + + bool isCursorVisible() const; + void setCursorVisible(bool on); + + int cursorPosition() const; + void setCursorPosition(int pos); + + QDeclarativeComponent* cursorDelegate() const; + void setCursorDelegate(QDeclarativeComponent*); + + int selectionStart() const; + int selectionEnd() const; + + QString selectedText() const; + + bool focusOnPress() const; + void setFocusOnPress(bool on); + + bool persistentSelection() const; + void setPersistentSelection(bool on); + + qreal textMargin() const; + void setTextMargin(qreal margin); + + bool selectByMouse() const; + void setSelectByMouse(bool); + + SelectionMode mouseSelectionMode() const; + void setMouseSelectionMode(SelectionMode mode); + + bool canPaste() const; + + virtual void componentComplete(); + + /* FROM EDIT */ + void setReadOnly(bool); + bool isReadOnly() const; + + void setTextInteractionFlags(Qt::TextInteractionFlags flags); + Qt::TextInteractionFlags textInteractionFlags() const; + + QRect cursorRectangle() const; + + QVariant inputMethodQuery(Qt::InputMethodQuery property) const; + + qreal paintedWidth() const; + qreal paintedHeight() const; + + Q_INVOKABLE QRectF positionToRectangle(int) const; + Q_INVOKABLE int positionAt(int x, int y) const; + Q_INVOKABLE void moveCursorSelection(int pos); + Q_INVOKABLE Q_REVISION(1) void moveCursorSelection(int pos, SelectionMode mode); + + QRectF boundingRect() const; + + bool isInputMethodComposing() const; + +Q_SIGNALS: + void textChanged(const QString &); + void paintedSizeChanged(); + void cursorPositionChanged(); + void cursorRectangleChanged(); + void selectionStartChanged(); + void selectionEndChanged(); + void selectionChanged(); + void colorChanged(const QColor &color); + void selectionColorChanged(const QColor &color); + void selectedTextColorChanged(const QColor &color); + void fontChanged(const QFont &font); + void horizontalAlignmentChanged(HAlignment alignment); + void verticalAlignmentChanged(VAlignment alignment); + void wrapModeChanged(); + void lineCountChanged(); + void textFormatChanged(TextFormat textFormat); + void readOnlyChanged(bool isReadOnly); + void cursorVisibleChanged(bool isCursorVisible); + void cursorDelegateChanged(); + void activeFocusOnPressChanged(bool activeFocusOnPressed); + void persistentSelectionChanged(bool isPersistentSelection); + void textMarginChanged(qreal textMargin); + void selectByMouseChanged(bool selectByMouse); + Q_REVISION(1) void mouseSelectionModeChanged(SelectionMode mode); + Q_REVISION(1) void linkActivated(const QString &link); + Q_REVISION(1) void canPasteChanged(); + Q_REVISION(1) void inputMethodComposingChanged(); + +public Q_SLOTS: + void selectAll(); + void selectWord(); + void select(int start, int end); + Q_REVISION(1) void deselect(); + Q_REVISION(1) bool isRightToLeft(int start, int end); +#ifndef QT_NO_CLIPBOARD + void cut(); + void copy(); + void paste(); +#endif + +private Q_SLOTS: + void updateImgCache(const QRectF &rect); + void q_textChanged(); + void updateSelectionMarkers(); + void moveCursorDelegate(); + void loadCursorDelegate(); + void q_canPasteChanged(); + +private: + void updateSize(); + void updateTotalLines(); + +protected: + virtual void geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry); + + bool event(QEvent *); + void keyPressEvent(QKeyEvent *); + void keyReleaseEvent(QKeyEvent *); + void focusInEvent(QFocusEvent *event); + + // mouse filter? + void mousePressEvent(QGraphicsSceneMouseEvent *event); + void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); + void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event); + void mouseMoveEvent(QGraphicsSceneMouseEvent *event); + + void inputMethodEvent(QInputMethodEvent *e); + + void drawContents(QPainter *, const QRect &); +private: + Q_DISABLE_COPY(QDeclarativeTextEdit) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarativeTextEdit) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeTextEdit) + +QT_END_HEADER + +#endif diff --git a/src/declarative/graphicsitems/qdeclarativetextedit_p_p.h b/src/declarative/graphicsitems/qdeclarativetextedit_p_p.h new file mode 100644 index 00000000..bdc4351c --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativetextedit_p_p.h @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVETEXTEDIT_P_H +#define QDECLARATIVETEXTEDIT_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 "qdeclarativeitem.h" +#include "private/qdeclarativeimplicitsizeitem_p_p.h" + +#include + +QT_BEGIN_NAMESPACE +class QTextLayout; +class QTextDocument; +class QTextControl; +class QDeclarativeTextEditPrivate : public QDeclarativeImplicitSizePaintedItemPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeTextEdit) + +public: + QDeclarativeTextEditPrivate() + : color("black"), hAlign(QDeclarativeTextEdit::AlignLeft), vAlign(QDeclarativeTextEdit::AlignTop), + imgDirty(true), dirty(false), richText(false), cursorVisible(false), focusOnPress(true), + showInputPanelOnFocus(true), clickCausedFocus(false), persistentSelection(true), requireImplicitWidth(false), + hAlignImplicit(true), rightToLeftText(false), textMargin(0.0), lastSelectionStart(0), lastSelectionEnd(0), + cursorComponent(0), cursor(0), format(QDeclarativeTextEdit::AutoText), document(0), wrapMode(QDeclarativeTextEdit::NoWrap), + mouseSelectionMode(QDeclarativeTextEdit::SelectCharacters), selectByMouse(false), canPaste(false), + yoff(0) + { +#ifdef Q_OS_SYMBIAN + if (QSysInfo::symbianVersion() >= QSysInfo::SV_SF_1) { + showInputPanelOnFocus = false; + } +#endif + } + + void init(); + + void updateDefaultTextOption(); + void relayoutDocument(); + void updateSelection(); + bool determineHorizontalAlignment(); + bool setHAlign(QDeclarativeTextEdit::HAlignment, bool forceAlign = false); + void mirrorChange(); + qreal implicitWidth() const; + void focusChanged(bool); + + QString text; + QFont font; + QFont sourceFont; + QColor color; + QColor selectionColor; + QColor selectedTextColor; + QString style; + QColor styleColor; + QPixmap imgCache; + QPixmap imgStyleCache; + QDeclarativeTextEdit::HAlignment hAlign; + QDeclarativeTextEdit::VAlignment vAlign; + bool imgDirty : 1; + bool dirty : 1; + bool richText : 1; + bool cursorVisible : 1; + bool focusOnPress : 1; + bool showInputPanelOnFocus : 1; + bool clickCausedFocus : 1; + bool persistentSelection : 1; + bool requireImplicitWidth:1; + bool hAlignImplicit:1; + bool rightToLeftText:1; + qreal textMargin; + int lastSelectionStart; + int lastSelectionEnd; + QDeclarativeComponent* cursorComponent; + QDeclarativeItem* cursor; + QDeclarativeTextEdit::TextFormat format; + QTextDocument *document; + QTextControl *control; + QDeclarativeTextEdit::WrapMode wrapMode; + QDeclarativeTextEdit::SelectionMode mouseSelectionMode; + int lineCount; + bool selectByMouse; + bool canPaste; + int yoff; + QSize paintedSize; +}; + +QT_END_NAMESPACE +#endif diff --git a/src/declarative/graphicsitems/qdeclarativetextinput.cpp b/src/declarative/graphicsitems/qdeclarativetextinput.cpp new file mode 100644 index 00000000..18db4fda --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativetextinput.cpp @@ -0,0 +1,2009 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativetextinput_p.h" +#include "private/qdeclarativetextinput_p_p.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef QT_NO_LINEEDIT + +QT_BEGIN_NAMESPACE + +/*! + \qmlclass TextInput QDeclarativeTextInput + \ingroup qml-basic-visual-elements + \since 4.7 + \brief The TextInput item displays an editable line of text. + \inherits Item + + The TextInput element displays a single line of editable plain text. + + TextInput is used to accept a line of text input. Input constraints + can be placed on a TextInput item (for example, through a \l validator or \l inputMask), + and setting \l echoMode to an appropriate value enables TextInput to be used for + a password input field. + + On Mac OS X, the Up/Down key bindings for Home/End are explicitly disabled. + If you want such bindings (on any platform), you will need to construct them in QML. + + \sa TextEdit, Text, {declarative/text/textselection}{Text Selection example} +*/ +QDeclarativeTextInput::QDeclarativeTextInput(QDeclarativeItem* parent) + : QDeclarativeImplicitSizePaintedItem(*(new QDeclarativeTextInputPrivate), parent) +{ + Q_D(QDeclarativeTextInput); + d->init(); +} + +QDeclarativeTextInput::~QDeclarativeTextInput() +{ +} + +/*! + \qmlproperty string TextInput::text + + The text in the TextInput. +*/ + +QString QDeclarativeTextInput::text() const +{ + Q_D(const QDeclarativeTextInput); + return d->control->text(); +} + +void QDeclarativeTextInput::setText(const QString &s) +{ + Q_D(QDeclarativeTextInput); + if(s == text()) + return; + d->control->setText(s); +} + +/*! + \qmlproperty string TextInput::font.family + + Sets the family name of the font. + + The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]". + If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen. + If the family isn't available a family will be set using the font matching algorithm. +*/ + +/*! + \qmlproperty bool TextInput::font.bold + + Sets whether the font weight is bold. +*/ + +/*! + \qmlproperty enumeration TextInput::font.weight + + Sets the font's weight. + + The weight can be one of: + \list + \o Font.Light + \o Font.Normal - the default + \o Font.DemiBold + \o Font.Bold + \o Font.Black + \endlist + + \qml + TextInput { text: "Hello"; font.weight: Font.DemiBold } + \endqml +*/ + +/*! + \qmlproperty bool TextInput::font.italic + + Sets whether the font has an italic style. +*/ + +/*! + \qmlproperty bool TextInput::font.underline + + Sets whether the text is underlined. +*/ + +/*! + \qmlproperty bool TextInput::font.strikeout + + Sets whether the font has a strikeout style. +*/ + +/*! + \qmlproperty real TextInput::font.pointSize + + Sets the font size in points. The point size must be greater than zero. +*/ + +/*! + \qmlproperty int TextInput::font.pixelSize + + Sets the font size in pixels. + + Using this function makes the font device dependent. + Use \c pointSize to set the size of the font in a device independent manner. +*/ + +/*! + \qmlproperty real TextInput::font.letterSpacing + + Sets the letter spacing for the font. + + Letter spacing changes the default spacing between individual letters in the font. + A positive value increases the letter spacing by the corresponding pixels; a negative value decreases the spacing. +*/ + +/*! + \qmlproperty real TextInput::font.wordSpacing + + Sets the word spacing for the font. + + Word spacing changes the default spacing between individual words. + A positive value increases the word spacing by a corresponding amount of pixels, + while a negative value decreases the inter-word spacing accordingly. +*/ + +/*! + \qmlproperty enumeration TextInput::font.capitalization + + Sets the capitalization for the text. + + \list + \o Font.MixedCase - This is the normal text rendering option where no capitalization change is applied. + \o Font.AllUppercase - This alters the text to be rendered in all uppercase type. + \o Font.AllLowercase - This alters the text to be rendered in all lowercase type. + \o Font.SmallCaps - This alters the text to be rendered in small-caps type. + \o Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character. + \endlist + + \qml + TextInput { text: "Hello"; font.capitalization: Font.AllLowercase } + \endqml +*/ + +QFont QDeclarativeTextInput::font() const +{ + Q_D(const QDeclarativeTextInput); + return d->sourceFont; +} + +void QDeclarativeTextInput::setFont(const QFont &font) +{ + Q_D(QDeclarativeTextInput); + if (d->sourceFont == font) + return; + + d->sourceFont = font; + QFont oldFont = d->font; + d->font = font; + if (d->font.pointSizeF() != -1) { + // 0.5pt resolution + qreal size = qRound(d->font.pointSizeF()*2.0); + d->font.setPointSizeF(size/2.0); + } + + if (oldFont != d->font) { + d->control->setFont(d->font); + updateSize(); + updateCursorRectangle(); + if(d->cursorItem){ + d->cursorItem->setHeight(QFontMetrics(d->font).height()); + } + } + emit fontChanged(d->sourceFont); +} + +/*! + \qmlproperty color TextInput::color + + The text color. +*/ +QColor QDeclarativeTextInput::color() const +{ + Q_D(const QDeclarativeTextInput); + return d->color; +} + +void QDeclarativeTextInput::setColor(const QColor &c) +{ + Q_D(QDeclarativeTextInput); + if (c != d->color) { + d->color = c; + clearCache(); + update(); + emit colorChanged(c); + } +} + + +/*! + \qmlproperty color TextInput::selectionColor + + The text highlight color, used behind selections. +*/ +QColor QDeclarativeTextInput::selectionColor() const +{ + Q_D(const QDeclarativeTextInput); + return d->selectionColor; +} + +void QDeclarativeTextInput::setSelectionColor(const QColor &color) +{ + Q_D(QDeclarativeTextInput); + if (d->selectionColor == color) + return; + + d->selectionColor = color; + QPalette p = d->control->palette(); + p.setColor(QPalette::Highlight, d->selectionColor); + d->control->setPalette(p); + if (d->control->hasSelectedText()) { + clearCache(); + update(); + } + emit selectionColorChanged(color); +} + +/*! + \qmlproperty color TextInput::selectedTextColor + + The highlighted text color, used in selections. +*/ +QColor QDeclarativeTextInput::selectedTextColor() const +{ + Q_D(const QDeclarativeTextInput); + return d->selectedTextColor; +} + +void QDeclarativeTextInput::setSelectedTextColor(const QColor &color) +{ + Q_D(QDeclarativeTextInput); + if (d->selectedTextColor == color) + return; + + d->selectedTextColor = color; + QPalette p = d->control->palette(); + p.setColor(QPalette::HighlightedText, d->selectedTextColor); + d->control->setPalette(p); + if (d->control->hasSelectedText()) { + clearCache(); + update(); + } + emit selectedTextColorChanged(color); +} + +/*! + \qmlproperty enumeration TextInput::horizontalAlignment + + Sets the horizontal alignment of the text within the TextInput item's + width and height. By default, the text alignment follows the natural alignment + of the text, for example text that is read from left to right will be aligned to + the left. + + TextInput does not have vertical alignment, as the natural height is + exactly the height of the single line of text. If you set the height + manually to something larger, TextInput will always be top aligned + vertically. You can use anchors to align it however you want within + another item. + + The valid values for \c horizontalAlignment are \c TextInput.AlignLeft, \c TextInput.AlignRight and + \c TextInput.AlignHCenter. + + When using the attached property \l {LayoutMirroring::enabled} to mirror application + layouts, the horizontal alignment of text will also be mirrored. However, the property + \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment + of TextInput, use the property \l {LayoutMirroring::enabled}. +*/ +QDeclarativeTextInput::HAlignment QDeclarativeTextInput::hAlign() const +{ + Q_D(const QDeclarativeTextInput); + return d->hAlign; +} + +void QDeclarativeTextInput::setHAlign(HAlignment align) +{ + Q_D(QDeclarativeTextInput); + bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror; + d->hAlignImplicit = false; + if (d->setHAlign(align, forceAlign) && isComponentComplete()) { + updateCursorRectangle(); + } +} + +void QDeclarativeTextInput::resetHAlign() +{ + Q_D(QDeclarativeTextInput); + d->hAlignImplicit = true; + if (d->determineHorizontalAlignment() && isComponentComplete()) { + updateCursorRectangle(); + } +} + +QDeclarativeTextInput::HAlignment QDeclarativeTextInput::effectiveHAlign() const +{ + Q_D(const QDeclarativeTextInput); + QDeclarativeTextInput::HAlignment effectiveAlignment = d->hAlign; + if (!d->hAlignImplicit && d->effectiveLayoutMirror) { + switch (d->hAlign) { + case QDeclarativeTextInput::AlignLeft: + effectiveAlignment = QDeclarativeTextInput::AlignRight; + break; + case QDeclarativeTextInput::AlignRight: + effectiveAlignment = QDeclarativeTextInput::AlignLeft; + break; + default: + break; + } + } + return effectiveAlignment; +} + +bool QDeclarativeTextInputPrivate::setHAlign(QDeclarativeTextInput::HAlignment alignment, bool forceAlign) +{ + Q_Q(QDeclarativeTextInput); + if ((hAlign != alignment || forceAlign) && alignment <= QDeclarativeTextInput::AlignHCenter) { // justify not supported + QDeclarativeTextInput::HAlignment oldEffectiveHAlign = q->effectiveHAlign(); + hAlign = alignment; + emit q->horizontalAlignmentChanged(alignment); + return true; + } + return false; +} + +bool QDeclarativeTextInputPrivate::determineHorizontalAlignment() +{ + if (hAlignImplicit) { + // if no explicit alignment has been set, follow the natural layout direction of the text + QString text = control->text(); + if (text.isEmpty()) + text = control->preeditAreaText(); + bool isRightToLeft = text.isEmpty() + ? QApplication::keyboardInputDirection() == Qt::RightToLeft + : text.isRightToLeft(); + return setHAlign(isRightToLeft ? QDeclarativeTextInput::AlignRight : QDeclarativeTextInput::AlignLeft); + } + return false; +} + +void QDeclarativeTextInputPrivate::mirrorChange() +{ + Q_Q(QDeclarativeTextInput); + if (q->isComponentComplete()) { + if (!hAlignImplicit && (hAlign == QDeclarativeTextInput::AlignRight || hAlign == QDeclarativeTextInput::AlignLeft)) { + q->updateCursorRectangle(); + updateHorizontalScroll(); + } + } +} + +/*! + \qmlproperty bool TextInput::readOnly + + Sets whether user input can modify the contents of the TextInput. + + If readOnly is set to true, then user input will not affect the text + property. Any bindings or attempts to set the text property will still + work. +*/ + +bool QDeclarativeTextInput::isReadOnly() const +{ + Q_D(const QDeclarativeTextInput); + return d->control->isReadOnly(); +} + +void QDeclarativeTextInput::setReadOnly(bool ro) +{ + Q_D(QDeclarativeTextInput); + if (d->control->isReadOnly() == ro) + return; + + setFlag(QGraphicsItem::ItemAcceptsInputMethod, !ro); + d->control->setReadOnly(ro); + + emit readOnlyChanged(ro); +} + +/*! + \qmlproperty int TextInput::maximumLength + The maximum permitted length of the text in the TextInput. + + If the text is too long, it is truncated at the limit. + + By default, this property contains a value of 32767. +*/ +int QDeclarativeTextInput::maxLength() const +{ + Q_D(const QDeclarativeTextInput); + return d->control->maxLength(); +} + +void QDeclarativeTextInput::setMaxLength(int ml) +{ + Q_D(QDeclarativeTextInput); + if (d->control->maxLength() == ml) + return; + + d->control->setMaxLength(ml); + + emit maximumLengthChanged(ml); +} + +/*! + \qmlproperty bool TextInput::cursorVisible + Set to true when the TextInput shows a cursor. + + This property is set and unset when the TextInput gets active focus, so that other + properties can be bound to whether the cursor is currently showing. As it + gets set and unset automatically, when you set the value yourself you must + keep in mind that your value may be overwritten. + + It can be set directly in script, for example if a KeyProxy might + forward keys to it and you desire it to look active when this happens + (but without actually giving it active focus). + + It should not be set directly on the element, like in the below QML, + as the specified value will be overridden an lost on focus changes. + + \code + TextInput { + text: "Text" + cursorVisible: false + } + \endcode + + In the above snippet the cursor will still become visible when the + TextInput gains active focus. +*/ +bool QDeclarativeTextInput::isCursorVisible() const +{ + Q_D(const QDeclarativeTextInput); + return d->cursorVisible; +} + +void QDeclarativeTextInput::setCursorVisible(bool on) +{ + Q_D(QDeclarativeTextInput); + if (d->cursorVisible == on) + return; + d->cursorVisible = on; + d->control->setCursorBlinkPeriod(on?QApplication::cursorFlashTime():0); + QRect r = d->control->cursorRect(); + if (d->control->inputMask().isEmpty()) + updateRect(r); + else + updateRect(); + emit cursorVisibleChanged(d->cursorVisible); +} + +/*! + \qmlproperty int TextInput::cursorPosition + The position of the cursor in the TextInput. +*/ +int QDeclarativeTextInput::cursorPosition() const +{ + Q_D(const QDeclarativeTextInput); + return d->control->cursor(); +} +void QDeclarativeTextInput::setCursorPosition(int cp) +{ + Q_D(QDeclarativeTextInput); + if (cp < 0 || cp > d->control->text().length()) + return; + d->control->moveCursor(cp); +} + +/*! + Returns a Rect which encompasses the cursor, but which may be larger than is + required. Ignores custom cursor delegates. +*/ +QRect QDeclarativeTextInput::cursorRectangle() const +{ + Q_D(const QDeclarativeTextInput); + QRect r = d->control->cursorRect(); + // Scroll and make consistent with TextEdit + // QLineControl inexplicably adds 1 to the height and horizontal padding + // for unicode direction markers. + r.adjust(5 - d->hscroll, 0, -4 - d->hscroll, -1); + return r; +} + +/*! + \qmlproperty int TextInput::selectionStart + + The cursor position before the first character in the current selection. + + This property is read-only. To change the selection, use select(start,end), + selectAll(), or selectWord(). + + \sa selectionEnd, cursorPosition, selectedText +*/ +int QDeclarativeTextInput::selectionStart() const +{ + Q_D(const QDeclarativeTextInput); + return d->lastSelectionStart; +} + +/*! + \qmlproperty int TextInput::selectionEnd + + The cursor position after the last character in the current selection. + + This property is read-only. To change the selection, use select(start,end), + selectAll(), or selectWord(). + + \sa selectionStart, cursorPosition, selectedText +*/ +int QDeclarativeTextInput::selectionEnd() const +{ + Q_D(const QDeclarativeTextInput); + return d->lastSelectionEnd; +} + +/*! + \qmlmethod void TextInput::select(int start, int end) + + Causes the text from \a start to \a end to be selected. + + If either start or end is out of range, the selection is not changed. + + After calling this, selectionStart will become the lesser + and selectionEnd will become the greater (regardless of the order passed + to this method). + + \sa selectionStart, selectionEnd +*/ +void QDeclarativeTextInput::select(int start, int end) +{ + Q_D(QDeclarativeTextInput); + if (start < 0 || end < 0 || start > d->control->text().length() || end > d->control->text().length()) + return; + d->control->setSelection(start, end-start); +} + +/*! + \qmlproperty string TextInput::selectedText + + This read-only property provides the text currently selected in the + text input. + + It is equivalent to the following snippet, but is faster and easier + to use. + + \js + myTextInput.text.toString().substring(myTextInput.selectionStart, + myTextInput.selectionEnd); + \endjs +*/ +QString QDeclarativeTextInput::selectedText() const +{ + Q_D(const QDeclarativeTextInput); + return d->control->selectedText(); +} + +/*! + \qmlproperty bool TextInput::activeFocusOnPress + + Whether the TextInput should gain active focus on a mouse press. By default this is + set to true. +*/ +bool QDeclarativeTextInput::focusOnPress() const +{ + Q_D(const QDeclarativeTextInput); + return d->focusOnPress; +} + +void QDeclarativeTextInput::setFocusOnPress(bool b) +{ + Q_D(QDeclarativeTextInput); + if (d->focusOnPress == b) + return; + + d->focusOnPress = b; + + emit activeFocusOnPressChanged(d->focusOnPress); +} + +/*! + \qmlproperty bool TextInput::autoScroll + + Whether the TextInput should scroll when the text is longer than the width. By default this is + set to true. +*/ +bool QDeclarativeTextInput::autoScroll() const +{ + Q_D(const QDeclarativeTextInput); + return d->autoScroll; +} + +void QDeclarativeTextInput::setAutoScroll(bool b) +{ + Q_D(QDeclarativeTextInput); + if (d->autoScroll == b) + return; + + d->autoScroll = b; + //We need to repaint so that the scrolling is taking into account. + updateSize(true); + updateCursorRectangle(); + emit autoScrollChanged(d->autoScroll); +} + +/*! + \qmlclass IntValidator QIntValidator + \ingroup qml-basic-visual-elements + + This element provides a validator for integer values. + + IntValidator uses the \l {QLocale::setDefault()}{default locale} to interpret the number and + will accept locale specific digits, group separators, and positive and negative signs. In + addition, IntValidator is always guaranteed to accept a number formatted according to the "C" + locale. +*/ +/*! + \qmlproperty int IntValidator::top + + This property holds the validator's highest acceptable value. + By default, this property's value is derived from the highest signed integer available (typically 2147483647). +*/ +/*! + \qmlproperty int IntValidator::bottom + + This property holds the validator's lowest acceptable value. + By default, this property's value is derived from the lowest signed integer available (typically -2147483647). +*/ + +/*! + \qmlclass DoubleValidator QDoubleValidator + \ingroup qml-basic-visual-elements + + This element provides a validator for non-integer numbers. +*/ + +/*! + \qmlproperty real DoubleValidator::top + + This property holds the validator's maximum acceptable value. + By default, this property contains a value of infinity. +*/ +/*! + \qmlproperty real DoubleValidator::bottom + + This property holds the validator's minimum acceptable value. + By default, this property contains a value of -infinity. +*/ +/*! + \qmlproperty int DoubleValidator::decimals + + This property holds the validator's maximum number of digits after the decimal point. + By default, this property contains a value of 1000. +*/ +/*! + \qmlproperty enumeration DoubleValidator::notation + This property holds the notation of how a string can describe a number. + + The possible values for this property are: + + \list + \o DoubleValidator.StandardNotation + \o DoubleValidator.ScientificNotation (default) + \endlist + + If this property is set to DoubleValidator.ScientificNotation, the written number may have an exponent part (e.g. 1.5E-2). +*/ + +/*! + \qmlclass RegExpValidator QRegExpValidator + \ingroup qml-basic-visual-elements + + This element provides a validator, which counts as valid any string which + matches a specified regular expression. +*/ +/*! + \qmlproperty regExp RegExpValidator::regExp + + This property holds the regular expression used for validation. + + Note that this property should be a regular expression in JS syntax, e.g /a/ for the regular expression + matching "a". + + By default, this property contains a regular expression with the pattern .* that matches any string. +*/ + +/*! + \qmlproperty Validator TextInput::validator + + Allows you to set a validator on the TextInput. When a validator is set + the TextInput will only accept input which leaves the text property in + an acceptable or intermediate state. The accepted signal will only be sent + if the text is in an acceptable state when enter is pressed. + + Currently supported validators are IntValidator, DoubleValidator and + RegExpValidator. An example of using validators is shown below, which allows + input of integers between 11 and 31 into the text input: + + \code + import QtQuick 1.0 + TextInput{ + validator: IntValidator{bottom: 11; top: 31;} + focus: true + } + \endcode + + \sa acceptableInput, inputMask +*/ +#ifndef QT_NO_VALIDATOR +QValidator* QDeclarativeTextInput::validator() const +{ + Q_D(const QDeclarativeTextInput); + //###const cast isn't good, but needed for property system? + return const_cast(d->control->validator()); +} + +void QDeclarativeTextInput::setValidator(QValidator* v) +{ + Q_D(QDeclarativeTextInput); + if (d->control->validator() == v) + return; + + d->control->setValidator(v); + if(!d->control->hasAcceptableInput()){ + d->oldValidity = false; + emit acceptableInputChanged(); + } + + emit validatorChanged(); +} +#endif // QT_NO_VALIDATOR + +/*! + \qmlproperty string TextInput::inputMask + + Allows you to set an input mask on the TextInput, restricting the allowable + text inputs. See QLineEdit::inputMask for further details, as the exact + same mask strings are used by TextInput. + + \sa acceptableInput, validator +*/ +QString QDeclarativeTextInput::inputMask() const +{ + Q_D(const QDeclarativeTextInput); + return d->control->inputMask(); +} + +void QDeclarativeTextInput::setInputMask(const QString &im) +{ + Q_D(QDeclarativeTextInput); + if (d->control->inputMask() == im) + return; + + d->control->setInputMask(im); + emit inputMaskChanged(d->control->inputMask()); +} + +/*! + \qmlproperty bool TextInput::acceptableInput + + This property is always true unless a validator or input mask has been set. + If a validator or input mask has been set, this property will only be true + if the current text is acceptable to the validator or input mask as a final + string (not as an intermediate string). +*/ +bool QDeclarativeTextInput::hasAcceptableInput() const +{ + Q_D(const QDeclarativeTextInput); + return d->control->hasAcceptableInput(); +} + +/*! + \qmlsignal TextInput::onAccepted() + + This handler is called when the Return or Enter key is pressed. + Note that if there is a \l validator or \l inputMask set on the text + input, the handler will only be emitted if the input is in an acceptable + state. +*/ + +void QDeclarativeTextInputPrivate::updateInputMethodHints() +{ + Q_Q(QDeclarativeTextInput); + Qt::InputMethodHints hints = inputMethodHints; + uint echo = control->echoMode(); + if (echo == QDeclarativeTextInput::Password || echo == QDeclarativeTextInput::NoEcho) + hints |= Qt::ImhHiddenText; + else if (echo == QDeclarativeTextInput::PasswordEchoOnEdit) + hints &= ~Qt::ImhHiddenText; + if (echo != QDeclarativeTextInput::Normal) + hints |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText); + q->setInputMethodHints(hints); +} + +/*! + \qmlproperty enumeration TextInput::echoMode + + Specifies how the text should be displayed in the TextInput. + \list + \o TextInput.Normal - Displays the text as it is. (Default) + \o TextInput.Password - Displays asterixes instead of characters. + \o TextInput.NoEcho - Displays nothing. + \o TextInput.PasswordEchoOnEdit - Displays characters as they are entered + while editing, otherwise displays asterisks. + \endlist +*/ +QDeclarativeTextInput::EchoMode QDeclarativeTextInput::echoMode() const +{ + Q_D(const QDeclarativeTextInput); + return (QDeclarativeTextInput::EchoMode)d->control->echoMode(); +} + +void QDeclarativeTextInput::setEchoMode(QDeclarativeTextInput::EchoMode echo) +{ + Q_D(QDeclarativeTextInput); + if (echoMode() == echo) + return; + d->control->setEchoMode((uint)echo); + d->updateInputMethodHints(); + q_textChanged(); + emit echoModeChanged(echoMode()); +} + +Qt::InputMethodHints QDeclarativeTextInput::imHints() const +{ + Q_D(const QDeclarativeTextInput); + return d->inputMethodHints; +} + +void QDeclarativeTextInput::setIMHints(Qt::InputMethodHints hints) +{ + Q_D(QDeclarativeTextInput); + if (d->inputMethodHints == hints) + return; + d->inputMethodHints = hints; + d->updateInputMethodHints(); +} + +/*! + \qmlproperty Component TextInput::cursorDelegate + The delegate for the cursor in the TextInput. + + If you set a cursorDelegate for a TextInput, this delegate will be used for + drawing the cursor instead of the standard cursor. An instance of the + delegate will be created and managed by the TextInput when a cursor is + needed, and the x property of delegate instance will be set so as + to be one pixel before the top left of the current character. + + Note that the root item of the delegate component must be a QDeclarativeItem or + QDeclarativeItem derived item. +*/ +QDeclarativeComponent* QDeclarativeTextInput::cursorDelegate() const +{ + Q_D(const QDeclarativeTextInput); + return d->cursorComponent; +} + +void QDeclarativeTextInput::setCursorDelegate(QDeclarativeComponent* c) +{ + Q_D(QDeclarativeTextInput); + if (d->cursorComponent == c) + return; + + d->cursorComponent = c; + if(!c){ + //note that the components are owned by something else + delete d->cursorItem; + }else{ + d->startCreatingCursor(); + } + + emit cursorDelegateChanged(); +} + +void QDeclarativeTextInputPrivate::startCreatingCursor() +{ + Q_Q(QDeclarativeTextInput); + if(cursorComponent->isReady()){ + q->createCursor(); + }else if(cursorComponent->isLoading()){ + q->connect(cursorComponent, SIGNAL(statusChanged(int)), + q, SLOT(createCursor())); + }else {//isError + qmlInfo(q, cursorComponent->errors()) << QDeclarativeTextInput::tr("Could not load cursor delegate"); + } +} + +void QDeclarativeTextInput::createCursor() +{ + Q_D(QDeclarativeTextInput); + if(d->cursorComponent->isError()){ + qmlInfo(this, d->cursorComponent->errors()) << tr("Could not load cursor delegate"); + return; + } + + if(!d->cursorComponent->isReady()) + return; + + if(d->cursorItem) + delete d->cursorItem; + d->cursorItem = qobject_cast(d->cursorComponent->create()); + if(!d->cursorItem){ + qmlInfo(this, d->cursorComponent->errors()) << tr("Could not instantiate cursor delegate"); + return; + } + + QDeclarative_setParent_noEvent(d->cursorItem, this); + d->cursorItem->setParentItem(this); + d->cursorItem->setX(d->control->cursorToX()); + d->cursorItem->setHeight(d->control->height()-1); // -1 to counter QLineControl's +1 which is not consistent with Text. +} + +/*! + \qmlmethod rect TextInput::positionToRectangle(int pos) + + This function takes a character position and returns the rectangle that the + cursor would occupy, if it was placed at that character position. + + This is similar to setting the cursorPosition, and then querying the cursor + rectangle, but the cursorPosition is not changed. +*/ +QRectF QDeclarativeTextInput::positionToRectangle(int pos) const +{ + Q_D(const QDeclarativeTextInput); + if (pos > d->control->cursorPosition()) + pos += d->control->preeditAreaText().length(); + return QRectF(d->control->cursorToX(pos)-d->hscroll, + 0.0, + d->control->cursorWidth(), + cursorRectangle().height()); +} + +int QDeclarativeTextInput::positionAt(int x) const +{ + return positionAt(x, CursorBetweenCharacters); +} + +/*! + \qmlmethod int TextInput::positionAt(int x, CursorPosition position = CursorBetweenCharacters) + \since QtQuick 1.1 + + This function returns the character position at + x pixels from the left of the textInput. Position 0 is before the + first character, position 1 is after the first character but before the second, + and so on until position text.length, which is after all characters. + + This means that for all x values before the first character this function returns 0, + and for all x values after the last character this function returns text.length. + + The cursor position type specifies how the cursor position should be resolved. + + \list + \o TextInput.CursorBetweenCharacters - Returns the position between characters that is nearest x. + \o TextInput.CursorOnCharacter - Returns the position before the character that is nearest x. + \endlist +*/ +int QDeclarativeTextInput::positionAt(int x, CursorPosition position) const +{ + Q_D(const QDeclarativeTextInput); + int pos = d->control->xToPos(x + d->hscroll, QTextLine::CursorPosition(position)); + const int cursor = d->control->cursor(); + if (pos > cursor) { + const int preeditLength = d->control->preeditAreaText().length(); + pos = pos > cursor + preeditLength + ? pos - preeditLength + : cursor; + } + return pos; +} + +void QDeclarativeTextInputPrivate::focusChanged(bool hasFocus) +{ + Q_Q(QDeclarativeTextInput); + focused = hasFocus; + q->setCursorVisible(hasFocus && scene && scene->hasFocus()); + if(!hasFocus && control->passwordEchoEditing()) + control->updatePasswordEchoEditing(false);//QLineControl sets it on key events, but doesn't deal with focus events + if (!hasFocus) + control->deselect(); + QDeclarativeItemPrivate::focusChanged(hasFocus); +} + +void QDeclarativeTextInput::keyPressEvent(QKeyEvent* ev) +{ + Q_D(QDeclarativeTextInput); + keyPressPreHandler(ev); + if (ev->isAccepted()) + return; + + // Don't allow MacOSX up/down support, and we don't allow a completer. + bool ignore = (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down) && ev->modifiers() == Qt::NoModifier; + if (!ignore && (d->lastSelectionStart == d->lastSelectionEnd) && (ev->key() == Qt::Key_Right || ev->key() == Qt::Key_Left)) { + // Ignore when moving off the end unless there is a selection, + // because then moving will do something (deselect). + int cursorPosition = d->control->cursor(); + if (cursorPosition == 0) + ignore = ev->key() == (d->control->layoutDirection() == Qt::LeftToRight ? Qt::Key_Left : Qt::Key_Right); + if (cursorPosition == d->control->text().length()) + ignore = ev->key() == (d->control->layoutDirection() == Qt::LeftToRight ? Qt::Key_Right : Qt::Key_Left); + } + if (ignore) { + ev->ignore(); + } else { + d->control->processKeyEvent(ev); + } + if (!ev->isAccepted()) + QDeclarativePaintedItem::keyPressEvent(ev); +} + +void QDeclarativeTextInput::inputMethodEvent(QInputMethodEvent *ev) +{ + Q_D(QDeclarativeTextInput); + ev->ignore(); + const bool wasComposing = d->control->preeditAreaText().length() > 0; + inputMethodPreHandler(ev); + if (!ev->isAccepted()) { + if (d->control->isReadOnly()) { + ev->ignore(); + } else { + d->control->processInputMethodEvent(ev); + } + } + if (!ev->isAccepted()) + QDeclarativePaintedItem::inputMethodEvent(ev); + + if (wasComposing != (d->control->preeditAreaText().length() > 0)) + emit inputMethodComposingChanged(); +} + +/*! +\overload +Handles the given mouse \a event. +*/ +void QDeclarativeTextInput::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarativeTextInput); + if (d->sendMouseEventToInputContext(event, QEvent::MouseButtonDblClick)) + return; + if (d->selectByMouse) { + int cursor = d->xToPos(event->pos().x()); + d->control->selectWordAtPos(cursor); + event->setAccepted(true); + } else { + QDeclarativePaintedItem::mouseDoubleClickEvent(event); + } +} + +void QDeclarativeTextInput::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarativeTextInput); + if (d->sendMouseEventToInputContext(event, QEvent::MouseButtonPress)) + return; + if(d->focusOnPress){ + bool hadActiveFocus = hasActiveFocus(); + forceActiveFocus(); + if (d->showInputPanelOnFocus) { + if (hasActiveFocus() && hadActiveFocus && !isReadOnly()) { + // re-open input panel on press if already focused + openSoftwareInputPanel(); + } + } else { // show input panel on click + if (hasActiveFocus() && !hadActiveFocus) { + d->clickCausedFocus = true; + } + } + } + if (d->selectByMouse) { + setKeepMouseGrab(false); + d->selectPressed = true; + d->pressPos = event->pos(); + } + bool mark = (event->modifiers() & Qt::ShiftModifier) && d->selectByMouse; + int cursor = d->xToPos(event->pos().x()); + d->control->moveCursor(cursor, mark); + event->setAccepted(true); +} + +void QDeclarativeTextInput::mouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarativeTextInput); + if (d->sendMouseEventToInputContext(event, QEvent::MouseMove)) + return; + if (d->selectPressed) { + if (qAbs(int(event->pos().x() - d->pressPos.x())) > QApplication::startDragDistance()) + setKeepMouseGrab(true); + moveCursorSelection(d->xToPos(event->pos().x()), d->mouseSelectionMode); + event->setAccepted(true); + } else { + QDeclarativePaintedItem::mouseMoveEvent(event); + } +} + +/*! +\overload +Handles the given mouse \a event. +*/ +void QDeclarativeTextInput::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarativeTextInput); + if (d->sendMouseEventToInputContext(event, QEvent::MouseButtonRelease)) + return; + if (d->selectPressed) { + d->selectPressed = false; + setKeepMouseGrab(false); + } + if (!d->showInputPanelOnFocus) { // input panel on click + if (d->focusOnPress && !isReadOnly() && boundingRect().contains(event->pos())) { + if (QGraphicsView * view = qobject_cast(qApp->focusWidget())) { + if (view->scene() && view->scene() == scene()) { + qt_widget_private(view)->handleSoftwareInputPanel(event->button(), d->clickCausedFocus); + } + } + } + } + d->clickCausedFocus = false; + d->control->processEvent(event); + if (!event->isAccepted()) + QDeclarativePaintedItem::mouseReleaseEvent(event); +} + +bool QDeclarativeTextInputPrivate::sendMouseEventToInputContext( + QGraphicsSceneMouseEvent *event, QEvent::Type eventType) +{ +#if !defined QT_NO_IM + if (event->widget() && control->composeMode()) { + int tmp_cursor = xToPos(event->pos().x()); + int mousePos = tmp_cursor - control->cursor(); + if (mousePos < 0 || mousePos > control->preeditAreaText().length()) { + mousePos = -1; + // don't send move events outside the preedit area + if (eventType == QEvent::MouseMove) + return true; + } + + QInputContext *qic = event->widget()->inputContext(); + if (qic) { + QMouseEvent mouseEvent( + eventType, + event->widget()->mapFromGlobal(event->screenPos()), + event->screenPos(), + event->button(), + event->buttons(), + event->modifiers()); + // may be causing reset() in some input methods + qic->mouseHandler(mousePos, &mouseEvent); + event->setAccepted(mouseEvent.isAccepted()); + } + if (!control->preeditAreaText().isEmpty()) + return true; + } +#else + Q_UNUSED(event); + Q_UNUSED(eventType) +#endif + + return false; +} + +bool QDeclarativeTextInput::sceneEvent(QEvent *event) +{ + Q_D(QDeclarativeTextInput); + bool rv = QDeclarativeItem::sceneEvent(event); + if (event->type() == QEvent::UngrabMouse) { + d->selectPressed = false; + setKeepMouseGrab(false); + } + return rv; +} + +bool QDeclarativeTextInput::event(QEvent* ev) +{ + Q_D(QDeclarativeTextInput); + //Anything we don't deal with ourselves, pass to the control + bool handled = false; + switch(ev->type()){ + case QEvent::KeyPress: + case QEvent::KeyRelease://###Should the control be doing anything with release? + case QEvent::InputMethod: + case QEvent::GraphicsSceneMousePress: + case QEvent::GraphicsSceneMouseMove: + case QEvent::GraphicsSceneMouseRelease: + case QEvent::GraphicsSceneMouseDoubleClick: + break; + default: + handled = d->control->processEvent(ev); + } + if(!handled) + handled = QDeclarativePaintedItem::event(ev); + return handled; +} + +void QDeclarativeTextInput::geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry) +{ + Q_D(QDeclarativeTextInput); + if (newGeometry.width() != oldGeometry.width()) { + updateSize(); + updateCursorRectangle(); + } + QDeclarativePaintedItem::geometryChanged(newGeometry, oldGeometry); +} + +int QDeclarativeTextInputPrivate::calculateTextWidth() +{ + return qRound(control->naturalTextWidth()); +} + +void QDeclarativeTextInputPrivate::updateHorizontalScroll() +{ + Q_Q(QDeclarativeTextInput); + const int preeditLength = control->preeditAreaText().length(); + int cix = qRound(control->cursorToX(control->cursor() + preeditLength)); + QRect br(q->boundingRect().toRect()); + int widthUsed = calculateTextWidth(); + + QDeclarativeTextInput::HAlignment effectiveHAlign = q->effectiveHAlign(); + if (autoScroll) { + if (widthUsed <= br.width()) { + // text fits in br; use hscroll for alignment + switch (effectiveHAlign & ~(Qt::AlignAbsolute|Qt::AlignVertical_Mask)) { + case Qt::AlignRight: + hscroll = widthUsed - br.width() - 1; + break; + case Qt::AlignHCenter: + hscroll = (widthUsed - br.width()) / 2; + break; + default: + // Left + hscroll = 0; + break; + } + } else if (cix - hscroll >= br.width()) { + // text doesn't fit, cursor is to the right of br (scroll right) + hscroll = cix - br.width() + 1; + } else if (cix - hscroll < 0 && hscroll < widthUsed) { + // text doesn't fit, cursor is to the left of br (scroll left) + hscroll = cix; + } else if (widthUsed - hscroll < br.width()) { + // text doesn't fit, text document is to the left of br; align + // right + hscroll = widthUsed - br.width() + 1; + } + if (preeditLength > 0) { + // check to ensure long pre-edit text doesn't push the cursor + // off to the left + cix = qRound(control->cursorToX( + control->cursor() + qMax(0, control->preeditCursor() - 1))); + if (cix < hscroll) + hscroll = cix; + } + } else { + switch (effectiveHAlign) { + case QDeclarativeTextInput::AlignRight: + hscroll = q->width() - widthUsed; + break; + case QDeclarativeTextInput::AlignHCenter: + hscroll = (q->width() - widthUsed) / 2; + break; + default: + // Left + hscroll = 0; + break; + } + } +} + +void QDeclarativeTextInput::drawContents(QPainter *p, const QRect &r) +{ + Q_D(QDeclarativeTextInput); + p->setRenderHint(QPainter::TextAntialiasing, true); + p->save(); + p->setPen(QPen(d->color)); + int flags = QLineControl::DrawText; + if(!isReadOnly() && d->cursorVisible && !d->cursorItem) + flags |= QLineControl::DrawCursor; + if (d->control->hasSelectedText()) + flags |= QLineControl::DrawSelections; + QPoint offset = QPoint(0,0); + QFontMetrics fm = QFontMetrics(d->font); + QRect br(boundingRect().toRect()); + if (d->autoScroll) { + // the y offset is there to keep the baseline constant in case we have script changes in the text. + offset = br.topLeft() - QPoint(d->hscroll, d->control->ascent() - fm.ascent()); + } else { + offset = QPoint(d->hscroll, 0); + } + d->control->draw(p, offset, r, flags); + p->restore(); +} + +/*! +\overload +Returns the value of the given \a property. +*/ +QVariant QDeclarativeTextInput::inputMethodQuery(Qt::InputMethodQuery property) const +{ + Q_D(const QDeclarativeTextInput); + switch(property) { + case Qt::ImMicroFocus: + return cursorRectangle(); + case Qt::ImFont: + return font(); + case Qt::ImCursorPosition: + return QVariant(d->control->cursor()); + case Qt::ImSurroundingText: + if (d->control->echoMode() == PasswordEchoOnEdit && !d->control->passwordEchoEditing()) + return QVariant(displayText()); + else + return QVariant(text()); + case Qt::ImCurrentSelection: + return QVariant(selectedText()); + case Qt::ImMaximumTextLength: + return QVariant(maxLength()); + case Qt::ImAnchorPosition: + if (d->control->selectionStart() == d->control->selectionEnd()) + return QVariant(d->control->cursor()); + else if (d->control->selectionStart() == d->control->cursor()) + return QVariant(d->control->selectionEnd()); + else + return QVariant(d->control->selectionStart()); + default: + return QVariant(); + } +} + +/*! + \qmlmethod void TextInput::deselect() + \since QtQuick 1.1 + + Removes active text selection. +*/ +void QDeclarativeTextInput::deselect() +{ + Q_D(QDeclarativeTextInput); + d->control->deselect(); +} + +/*! + \qmlmethod void TextInput::selectAll() + + Causes all text to be selected. +*/ +void QDeclarativeTextInput::selectAll() +{ + Q_D(QDeclarativeTextInput); + d->control->setSelection(0, d->control->text().length()); +} + +/*! + \qmlmethod void TextInput::isRightToLeft(int start, int end) + + Returns true if the natural reading direction of the editor text + found between positions \a start and \a end is right to left. +*/ +bool QDeclarativeTextInput::isRightToLeft(int start, int end) +{ + Q_D(QDeclarativeTextInput); + if (start > end) { + qmlInfo(this) << "isRightToLeft(start, end) called with the end property being smaller than the start."; + return false; + } else { + return d->control->text().mid(start, end - start).isRightToLeft(); + } +} + +#ifndef QT_NO_CLIPBOARD +/*! + \qmlmethod TextInput::cut() + + Moves the currently selected text to the system clipboard. +*/ +void QDeclarativeTextInput::cut() +{ + Q_D(QDeclarativeTextInput); + d->control->copy(); + d->control->del(); +} + +/*! + \qmlmethod TextInput::copy() + + Copies the currently selected text to the system clipboard. +*/ +void QDeclarativeTextInput::copy() +{ + Q_D(QDeclarativeTextInput); + d->control->copy(); +} + +/*! + \qmlmethod TextInput::paste() + + Replaces the currently selected text by the contents of the system clipboard. +*/ +void QDeclarativeTextInput::paste() +{ + Q_D(QDeclarativeTextInput); + if(!d->control->isReadOnly()) + d->control->paste(); +} +#endif // QT_NO_CLIPBOARD + +/*! + \qmlmethod void TextInput::selectWord() + + Causes the word closest to the current cursor position to be selected. +*/ +void QDeclarativeTextInput::selectWord() +{ + Q_D(QDeclarativeTextInput); + d->control->selectWordAtPos(d->control->cursor()); +} + +/*! + \qmlproperty bool TextInput::smooth + + This property holds whether the text is smoothly scaled or transformed. + + Smooth filtering gives better visual quality, but is slower. If + the item is displayed at its natural size, this property has no visual or + performance effect. + + \note Generally scaling artifacts are only visible if the item is stationary on + the screen. A common pattern when animating an item is to disable smooth + filtering at the beginning of the animation and reenable it at the conclusion. +*/ + +/*! + \qmlproperty string TextInput::passwordCharacter + + This is the character displayed when echoMode is set to Password or + PasswordEchoOnEdit. By default it is an asterisk. + + If this property is set to a string with more than one character, + the first character is used. If the string is empty, the value + is ignored and the property is not set. +*/ +QString QDeclarativeTextInput::passwordCharacter() const +{ + Q_D(const QDeclarativeTextInput); + return QString(d->control->passwordCharacter()); +} + +void QDeclarativeTextInput::setPasswordCharacter(const QString &str) +{ + Q_D(QDeclarativeTextInput); + if(str.length() < 1) + return; + d->control->setPasswordCharacter(str.constData()[0]); + EchoMode echoMode_ = echoMode(); + if (echoMode_ == Password || echoMode_ == PasswordEchoOnEdit) { + updateSize(); + } + emit passwordCharacterChanged(); +} + +/*! + \qmlproperty string TextInput::displayText + + This is the text displayed in the TextInput. + + If \l echoMode is set to TextInput::Normal, this holds the + same value as the TextInput::text property. Otherwise, + this property holds the text visible to the user, while + the \l text property holds the actual entered text. +*/ +QString QDeclarativeTextInput::displayText() const +{ + Q_D(const QDeclarativeTextInput); + return d->control->displayText(); +} + +/*! + \qmlproperty bool TextInput::selectByMouse + + Defaults to false. + + If true, the user can use the mouse to select text in some + platform-specific way. Note that for some platforms this may + not be an appropriate interaction (eg. may conflict with how + the text needs to behave inside a Flickable. +*/ +bool QDeclarativeTextInput::selectByMouse() const +{ + Q_D(const QDeclarativeTextInput); + return d->selectByMouse; +} + +void QDeclarativeTextInput::setSelectByMouse(bool on) +{ + Q_D(QDeclarativeTextInput); + if (d->selectByMouse != on) { + d->selectByMouse = on; + emit selectByMouseChanged(on); + } +} + +/*! + \qmlproperty enum TextInput::mouseSelectionMode + \since QtQuick 1.1 + + Specifies how text should be selected using a mouse. + + \list + \o TextInput.SelectCharacters - The selection is updated with individual characters. (Default) + \o TextInput.SelectWords - The selection is updated with whole words. + \endlist + + This property only applies when \l selectByMouse is true. +*/ + +QDeclarativeTextInput::SelectionMode QDeclarativeTextInput::mouseSelectionMode() const +{ + Q_D(const QDeclarativeTextInput); + return d->mouseSelectionMode; +} + +void QDeclarativeTextInput::setMouseSelectionMode(SelectionMode mode) +{ + Q_D(QDeclarativeTextInput); + if (d->mouseSelectionMode != mode) { + d->mouseSelectionMode = mode; + emit mouseSelectionModeChanged(mode); + } +} + +/*! + \qmlproperty bool TextInput::canPaste + \since QtQuick 1.1 + + Returns true if the TextInput is writable and the content of the clipboard is + suitable for pasting into the TextEdit. +*/ +bool QDeclarativeTextInput::canPaste() const +{ + Q_D(const QDeclarativeTextInput); + return d->canPaste; +} + +void QDeclarativeTextInput::moveCursorSelection(int position) +{ + Q_D(QDeclarativeTextInput); + d->control->moveCursor(position, true); +} + +/*! + \qmlmethod void TextInput::moveCursorSelection(int position, SelectionMode mode = TextInput.SelectCharacters) + \since QtQuick 1.1 + + Moves the cursor to \a position and updates the selection according to the optional \a mode + parameter. (To only move the cursor, set the \l cursorPosition property.) + + When this method is called it additionally sets either the + selectionStart or the selectionEnd (whichever was at the previous cursor position) + to the specified position. This allows you to easily extend and contract the selected + text range. + + The selection mode specifies whether the selection is updated on a per character or a per word + basis. If not specified the selection mode will default to TextInput.SelectCharacters. + + \list + \o TextEdit.SelectCharacters - Sets either the selectionStart or selectionEnd (whichever was at + the previous cursor position) to the specified position. + \o TextEdit.SelectWords - Sets the selectionStart and selectionEnd to include all + words between the specified postion and the previous cursor position. Words partially in the + range are included. + \endlist + + For example, take this sequence of calls: + + \code + cursorPosition = 5 + moveCursorSelection(9, TextInput.SelectCharacters) + moveCursorSelection(7, TextInput.SelectCharacters) + \endcode + + This moves the cursor to position 5, extend the selection end from 5 to 9 + and then retract the selection end from 9 to 7, leaving the text from position 5 to 7 + selected (the 6th and 7th characters). + + The same sequence with TextInput.SelectWords will extend the selection start to a word boundary + before or on position 5 and extend the selection end to a word boundary on or past position 9. +*/ +void QDeclarativeTextInput::moveCursorSelection(int pos, SelectionMode mode) +{ + Q_D(QDeclarativeTextInput); + + if (mode == SelectCharacters) { + d->control->moveCursor(pos, true); + } else if (pos != d->control->cursor()){ + const int cursor = d->control->cursor(); + int anchor; + if (!d->control->hasSelectedText()) + anchor = d->control->cursor(); + else if (d->control->selectionStart() == d->control->cursor()) + anchor = d->control->selectionEnd(); + else + anchor = d->control->selectionStart(); + + if (anchor < pos || (anchor == pos && cursor < pos)) { + const QString text = d->control->text(); + QTextBoundaryFinder finder(QTextBoundaryFinder::Word, text); + finder.setPosition(anchor); + + const QTextBoundaryFinder::BoundaryReasons reasons = finder.boundaryReasons(); + if (anchor < text.length() && (!(reasons & QTextBoundaryFinder::StartWord) + || ((reasons & QTextBoundaryFinder::EndWord) && anchor > cursor))) { + finder.toPreviousBoundary(); + } + anchor = finder.position() != -1 ? finder.position() : 0; + + finder.setPosition(pos); + if (pos > 0 && !finder.boundaryReasons()) + finder.toNextBoundary(); + const int cursor = finder.position() != -1 ? finder.position() : text.length(); + + d->control->setSelection(anchor, cursor - anchor); + } else if (anchor > pos || (anchor == pos && cursor > pos)) { + const QString text = d->control->text(); + QTextBoundaryFinder finder(QTextBoundaryFinder::Word, text); + finder.setPosition(anchor); + + const QTextBoundaryFinder::BoundaryReasons reasons = finder.boundaryReasons(); + if (anchor > 0 && (!(reasons & QTextBoundaryFinder::EndWord) + || ((reasons & QTextBoundaryFinder::StartWord) && anchor < cursor))) { + finder.toNextBoundary(); + } + anchor = finder.position() != -1 ? finder.position() : text.length(); + + finder.setPosition(pos); + if (pos < text.length() && !finder.boundaryReasons()) + finder.toPreviousBoundary(); + const int cursor = finder.position() != -1 ? finder.position() : 0; + + d->control->setSelection(anchor, cursor - anchor); + } + } +} + +/*! + \qmlmethod void TextInput::openSoftwareInputPanel() + + Opens software input panels like virtual keyboards for typing, useful for + customizing when you want the input keyboard to be shown and hidden in + your application. + + By default the opening of input panels follows the platform style. On Symbian^1 and + Symbian^3 -based devices the panels are opened by clicking TextInput. On other platforms + the panels are automatically opened when TextInput element gains active focus. Input panels are + always closed if no editor has active focus. + + . You can disable the automatic behavior by setting the property \c activeFocusOnPress to false + and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement + the behavior you want. + + Only relevant on platforms, which provide virtual keyboards. + + \qml + import QtQuick 1.0 + TextInput { + id: textInput + text: "Hello world!" + activeFocusOnPress: false + MouseArea { + anchors.fill: parent + onClicked: { + if (!textInput.activeFocus) { + textInput.forceActiveFocus() + textInput.openSoftwareInputPanel(); + } else { + textInput.focus = false; + } + } + onPressAndHold: textInput.closeSoftwareInputPanel(); + } + } + \endqml +*/ +void QDeclarativeTextInput::openSoftwareInputPanel() +{ + QEvent event(QEvent::RequestSoftwareInputPanel); + if (qApp) { + if (QGraphicsView * view = qobject_cast(qApp->focusWidget())) { + if (view->scene() && view->scene() == scene()) { + QApplication::sendEvent(view, &event); + } + } + } +} + +/*! + \qmlmethod void TextInput::closeSoftwareInputPanel() + + Closes a software input panel like a virtual keyboard shown on the screen, useful + for customizing when you want the input keyboard to be shown and hidden in + your application. + + By default the opening of input panels follows the platform style. On Symbian^1 and + Symbian^3 -based devices the panels are opened by clicking TextInput. On other platforms + the panels are automatically opened when TextInput element gains active focus. Input panels are + always closed if no editor has active focus. + + . You can disable the automatic behavior by setting the property \c activeFocusOnPress to false + and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement + the behavior you want. + + Only relevant on platforms, which provide virtual keyboards. + + \qml + import QtQuick 1.0 + TextInput { + id: textInput + text: "Hello world!" + activeFocusOnPress: false + MouseArea { + anchors.fill: parent + onClicked: { + if (!textInput.activeFocus) { + textInput.forceActiveFocus(); + textInput.openSoftwareInputPanel(); + } else { + textInput.focus = false; + } + } + onPressAndHold: textInput.closeSoftwareInputPanel(); + } + } + \endqml +*/ +void QDeclarativeTextInput::closeSoftwareInputPanel() +{ + QEvent event(QEvent::CloseSoftwareInputPanel); + if (qApp) { + QEvent event(QEvent::CloseSoftwareInputPanel); + if (QGraphicsView * view = qobject_cast(qApp->focusWidget())) { + if (view->scene() && view->scene() == scene()) { + QApplication::sendEvent(view, &event); + } + } + } +} + +void QDeclarativeTextInput::focusInEvent(QFocusEvent *event) +{ + Q_D(const QDeclarativeTextInput); + if (d->showInputPanelOnFocus) { + if (d->focusOnPress && !isReadOnly()) { + openSoftwareInputPanel(); + } + } + QDeclarativePaintedItem::focusInEvent(event); +} + +/*! + \qmlproperty bool TextInput::inputMethodComposing + + \since QtQuick 1.1 + + This property holds whether the TextInput has partial text input from an + input method. + + While it is composing an input method may rely on mouse or key events from + the TextInput to edit or commit the partial text. This property can be + used to determine when to disable events handlers that may interfere with + the correct operation of an input method. +*/ +bool QDeclarativeTextInput::isInputMethodComposing() const +{ + Q_D(const QDeclarativeTextInput); + return d->control->preeditAreaText().length() > 0; +} + +void QDeclarativeTextInputPrivate::init() +{ + Q_Q(QDeclarativeTextInput); + control->setParent(q); + control->setCursorWidth(1); + control->setPasswordCharacter(QLatin1Char('*')); + q->setSmooth(smooth); + q->setAcceptedMouseButtons(Qt::LeftButton); + q->setFlag(QGraphicsItem::ItemHasNoContents, false); + q->setFlag(QGraphicsItem::ItemAcceptsInputMethod); + q->connect(control, SIGNAL(cursorPositionChanged(int,int)), + q, SLOT(cursorPosChanged())); + q->connect(control, SIGNAL(selectionChanged()), + q, SLOT(selectionChanged())); + q->connect(control, SIGNAL(textChanged(QString)), + q, SLOT(q_textChanged())); + q->connect(control, SIGNAL(accepted()), + q, SIGNAL(accepted())); + q->connect(control, SIGNAL(updateNeeded(QRect)), + q, SLOT(updateRect(QRect))); +#ifndef QT_NO_CLIPBOARD + q->connect(q, SIGNAL(readOnlyChanged(bool)), + q, SLOT(q_canPasteChanged())); + q->connect(QApplication::clipboard(), SIGNAL(dataChanged()), + q, SLOT(q_canPasteChanged())); + canPaste = !control->isReadOnly() && QApplication::clipboard()->text().length() != 0; +#endif // QT_NO_CLIPBOARD + q->connect(control, SIGNAL(updateMicroFocus()), + q, SLOT(updateCursorRectangle())); + q->connect(control, SIGNAL(displayTextChanged(QString)), + q, SLOT(updateRect())); + q->updateSize(); + oldValidity = control->hasAcceptableInput(); + lastSelectionStart = 0; + lastSelectionEnd = 0; + QPalette p = control->palette(); + selectedTextColor = p.color(QPalette::HighlightedText); + selectionColor = p.color(QPalette::Highlight); + determineHorizontalAlignment(); +} + +void QDeclarativeTextInput::cursorPosChanged() +{ + Q_D(QDeclarativeTextInput); + updateCursorRectangle(); + emit cursorPositionChanged(); + d->control->resetCursorBlinkTimer(); + + if(!d->control->hasSelectedText()){ + if(d->lastSelectionStart != d->control->cursor()){ + d->lastSelectionStart = d->control->cursor(); + emit selectionStartChanged(); + } + if(d->lastSelectionEnd != d->control->cursor()){ + d->lastSelectionEnd = d->control->cursor(); + emit selectionEndChanged(); + } + } +} + +void QDeclarativeTextInput::updateCursorRectangle() +{ + Q_D(QDeclarativeTextInput); + d->determineHorizontalAlignment(); + d->updateHorizontalScroll(); + updateRect();//TODO: Only update rect between pos's + updateMicroFocus(); + emit cursorRectangleChanged(); + if (d->cursorItem) + d->cursorItem->setX(d->control->cursorToX() - d->hscroll); +} + +void QDeclarativeTextInput::selectionChanged() +{ + Q_D(QDeclarativeTextInput); + updateRect();//TODO: Only update rect in selection + emit selectedTextChanged(); + + if(d->lastSelectionStart != d->control->selectionStart()){ + d->lastSelectionStart = d->control->selectionStart(); + if(d->lastSelectionStart == -1) + d->lastSelectionStart = d->control->cursor(); + emit selectionStartChanged(); + } + if(d->lastSelectionEnd != d->control->selectionEnd()){ + d->lastSelectionEnd = d->control->selectionEnd(); + if(d->lastSelectionEnd == -1) + d->lastSelectionEnd = d->control->cursor(); + emit selectionEndChanged(); + } +} + +void QDeclarativeTextInput::q_textChanged() +{ + Q_D(QDeclarativeTextInput); + emit textChanged(); + emit displayTextChanged(); + updateSize(); + d->determineHorizontalAlignment(); + d->updateHorizontalScroll(); + updateMicroFocus(); + if(hasAcceptableInput() != d->oldValidity){ + d->oldValidity = hasAcceptableInput(); + emit acceptableInputChanged(); + } +} + +void QDeclarativeTextInput::updateRect(const QRect &r) +{ + Q_D(QDeclarativeTextInput); + if(r == QRect()) + clearCache(); + else + dirtyCache(QRect(r.x() - d->hscroll, r.y(), r.width(), r.height())); + update(); +} + +QRectF QDeclarativeTextInput::boundingRect() const +{ + Q_D(const QDeclarativeTextInput); + QRectF r = QDeclarativePaintedItem::boundingRect(); + + int cursorWidth = d->cursorItem ? d->cursorItem->width() : d->control->cursorWidth(); + + // Could include font max left/right bearings to either side of rectangle. + + r.setRight(r.right() + cursorWidth); + return r; +} + +void QDeclarativeTextInput::updateSize(bool needsRedraw) +{ + Q_D(QDeclarativeTextInput); + int w = width(); + int h = height(); + setImplicitHeight(d->control->height()-1); // -1 to counter QLineControl's +1 which is not consistent with Text. + setImplicitWidth(d->calculateTextWidth()); + setContentsSize(QSize(width(), height()));//Repaints if changed + if(w==width() && h==height() && needsRedraw){ + clearCache(); + update(); + } +} + +void QDeclarativeTextInput::q_canPasteChanged() +{ + Q_D(QDeclarativeTextInput); + bool old = d->canPaste; +#ifndef QT_NO_CLIPBOARD + d->canPaste = !d->control->isReadOnly() && QApplication::clipboard()->text().length() != 0; +#endif + if(d->canPaste != old) + emit canPasteChanged(); +} + +QT_END_NAMESPACE + +#endif // QT_NO_LINEEDIT + diff --git a/src/declarative/graphicsitems/qdeclarativetextinput_p.h b/src/declarative/graphicsitems/qdeclarativetextinput_p.h new file mode 100644 index 00000000..2c7eb3c9 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativetextinput_p.h @@ -0,0 +1,304 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVETEXTINPUT_H +#define QDECLARATIVETEXTINPUT_H + +#include "private/qdeclarativetext_p.h" +#include "private/qdeclarativeimplicitsizeitem_p.h" + +#include +#include + +#ifndef QT_NO_LINEEDIT + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeTextInputPrivate; +class QValidator; +class Q_AUTOTEST_EXPORT QDeclarativeTextInput : public QDeclarativeImplicitSizePaintedItem +{ + Q_OBJECT + Q_ENUMS(HAlignment) + Q_ENUMS(EchoMode) + Q_ENUMS(SelectionMode) + + Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) + Q_PROPERTY(QColor selectionColor READ selectionColor WRITE setSelectionColor NOTIFY selectionColorChanged) + Q_PROPERTY(QColor selectedTextColor READ selectedTextColor WRITE setSelectedTextColor NOTIFY selectedTextColorChanged) + Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY fontChanged) + Q_PROPERTY(HAlignment horizontalAlignment READ hAlign WRITE setHAlign RESET resetHAlign NOTIFY horizontalAlignmentChanged) + Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly NOTIFY readOnlyChanged) + Q_PROPERTY(bool cursorVisible READ isCursorVisible WRITE setCursorVisible NOTIFY cursorVisibleChanged) + Q_PROPERTY(int cursorPosition READ cursorPosition WRITE setCursorPosition NOTIFY cursorPositionChanged) + Q_PROPERTY(QRect cursorRectangle READ cursorRectangle NOTIFY cursorRectangleChanged) + Q_PROPERTY(QDeclarativeComponent *cursorDelegate READ cursorDelegate WRITE setCursorDelegate NOTIFY cursorDelegateChanged) + Q_PROPERTY(int selectionStart READ selectionStart NOTIFY selectionStartChanged) + Q_PROPERTY(int selectionEnd READ selectionEnd NOTIFY selectionEndChanged) + Q_PROPERTY(QString selectedText READ selectedText NOTIFY selectedTextChanged) + + Q_PROPERTY(int maximumLength READ maxLength WRITE setMaxLength NOTIFY maximumLengthChanged) +#ifndef QT_NO_VALIDATOR + Q_PROPERTY(QValidator* validator READ validator WRITE setValidator NOTIFY validatorChanged) +#endif + Q_PROPERTY(QString inputMask READ inputMask WRITE setInputMask NOTIFY inputMaskChanged) + Q_PROPERTY(Qt::InputMethodHints inputMethodHints READ imHints WRITE setIMHints) + + Q_PROPERTY(bool acceptableInput READ hasAcceptableInput NOTIFY acceptableInputChanged) + Q_PROPERTY(EchoMode echoMode READ echoMode WRITE setEchoMode NOTIFY echoModeChanged) + Q_PROPERTY(bool activeFocusOnPress READ focusOnPress WRITE setFocusOnPress NOTIFY activeFocusOnPressChanged) + Q_PROPERTY(QString passwordCharacter READ passwordCharacter WRITE setPasswordCharacter NOTIFY passwordCharacterChanged) + Q_PROPERTY(QString displayText READ displayText NOTIFY displayTextChanged) + Q_PROPERTY(bool autoScroll READ autoScroll WRITE setAutoScroll NOTIFY autoScrollChanged) + Q_PROPERTY(bool selectByMouse READ selectByMouse WRITE setSelectByMouse NOTIFY selectByMouseChanged) + Q_PROPERTY(SelectionMode mouseSelectionMode READ mouseSelectionMode WRITE setMouseSelectionMode NOTIFY mouseSelectionModeChanged REVISION 1) + Q_PROPERTY(bool canPaste READ canPaste NOTIFY canPasteChanged REVISION 1) + Q_PROPERTY(bool inputMethodComposing READ isInputMethodComposing NOTIFY inputMethodComposingChanged REVISION 1) + +public: + QDeclarativeTextInput(QDeclarativeItem* parent=0); + ~QDeclarativeTextInput(); + + enum EchoMode {//To match QLineEdit::EchoMode + Normal, + NoEcho, + Password, + PasswordEchoOnEdit + }; + + enum HAlignment { + AlignLeft = Qt::AlignLeft, + AlignRight = Qt::AlignRight, + AlignHCenter = Qt::AlignHCenter + }; + + enum SelectionMode { + SelectCharacters, + SelectWords + }; + + enum CursorPosition { + CursorBetweenCharacters, + CursorOnCharacter + }; + + //Auxilliary functions needed to control the TextInput from QML + Q_INVOKABLE int positionAt(int x) const; + Q_INVOKABLE Q_REVISION(1) int positionAt(int x, CursorPosition position) const; + Q_INVOKABLE QRectF positionToRectangle(int pos) const; + Q_INVOKABLE void moveCursorSelection(int pos); + Q_INVOKABLE Q_REVISION(1) void moveCursorSelection(int pos, SelectionMode mode); + + Q_INVOKABLE void openSoftwareInputPanel(); + Q_INVOKABLE void closeSoftwareInputPanel(); + + QString text() const; + void setText(const QString &); + + QFont font() const; + void setFont(const QFont &font); + + QColor color() const; + void setColor(const QColor &c); + + QColor selectionColor() const; + void setSelectionColor(const QColor &c); + + QColor selectedTextColor() const; + void setSelectedTextColor(const QColor &c); + + HAlignment hAlign() const; + void setHAlign(HAlignment align); + void resetHAlign(); + HAlignment effectiveHAlign() const; + + bool isReadOnly() const; + void setReadOnly(bool); + + bool isCursorVisible() const; + void setCursorVisible(bool on); + + int cursorPosition() const; + void setCursorPosition(int cp); + + QRect cursorRectangle() const; + + int selectionStart() const; + int selectionEnd() const; + + QString selectedText() const; + + int maxLength() const; + void setMaxLength(int ml); + +#ifndef QT_NO_VALIDATOR + QValidator * validator() const; + void setValidator(QValidator* v); +#endif + QString inputMask() const; + void setInputMask(const QString &im); + + EchoMode echoMode() const; + void setEchoMode(EchoMode echo); + + QString passwordCharacter() const; + void setPasswordCharacter(const QString &str); + + QString displayText() const; + + QDeclarativeComponent* cursorDelegate() const; + void setCursorDelegate(QDeclarativeComponent*); + + bool focusOnPress() const; + void setFocusOnPress(bool); + + bool autoScroll() const; + void setAutoScroll(bool); + + bool selectByMouse() const; + void setSelectByMouse(bool); + + SelectionMode mouseSelectionMode() const; + void setMouseSelectionMode(SelectionMode mode); + + bool hasAcceptableInput() const; + + void drawContents(QPainter *p,const QRect &r); + QVariant inputMethodQuery(Qt::InputMethodQuery property) const; + + QRectF boundingRect() const; + bool canPaste() const; + + bool isInputMethodComposing() const; + + Qt::InputMethodHints imHints() const; + void setIMHints(Qt::InputMethodHints hints); + +Q_SIGNALS: + void textChanged(); + void cursorPositionChanged(); + void cursorRectangleChanged(); + void selectionStartChanged(); + void selectionEndChanged(); + void selectedTextChanged(); + void accepted(); + void acceptableInputChanged(); + void colorChanged(const QColor &color); + void selectionColorChanged(const QColor &color); + void selectedTextColorChanged(const QColor &color); + void fontChanged(const QFont &font); + void horizontalAlignmentChanged(HAlignment alignment); + void readOnlyChanged(bool isReadOnly); + void cursorVisibleChanged(bool isCursorVisible); + void cursorDelegateChanged(); + void maximumLengthChanged(int maximumLength); + void validatorChanged(); + void inputMaskChanged(const QString &inputMask); + void echoModeChanged(EchoMode echoMode); + void passwordCharacterChanged(); + void displayTextChanged(); + void activeFocusOnPressChanged(bool activeFocusOnPress); + void autoScrollChanged(bool autoScroll); + void selectByMouseChanged(bool selectByMouse); + Q_REVISION(1) void mouseSelectionModeChanged(SelectionMode mode); + Q_REVISION(1) void canPasteChanged(); + Q_REVISION(1) void inputMethodComposingChanged(); + +protected: + virtual void geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry); + + void mousePressEvent(QGraphicsSceneMouseEvent *event); + void mouseMoveEvent(QGraphicsSceneMouseEvent *event); + void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); + void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event); + bool sceneEvent(QEvent *event); + void keyPressEvent(QKeyEvent* ev); + void inputMethodEvent(QInputMethodEvent *); + bool event(QEvent *e); + void focusInEvent(QFocusEvent *event); + +public Q_SLOTS: + void selectAll(); + void selectWord(); + void select(int start, int end); + Q_REVISION(1) void deselect(); + Q_REVISION(1) bool isRightToLeft(int start, int end); +#ifndef QT_NO_CLIPBOARD + void cut(); + void copy(); + void paste(); +#endif + +private Q_SLOTS: + void updateSize(bool needsRedraw = true); + void q_textChanged(); + void selectionChanged(); + void createCursor(); + void cursorPosChanged(); + void updateCursorRectangle(); + void updateRect(const QRect &r = QRect()); + void q_canPasteChanged(); + +private: + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarativeTextInput) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeTextInput) +#ifndef QT_NO_VALIDATOR +QML_DECLARE_TYPE(QValidator) +QML_DECLARE_TYPE(QIntValidator) +QML_DECLARE_TYPE(QDoubleValidator) +QML_DECLARE_TYPE(QRegExpValidator) +#endif + +QT_END_HEADER + +#endif // QT_NO_LINEEDIT + +#endif // QDECLARATIVETEXTINPUT_H diff --git a/src/declarative/graphicsitems/qdeclarativetextinput_p_p.h b/src/declarative/graphicsitems/qdeclarativetextinput_p_p.h new file mode 100644 index 00000000..f968460d --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativetextinput_p_p.h @@ -0,0 +1,157 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVETEXTINPUT_P_H +#define QDECLARATIVETEXTINPUT_P_H + +#include "private/qdeclarativetextinput_p.h" + +#include "private/qdeclarativeimplicitsizeitem_p_p.h" + +#include + +#include + +#include + +// +// 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. + +#ifndef QT_NO_LINEEDIT + +QT_BEGIN_NAMESPACE + +class Q_AUTOTEST_EXPORT QDeclarativeTextInputPrivate : public QDeclarativeImplicitSizePaintedItemPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeTextInput) +public: + QDeclarativeTextInputPrivate() : control(new QLineControl), + color((QRgb)0), style(QDeclarativeText::Normal), + styleColor((QRgb)0), hAlign(QDeclarativeTextInput::AlignLeft), + mouseSelectionMode(QDeclarativeTextInput::SelectCharacters), inputMethodHints(Qt::ImhNone), + hscroll(0), oldScroll(0), oldValidity(false), focused(false), focusOnPress(true), + showInputPanelOnFocus(true), clickCausedFocus(false), cursorVisible(false), + autoScroll(true), selectByMouse(false), canPaste(false), hAlignImplicit(true), + selectPressed(false) + { +#ifdef Q_OS_SYMBIAN + if (QSysInfo::symbianVersion() >= QSysInfo::SV_SF_1) { + showInputPanelOnFocus = false; + } +#endif + + } + + ~QDeclarativeTextInputPrivate() + { + } + + int xToPos(int x, QTextLine::CursorPosition betweenOrOn = QTextLine::CursorBetweenCharacters) const + { + Q_Q(const QDeclarativeTextInput); + QRect cr = q->boundingRect().toRect(); + x-= cr.x() - hscroll; + return control->xToPos(x, betweenOrOn); + } + + void init(); + void startCreatingCursor(); + void focusChanged(bool hasFocus); + void updateHorizontalScroll(); + bool determineHorizontalAlignment(); + bool setHAlign(QDeclarativeTextInput::HAlignment, bool forceAlign = false); + void mirrorChange(); + int calculateTextWidth(); + bool sendMouseEventToInputContext(QGraphicsSceneMouseEvent *event, QEvent::Type eventType); + void updateInputMethodHints(); + + QLineControl* control; + + QFont font; + QFont sourceFont; + QColor color; + QColor selectionColor; + QColor selectedTextColor; + QDeclarativeText::TextStyle style; + QColor styleColor; + QDeclarativeTextInput::HAlignment hAlign; + QDeclarativeTextInput::SelectionMode mouseSelectionMode; + Qt::InputMethodHints inputMethodHints; + QPointer cursorComponent; + QPointer cursorItem; + QPointF pressPos; + + int lastSelectionStart; + int lastSelectionEnd; + int oldHeight; + int oldWidth; + int hscroll; + int oldScroll; + bool oldValidity:1; + bool focused:1; + bool focusOnPress:1; + bool showInputPanelOnFocus:1; + bool clickCausedFocus:1; + bool cursorVisible:1; + bool autoScroll:1; + bool selectByMouse:1; + bool canPaste:1; + bool hAlignImplicit:1; + bool selectPressed:1; + + static inline QDeclarativeTextInputPrivate *get(QDeclarativeTextInput *t) { + return t->d_func(); + } +}; + +QT_END_NAMESPACE + +#endif // QT_NO_LINEEDIT + +#endif + diff --git a/src/declarative/graphicsitems/qdeclarativetextlayout.cpp b/src/declarative/graphicsitems/qdeclarativetextlayout.cpp new file mode 100644 index 00000000..6b1a0144 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativetextlayout.cpp @@ -0,0 +1,391 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativetextlayout_p.h" +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeTextLayoutPrivate +{ +public: + QDeclarativeTextLayoutPrivate() + : cached(false) {} + + QPointF position; + + bool cached; + QVector items; + QVector positions; + QVector glyphs; + QVector chars; +}; + +namespace { +class DrawTextItemRecorder: public QPaintEngine +{ + public: + DrawTextItemRecorder(bool untransformedCoordinates, bool useBackendOptimizations) + : m_inertText(0), m_dirtyPen(false), m_useBackendOptimizations(useBackendOptimizations), + m_untransformedCoordinates(untransformedCoordinates), m_currentColor(Qt::black) + { + } + + virtual void updateState(const QPaintEngineState &newState) + { + if (newState.state() & QPaintEngine::DirtyPen + && newState.pen().color() != m_currentColor) { + m_dirtyPen = true; + m_currentColor = newState.pen().color(); + } + } + + virtual void drawTextItem(const QPointF &position, const QTextItem &textItem) + { + int glyphOffset = m_inertText->glyphs.size(); // Store offset into glyph pool + int positionOffset = m_inertText->glyphs.size(); // Offset into position pool + int charOffset = m_inertText->chars.size(); + + const QTextItemInt &ti = static_cast(textItem); + + bool needFreshCurrentItem = true; + if (!m_inertText->items.isEmpty()) { + QStaticTextItem &last = m_inertText->items[m_inertText->items.count() - 1]; + + if (last.fontEngine() == ti.fontEngine && last.font == ti.font() && + (!m_dirtyPen || last.color == state->pen().color())) { + needFreshCurrentItem = false; + + last.numChars += ti.num_chars; + + } + } + + if (needFreshCurrentItem) { + QStaticTextItem currentItem; + + currentItem.setFontEngine(ti.fontEngine); + currentItem.font = ti.font(); + currentItem.charOffset = charOffset; + currentItem.numChars = ti.num_chars; + currentItem.numGlyphs = 0; + currentItem.glyphOffset = glyphOffset; + currentItem.positionOffset = positionOffset; + currentItem.useBackendOptimizations = m_useBackendOptimizations; + if (m_dirtyPen) + currentItem.color = m_currentColor; + + m_inertText->items.append(currentItem); + } + + QStaticTextItem ¤tItem = m_inertText->items.last(); + + QTransform matrix = m_untransformedCoordinates ? QTransform() : state->transform(); + matrix.translate(position.x(), position.y()); + + QVarLengthArray glyphs; + QVarLengthArray positions; + ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions); + + int size = glyphs.size(); + Q_ASSERT(size == positions.size()); + currentItem.numGlyphs += size; + + m_inertText->glyphs.resize(m_inertText->glyphs.size() + size); + m_inertText->positions.resize(m_inertText->glyphs.size()); + m_inertText->chars.resize(m_inertText->chars.size() + ti.num_chars); + + glyph_t *glyphsDestination = m_inertText->glyphs.data() + glyphOffset; + qMemCopy(glyphsDestination, glyphs.constData(), sizeof(glyph_t) * size); + + QFixedPoint *positionsDestination = m_inertText->positions.data() + positionOffset; + qMemCopy(positionsDestination, positions.constData(), sizeof(QFixedPoint) * size); + + QChar *charsDestination = m_inertText->chars.data() + charOffset; + qMemCopy(charsDestination, ti.chars, sizeof(QChar) * ti.num_chars); + + } + + virtual void drawPolygon(const QPointF *, int , PolygonDrawMode ) + { + /* intentionally empty */ + } + + virtual bool begin(QPaintDevice *) { return true; } + virtual bool end() { return true; } + virtual void drawPixmap(const QRectF &, const QPixmap &, const QRectF &) {} + virtual Type type() const + { + return User; + } + + void begin(QDeclarativeTextLayoutPrivate *t) { + m_inertText = t; + m_dirtyPen = false; + } + + private: + QDeclarativeTextLayoutPrivate *m_inertText; + + bool m_dirtyPen; + bool m_useBackendOptimizations; + bool m_untransformedCoordinates; + QColor m_currentColor; +}; + +class DrawTextItemDevice: public QPaintDevice +{ + public: + DrawTextItemDevice(bool untransformedCoordinates, bool useBackendOptimizations) + { + m_paintEngine = new DrawTextItemRecorder(untransformedCoordinates, + useBackendOptimizations); + } + + ~DrawTextItemDevice() + { + delete m_paintEngine; + } + + void begin(QDeclarativeTextLayoutPrivate *t) { + m_paintEngine->begin(t); + } + + int metric(PaintDeviceMetric m) const + { + int val; + switch (m) { + case PdmWidth: + case PdmHeight: + case PdmWidthMM: + case PdmHeightMM: + val = 0; + break; + case PdmDpiX: + case PdmPhysicalDpiX: + val = qt_defaultDpiX(); + break; + case PdmDpiY: + case PdmPhysicalDpiY: + val = qt_defaultDpiY(); + break; + case PdmNumColors: + val = 16777216; + break; + case PdmDepth: + val = 24; + break; + default: + val = 0; + qWarning("DrawTextItemDevice::metric: Invalid metric command"); + } + return val; + } + + virtual QPaintEngine *paintEngine() const + { + return m_paintEngine; + } + + private: + DrawTextItemRecorder *m_paintEngine; +}; + +struct InertTextPainter { + InertTextPainter() + : device(true, true), painter(&device) {} + + DrawTextItemDevice device; + QPainter painter; +}; +} + +Q_GLOBAL_STATIC(InertTextPainter, inertTextPainter); + +/*! +\class QDeclarativeTextLayout +\brief The QDeclarativeTextLayout class is a version of QStaticText that works with QTextLayouts. +\internal + +This class is basically a copy of the QStaticText code, but it is adapted to source its text from +QTextLayout. + +It is also considerably faster to create a QDeclarativeTextLayout than a QStaticText because it uses +a single, shared QPainter instance. QStaticText by comparison creates a new QPainter per instance. +As a consequence this means that QDeclarativeTextLayout is not re-enterant. Adding a lock around +the shared painter solves this, and only introduces a minor performance penalty, but is unnecessary +for QDeclarativeTextLayout's current use (QDeclarativeText is already tied to the GUI thread). +*/ + +QDeclarativeTextLayout::QDeclarativeTextLayout() +: d(0) +{ +} + +QDeclarativeTextLayout::QDeclarativeTextLayout(const QString &text) +: QTextLayout(text), d(0) +{ +} + +QDeclarativeTextLayout::~QDeclarativeTextLayout() +{ + if (d) delete d; +} + +void QDeclarativeTextLayout::beginLayout() +{ + if (d && d->cached) { + d->cached = false; + d->items.clear(); + d->positions.clear(); + d->glyphs.clear(); + d->chars.clear(); + d->position = QPointF(); + } + QTextLayout::beginLayout(); +} + +void QDeclarativeTextLayout::clearLayout() +{ + if (d && d->cached) { + d->cached = false; + d->items.clear(); + d->positions.clear(); + d->glyphs.clear(); + d->chars.clear(); + d->position = QPointF(); + } + QTextLayout::clearLayout(); +} + +void QDeclarativeTextLayout::prepare() +{ + if (!d || !d->cached) { + + if (!d) + d = new QDeclarativeTextLayoutPrivate; + + InertTextPainter *itp = inertTextPainter(); + itp->device.begin(d); + QTextLayout::draw(&itp->painter, QPointF(0, 0)); + + glyph_t *glyphPool = d->glyphs.data(); + QFixedPoint *positionPool = d->positions.data(); + QChar *charPool = d->chars.data(); + + int itemCount = d->items.count(); + for (int ii = 0; ii < itemCount; ++ii) { + QStaticTextItem &item = d->items[ii]; + item.glyphs = glyphPool + item.glyphOffset; + item.glyphPositions = positionPool + item.positionOffset; + item.chars = charPool + item.charOffset; + } + + d->cached = true; + } +} + +// Defined in qpainter.cpp +extern Q_GUI_EXPORT void qt_draw_decoration_for_glyphs(QPainter *painter, const glyph_t *glyphArray, + const QFixedPoint *positions, int glyphCount, + QFontEngine *fontEngine, const QFont &font, + const QTextCharFormat &charFormat); + +void QDeclarativeTextLayout::draw(QPainter *painter, const QPointF &p) +{ + QPainterPrivate *priv = QPainterPrivate::get(painter); + + bool paintEngineSupportsTransformations = priv->extended && + (priv->extended->type() == QPaintEngine::OpenGL2 || + priv->extended->type() == QPaintEngine::OpenVG || + priv->extended->type() == QPaintEngine::OpenGL); + + if (!paintEngineSupportsTransformations || !priv->state->matrix.isAffine()) { + QTextLayout::draw(painter, p); + return; + } + + prepare(); + + int itemCount = d->items.count(); + + if (p != d->position) { + QFixed fx = QFixed::fromReal(p.x()); + QFixed fy = QFixed::fromReal(p.y()); + QFixed oldX = QFixed::fromReal(d->position.x()); + QFixed oldY = QFixed::fromReal(d->position.y()); + for (int item = 0; item < itemCount; ++item) { + QStaticTextItem &textItem = d->items[item]; + + for (int ii = 0; ii < textItem.numGlyphs; ++ii) { + textItem.glyphPositions[ii].x += fx - oldX; + textItem.glyphPositions[ii].y += fy - oldY; + } + textItem.userDataNeedsUpdate = true; + } + + d->position = p; + } + + QPen oldPen = priv->state->pen; + QColor currentColor = oldPen.color(); + for (int ii = 0; ii < itemCount; ++ii) { + QStaticTextItem &item = d->items[ii]; + if (item.color.isValid() && currentColor != item.color) { + painter->setPen(item.color); + currentColor = item.color; + } + priv->extended->drawStaticTextItem(&item); + + qt_draw_decoration_for_glyphs(painter, item.glyphs, item.glyphPositions, + item.numGlyphs, item.fontEngine(), painter->font(), + QTextCharFormat()); + } + if (currentColor != oldPen.color()) + painter->setPen(oldPen); +} + +QT_END_NAMESPACE + diff --git a/src/declarative/graphicsitems/qdeclarativetextlayout_p.h b/src/declarative/graphicsitems/qdeclarativetextlayout_p.h new file mode 100644 index 00000000..791a32b9 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativetextlayout_p.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVETEXTLAYOUT_P_H +#define QDECLARATIVETEXTLAYOUT_P_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeTextLayoutPrivate; +class QDeclarativeTextLayout : public QTextLayout +{ +public: + QDeclarativeTextLayout(); + QDeclarativeTextLayout(const QString &); + ~QDeclarativeTextLayout(); + + void beginLayout(); + void clearLayout(); + + void prepare(); + void draw(QPainter *, const QPointF & = QPointF()); + +private: + QDeclarativeTextLayoutPrivate *d; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVETEXTLAYOUT_P_H diff --git a/src/declarative/graphicsitems/qdeclarativetranslate.cpp b/src/declarative/graphicsitems/qdeclarativetranslate.cpp new file mode 100644 index 00000000..89513316 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativetranslate.cpp @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativetranslate_p.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeTranslatePrivate : public QGraphicsTransformPrivate +{ +public: + QDeclarativeTranslatePrivate() + : x(0), y(0) {} + qreal x; + qreal y; +}; + +/*! + Constructs an empty QDeclarativeTranslate object with the given \a parent. +*/ +QDeclarativeTranslate::QDeclarativeTranslate(QObject *parent) + : QGraphicsTransform(*new QDeclarativeTranslatePrivate, parent) +{ +} + +/*! + Destroys the graphics scale. +*/ +QDeclarativeTranslate::~QDeclarativeTranslate() +{ +} + +/*! + \property QDeclarativeTranslate::x + \brief the horizontal translation. + + The translation can be any real number; the default value is 0.0. + + \sa y +*/ +qreal QDeclarativeTranslate::x() const +{ + Q_D(const QDeclarativeTranslate); + return d->x; +} +void QDeclarativeTranslate::setX(qreal x) +{ + Q_D(QDeclarativeTranslate); + if (d->x == x) + return; + d->x = x; + update(); + emit xChanged(); +} + +/*! + \property QDeclarativeTranslate::y + \brief the vertical translation. + + The translation can be any real number; the default value is 0.0. + + \sa x +*/ +qreal QDeclarativeTranslate::y() const +{ + Q_D(const QDeclarativeTranslate); + return d->y; +} +void QDeclarativeTranslate::setY(qreal y) +{ + Q_D(QDeclarativeTranslate); + if (d->y == y) + return; + d->y = y; + update(); + emit yChanged(); +} + +void QDeclarativeTranslate::applyTo(QMatrix4x4 *matrix) const +{ + Q_D(const QDeclarativeTranslate); + matrix->translate(d->x, d->y, 0); +} + +QT_END_NAMESPACE diff --git a/src/declarative/graphicsitems/qdeclarativetranslate_p.h b/src/declarative/graphicsitems/qdeclarativetranslate_p.h new file mode 100644 index 00000000..bf0e6112 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativetranslate_p.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVETRANSLATE_H +#define QDECLARATIVETRANSLATE_H + +#include "qdeclarativeitem.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeTranslatePrivate; + +class Q_AUTOTEST_EXPORT QDeclarativeTranslate : public QGraphicsTransform +{ + Q_OBJECT + + Q_PROPERTY(qreal x READ x WRITE setX NOTIFY xChanged) + Q_PROPERTY(qreal y READ y WRITE setY NOTIFY yChanged) + +public: + QDeclarativeTranslate(QObject *parent = 0); + ~QDeclarativeTranslate(); + + qreal x() const; + void setX(qreal); + + qreal y() const; + void setY(qreal); + + void applyTo(QMatrix4x4 *matrix) const; + +Q_SIGNALS: + void xChanged(); + void yChanged(); + +private: + Q_DECLARE_PRIVATE(QDeclarativeTranslate) + Q_DISABLE_COPY(QDeclarativeTranslate) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeTranslate) + +QT_END_HEADER + +#endif diff --git a/src/declarative/graphicsitems/qdeclarativevisualitemmodel.cpp b/src/declarative/graphicsitems/qdeclarativevisualitemmodel.cpp new file mode 100644 index 00000000..3f71e574 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativevisualitemmodel.cpp @@ -0,0 +1,1425 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativevisualitemmodel_p.h" + +#include "qdeclarativeitem.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +QHash QDeclarativeVisualItemModelAttached::attachedProperties; + + +class QDeclarativeVisualItemModelPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeVisualItemModel) +public: + QDeclarativeVisualItemModelPrivate() : QObjectPrivate() {} + + static void children_append(QDeclarativeListProperty *prop, QDeclarativeItem *item) { + QDeclarative_setParent_noEvent(item, prop->object); + static_cast(prop->data)->children.append(Item(item)); + static_cast(prop->data)->itemAppended(); + static_cast(prop->data)->emitChildrenChanged(); + } + + static int children_count(QDeclarativeListProperty *prop) { + return static_cast(prop->data)->children.count(); + } + + static QDeclarativeItem *children_at(QDeclarativeListProperty *prop, int index) { + return static_cast(prop->data)->children.at(index).item; + } + + void itemAppended() { + Q_Q(QDeclarativeVisualItemModel); + QDeclarativeVisualItemModelAttached *attached = QDeclarativeVisualItemModelAttached::properties(children.last().item); + attached->setIndex(children.count()-1); + emit q->itemsInserted(children.count()-1, 1); + emit q->countChanged(); + } + + void emitChildrenChanged() { + Q_Q(QDeclarativeVisualItemModel); + emit q->childrenChanged(); + } + + int indexOf(QDeclarativeItem *item) const { + for (int i = 0; i < children.count(); ++i) + if (children.at(i).item == item) + return i; + return -1; + } + + class Item { + public: + Item(QDeclarativeItem *i) : item(i), ref(0) {} + + void addRef() { ++ref; } + bool deref() { return --ref == 0; } + + QDeclarativeItem *item; + int ref; + }; + + QList children; +}; + + +/*! + \qmlclass VisualItemModel QDeclarativeVisualItemModel + \ingroup qml-working-with-data + \since 4.7 + \brief The VisualItemModel allows items to be provided to a view. + + A VisualItemModel contains the visual items to be used in a view. + When a VisualItemModel is used in a view, the view does not require + a delegate since the VisualItemModel already contains the visual + delegate (items). + + An item can determine its index within the + model via the \l{VisualItemModel::index}{index} attached property. + + The example below places three colored rectangles in a ListView. + \code + import QtQuick 1.0 + + Rectangle { + VisualItemModel { + id: itemModel + Rectangle { height: 30; width: 80; color: "red" } + Rectangle { height: 30; width: 80; color: "green" } + Rectangle { height: 30; width: 80; color: "blue" } + } + + ListView { + anchors.fill: parent + model: itemModel + } + } + \endcode + + \image visualitemmodel.png + + \sa {declarative/modelviews/visualitemmodel}{VisualItemModel example} +*/ +QDeclarativeVisualItemModel::QDeclarativeVisualItemModel(QObject *parent) + : QDeclarativeVisualModel(*(new QDeclarativeVisualItemModelPrivate), parent) +{ +} + +/*! + \qmlattachedproperty int VisualItemModel::index + This attached property holds the index of this delegate's item within the model. + + It is attached to each instance of the delegate. +*/ + +QDeclarativeListProperty QDeclarativeVisualItemModel::children() +{ + Q_D(QDeclarativeVisualItemModel); + return QDeclarativeListProperty(this, d, d->children_append, + d->children_count, d->children_at); +} + +/*! + \qmlproperty int VisualItemModel::count + + The number of items in the model. This property is readonly. +*/ +int QDeclarativeVisualItemModel::count() const +{ + Q_D(const QDeclarativeVisualItemModel); + return d->children.count(); +} + +bool QDeclarativeVisualItemModel::isValid() const +{ + return true; +} + +QDeclarativeItem *QDeclarativeVisualItemModel::item(int index, bool) +{ + Q_D(QDeclarativeVisualItemModel); + QDeclarativeVisualItemModelPrivate::Item &item = d->children[index]; + item.addRef(); + return item.item; +} + +QDeclarativeVisualModel::ReleaseFlags QDeclarativeVisualItemModel::release(QDeclarativeItem *item) +{ + Q_D(QDeclarativeVisualItemModel); + int idx = d->indexOf(item); + if (idx >= 0) { + if (d->children[idx].deref()) { + if (item->scene()) + item->scene()->removeItem(item); + QDeclarative_setParent_noEvent(item, this); + } + } + return 0; +} + +bool QDeclarativeVisualItemModel::completePending() const +{ + return false; +} + +void QDeclarativeVisualItemModel::completeItem() +{ + // Nothing to do +} + +QString QDeclarativeVisualItemModel::stringValue(int index, const QString &name) +{ + Q_D(QDeclarativeVisualItemModel); + if (index < 0 || index >= d->children.count()) + return QString(); + return QDeclarativeEngine::contextForObject(d->children.at(index).item)->contextProperty(name).toString(); +} + +int QDeclarativeVisualItemModel::indexOf(QDeclarativeItem *item, QObject *) const +{ + Q_D(const QDeclarativeVisualItemModel); + return d->indexOf(item); +} + +QDeclarativeVisualItemModelAttached *QDeclarativeVisualItemModel::qmlAttachedProperties(QObject *obj) +{ + return QDeclarativeVisualItemModelAttached::properties(obj); +} + +//============================================================================ + +class VDMDelegateDataType : public QDeclarativeOpenMetaObjectType +{ +public: + VDMDelegateDataType(const QMetaObject *base, QDeclarativeEngine *engine) : QDeclarativeOpenMetaObjectType(base, engine) {} + + void propertyCreated(int, QMetaPropertyBuilder &prop) { + prop.setWritable(false); + } +}; + +class QDeclarativeVisualDataModelParts; +class QDeclarativeVisualDataModelData; +class QDeclarativeVisualDataModelPrivate : public QObjectPrivate +{ +public: + QDeclarativeVisualDataModelPrivate(QDeclarativeContext *); + + static QDeclarativeVisualDataModelPrivate *get(QDeclarativeVisualDataModel *m) { + return static_cast(QObjectPrivate::get(m)); + } + + QDeclarativeGuard m_listModelInterface; + QDeclarativeGuard m_abstractItemModel; + QDeclarativeGuard m_visualItemModel; + QString m_part; + + QDeclarativeComponent *m_delegate; + QDeclarativeGuard m_context; + QList m_roles; + QHash m_roleNames; + void ensureRoles() { + if (m_roleNames.isEmpty()) { + if (m_listModelInterface) { + m_roles = m_listModelInterface->roles(); + for (int ii = 0; ii < m_roles.count(); ++ii) + m_roleNames.insert(m_listModelInterface->toString(m_roles.at(ii)).toUtf8(), m_roles.at(ii)); + } else if (m_abstractItemModel) { + for (QHash::const_iterator it = m_abstractItemModel->roleNames().begin(); + it != m_abstractItemModel->roleNames().end(); ++it) { + m_roles.append(it.key()); + m_roleNames.insert(*it, it.key()); + } + if (m_roles.count()) + m_roleNames.insert("hasModelChildren", -1); + } else if (m_listAccessor) { + m_roleNames.insert("modelData", 0); + if (m_listAccessor->type() == QDeclarativeListAccessor::Instance) { + if (QObject *object = m_listAccessor->at(0).value()) { + int count = object->metaObject()->propertyCount(); + for (int ii = 1; ii < count; ++ii) { + const QMetaProperty &prop = object->metaObject()->property(ii); + m_roleNames.insert(prop.name(), 0); + } + } + } + } + } + } + + QHash m_roleToPropId; + int m_modelDataPropId; + void createMetaData() { + if (!m_metaDataCreated) { + ensureRoles(); + if (m_roleNames.count()) { + QHash::const_iterator it = m_roleNames.begin(); + while (it != m_roleNames.end()) { + int propId = m_delegateDataType->createProperty(it.key()) - m_delegateDataType->propertyOffset(); + m_roleToPropId.insert(*it, propId); + ++it; + } + // Add modelData property + if (m_roles.count() == 1) + m_modelDataPropId = m_delegateDataType->createProperty("modelData") - m_delegateDataType->propertyOffset(); + m_metaDataCreated = true; + } + } + } + + struct ObjectRef { + ObjectRef(QObject *object=0) : obj(object), ref(1) {} + QObject *obj; + int ref; + }; + class Cache : public QHash { + public: + QObject *getItem(int index) { + QObject *item = 0; + QHash::iterator it = find(index); + if (it != end()) { + (*it).ref++; + item = (*it).obj; + } + return item; + } + QObject *item(int index) { + QObject *item = 0; + QHash::const_iterator it = find(index); + if (it != end()) + item = (*it).obj; + return item; + } + void insertItem(int index, QObject *obj) { + insert(index, ObjectRef(obj)); + } + bool releaseItem(QObject *obj) { + QHash::iterator it = begin(); + for (; it != end(); ++it) { + ObjectRef &objRef = *it; + if (objRef.obj == obj) { + if (--objRef.ref == 0) { + erase(it); + return true; + } + break; + } + } + return false; + } + }; + + int modelCount() const { + if (m_visualItemModel) + return m_visualItemModel->count(); + if (m_listModelInterface) + return m_listModelInterface->count(); + if (m_abstractItemModel) + return m_abstractItemModel->rowCount(m_root); + if (m_listAccessor) + return m_listAccessor->count(); + return 0; + } + + Cache m_cache; + QHash m_packaged; + + QDeclarativeVisualDataModelParts *m_parts; + friend class QDeclarativeVisualItemParts; + + VDMDelegateDataType *m_delegateDataType; + friend class QDeclarativeVisualDataModelData; + bool m_metaDataCreated : 1; + bool m_metaDataCacheable : 1; + bool m_delegateValidated : 1; + bool m_completePending : 1; + + QDeclarativeVisualDataModelData *data(QObject *item); + + QVariant m_modelVariant; + QDeclarativeListAccessor *m_listAccessor; + + QModelIndex m_root; + QList watchedRoles; + QList watchedRoleIds; +}; + +class QDeclarativeVisualDataModelDataMetaObject : public QDeclarativeOpenMetaObject +{ +public: + QDeclarativeVisualDataModelDataMetaObject(QObject *parent, QDeclarativeOpenMetaObjectType *type) + : QDeclarativeOpenMetaObject(parent, type) {} + + virtual QVariant initialValue(int); + virtual int createProperty(const char *, const char *); + +private: + friend class QDeclarativeVisualDataModelData; +}; + +class QDeclarativeVisualDataModelData : public QObject +{ +Q_OBJECT +public: + QDeclarativeVisualDataModelData(int index, QDeclarativeVisualDataModel *model); + ~QDeclarativeVisualDataModelData(); + + Q_PROPERTY(int index READ index NOTIFY indexChanged) + int index() const; + void setIndex(int index); + + int propForRole(int) const; + int modelDataPropertyId() const { + QDeclarativeVisualDataModelPrivate *model = QDeclarativeVisualDataModelPrivate::get(m_model); + return model->m_modelDataPropId; + } + + void setValue(int, const QVariant &); + bool hasValue(int id) const { + return m_meta->hasValue(id); + } + + void ensureProperties(); + +Q_SIGNALS: + void indexChanged(); + +private: + friend class QDeclarativeVisualDataModelDataMetaObject; + int m_index; + QDeclarativeGuard m_model; + QDeclarativeVisualDataModelDataMetaObject *m_meta; +}; + +int QDeclarativeVisualDataModelData::propForRole(int id) const +{ + QDeclarativeVisualDataModelPrivate *model = QDeclarativeVisualDataModelPrivate::get(m_model); + QHash::const_iterator it = model->m_roleToPropId.find(id); + if (it != model->m_roleToPropId.end()) + return *it; + + return -1; +} + +void QDeclarativeVisualDataModelData::setValue(int id, const QVariant &val) +{ + m_meta->setValue(id, val); +} + +int QDeclarativeVisualDataModelDataMetaObject::createProperty(const char *name, const char *type) +{ + QDeclarativeVisualDataModelData *data = + static_cast(object()); + + if (!data->m_model) + return -1; + + QDeclarativeVisualDataModelPrivate *model = QDeclarativeVisualDataModelPrivate::get(data->m_model); + if (data->m_index < 0 || data->m_index >= model->modelCount()) + return -1; + + if ((!model->m_listModelInterface || !model->m_abstractItemModel) && model->m_listAccessor) { + if (model->m_listAccessor->type() == QDeclarativeListAccessor::ListProperty) { + model->ensureRoles(); + if (qstrcmp(name,"modelData") == 0) + return QDeclarativeOpenMetaObject::createProperty(name, type); + } + } + return -1; +} + +QVariant QDeclarativeVisualDataModelDataMetaObject::initialValue(int propId) +{ + QDeclarativeVisualDataModelData *data = + static_cast(object()); + + Q_ASSERT(data->m_model); + QDeclarativeVisualDataModelPrivate *model = QDeclarativeVisualDataModelPrivate::get(data->m_model); + + QByteArray propName = name(propId); + if ((!model->m_listModelInterface || !model->m_abstractItemModel) && model->m_listAccessor) { + if (propName == "modelData") { + if (model->m_listAccessor->type() == QDeclarativeListAccessor::Instance) { + QObject *object = model->m_listAccessor->at(0).value(); + return object->metaObject()->property(1).read(object); // the first property after objectName + } + return model->m_listAccessor->at(data->m_index); + } else { + // return any property of a single object instance. + QObject *object = model->m_listAccessor->at(data->m_index).value(); + return object->property(propName); + } + } else if (model->m_listModelInterface) { + model->ensureRoles(); + QHash::const_iterator it = model->m_roleNames.find(propName); + if (it != model->m_roleNames.end()) { + QVariant value = model->m_listModelInterface->data(data->m_index, *it); + return value; + } else if (model->m_roles.count() == 1 && propName == "modelData") { + //for compatibility with other lists, assign modelData if there is only a single role + QVariant value = model->m_listModelInterface->data(data->m_index, model->m_roles.first()); + return value; + } + } else if (model->m_abstractItemModel) { + model->ensureRoles(); + QModelIndex index = model->m_abstractItemModel->index(data->m_index, 0, model->m_root); + if (propName == "hasModelChildren") { + return model->m_abstractItemModel->hasChildren(index); + } else { + QHash::const_iterator it = model->m_roleNames.find(propName); + if (it != model->m_roleNames.end()) { + return model->m_abstractItemModel->data(index, *it); + } else if (model->m_roles.count() == 1 && propName == "modelData") { + //for compatibility with other lists, assign modelData if there is only a single role + return model->m_abstractItemModel->data(index, model->m_roles.first()); + } + } + } + Q_ASSERT(!"Can never be reached"); + return QVariant(); +} + +QDeclarativeVisualDataModelData::QDeclarativeVisualDataModelData(int index, + QDeclarativeVisualDataModel *model) +: m_index(index), m_model(model), +m_meta(new QDeclarativeVisualDataModelDataMetaObject(this, QDeclarativeVisualDataModelPrivate::get(model)->m_delegateDataType)) +{ + ensureProperties(); +} + +QDeclarativeVisualDataModelData::~QDeclarativeVisualDataModelData() +{ +} + +void QDeclarativeVisualDataModelData::ensureProperties() +{ + QDeclarativeVisualDataModelPrivate *modelPriv = QDeclarativeVisualDataModelPrivate::get(m_model); + if (modelPriv->m_metaDataCacheable) { + if (!modelPriv->m_metaDataCreated) + modelPriv->createMetaData(); + if (modelPriv->m_metaDataCreated) + m_meta->setCached(true); + } +} + +int QDeclarativeVisualDataModelData::index() const +{ + return m_index; +} + +// This is internal only - it should not be set from qml +void QDeclarativeVisualDataModelData::setIndex(int index) +{ + m_index = index; + emit indexChanged(); +} + +//--------------------------------------------------------------------------- + +class QDeclarativeVisualDataModelPartsMetaObject : public QDeclarativeOpenMetaObject +{ +public: + QDeclarativeVisualDataModelPartsMetaObject(QObject *parent) + : QDeclarativeOpenMetaObject(parent) {} + + virtual void propertyCreated(int, QMetaPropertyBuilder &); + virtual QVariant initialValue(int); +}; + +class QDeclarativeVisualDataModelParts : public QObject +{ +Q_OBJECT +public: + QDeclarativeVisualDataModelParts(QDeclarativeVisualDataModel *parent); + +private: + friend class QDeclarativeVisualDataModelPartsMetaObject; + QDeclarativeVisualDataModel *model; +}; + +void QDeclarativeVisualDataModelPartsMetaObject::propertyCreated(int, QMetaPropertyBuilder &prop) +{ + prop.setWritable(false); +} + +QVariant QDeclarativeVisualDataModelPartsMetaObject::initialValue(int id) +{ + QDeclarativeVisualDataModel *m = new QDeclarativeVisualDataModel; + m->setParent(object()); + m->setPart(QString::fromUtf8(name(id))); + m->setModel(QVariant::fromValue(static_cast(object())->model)); + + QVariant var = QVariant::fromValue((QObject *)m); + return var; +} + +QDeclarativeVisualDataModelParts::QDeclarativeVisualDataModelParts(QDeclarativeVisualDataModel *parent) +: QObject(parent), model(parent) +{ + new QDeclarativeVisualDataModelPartsMetaObject(this); +} + +QDeclarativeVisualDataModelPrivate::QDeclarativeVisualDataModelPrivate(QDeclarativeContext *ctxt) +: m_listModelInterface(0), m_abstractItemModel(0), m_visualItemModel(0), m_delegate(0) +, m_context(ctxt), m_modelDataPropId(-1), m_parts(0), m_delegateDataType(0), m_metaDataCreated(false) +, m_metaDataCacheable(false), m_delegateValidated(false), m_completePending(false), m_listAccessor(0) +{ +} + +QDeclarativeVisualDataModelData *QDeclarativeVisualDataModelPrivate::data(QObject *item) +{ + QDeclarativeVisualDataModelData *dataItem = + item->findChild(); + Q_ASSERT(dataItem); + return dataItem; +} + +//--------------------------------------------------------------------------- + +/*! + \qmlclass VisualDataModel QDeclarativeVisualDataModel + \ingroup qml-working-with-data + \brief The VisualDataModel encapsulates a model and delegate + + A VisualDataModel encapsulates a model and the delegate that will + be instantiated for items in the model. + + It is usually not necessary to create VisualDataModel elements. + However, it can be useful for manipulating and accessing the \l modelIndex + when a QAbstractItemModel subclass is used as the + model. Also, VisualDataModel is used together with \l Package to + provide delegates to multiple views. + + The example below illustrates using a VisualDataModel with a ListView. + + \snippet doc/src/snippets/declarative/visualdatamodel.qml 0 +*/ + +QDeclarativeVisualDataModel::QDeclarativeVisualDataModel() +: QDeclarativeVisualModel(*(new QDeclarativeVisualDataModelPrivate(0))) +{ +} + +QDeclarativeVisualDataModel::QDeclarativeVisualDataModel(QDeclarativeContext *ctxt, QObject *parent) +: QDeclarativeVisualModel(*(new QDeclarativeVisualDataModelPrivate(ctxt)), parent) +{ +} + +QDeclarativeVisualDataModel::~QDeclarativeVisualDataModel() +{ + Q_D(QDeclarativeVisualDataModel); + if (d->m_listAccessor) + delete d->m_listAccessor; + if (d->m_delegateDataType) + d->m_delegateDataType->release(); +} + +/*! + \qmlproperty model VisualDataModel::model + This property holds the model providing data for the VisualDataModel. + + The model provides a set of data that is used to create the items + for a view. For large or dynamic datasets the model is usually + provided by a C++ model object. The C++ model object must be a \l + {QAbstractItemModel} subclass or a simple list. + + Models can also be created directly in QML, using a \l{ListModel} or + \l{XmlListModel}. + + \sa {qmlmodels}{Data Models} +*/ +QVariant QDeclarativeVisualDataModel::model() const +{ + Q_D(const QDeclarativeVisualDataModel); + return d->m_modelVariant; +} + +void QDeclarativeVisualDataModel::setModel(const QVariant &model) +{ + Q_D(QDeclarativeVisualDataModel); + delete d->m_listAccessor; + d->m_listAccessor = 0; + d->m_modelVariant = model; + if (d->m_listModelInterface) { + // Assume caller has released all items. + QObject::disconnect(d->m_listModelInterface, SIGNAL(itemsChanged(int,int,QList)), + this, SLOT(_q_itemsChanged(int,int,QList))); + QObject::disconnect(d->m_listModelInterface, SIGNAL(itemsInserted(int,int)), + this, SLOT(_q_itemsInserted(int,int))); + QObject::disconnect(d->m_listModelInterface, SIGNAL(itemsRemoved(int,int)), + this, SLOT(_q_itemsRemoved(int,int))); + QObject::disconnect(d->m_listModelInterface, SIGNAL(itemsMoved(int,int,int)), + this, SLOT(_q_itemsMoved(int,int,int))); + d->m_listModelInterface = 0; + } else if (d->m_abstractItemModel) { + QObject::disconnect(d->m_abstractItemModel, SIGNAL(rowsInserted(QModelIndex,int,int)), + this, SLOT(_q_rowsInserted(QModelIndex,int,int))); + QObject::disconnect(d->m_abstractItemModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), + this, SLOT(_q_rowsRemoved(QModelIndex,int,int))); + QObject::disconnect(d->m_abstractItemModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), + this, SLOT(_q_dataChanged(QModelIndex,QModelIndex))); + QObject::disconnect(d->m_abstractItemModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), + this, SLOT(_q_rowsMoved(QModelIndex,int,int,QModelIndex,int))); + QObject::disconnect(d->m_abstractItemModel, SIGNAL(modelReset()), this, SLOT(_q_modelReset())); + QObject::disconnect(d->m_abstractItemModel, SIGNAL(layoutChanged()), this, SLOT(_q_layoutChanged())); + d->m_abstractItemModel = 0; + } else if (d->m_visualItemModel) { + QObject::disconnect(d->m_visualItemModel, SIGNAL(itemsInserted(int,int)), + this, SIGNAL(itemsInserted(int,int))); + QObject::disconnect(d->m_visualItemModel, SIGNAL(itemsRemoved(int,int)), + this, SIGNAL(itemsRemoved(int,int))); + QObject::disconnect(d->m_visualItemModel, SIGNAL(itemsMoved(int,int,int)), + this, SIGNAL(itemsMoved(int,int,int))); + QObject::disconnect(d->m_visualItemModel, SIGNAL(createdPackage(int,QDeclarativePackage*)), + this, SLOT(_q_createdPackage(int,QDeclarativePackage*))); + QObject::disconnect(d->m_visualItemModel, SIGNAL(destroyingPackage(QDeclarativePackage*)), + this, SLOT(_q_destroyingPackage(QDeclarativePackage*))); + d->m_visualItemModel = 0; + } + + d->m_roles.clear(); + d->m_roleNames.clear(); + if (d->m_delegateDataType) + d->m_delegateDataType->release(); + d->m_metaDataCreated = 0; + d->m_metaDataCacheable = false; + d->m_delegateDataType = new VDMDelegateDataType(&QDeclarativeVisualDataModelData::staticMetaObject, d->m_context?d->m_context->engine():qmlEngine(this)); + + QObject *object = qvariant_cast(model); + if (object && (d->m_listModelInterface = qobject_cast(object))) { + QObject::connect(d->m_listModelInterface, SIGNAL(itemsChanged(int,int,QList)), + this, SLOT(_q_itemsChanged(int,int,QList))); + QObject::connect(d->m_listModelInterface, SIGNAL(itemsInserted(int,int)), + this, SLOT(_q_itemsInserted(int,int))); + QObject::connect(d->m_listModelInterface, SIGNAL(itemsRemoved(int,int)), + this, SLOT(_q_itemsRemoved(int,int))); + QObject::connect(d->m_listModelInterface, SIGNAL(itemsMoved(int,int,int)), + this, SLOT(_q_itemsMoved(int,int,int))); + d->m_metaDataCacheable = true; + if (d->m_delegate && d->m_listModelInterface->count()) + emit itemsInserted(0, d->m_listModelInterface->count()); + return; + } else if (object && (d->m_abstractItemModel = qobject_cast(object))) { + QObject::connect(d->m_abstractItemModel, SIGNAL(rowsInserted(QModelIndex,int,int)), + this, SLOT(_q_rowsInserted(QModelIndex,int,int))); + QObject::connect(d->m_abstractItemModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), + this, SLOT(_q_rowsRemoved(QModelIndex,int,int))); + QObject::connect(d->m_abstractItemModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), + this, SLOT(_q_dataChanged(QModelIndex,QModelIndex))); + QObject::connect(d->m_abstractItemModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), + this, SLOT(_q_rowsMoved(QModelIndex,int,int,QModelIndex,int))); + QObject::connect(d->m_abstractItemModel, SIGNAL(modelReset()), this, SLOT(_q_modelReset())); + QObject::connect(d->m_abstractItemModel, SIGNAL(layoutChanged()), this, SLOT(_q_layoutChanged())); + d->m_metaDataCacheable = true; + if (d->m_abstractItemModel->canFetchMore(d->m_root)) + d->m_abstractItemModel->fetchMore(d->m_root); + return; + } + if ((d->m_visualItemModel = qvariant_cast(model))) { + QObject::connect(d->m_visualItemModel, SIGNAL(itemsInserted(int,int)), + this, SIGNAL(itemsInserted(int,int))); + QObject::connect(d->m_visualItemModel, SIGNAL(itemsRemoved(int,int)), + this, SIGNAL(itemsRemoved(int,int))); + QObject::connect(d->m_visualItemModel, SIGNAL(itemsMoved(int,int,int)), + this, SIGNAL(itemsMoved(int,int,int))); + QObject::connect(d->m_visualItemModel, SIGNAL(createdPackage(int,QDeclarativePackage*)), + this, SLOT(_q_createdPackage(int,QDeclarativePackage*))); + QObject::connect(d->m_visualItemModel, SIGNAL(destroyingPackage(QDeclarativePackage*)), + this, SLOT(_q_destroyingPackage(QDeclarativePackage*))); + return; + } + d->m_listAccessor = new QDeclarativeListAccessor; + d->m_listAccessor->setList(model, d->m_context?d->m_context->engine():qmlEngine(this)); + if (d->m_listAccessor->type() != QDeclarativeListAccessor::ListProperty) + d->m_metaDataCacheable = true; + if (d->m_delegate && d->modelCount()) { + emit itemsInserted(0, d->modelCount()); + emit countChanged(); + } +} + +/*! + \qmlproperty Component VisualDataModel::delegate + + The delegate provides a template defining each item instantiated by a view. + The index is exposed as an accessible \c index property. Properties of the + model are also available depending upon the type of \l {qmlmodels}{Data Model}. +*/ +QDeclarativeComponent *QDeclarativeVisualDataModel::delegate() const +{ + Q_D(const QDeclarativeVisualDataModel); + if (d->m_visualItemModel) + return d->m_visualItemModel->delegate(); + return d->m_delegate; +} + +void QDeclarativeVisualDataModel::setDelegate(QDeclarativeComponent *delegate) +{ + Q_D(QDeclarativeVisualDataModel); + bool wasValid = d->m_delegate != 0; + d->m_delegate = delegate; + d->m_delegateValidated = false; + if (!wasValid && d->modelCount() && d->m_delegate) { + emit itemsInserted(0, d->modelCount()); + emit countChanged(); + } + if (wasValid && !d->m_delegate && d->modelCount()) { + emit itemsRemoved(0, d->modelCount()); + emit countChanged(); + } +} + +/*! + \qmlproperty QModelIndex VisualDataModel::rootIndex + + QAbstractItemModel provides a hierarchical tree of data, whereas + QML only operates on list data. \c rootIndex allows the children of + any node in a QAbstractItemModel to be provided by this model. + + This property only affects models of type QAbstractItemModel that + are hierarchical (e.g, a tree model). + + For example, here is a simple interactive file system browser. + When a directory name is clicked, the view's \c rootIndex is set to the + QModelIndex node of the clicked directory, thus updating the view to show + the new directory's contents. + + \c main.cpp: + \snippet doc/src/snippets/declarative/visualdatamodel_rootindex/main.cpp 0 + + \c view.qml: + \snippet doc/src/snippets/declarative/visualdatamodel_rootindex/view.qml 0 + + If the \l model is a QAbstractItemModel subclass, the delegate can also + reference a \c hasModelChildren property (optionally qualified by a + \e model. prefix) that indicates whether the delegate's model item has + any child nodes. + + + \sa modelIndex(), parentModelIndex() +*/ +QVariant QDeclarativeVisualDataModel::rootIndex() const +{ + Q_D(const QDeclarativeVisualDataModel); + return QVariant::fromValue(d->m_root); +} + +void QDeclarativeVisualDataModel::setRootIndex(const QVariant &root) +{ + Q_D(QDeclarativeVisualDataModel); + QModelIndex modelIndex = qvariant_cast(root); + if (d->m_root != modelIndex) { + int oldCount = d->modelCount(); + d->m_root = modelIndex; + if (d->m_abstractItemModel && d->m_abstractItemModel->canFetchMore(modelIndex)) + d->m_abstractItemModel->fetchMore(modelIndex); + int newCount = d->modelCount(); + if (d->m_delegate && oldCount) + emit itemsRemoved(0, oldCount); + if (d->m_delegate && newCount) + emit itemsInserted(0, newCount); + if (newCount != oldCount) + emit countChanged(); + emit rootIndexChanged(); + } +} + + +/*! + \qmlmethod QModelIndex VisualDataModel::modelIndex(int index) + + QAbstractItemModel provides a hierarchical tree of data, whereas + QML only operates on list data. This function assists in using + tree models in QML. + + Returns a QModelIndex for the specified index. + This value can be assigned to rootIndex. + + \sa rootIndex +*/ +QVariant QDeclarativeVisualDataModel::modelIndex(int idx) const +{ + Q_D(const QDeclarativeVisualDataModel); + if (d->m_abstractItemModel) + return QVariant::fromValue(d->m_abstractItemModel->index(idx, 0, d->m_root)); + return QVariant::fromValue(QModelIndex()); +} + +/*! + \qmlmethod QModelIndex VisualDataModel::parentModelIndex() + + QAbstractItemModel provides a hierarchical tree of data, whereas + QML only operates on list data. This function assists in using + tree models in QML. + + Returns a QModelIndex for the parent of the current rootIndex. + This value can be assigned to rootIndex. + + \sa rootIndex +*/ +QVariant QDeclarativeVisualDataModel::parentModelIndex() const +{ + Q_D(const QDeclarativeVisualDataModel); + if (d->m_abstractItemModel) + return QVariant::fromValue(d->m_abstractItemModel->parent(d->m_root)); + return QVariant::fromValue(QModelIndex()); +} + +QString QDeclarativeVisualDataModel::part() const +{ + Q_D(const QDeclarativeVisualDataModel); + return d->m_part; +} + +void QDeclarativeVisualDataModel::setPart(const QString &part) +{ + Q_D(QDeclarativeVisualDataModel); + d->m_part = part; +} + +int QDeclarativeVisualDataModel::count() const +{ + Q_D(const QDeclarativeVisualDataModel); + if (d->m_visualItemModel) + return d->m_visualItemModel->count(); + if (!d->m_delegate) + return 0; + return d->modelCount(); +} + +QDeclarativeItem *QDeclarativeVisualDataModel::item(int index, bool complete) +{ + Q_D(QDeclarativeVisualDataModel); + if (d->m_visualItemModel) + return d->m_visualItemModel->item(index, d->m_part.toUtf8(), complete); + return item(index, QByteArray(), complete); +} + +/* + Returns ReleaseStatus flags. +*/ +QDeclarativeVisualDataModel::ReleaseFlags QDeclarativeVisualDataModel::release(QDeclarativeItem *item) +{ + Q_D(QDeclarativeVisualDataModel); + if (d->m_visualItemModel) + return d->m_visualItemModel->release(item); + + ReleaseFlags stat = 0; + QObject *obj = item; + bool inPackage = false; + + QHash::iterator it = d->m_packaged.find(item); + if (it != d->m_packaged.end()) { + QDeclarativePackage *package = *it; + d->m_packaged.erase(it); + if (d->m_packaged.contains(item)) + stat |= Referenced; + inPackage = true; + obj = package; // fall through and delete + } + + if (d->m_cache.releaseItem(obj)) { + // Remove any bindings to avoid warnings due to parent change. + QObjectPrivate *p = QObjectPrivate::get(obj); + Q_ASSERT(p->declarativeData); + QDeclarativeData *d = static_cast(p->declarativeData); + if (d->ownContext && d->context) + d->context->clearContext(); + + if (inPackage) { + emit destroyingPackage(qobject_cast(obj)); + } else { + if (item->scene()) + item->scene()->removeItem(item); + } + stat |= Destroyed; + obj->deleteLater(); + } else if (!inPackage) { + stat |= Referenced; + } + + return stat; +} + +/*! + \qmlproperty object VisualDataModel::parts + + The \a parts property selects a VisualDataModel which creates + delegates from the part named. This is used in conjunction with + the \l Package element. + + For example, the code below selects a model which creates + delegates named \e list from a \l Package: + + \code + VisualDataModel { + id: visualModel + delegate: Package { + Item { Package.name: "list" } + } + model: myModel + } + + ListView { + width: 200; height:200 + model: visualModel.parts.list + } + \endcode + + \sa Package +*/ +QObject *QDeclarativeVisualDataModel::parts() +{ + Q_D(QDeclarativeVisualDataModel); + if (!d->m_parts) + d->m_parts = new QDeclarativeVisualDataModelParts(this); + return d->m_parts; +} + +QDeclarativeItem *QDeclarativeVisualDataModel::item(int index, const QByteArray &viewId, bool complete) +{ + Q_D(QDeclarativeVisualDataModel); + if (d->m_visualItemModel) + return d->m_visualItemModel->item(index, viewId, complete); + + if (d->modelCount() <= 0 || !d->m_delegate) + return 0; + QObject *nobj = d->m_cache.getItem(index); + bool needComplete = false; + if (!nobj) { + QDeclarativeContext *ccontext = d->m_context; + if (!ccontext) ccontext = qmlContext(this); + QDeclarativeContext *ctxt = new QDeclarativeContext(ccontext); + QDeclarativeVisualDataModelData *data = new QDeclarativeVisualDataModelData(index, this); + if ((!d->m_listModelInterface || !d->m_abstractItemModel) && d->m_listAccessor + && d->m_listAccessor->type() == QDeclarativeListAccessor::ListProperty) { + ctxt->setContextObject(d->m_listAccessor->at(index).value()); + ctxt = new QDeclarativeContext(ctxt, ctxt); + } + ctxt->setContextProperty(QLatin1String("model"), data); + ctxt->setContextObject(data); + d->m_completePending = false; + nobj = d->m_delegate->beginCreate(ctxt); + if (complete) { + d->m_delegate->completeCreate(); + } else { + d->m_completePending = true; + needComplete = true; + } + if (nobj) { + QDeclarative_setParent_noEvent(ctxt, nobj); + QDeclarative_setParent_noEvent(data, nobj); + d->m_cache.insertItem(index, nobj); + if (QDeclarativePackage *package = qobject_cast(nobj)) + emit createdPackage(index, package); + } else { + delete data; + delete ctxt; + qmlInfo(this, d->m_delegate->errors()) << "Error creating delegate"; + } + } + QDeclarativeItem *item = qobject_cast(nobj); + if (!item) { + QDeclarativePackage *package = qobject_cast(nobj); + if (package) { + QObject *o = package->part(QString::fromUtf8(viewId)); + item = qobject_cast(o); + if (item) + d->m_packaged.insertMulti(item, package); + } + } + if (!item) { + if (needComplete) + d->m_delegate->completeCreate(); + d->m_cache.releaseItem(nobj); + if (!d->m_delegateValidated) { + qmlInfo(d->m_delegate) << QDeclarativeVisualDataModel::tr("Delegate component must be Item type."); + d->m_delegateValidated = true; + } + } + if (d->modelCount()-1 == index && d->m_abstractItemModel && d->m_abstractItemModel->canFetchMore(d->m_root)) + d->m_abstractItemModel->fetchMore(d->m_root); + + return item; +} + +bool QDeclarativeVisualDataModel::completePending() const +{ + Q_D(const QDeclarativeVisualDataModel); + if (d->m_visualItemModel) + return d->m_visualItemModel->completePending(); + return d->m_completePending; +} + +void QDeclarativeVisualDataModel::completeItem() +{ + Q_D(QDeclarativeVisualDataModel); + if (d->m_visualItemModel) { + d->m_visualItemModel->completeItem(); + return; + } + + d->m_delegate->completeCreate(); + d->m_completePending = false; +} + +QString QDeclarativeVisualDataModel::stringValue(int index, const QString &name) +{ + Q_D(QDeclarativeVisualDataModel); + if (d->m_visualItemModel) + return d->m_visualItemModel->stringValue(index, name); + + if ((!d->m_listModelInterface || !d->m_abstractItemModel) && d->m_listAccessor) { + if (QObject *object = d->m_listAccessor->at(index).value()) + return object->property(name.toUtf8()).toString(); + } + + if ((!d->m_listModelInterface && !d->m_abstractItemModel) || !d->m_delegate) + return QString(); + + QString val; + QObject *data = 0; + bool tempData = false; + + if (QObject *nobj = d->m_cache.item(index)) + data = d->data(nobj); + if (!data) { + data = new QDeclarativeVisualDataModelData(index, this); + tempData = true; + } + + QDeclarativeData *ddata = QDeclarativeData::get(data); + if (ddata && ddata->propertyCache) { + QDeclarativePropertyCache::Data *prop = ddata->propertyCache->property(name); + if (prop) { + if (prop->propType == QVariant::String) { + void *args[] = { &val, 0 }; + QMetaObject::metacall(data, QMetaObject::ReadProperty, prop->coreIndex, args); + } else if (prop->propType == qMetaTypeId()) { + QVariant v; + void *args[] = { &v, 0 }; + QMetaObject::metacall(data, QMetaObject::ReadProperty, prop->coreIndex, args); + val = v.toString(); + } + } else { + val = data->property(name.toUtf8()).toString(); + } + } else { + val = data->property(name.toUtf8()).toString(); + } + + if (tempData) + delete data; + + return val; +} + +int QDeclarativeVisualDataModel::indexOf(QDeclarativeItem *item, QObject *) const +{ + QVariant val = QDeclarativeEngine::contextForObject(item)->contextProperty(QLatin1String("index")); + return val.toInt(); + return -1; +} + +void QDeclarativeVisualDataModel::setWatchedRoles(QList roles) +{ + Q_D(QDeclarativeVisualDataModel); + d->watchedRoles = roles; + d->watchedRoleIds.clear(); +} + +void QDeclarativeVisualDataModel::_q_itemsChanged(int index, int count, + const QList &roles) +{ + Q_D(QDeclarativeVisualDataModel); + bool changed = false; + if (!d->watchedRoles.isEmpty() && d->watchedRoleIds.isEmpty()) { + foreach (QByteArray r, d->watchedRoles) { + if (d->m_roleNames.contains(r)) + d->watchedRoleIds << d->m_roleNames.value(r); + } + } + + for (QHash::ConstIterator iter = d->m_cache.begin(); + iter != d->m_cache.end(); ++iter) { + const int idx = iter.key(); + + if (idx >= index && idx < index+count) { + QDeclarativeVisualDataModelPrivate::ObjectRef objRef = *iter; + QDeclarativeVisualDataModelData *data = d->data(objRef.obj); + for (int roleIdx = 0; roleIdx < roles.count(); ++roleIdx) { + int role = roles.at(roleIdx); + if (!changed && !d->watchedRoleIds.isEmpty() && d->watchedRoleIds.contains(role)) + changed = true; + int propId = data->propForRole(role); + if (propId != -1) { + if (data->hasValue(propId)) { + if (d->m_listModelInterface) { + data->setValue(propId, d->m_listModelInterface->data(idx, role)); + } else if (d->m_abstractItemModel) { + QModelIndex index = d->m_abstractItemModel->index(idx, 0, d->m_root); + data->setValue(propId, d->m_abstractItemModel->data(index, role)); + } + } + } else { + QString roleName; + if (d->m_listModelInterface) + roleName = d->m_listModelInterface->toString(role); + else if (d->m_abstractItemModel) + roleName = QString::fromUtf8(d->m_abstractItemModel->roleNames().value(role)); + qmlInfo(this) << "Changing role not present in item: " << roleName; + } + } + if (d->m_roles.count() == 1) { + // Handle the modelData role we add if there is just one role. + int propId = data->modelDataPropertyId(); + if (data->hasValue(propId)) { + int role = d->m_roles.at(0); + if (d->m_listModelInterface) { + data->setValue(propId, d->m_listModelInterface->data(idx, role)); + } else if (d->m_abstractItemModel) { + QModelIndex index = d->m_abstractItemModel->index(idx, 0, d->m_root); + data->setValue(propId, d->m_abstractItemModel->data(index, role)); + } + } + } + } + } + if (changed) + emit itemsChanged(index, count); +} + +void QDeclarativeVisualDataModel::_q_itemsInserted(int index, int count) +{ + Q_D(QDeclarativeVisualDataModel); + if (!count) + return; + // XXX - highly inefficient + QHash items; + for (QHash::Iterator iter = d->m_cache.begin(); + iter != d->m_cache.end(); ) { + + if (iter.key() >= index) { + QDeclarativeVisualDataModelPrivate::ObjectRef objRef = *iter; + int index = iter.key() + count; + iter = d->m_cache.erase(iter); + + items.insert(index, objRef); + + QDeclarativeVisualDataModelData *data = d->data(objRef.obj); + data->setIndex(index); + } else { + ++iter; + } + } + d->m_cache.unite(items); + + emit itemsInserted(index, count); + emit countChanged(); +} + +void QDeclarativeVisualDataModel::_q_itemsRemoved(int index, int count) +{ + Q_D(QDeclarativeVisualDataModel); + if (!count) + return; + // XXX - highly inefficient + QHash items; + for (QHash::Iterator iter = d->m_cache.begin(); + iter != d->m_cache.end(); ) { + if (iter.key() >= index && iter.key() < index + count) { + QDeclarativeVisualDataModelPrivate::ObjectRef objRef = *iter; + iter = d->m_cache.erase(iter); + items.insertMulti(-1, objRef); //XXX perhaps better to maintain separately + QDeclarativeVisualDataModelData *data = d->data(objRef.obj); + data->setIndex(-1); + } else if (iter.key() >= index + count) { + QDeclarativeVisualDataModelPrivate::ObjectRef objRef = *iter; + int index = iter.key() - count; + iter = d->m_cache.erase(iter); + items.insert(index, objRef); + QDeclarativeVisualDataModelData *data = d->data(objRef.obj); + data->setIndex(index); + } else { + ++iter; + } + } + + d->m_cache.unite(items); + emit itemsRemoved(index, count); + emit countChanged(); +} + +void QDeclarativeVisualDataModel::_q_itemsMoved(int from, int to, int count) +{ + Q_D(QDeclarativeVisualDataModel); + // XXX - highly inefficient + QHash items; + for (QHash::Iterator iter = d->m_cache.begin(); + iter != d->m_cache.end(); ) { + + if (iter.key() >= from && iter.key() < from + count) { + QDeclarativeVisualDataModelPrivate::ObjectRef objRef = *iter; + int index = iter.key() - from + to; + iter = d->m_cache.erase(iter); + + items.insert(index, objRef); + + QDeclarativeVisualDataModelData *data = d->data(objRef.obj); + data->setIndex(index); + } else { + ++iter; + } + } + for (QHash::Iterator iter = d->m_cache.begin(); + iter != d->m_cache.end(); ) { + + int diff = from > to ? count : -count; + if (iter.key() >= qMin(from,to) && iter.key() < qMax(from+count,to+count)) { + QDeclarativeVisualDataModelPrivate::ObjectRef objRef = *iter; + int index = iter.key() + diff; + iter = d->m_cache.erase(iter); + + items.insert(index, objRef); + + QDeclarativeVisualDataModelData *data = d->data(objRef.obj); + data->setIndex(index); + } else { + ++iter; + } + } + d->m_cache.unite(items); + + emit itemsMoved(from, to, count); +} + +void QDeclarativeVisualDataModel::_q_rowsInserted(const QModelIndex &parent, int begin, int end) +{ + Q_D(QDeclarativeVisualDataModel); + if (parent == d->m_root) + _q_itemsInserted(begin, end - begin + 1); +} + +void QDeclarativeVisualDataModel::_q_rowsRemoved(const QModelIndex &parent, int begin, int end) +{ + Q_D(QDeclarativeVisualDataModel); + if (parent == d->m_root) + _q_itemsRemoved(begin, end - begin + 1); +} + +void QDeclarativeVisualDataModel::_q_rowsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow) +{ + Q_D(QDeclarativeVisualDataModel); + const int count = sourceEnd - sourceStart + 1; + if (destinationParent == d->m_root && sourceParent == d->m_root) { + _q_itemsMoved(sourceStart, sourceStart > destinationRow ? destinationRow : destinationRow-count, count); + } else if (sourceParent == d->m_root) { + _q_itemsRemoved(sourceStart, count); + } else if (destinationParent == d->m_root) { + _q_itemsInserted(destinationRow, count); + } +} + +void QDeclarativeVisualDataModel::_q_dataChanged(const QModelIndex &begin, const QModelIndex &end) +{ + Q_D(QDeclarativeVisualDataModel); + if (begin.parent() == d->m_root) + _q_itemsChanged(begin.row(), end.row() - begin.row() + 1, d->m_roles); +} + +void QDeclarativeVisualDataModel::_q_layoutChanged() +{ + Q_D(QDeclarativeVisualDataModel); + _q_itemsChanged(0, count(), d->m_roles); +} + +void QDeclarativeVisualDataModel::_q_modelReset() +{ + Q_D(QDeclarativeVisualDataModel); + d->m_root = QModelIndex(); + emit modelReset(); + emit rootIndexChanged(); + if (d->m_abstractItemModel && d->m_abstractItemModel->canFetchMore(d->m_root)) + d->m_abstractItemModel->fetchMore(d->m_root); +} + +void QDeclarativeVisualDataModel::_q_createdPackage(int index, QDeclarativePackage *package) +{ + Q_D(QDeclarativeVisualDataModel); + emit createdItem(index, qobject_cast(package->part(d->m_part))); +} + +void QDeclarativeVisualDataModel::_q_destroyingPackage(QDeclarativePackage *package) +{ + Q_D(QDeclarativeVisualDataModel); + emit destroyingItem(qobject_cast(package->part(d->m_part))); +} + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QListModelInterface) + +#include diff --git a/src/declarative/graphicsitems/qdeclarativevisualitemmodel_p.h b/src/declarative/graphicsitems/qdeclarativevisualitemmodel_p.h new file mode 100644 index 00000000..cce221e2 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativevisualitemmodel_p.h @@ -0,0 +1,257 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEVISUALDATAMODEL_H +#define QDECLARATIVEVISUALDATAMODEL_H + +#include + +#include +#include + +QT_BEGIN_HEADER + +Q_DECLARE_METATYPE(QModelIndex) + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeItem; +class QDeclarativeComponent; +class QDeclarativePackage; +class QDeclarativeVisualDataModelPrivate; + +class Q_AUTOTEST_EXPORT QDeclarativeVisualModel : public QObject +{ + Q_OBJECT + + Q_PROPERTY(int count READ count NOTIFY countChanged) + +public: + virtual ~QDeclarativeVisualModel() {} + + enum ReleaseFlag { Referenced = 0x01, Destroyed = 0x02 }; + Q_DECLARE_FLAGS(ReleaseFlags, ReleaseFlag) + + virtual int count() const = 0; + virtual bool isValid() const = 0; + virtual QDeclarativeItem *item(int index, bool complete=true) = 0; + virtual ReleaseFlags release(QDeclarativeItem *item) = 0; + virtual bool completePending() const = 0; + virtual void completeItem() = 0; + virtual QString stringValue(int, const QString &) = 0; + virtual void setWatchedRoles(QList roles) = 0; + + virtual int indexOf(QDeclarativeItem *item, QObject *objectContext) const = 0; + +Q_SIGNALS: + void countChanged(); + void itemsInserted(int index, int count); + void itemsRemoved(int index, int count); + void itemsMoved(int from, int to, int count); + void itemsChanged(int index, int count); + void modelReset(); + void createdItem(int index, QDeclarativeItem *item); + void destroyingItem(QDeclarativeItem *item); + +protected: + QDeclarativeVisualModel(QObjectPrivate &dd, QObject *parent = 0) + : QObject(dd, parent) {} + +private: + Q_DISABLE_COPY(QDeclarativeVisualModel) +}; + +class QDeclarativeVisualItemModelAttached; +class QDeclarativeVisualItemModelPrivate; +class Q_AUTOTEST_EXPORT QDeclarativeVisualItemModel : public QDeclarativeVisualModel +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeVisualItemModel) + + Q_PROPERTY(QDeclarativeListProperty children READ children NOTIFY childrenChanged DESIGNABLE false) + Q_CLASSINFO("DefaultProperty", "children") + +public: + QDeclarativeVisualItemModel(QObject *parent=0); + virtual ~QDeclarativeVisualItemModel() {} + + virtual int count() const; + virtual bool isValid() const; + virtual QDeclarativeItem *item(int index, bool complete=true); + virtual ReleaseFlags release(QDeclarativeItem *item); + virtual bool completePending() const; + virtual void completeItem(); + virtual QString stringValue(int index, const QString &role); + virtual void setWatchedRoles(QList) {} + + virtual int indexOf(QDeclarativeItem *item, QObject *objectContext) const; + + QDeclarativeListProperty children(); + + static QDeclarativeVisualItemModelAttached *qmlAttachedProperties(QObject *obj); + +Q_SIGNALS: + void childrenChanged(); + +private: + Q_DISABLE_COPY(QDeclarativeVisualItemModel) +}; + + +class Q_AUTOTEST_EXPORT QDeclarativeVisualDataModel : public QDeclarativeVisualModel +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeVisualDataModel) + + Q_PROPERTY(QVariant model READ model WRITE setModel) + Q_PROPERTY(QDeclarativeComponent *delegate READ delegate WRITE setDelegate) + Q_PROPERTY(QString part READ part WRITE setPart) + Q_PROPERTY(QObject *parts READ parts CONSTANT) + Q_PROPERTY(QVariant rootIndex READ rootIndex WRITE setRootIndex NOTIFY rootIndexChanged) + Q_CLASSINFO("DefaultProperty", "delegate") +public: + QDeclarativeVisualDataModel(); + QDeclarativeVisualDataModel(QDeclarativeContext *, QObject *parent=0); + virtual ~QDeclarativeVisualDataModel(); + + QVariant model() const; + void setModel(const QVariant &); + + QDeclarativeComponent *delegate() const; + void setDelegate(QDeclarativeComponent *); + + QVariant rootIndex() const; + void setRootIndex(const QVariant &root); + + Q_INVOKABLE QVariant modelIndex(int idx) const; + Q_INVOKABLE QVariant parentModelIndex() const; + + QString part() const; + void setPart(const QString &); + + int count() const; + bool isValid() const { return delegate() != 0; } + QDeclarativeItem *item(int index, bool complete=true); + QDeclarativeItem *item(int index, const QByteArray &, bool complete=true); + ReleaseFlags release(QDeclarativeItem *item); + bool completePending() const; + void completeItem(); + virtual QString stringValue(int index, const QString &role); + virtual void setWatchedRoles(QList roles); + + int indexOf(QDeclarativeItem *item, QObject *objectContext) const; + + QObject *parts(); + +Q_SIGNALS: + void createdPackage(int index, QDeclarativePackage *package); + void destroyingPackage(QDeclarativePackage *package); + void rootIndexChanged(); + +private Q_SLOTS: + void _q_itemsChanged(int, int, const QList &); + void _q_itemsInserted(int index, int count); + void _q_itemsRemoved(int index, int count); + void _q_itemsMoved(int from, int to, int count); + void _q_rowsInserted(const QModelIndex &,int,int); + void _q_rowsRemoved(const QModelIndex &,int,int); + void _q_rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int); + void _q_dataChanged(const QModelIndex&,const QModelIndex&); + void _q_layoutChanged(); + void _q_modelReset(); + void _q_createdPackage(int index, QDeclarativePackage *package); + void _q_destroyingPackage(QDeclarativePackage *package); + +private: + Q_DISABLE_COPY(QDeclarativeVisualDataModel) +}; + +class QDeclarativeVisualItemModelAttached : public QObject +{ + Q_OBJECT + +public: + QDeclarativeVisualItemModelAttached(QObject *parent) + : QObject(parent), m_index(0) {} + ~QDeclarativeVisualItemModelAttached() { + attachedProperties.remove(parent()); + } + + Q_PROPERTY(int index READ index NOTIFY indexChanged) + int index() const { return m_index; } + void setIndex(int idx) { + if (m_index != idx) { + m_index = idx; + emit indexChanged(); + } + } + + static QDeclarativeVisualItemModelAttached *properties(QObject *obj) { + QDeclarativeVisualItemModelAttached *rv = attachedProperties.value(obj); + if (!rv) { + rv = new QDeclarativeVisualItemModelAttached(obj); + attachedProperties.insert(obj, rv); + } + return rv; + } + +Q_SIGNALS: + void indexChanged(); + +public: + int m_index; + + static QHash attachedProperties; +}; + + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeVisualModel) +QML_DECLARE_TYPE(QDeclarativeVisualItemModel) +QML_DECLARE_TYPEINFO(QDeclarativeVisualItemModel, QML_HAS_ATTACHED_PROPERTIES) +QML_DECLARE_TYPE(QDeclarativeVisualDataModel) + +QT_END_HEADER + +#endif // QDECLARATIVEVISUALDATAMODEL_H diff --git a/src/declarative/qml/parser/parser.pri b/src/declarative/qml/parser/parser.pri new file mode 100644 index 00000000..fd4361c2 --- /dev/null +++ b/src/declarative/qml/parser/parser.pri @@ -0,0 +1,21 @@ +INCLUDEPATH += $$PWD + +HEADERS += \ + $$PWD/qdeclarativejsast_p.h \ + $$PWD/qdeclarativejsastfwd_p.h \ + $$PWD/qdeclarativejsastvisitor_p.h \ + $$PWD/qdeclarativejsengine_p.h \ + $$PWD/qdeclarativejsgrammar_p.h \ + $$PWD/qdeclarativejslexer_p.h \ + $$PWD/qdeclarativejsmemorypool_p.h \ + $$PWD/qdeclarativejsnodepool_p.h \ + $$PWD/qdeclarativejsparser_p.h \ + $$PWD/qdeclarativejsglobal_p.h + +SOURCES += \ + $$PWD/qdeclarativejsast.cpp \ + $$PWD/qdeclarativejsastvisitor.cpp \ + $$PWD/qdeclarativejsengine_p.cpp \ + $$PWD/qdeclarativejsgrammar.cpp \ + $$PWD/qdeclarativejslexer.cpp \ + $$PWD/qdeclarativejsparser.cpp diff --git a/src/declarative/qml/parser/qdeclarativejs.g b/src/declarative/qml/parser/qdeclarativejs.g new file mode 100644 index 00000000..3fb9412c --- /dev/null +++ b/src/declarative/qml/parser/qdeclarativejs.g @@ -0,0 +1,3149 @@ +---------------------------------------------------------------------------- +-- +-- Copyright (C) 2012 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-ONLY$ +-- GNU Lesser General Public License Usage +-- This file may be used under the terms of the GNU Lesser +-- General Public License version 2.1 as published by the Free Software +-- Foundation and appearing in the file LICENSE.LGPL included in the +-- packaging of this file. Please review the following information to +-- ensure the GNU Lesser General Public License version 2.1 requirements +-- will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +-- +-- If you have questions regarding the use of this file, please contact +-- Nokia at qt-info@nokia.com. +-- $QT_END_LICENSE$ +-- +---------------------------------------------------------------------------- + +%parser QDeclarativeJSGrammar +%decl qdeclarativejsparser_p.h +%impl qdeclarativejsparser.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" +%token T_ON "on" + +--- feed tokens +%token T_FEED_UI_PROGRAM +%token T_FEED_UI_OBJECT_MEMBER +%token T_FEED_JS_STATEMENT +%token T_FEED_JS_EXPRESSION +%token T_FEED_JS_SOURCE_ELEMENT +%token T_FEED_JS_PROGRAM + +%nonassoc SHIFT_THERE +%nonassoc T_IDENTIFIER T_COLON T_SIGNAL T_PROPERTY T_READONLY +%nonassoc REDUCE_HERE + +%start TopLevel + +/./**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include + +#include "private/qdeclarativejsengine_p.h" +#include "private/qdeclarativejslexer_p.h" +#include "private/qdeclarativejsast_p.h" +#include "private/qdeclarativejsnodepool_p.h" + +./ + +/:/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +// +// 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 QDECLARATIVEJSPARSER_P_H +#define QDECLARATIVEJSPARSER_P_H + +#include "private/qdeclarativejsglobal_p.h" +#include "private/qdeclarativejsgrammar_p.h" +#include "private/qdeclarativejsast_p.h" +#include "private/qdeclarativejsengine_p.h" + +#include +#include + +QT_QML_BEGIN_NAMESPACE + +namespace QDeclarativeJS { + +class Engine; +class NameId; + +class QML_PARSER_EXPORT 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 parseSourceElement() { return parse(T_FEED_JS_SOURCE_ELEMENT); } + bool parseUiObjectMember() { return parse(T_FEED_UI_OBJECT_MEMBER); } + bool parseProgram() { return parse(T_FEED_JS_PROGRAM); } + + AST::UiProgram *ast() const + { return AST::cast(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(); + } + + AST::Node *rootNode() const + { return program; } + + QList 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 diagnostic_messages; +}; + +} // end of namespace QDeclarativeJS + + +:/ + + +/. + +#include "private/qdeclarativejsparser_p.h" +#include + +// +// This file is automatically generated from qmljs.g. +// Changes will be lost. +// + +using namespace QDeclarativeJS; + +QT_QML_BEGIN_NAMESPACE + +void Parser::reallocateStack() +{ + if (! stack_size) + stack_size = 128; + else + stack_size <<= 1; + + sym_stack = reinterpret_cast (qRealloc(sym_stack, stack_size * sizeof(Value))); + state_stack = reinterpret_cast (qRealloc(state_stack, stack_size * sizeof(int))); + location_stack = reinterpret_cast (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 nameIds; + QVarLengthArray locations; + + AST::ExpressionNode *it = expr; + while (AST::FieldMemberExpression *m = AST::cast(it)) { + nameIds.append(m->name); + locations.append(m->identifierToken); + it = m->base; + } + + if (AST::IdentifierExpression *idExpr = AST::cast(it)) { + AST::UiQualifiedId *q = makeAstNode(driver->nodePool(), idExpr->name); + q->identifierToken = idExpr->identifierToken; + + AST::UiQualifiedId *currentId = q; + for (int i = nameIds.size() - 1; i != -1; --i) { + currentId = makeAstNode(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_JS_SOURCE_ELEMENT SourceElement ; +/. +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; +./ + +TopLevel: T_FEED_JS_PROGRAM Program ; +/. +case $rule_number: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; +./ + +UiProgram: UiImportListOpt UiRootMember ; +/. +case $rule_number: { + sym(1).UiProgram = makeAstNode (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 (driver->nodePool(), sym(1).UiImport); +} break; +./ + +UiImportList: UiImportList UiImport ; +/. +case $rule_number: { + sym(1).Node = makeAstNode (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(sym(2).Expression)) { + node = makeAstNode(driver->nodePool(), importIdLiteral->value); + node->fileNameToken = loc(2); + } else if (AST::UiQualifiedId *qualifiedId = reparseAsQualifiedId(sym(2).Expression)) { + node = makeAstNode(driver->nodePool(), qualifiedId); + node->fileNameToken = loc(2); + } + + sym(1).Node = node; + + if (node) { + node->importToken = loc(1); + } else { + 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 (driver->nodePool(), sym(1).UiObjectMember); +} break; +./ + +UiObjectMemberList: UiObjectMember ; +/. +case $rule_number: { + sym(1).Node = makeAstNode (driver->nodePool(), sym(1).UiObjectMember); +} break; +./ + +UiObjectMemberList: UiObjectMemberList UiObjectMember ; +/. +case $rule_number: { + AST::UiObjectMemberList *node = makeAstNode (driver->nodePool(), + sym(1).UiObjectMemberList, sym(2).UiObjectMember); + sym(1).Node = node; +} break; +./ + +UiArrayMemberList: UiObjectDefinition ; +/. +case $rule_number: { + sym(1).Node = makeAstNode (driver->nodePool(), sym(1).UiObjectMember); +} break; +./ + +UiArrayMemberList: UiArrayMemberList T_COMMA UiObjectDefinition ; +/. +case $rule_number: { + AST::UiArrayMemberList *node = makeAstNode (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 (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 (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 (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 (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 (driver->nodePool(), + sym(1).UiQualifiedId, sym(3).UiQualifiedId, sym(4).UiObjectInitializer); + node->colonToken = loc(2); + sym(1).Node = node; +} break; +./ + +UiObjectMember: UiQualifiedId T_ON UiQualifiedId UiObjectInitializer ; +/. +case $rule_number: { + AST::UiObjectBinding *node = makeAstNode (driver->nodePool(), + sym(3).UiQualifiedId, sym(1).UiQualifiedId, sym(4).UiObjectInitializer); + node->colonToken = loc(2); + node->hasOnToken = true; + 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 (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 (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 (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 (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 (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 JsIdentifier T_AUTOMATIC_SEMICOLON ; +UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT JsIdentifier T_SEMICOLON ; +/. +case $rule_number: { + AST::UiPublicMember *node = makeAstNode (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 JsIdentifier T_AUTOMATIC_SEMICOLON ; +UiObjectMember: T_PROPERTY UiPropertyType JsIdentifier T_SEMICOLON ; +/. +case $rule_number: { + AST::UiPublicMember *node = makeAstNode (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 JsIdentifier T_AUTOMATIC_SEMICOLON ; +UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType JsIdentifier T_SEMICOLON ; +/. +case $rule_number: { + AST::UiPublicMember *node = makeAstNode (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 JsIdentifier T_COLON Expression T_AUTOMATIC_SEMICOLON ; +UiObjectMember: T_PROPERTY UiPropertyType JsIdentifier T_COLON Expression T_SEMICOLON ; +/. +case $rule_number: { + AST::UiPublicMember *node = makeAstNode (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 JsIdentifier T_COLON Expression T_AUTOMATIC_SEMICOLON ; +UiObjectMember: T_READONLY T_PROPERTY UiPropertyType JsIdentifier T_COLON Expression T_SEMICOLON ; +/. +case $rule_number: { + AST::UiPublicMember *node = makeAstNode (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 JsIdentifier T_COLON Expression T_AUTOMATIC_SEMICOLON ; +UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType JsIdentifier T_COLON Expression T_SEMICOLON ; +/. +case $rule_number: { + AST::UiPublicMember *node = makeAstNode (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: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT JsIdentifier T_COLON T_LBRACKET UiArrayMemberList T_RBRACKET ; +/. +case $rule_number: { + AST::UiPublicMember *node = makeAstNode (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); // insert a fake ';' before ':' + + AST::UiQualifiedId *propertyName = makeAstNode(driver->nodePool(), sym(6).sval); + propertyName->identifierToken = loc(6); + propertyName->next = 0; + + AST::UiArrayBinding *binding = makeAstNode (driver->nodePool(), + propertyName, sym(9).UiArrayMemberList->finish()); + binding->colonToken = loc(7); + binding->lbracketToken = loc(8); + binding->rbracketToken = loc(10); + + node->binding = binding; + + sym(1).Node = node; +} break; +./ + +UiObjectMember: T_PROPERTY UiPropertyType JsIdentifier T_COLON UiQualifiedId UiObjectInitializer ; +/. +case $rule_number: { + AST::UiPublicMember *node = makeAstNode (driver->nodePool(), sym(2).sval, sym(3).sval); + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(3); + node->semicolonToken = loc(4); // insert a fake ';' before ':' + + AST::UiQualifiedId *propertyName = makeAstNode(driver->nodePool(), sym(3).sval); + propertyName->identifierToken = loc(3); + propertyName->next = 0; + + AST::UiObjectBinding *binding = makeAstNode (driver->nodePool(), + propertyName, sym(5).UiQualifiedId, sym(6).UiObjectInitializer); + binding->colonToken = loc(4); + + node->binding = binding; + + sym(1).Node = node; +} break; +./ + +UiObjectMember: FunctionDeclaration ; +/. +case $rule_number: { + sym(1).Node = makeAstNode(driver->nodePool(), sym(1).Node); +} break; +./ + +UiObjectMember: VariableStatement ; +/. +case $rule_number: { + sym(1).Node = makeAstNode(driver->nodePool(), sym(1).Node); +} break; +./ + +JsIdentifier: T_IDENTIFIER; + +JsIdentifier: T_PROPERTY ; +/. +case $rule_number: { + QString s = QLatin1String(QDeclarativeJSGrammar::spell[T_PROPERTY]); + sym(1).sval = driver->intern(s.constData(), s.length()); + break; +} +./ + +JsIdentifier: T_SIGNAL ; +/. +case $rule_number: { + QString s = QLatin1String(QDeclarativeJSGrammar::spell[T_SIGNAL]); + sym(1).sval = driver->intern(s.constData(), s.length()); + break; +} +./ + +JsIdentifier: T_READONLY ; +/. +case $rule_number: { + QString s = QLatin1String(QDeclarativeJSGrammar::spell[T_READONLY]); + sym(1).sval = driver->intern(s.constData(), s.length()); + break; +} +./ + +JsIdentifier: T_ON ; +/. +case $rule_number: { + QString s = QLatin1String(QDeclarativeJSGrammar::spell[T_ON]); + sym(1).sval = driver->intern(s.constData(), s.length()); + break; +} +./ + +-------------------------------------------------------------------------------------------------------- +-- Expressions +-------------------------------------------------------------------------------------------------------- + +PrimaryExpression: T_THIS ; +/. +case $rule_number: { + AST::ThisExpression *node = makeAstNode (driver->nodePool()); + node->thisToken = loc(1); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: JsIdentifier ; +/. +case $rule_number: { + AST::IdentifierExpression *node = makeAstNode (driver->nodePool(), sym(1).sval); + node->identifierToken = loc(1); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_NULL ; +/. +case $rule_number: { + AST::NullExpression *node = makeAstNode (driver->nodePool()); + node->nullToken = loc(1); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_TRUE ; +/. +case $rule_number: { + AST::TrueLiteral *node = makeAstNode (driver->nodePool()); + node->trueToken = loc(1); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_FALSE ; +/. +case $rule_number: { + AST::FalseLiteral *node = makeAstNode (driver->nodePool()); + node->falseToken = loc(1); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_NUMERIC_LITERAL ; +/. +case $rule_number: { + AST::NumericLiteral *node = makeAstNode (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 (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 + } + + loc(1).length = lexer->tokenLength(); + + AST::RegExpLiteral *node = makeAstNode (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; + } + + loc(1).length = lexer->tokenLength(); + + AST::RegExpLiteral *node = makeAstNode (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 (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 (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 (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 (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 (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 (driver->nodePool()); +-- } break; +-- ./ + +PrimaryExpression: T_LBRACE PropertyNameAndValueListOpt T_RBRACE ; +/. +case $rule_number: { + AST::ObjectLiteral *node = 0; + if (sym(2).Node) + node = makeAstNode (driver->nodePool(), + sym(2).PropertyNameAndValueList->finish ()); + else + node = makeAstNode (driver->nodePool()); + node->lbraceToken = loc(1); + node->rbraceToken = loc(3); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_LBRACE PropertyNameAndValueList T_COMMA T_RBRACE ; +/. +case $rule_number: { + AST::ObjectLiteral *node = makeAstNode (driver->nodePool(), + sym(2).PropertyNameAndValueList->finish ()); + node->lbraceToken = loc(1); + node->rbraceToken = loc(4); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_LPAREN Expression T_RPAREN ; +/. +case $rule_number: { + AST::NestedExpression *node = makeAstNode(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(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 (driver->nodePool(), (AST::Elision *) 0, sym(1).Expression); +} break; +./ + +ElementList: Elision AssignmentExpression ; +/. +case $rule_number: { + sym(1).Node = makeAstNode (driver->nodePool(), sym(1).Elision->finish(), sym(2).Expression); +} break; +./ + +ElementList: ElementList T_COMMA AssignmentExpression ; +/. +case $rule_number: { + AST::ElementList *node = makeAstNode (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 (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 (driver->nodePool()); + node->commaToken = loc(1); + sym(1).Node = node; +} break; +./ + +Elision: Elision T_COMMA ; +/. +case $rule_number: { + AST::Elision *node = makeAstNode (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 (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 (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 SHIFT_THERE ; +/. +case $rule_number: { + AST::IdentifierPropertyName *node = makeAstNode (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 (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 (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 (driver->nodePool(), sym(1).dval); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; +./ + +PropertyName: ReservedIdentifier ; +/. +case $rule_number: { + AST::IdentifierPropertyName *node = makeAstNode (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 (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 (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 (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 (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 (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 (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 (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 (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 (driver->nodePool(), sym(1).Expression); +} break; +./ + +ArgumentList: ArgumentList T_COMMA AssignmentExpression ; +/. +case $rule_number: { + AST::ArgumentList *node = makeAstNode (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (driver->nodePool(), sym(1).Statement); +} break; +./ + +StatementList: StatementList Statement ; +/. +case $rule_number: { + sym(1).Node = makeAstNode (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 (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 (driver->nodePool(), sym(1).VariableDeclaration); +} break; +./ + +VariableDeclarationList: VariableDeclarationList T_COMMA VariableDeclaration ; +/. +case $rule_number: { + AST::VariableDeclarationList *node = makeAstNode (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 (driver->nodePool(), sym(1).VariableDeclaration); +} break; +./ + +VariableDeclarationListNotIn: VariableDeclarationListNotIn T_COMMA VariableDeclarationNotIn ; +/. +case $rule_number: { + sym(1).Node = makeAstNode (driver->nodePool(), sym(1).VariableDeclarationList, sym(3).VariableDeclaration); +} break; +./ + +VariableDeclaration: JsIdentifier InitialiserOpt ; +/. +case $rule_number: { + AST::VariableDeclaration *node = makeAstNode (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (driver->nodePool(), sym(1).CaseClause); +} break; +./ + +CaseClauses: CaseClauses CaseClause ; +/. +case $rule_number: { + sym(1).Node = makeAstNode (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (driver->nodePool(), sym(1).SourceElements->finish ()); +} break; +./ + +Program: SourceElements ; +/. +case $rule_number: { + sym(1).Node = makeAstNode (driver->nodePool(), sym(1).SourceElements->finish ()); +} break; +./ + +SourceElements: SourceElement ; +/. +case $rule_number: { + sym(1).Node = makeAstNode (driver->nodePool(), sym(1).SourceElement); +} break; +./ + +SourceElements: SourceElements SourceElement ; +/. +case $rule_number: { + sym(1).Node = makeAstNode (driver->nodePool(), sym(1).SourceElements, sym(2).SourceElement); +} break; +./ + +SourceElement: Statement ; +/. +case $rule_number: { + sym(1).Node = makeAstNode (driver->nodePool(), sym(1).Statement); +} break; +./ + +SourceElement: FunctionDeclaration ; +/. +case $rule_number: { + sym(1).Node = makeAstNode (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 (yytoken != -1 && 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 = qApp->translate("QDeclarativeParser", "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)) { + QString msg; + int token = token_buffer[0].token; + if (token < 0 || token >= TERMINAL_COUNT) + msg = qApp->translate("QDeclarativeParser", "Syntax error"); + else + msg = qApp->translate("QDeclarativeParser", "Unexpected token `%1'").arg(QLatin1String(spell[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 = qApp->translate("QDeclarativeParser", "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 || + tk == T_FEED_JS_SOURCE_ELEMENT) + continue; + + int a = t_action(errorState, tk); + if (a > 0 && t_action(a, yytoken)) { + const QString msg = qApp->translate("QDeclarativeParser", "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 = qApp->translate("QDeclarativeParser", "Syntax error"); + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); + } + + return false; +} + +QT_QML_END_NAMESPACE + + +./ +/: +QT_QML_END_NAMESPACE + + + +#endif // QDECLARATIVEJSPARSER_P_H +:/ diff --git a/src/declarative/qml/parser/qdeclarativejsast.cpp b/src/declarative/qml/parser/qdeclarativejsast.cpp new file mode 100644 index 00000000..ad79dddd --- /dev/null +++ b/src/declarative/qml/parser/qdeclarativejsast.cpp @@ -0,0 +1,956 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativejsast_p.h" + +#include "private/qdeclarativejsastvisitor_p.h" + +QT_QML_BEGIN_NAMESPACE + +namespace QDeclarativeJS { 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); + accept(binding, 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 QDeclarativeJS::AST + +QT_QML_END_NAMESPACE + + diff --git a/src/declarative/qml/parser/qdeclarativejsast_p.h b/src/declarative/qml/parser/qdeclarativejsast_p.h new file mode 100644 index 00000000..07ce3e09 --- /dev/null +++ b/src/declarative/qml/parser/qdeclarativejsast_p.h @@ -0,0 +1,2546 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEJSAST_P_H +#define QDECLARATIVEJSAST_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 "private/qdeclarativejsastvisitor_p.h" +#include "private/qdeclarativejsglobal_p.h" + +#include + +QT_QML_BEGIN_NAMESPACE + +#define QDECLARATIVEJS_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 QDeclarativeJS { +class NameId; +namespace AST { + +template +_T1 cast(_T2 *ast) +{ + if (ast && ast->kind == static_cast<_T1>(0)->K) + return static_cast<_T1>(ast); + + return 0; +} + +class QML_PARSER_EXPORT 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) {} + + // NOTE: node destructors are never called, + // instead we block free the memory + // (see the NodePool class) + 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 QML_PARSER_EXPORT ExpressionNode: public Node +{ +public: + ExpressionNode() {} + + virtual ExpressionNode *expressionCast(); + + virtual SourceLocation firstSourceLocation() const = 0; + virtual SourceLocation lastSourceLocation() const = 0; +}; + +class QML_PARSER_EXPORT Statement: public Node +{ +public: + Statement() {} + + virtual Statement *statementCast(); + + virtual SourceLocation firstSourceLocation() const = 0; + virtual SourceLocation lastSourceLocation() const = 0; +}; + +class QML_PARSER_EXPORT UiFormal: public Node +{ +public: + QDECLARATIVEJS_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 QML_PARSER_EXPORT UiFormalList: public Node +{ +public: + QDECLARATIVEJS_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 QML_PARSER_EXPORT UiSignature: public Node +{ +public: + QDECLARATIVEJS_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 QML_PARSER_EXPORT NestedExpression: public ExpressionNode +{ +public: + QDECLARATIVEJS_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 QML_PARSER_EXPORT ThisExpression: public ExpressionNode +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(ThisExpression) + + ThisExpression() { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return thisToken; } + + virtual SourceLocation lastSourceLocation() const + { return thisToken; } + +// attributes + SourceLocation thisToken; +}; + +class QML_PARSER_EXPORT IdentifierExpression: public ExpressionNode +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(IdentifierExpression) + + IdentifierExpression(NameId *n): + name (n) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return identifierToken; } + + virtual SourceLocation lastSourceLocation() const + { return identifierToken; } + +// attributes + NameId *name; + SourceLocation identifierToken; +}; + +class QML_PARSER_EXPORT NullExpression: public ExpressionNode +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(NullExpression) + + NullExpression() { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return nullToken; } + + virtual SourceLocation lastSourceLocation() const + { return nullToken; } + +// attributes + SourceLocation nullToken; +}; + +class QML_PARSER_EXPORT TrueLiteral: public ExpressionNode +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(TrueLiteral) + + TrueLiteral() { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return trueToken; } + + virtual SourceLocation lastSourceLocation() const + { return trueToken; } + +// attributes + SourceLocation trueToken; +}; + +class QML_PARSER_EXPORT FalseLiteral: public ExpressionNode +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(FalseLiteral) + + FalseLiteral() { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return falseToken; } + + virtual SourceLocation lastSourceLocation() const + { return falseToken; } + +// attributes + SourceLocation falseToken; +}; + +class QML_PARSER_EXPORT NumericLiteral: public ExpressionNode +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(NumericLiteral) + + NumericLiteral(double v): + value(v) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return literalToken; } + + virtual SourceLocation lastSourceLocation() const + { return literalToken; } + +// attributes: + double value; + SourceLocation literalToken; +}; + +class QML_PARSER_EXPORT StringLiteral: public ExpressionNode +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(StringLiteral) + + StringLiteral(NameId *v): + value (v) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return literalToken; } + + virtual SourceLocation lastSourceLocation() const + { return literalToken; } + +// attributes: + NameId *value; + SourceLocation literalToken; +}; + +class QML_PARSER_EXPORT RegExpLiteral: public ExpressionNode +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(RegExpLiteral) + + RegExpLiteral(NameId *p, int f): + pattern (p), flags (f) { kind = K; } + + 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 QML_PARSER_EXPORT ArrayLiteral: public ExpressionNode +{ +public: + QDECLARATIVEJS_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 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 QML_PARSER_EXPORT ObjectLiteral: public ExpressionNode +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(ObjectLiteral) + + ObjectLiteral(): + properties (0) { kind = K; } + + ObjectLiteral(PropertyNameAndValueList *plist): + properties (plist) { kind = K; } + + 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 QML_PARSER_EXPORT ElementList: public Node +{ +public: + QDECLARATIVEJS_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; + } + + 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 QML_PARSER_EXPORT Elision: public Node +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(Elision) + + Elision(): + next (this) { kind = K; } + + Elision(Elision *previous) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual void accept0(Visitor *visitor); + + inline Elision *finish () + { + Elision *front = next; + next = 0; + return front; + } + +// attributes + Elision *next; + SourceLocation commaToken; +}; + +class QML_PARSER_EXPORT PropertyNameAndValueList: public Node +{ +public: + QDECLARATIVEJS_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 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 QML_PARSER_EXPORT PropertyName: public Node +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(PropertyName) + + PropertyName() { kind = K; } + +// attributes + SourceLocation propertyNameToken; +}; + +class QML_PARSER_EXPORT IdentifierPropertyName: public PropertyName +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(IdentifierPropertyName) + + IdentifierPropertyName(NameId *n): + id (n) { kind = K; } + + virtual void accept0(Visitor *visitor); + +// attributes + NameId *id; +}; + +class QML_PARSER_EXPORT StringLiteralPropertyName: public PropertyName +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(StringLiteralPropertyName) + + StringLiteralPropertyName(NameId *n): + id (n) { kind = K; } + + virtual void accept0(Visitor *visitor); + +// attributes + NameId *id; +}; + +class QML_PARSER_EXPORT NumericLiteralPropertyName: public PropertyName +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(NumericLiteralPropertyName) + + NumericLiteralPropertyName(double n): + id (n) { kind = K; } + + virtual void accept0(Visitor *visitor); + +// attributes + double id; +}; + +class QML_PARSER_EXPORT ArrayMemberExpression: public ExpressionNode +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(ArrayMemberExpression) + + ArrayMemberExpression(ExpressionNode *b, ExpressionNode *e): + base (b), expression (e) + { kind = K; } + + 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 QML_PARSER_EXPORT FieldMemberExpression: public ExpressionNode +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(FieldMemberExpression) + + FieldMemberExpression(ExpressionNode *b, NameId *n): + base (b), name (n) + { kind = K; } + + 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 QML_PARSER_EXPORT NewMemberExpression: public ExpressionNode +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(NewMemberExpression) + + NewMemberExpression(ExpressionNode *b, ArgumentList *a): + base (b), arguments (a) + { kind = K; } + + 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 QML_PARSER_EXPORT NewExpression: public ExpressionNode +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(NewExpression) + + NewExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return newToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation newToken; +}; + +class QML_PARSER_EXPORT CallExpression: public ExpressionNode +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(CallExpression) + + CallExpression(ExpressionNode *b, ArgumentList *a): + base (b), arguments (a) + { kind = K; } + + 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 QML_PARSER_EXPORT ArgumentList: public Node +{ +public: + QDECLARATIVEJS_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 void accept0(Visitor *visitor); + + inline ArgumentList *finish () + { + ArgumentList *front = next; + next = 0; + return front; + } + +// attributes + ExpressionNode *expression; + ArgumentList *next; + SourceLocation commaToken; +}; + +class QML_PARSER_EXPORT PostIncrementExpression: public ExpressionNode +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(PostIncrementExpression) + + PostIncrementExpression(ExpressionNode *b): + base (b) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return base->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return incrementToken; } + +// attributes + ExpressionNode *base; + SourceLocation incrementToken; +}; + +class QML_PARSER_EXPORT PostDecrementExpression: public ExpressionNode +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(PostDecrementExpression) + + PostDecrementExpression(ExpressionNode *b): + base (b) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return base->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return decrementToken; } + +// attributes + ExpressionNode *base; + SourceLocation decrementToken; +}; + +class QML_PARSER_EXPORT DeleteExpression: public ExpressionNode +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(DeleteExpression) + + DeleteExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return deleteToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation deleteToken; +}; + +class QML_PARSER_EXPORT VoidExpression: public ExpressionNode +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(VoidExpression) + + VoidExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return voidToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation voidToken; +}; + +class QML_PARSER_EXPORT TypeOfExpression: public ExpressionNode +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(TypeOfExpression) + + TypeOfExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return typeofToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation typeofToken; +}; + +class QML_PARSER_EXPORT PreIncrementExpression: public ExpressionNode +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(PreIncrementExpression) + + PreIncrementExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return incrementToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation incrementToken; +}; + +class QML_PARSER_EXPORT PreDecrementExpression: public ExpressionNode +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(PreDecrementExpression) + + PreDecrementExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return decrementToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation decrementToken; +}; + +class QML_PARSER_EXPORT UnaryPlusExpression: public ExpressionNode +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(UnaryPlusExpression) + + UnaryPlusExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return plusToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation plusToken; +}; + +class QML_PARSER_EXPORT UnaryMinusExpression: public ExpressionNode +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(UnaryMinusExpression) + + UnaryMinusExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return minusToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation minusToken; +}; + +class QML_PARSER_EXPORT TildeExpression: public ExpressionNode +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(TildeExpression) + + TildeExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return tildeToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation tildeToken; +}; + +class QML_PARSER_EXPORT NotExpression: public ExpressionNode +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(NotExpression) + + NotExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return notToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation notToken; +}; + +class QML_PARSER_EXPORT BinaryExpression: public ExpressionNode +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(BinaryExpression) + + BinaryExpression(ExpressionNode *l, int o, ExpressionNode *r): + left (l), op (o), right (r) + { kind = K; } + + 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 QML_PARSER_EXPORT ConditionalExpression: public ExpressionNode +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(ConditionalExpression) + + ConditionalExpression(ExpressionNode *e, ExpressionNode *t, ExpressionNode *f): + expression (e), ok (t), ko (f) + { kind = K; } + + 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 QML_PARSER_EXPORT Expression: public ExpressionNode // ### rename +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(Expression) + + Expression(ExpressionNode *l, ExpressionNode *r): + left (l), right (r) { kind = K; } + + 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 QML_PARSER_EXPORT Block: public Statement +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(Block) + + Block(StatementList *slist): + statements (slist) { kind = K; } + + 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 QML_PARSER_EXPORT StatementList: public Node +{ +public: + QDECLARATIVEJS_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 void accept0(Visitor *visitor); + + inline StatementList *finish () + { + StatementList *front = next; + next = 0; + return front; + } + +// attributes + Statement *statement; + StatementList *next; +}; + +class QML_PARSER_EXPORT VariableStatement: public Statement +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(VariableStatement) + + VariableStatement(VariableDeclarationList *vlist): + declarations (vlist) + { kind = K; } + + 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 QML_PARSER_EXPORT VariableDeclaration: public Node +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(VariableDeclaration) + + VariableDeclaration(NameId *n, ExpressionNode *e): + name (n), expression (e), readOnly(false) + { kind = K; } + + virtual void accept0(Visitor *visitor); + +// attributes + NameId *name; + ExpressionNode *expression; + bool readOnly; + SourceLocation identifierToken; +}; + +class QML_PARSER_EXPORT VariableDeclarationList: public Node +{ +public: + QDECLARATIVEJS_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 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 QML_PARSER_EXPORT EmptyStatement: public Statement +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(EmptyStatement) + + EmptyStatement() { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return semicolonToken; } + + virtual SourceLocation lastSourceLocation() const + { return semicolonToken; } + +// attributes + SourceLocation semicolonToken; +}; + +class QML_PARSER_EXPORT ExpressionStatement: public Statement +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(ExpressionStatement) + + ExpressionStatement(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return expression->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return semicolonToken; } + +// attributes + ExpressionNode *expression; + SourceLocation semicolonToken; +}; + +class QML_PARSER_EXPORT IfStatement: public Statement +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(IfStatement) + + IfStatement(ExpressionNode *e, Statement *t, Statement *f = 0): + expression (e), ok (t), ko (f) + { kind = K; } + + 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 QML_PARSER_EXPORT DoWhileStatement: public Statement +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(DoWhileStatement) + + DoWhileStatement(Statement *stmt, ExpressionNode *e): + statement (stmt), expression (e) + { kind = K; } + + 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 QML_PARSER_EXPORT WhileStatement: public Statement +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(WhileStatement) + + WhileStatement(ExpressionNode *e, Statement *stmt): + expression (e), statement (stmt) + { kind = K; } + + 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 QML_PARSER_EXPORT ForStatement: public Statement +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(ForStatement) + + ForStatement(ExpressionNode *i, ExpressionNode *c, ExpressionNode *e, Statement *stmt): + initialiser (i), condition (c), expression (e), statement (stmt) + { kind = K; } + + 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 QML_PARSER_EXPORT LocalForStatement: public Statement +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(LocalForStatement) + + LocalForStatement(VariableDeclarationList *vlist, ExpressionNode *c, ExpressionNode *e, Statement *stmt): + declarations (vlist), condition (c), expression (e), statement (stmt) + { kind = K; } + + 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 QML_PARSER_EXPORT ForEachStatement: public Statement +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(ForEachStatement) + + ForEachStatement(ExpressionNode *i, ExpressionNode *e, Statement *stmt): + initialiser (i), expression (e), statement (stmt) + { kind = K; } + + 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 QML_PARSER_EXPORT LocalForEachStatement: public Statement +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(LocalForEachStatement) + + LocalForEachStatement(VariableDeclaration *v, ExpressionNode *e, Statement *stmt): + declaration (v), expression (e), statement (stmt) + { kind = K; } + + 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 QML_PARSER_EXPORT ContinueStatement: public Statement +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(ContinueStatement) + + ContinueStatement(NameId *l = 0): + label (l) { kind = K; } + + 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 QML_PARSER_EXPORT BreakStatement: public Statement +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(BreakStatement) + + BreakStatement(NameId *l = 0): + label (l) { kind = K; } + + 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 QML_PARSER_EXPORT ReturnStatement: public Statement +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(ReturnStatement) + + ReturnStatement(ExpressionNode *e): + expression (e) { kind = K; } + + 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 QML_PARSER_EXPORT WithStatement: public Statement +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(WithStatement) + + WithStatement(ExpressionNode *e, Statement *stmt): + expression (e), statement (stmt) + { kind = K; } + + 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 QML_PARSER_EXPORT CaseBlock: public Node +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(CaseBlock) + + CaseBlock(CaseClauses *c, DefaultClause *d = 0, CaseClauses *r = 0): + clauses (c), defaultClause (d), moreClauses (r) + { kind = K; } + + virtual void accept0(Visitor *visitor); + +// attributes + CaseClauses *clauses; + DefaultClause *defaultClause; + CaseClauses *moreClauses; + SourceLocation lbraceToken; + SourceLocation rbraceToken; +}; + +class QML_PARSER_EXPORT SwitchStatement: public Statement +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(SwitchStatement) + + SwitchStatement(ExpressionNode *e, CaseBlock *b): + expression (e), block (b) + { kind = K; } + + 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 QML_PARSER_EXPORT CaseClauses: public Node +{ +public: + QDECLARATIVEJS_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 void accept0(Visitor *visitor); + + inline CaseClauses *finish () + { + CaseClauses *front = next; + next = 0; + return front; + } + +//attributes + CaseClause *clause; + CaseClauses *next; +}; + +class QML_PARSER_EXPORT CaseClause: public Node +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(CaseClause) + + CaseClause(ExpressionNode *e, StatementList *slist): + expression (e), statements (slist) + { kind = K; } + + virtual void accept0(Visitor *visitor); + +// attributes + ExpressionNode *expression; + StatementList *statements; + SourceLocation caseToken; + SourceLocation colonToken; +}; + +class QML_PARSER_EXPORT DefaultClause: public Node +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(DefaultClause) + + DefaultClause(StatementList *slist): + statements (slist) + { kind = K; } + + virtual void accept0(Visitor *visitor); + +// attributes + StatementList *statements; + SourceLocation defaultToken; + SourceLocation colonToken; +}; + +class QML_PARSER_EXPORT LabelledStatement: public Statement +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(LabelledStatement) + + LabelledStatement(NameId *l, Statement *stmt): + label (l), statement (stmt) + { kind = K; } + + 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 QML_PARSER_EXPORT ThrowStatement: public Statement +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(ThrowStatement) + + ThrowStatement(ExpressionNode *e): + expression (e) { kind = K; } + + 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 QML_PARSER_EXPORT Catch: public Node +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(Catch) + + Catch(NameId *n, Block *stmt): + name (n), statement (stmt) + { kind = K; } + + virtual void accept0(Visitor *visitor); + +// attributes + NameId *name; + Block *statement; + SourceLocation catchToken; + SourceLocation lparenToken; + SourceLocation identifierToken; + SourceLocation rparenToken; +}; + +class QML_PARSER_EXPORT Finally: public Node +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(Finally) + + Finally(Block *stmt): + statement (stmt) + { kind = K; } + + virtual void accept0(Visitor *visitor); + +// attributes + Block *statement; + SourceLocation finallyToken; +}; + +class QML_PARSER_EXPORT TryStatement: public Statement +{ +public: + QDECLARATIVEJS_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 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 QML_PARSER_EXPORT FunctionExpression: public ExpressionNode +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(FunctionExpression) + + FunctionExpression(NameId *n, FormalParameterList *f, FunctionBody *b): + name (n), formals (f), body (b) + { kind = K; } + + 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 QML_PARSER_EXPORT FunctionDeclaration: public FunctionExpression +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(FunctionDeclaration) + + FunctionDeclaration(NameId *n, FormalParameterList *f, FunctionBody *b): + FunctionExpression(n, f, b) + { kind = K; } + + virtual void accept0(Visitor *visitor); +}; + +class QML_PARSER_EXPORT FormalParameterList: public Node +{ +public: + QDECLARATIVEJS_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 void accept0(Visitor *visitor); + + inline FormalParameterList *finish () + { + FormalParameterList *front = next; + next = 0; + return front; + } + +// attributes + NameId *name; + FormalParameterList *next; + SourceLocation commaToken; + SourceLocation identifierToken; +}; + +class QML_PARSER_EXPORT FunctionBody: public Node +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(FunctionBody) + + FunctionBody(SourceElements *elts): + elements (elts) + { kind = K; } + + virtual void accept0(Visitor *visitor); + +// attributes + SourceElements *elements; +}; + +class QML_PARSER_EXPORT Program: public Node +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(Program) + + Program(SourceElements *elts): + elements (elts) + { kind = K; } + + virtual void accept0(Visitor *visitor); + +// attributes + SourceElements *elements; +}; + +class QML_PARSER_EXPORT SourceElements: public Node +{ +public: + QDECLARATIVEJS_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 void accept0(Visitor *visitor); + + inline SourceElements *finish () + { + SourceElements *front = next; + next = 0; + return front; + } + +// attributes + SourceElement *element; + SourceElements *next; +}; + +class QML_PARSER_EXPORT SourceElement: public Node +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(SourceElement) + + inline SourceElement() + { kind = K; } +}; + +class QML_PARSER_EXPORT FunctionSourceElement: public SourceElement +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(FunctionSourceElement) + + FunctionSourceElement(FunctionDeclaration *f): + declaration (f) + { kind = K; } + + virtual void accept0(Visitor *visitor); + +// attributes + FunctionDeclaration *declaration; +}; + +class QML_PARSER_EXPORT StatementSourceElement: public SourceElement +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(StatementSourceElement) + + StatementSourceElement(Statement *stmt): + statement (stmt) + { kind = K; } + + virtual void accept0(Visitor *visitor); + +// attributes + Statement *statement; +}; + +class QML_PARSER_EXPORT DebuggerStatement: public Statement +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(DebuggerStatement) + + DebuggerStatement() + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return debuggerToken; } + + virtual SourceLocation lastSourceLocation() const + { return semicolonToken; } + +// attributes + SourceLocation debuggerToken; + SourceLocation semicolonToken; +}; + +class QML_PARSER_EXPORT UiProgram: public Node +{ +public: + QDECLARATIVEJS_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 QML_PARSER_EXPORT UiQualifiedId: public Node +{ +public: + QDECLARATIVEJS_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; + } + + UiQualifiedId *finish() + { + UiQualifiedId *head = next; + next = 0; + return head; + } + + virtual void accept0(Visitor *visitor); + +// attributes + UiQualifiedId *next; + NameId *name; + SourceLocation identifierToken; +}; + +class QML_PARSER_EXPORT UiImport: public Node +{ +public: + QDECLARATIVEJS_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 QML_PARSER_EXPORT UiImportList: public Node +{ +public: + QDECLARATIVEJS_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 QML_PARSER_EXPORT UiObjectMember: public Node +{ +public: + virtual SourceLocation firstSourceLocation() const = 0; + virtual SourceLocation lastSourceLocation() const = 0; + + virtual UiObjectMember *uiObjectMemberCast(); +}; + +class QML_PARSER_EXPORT UiObjectMemberList: public Node +{ +public: + QDECLARATIVEJS_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 QML_PARSER_EXPORT UiArrayMemberList: public Node +{ +public: + QDECLARATIVEJS_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 QML_PARSER_EXPORT UiObjectInitializer: public Node +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(UiObjectInitializer) + + UiObjectInitializer(UiObjectMemberList *members) + : members(members) + { kind = K; } + + virtual void accept0(Visitor *visitor); + +// attributes + SourceLocation lbraceToken; + UiObjectMemberList *members; + SourceLocation rbraceToken; +}; + +class QML_PARSER_EXPORT UiParameterList: public Node +{ +public: + QDECLARATIVEJS_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 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 QML_PARSER_EXPORT UiPublicMember: public UiObjectMember +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(UiPublicMember) + + UiPublicMember(NameId *memberType, + NameId *name) + : type(Property), typeModifier(0), memberType(memberType), name(name), expression(0), binding(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), binding(0), 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 + { + if (binding) + return binding->lastSourceLocation(); + + return semicolonToken; + } + + virtual void accept0(Visitor *visitor); + +// attributes + enum { Signal, Property } type; + NameId *typeModifier; + NameId *memberType; + NameId *name; + ExpressionNode *expression; // initialized with a JS expression + UiObjectMember *binding; // initialized with a QML object or array. + bool isDefaultMember; + bool isReadonlyMember; + UiParameterList *parameters; + SourceLocation defaultToken; + SourceLocation readonlyToken; + SourceLocation propertyToken; + SourceLocation typeModifierToken; + SourceLocation typeToken; + SourceLocation identifierToken; + SourceLocation colonToken; + SourceLocation semicolonToken; +}; + +class QML_PARSER_EXPORT UiObjectDefinition: public UiObjectMember +{ +public: + QDECLARATIVEJS_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 QML_PARSER_EXPORT UiSourceElement: public UiObjectMember +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(UiSourceElement) + + UiSourceElement(Node *sourceElement) + : sourceElement(sourceElement) + { kind = K; } + + virtual SourceLocation firstSourceLocation() const + { + if (FunctionDeclaration *funDecl = cast(sourceElement)) + return funDecl->firstSourceLocation(); + else if (VariableStatement *varStmt = cast(sourceElement)) + return varStmt->firstSourceLocation(); + + return SourceLocation(); + } + + virtual SourceLocation lastSourceLocation() const + { + if (FunctionDeclaration *funDecl = cast(sourceElement)) + return funDecl->lastSourceLocation(); + else if (VariableStatement *varStmt = cast(sourceElement)) + return varStmt->lastSourceLocation(); + + return SourceLocation(); + } + + + virtual void accept0(Visitor *visitor); + +// attributes + Node *sourceElement; +}; + +class QML_PARSER_EXPORT UiObjectBinding: public UiObjectMember +{ +public: + QDECLARATIVEJS_DECLARE_AST_NODE(UiObjectBinding) + + UiObjectBinding(UiQualifiedId *qualifiedId, + UiQualifiedId *qualifiedTypeNameId, + UiObjectInitializer *initializer) + : qualifiedId(qualifiedId), + qualifiedTypeNameId(qualifiedTypeNameId), + initializer(initializer), + hasOnToken(false) + { kind = K; } + + virtual SourceLocation firstSourceLocation() const + { + if (hasOnToken && qualifiedTypeNameId) + return qualifiedTypeNameId->identifierToken; + + return qualifiedId->identifierToken; + } + + virtual SourceLocation lastSourceLocation() const + { return initializer->rbraceToken; } + + virtual void accept0(Visitor *visitor); + +// attributes + UiQualifiedId *qualifiedId; + UiQualifiedId *qualifiedTypeNameId; + UiObjectInitializer *initializer; + SourceLocation colonToken; + bool hasOnToken; +}; + +class QML_PARSER_EXPORT UiScriptBinding: public UiObjectMember +{ +public: + QDECLARATIVEJS_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 QML_PARSER_EXPORT UiArrayBinding: public UiObjectMember +{ +public: + QDECLARATIVEJS_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/declarative/qml/parser/qdeclarativejsastfwd_p.h b/src/declarative/qml/parser/qdeclarativejsastfwd_p.h new file mode 100644 index 00000000..83cde71f --- /dev/null +++ b/src/declarative/qml/parser/qdeclarativejsastfwd_p.h @@ -0,0 +1,189 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEJSAST_FWD_P_H +#define QDECLARATIVEJSAST_FWD_P_H + +#include "private/qdeclarativejsglobal_p.h" + +#include + +// +// 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 QDeclarativeJS { 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/declarative/qml/parser/qdeclarativejsastvisitor.cpp b/src/declarative/qml/parser/qdeclarativejsastvisitor.cpp new file mode 100644 index 00000000..c5051218 --- /dev/null +++ b/src/declarative/qml/parser/qdeclarativejsastvisitor.cpp @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativejsastvisitor_p.h" + +QT_QML_BEGIN_NAMESPACE + +namespace QDeclarativeJS { namespace AST { + +Visitor::Visitor() +{ +} + +Visitor::~Visitor() +{ +} + +} } // namespace QDeclarativeJS::AST + +QT_QML_END_NAMESPACE diff --git a/src/declarative/qml/parser/qdeclarativejsastvisitor_p.h b/src/declarative/qml/parser/qdeclarativejsastvisitor_p.h new file mode 100644 index 00000000..8d6fc616 --- /dev/null +++ b/src/declarative/qml/parser/qdeclarativejsastvisitor_p.h @@ -0,0 +1,335 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEJSASTVISITOR_P_H +#define QDECLARATIVEJSASTVISITOR_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 "private/qdeclarativejsastfwd_p.h" +#include "private/qdeclarativejsglobal_p.h" + +QT_QML_BEGIN_NAMESPACE + +namespace QDeclarativeJS { namespace AST { + +class QML_PARSER_EXPORT 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 *) {} + + // QDeclarativeJS + 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 // QDECLARATIVEJSASTVISITOR_P_H diff --git a/src/declarative/qml/parser/qdeclarativejsengine_p.cpp b/src/declarative/qml/parser/qdeclarativejsengine_p.cpp new file mode 100644 index 00000000..0d7a7f53 --- /dev/null +++ b/src/declarative/qml/parser/qdeclarativejsengine_p.cpp @@ -0,0 +1,212 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativejsengine_p.h" + +#include "private/qdeclarativejsglobal_p.h" +#include "private/qdeclarativejsnodepool_p.h" + +#include +#include + +QT_QML_BEGIN_NAMESPACE + +namespace QDeclarativeJS { + +uint qHash(const QDeclarativeJS::NameId &id) +{ return qHash(id.asString()); } + +QString numberToString(double value) +{ return QString::number(value); } + +int Ecma::RegExp::flagFromChar(const QChar &ch) +{ + static QHash flagsHash; + if (flagsHash.isEmpty()) { + flagsHash[QLatin1Char('g')] = Global; + flagsHash[QLatin1Char('i')] = IgnoreCase; + flagsHash[QLatin1Char('m')] = Multiline; + } + QHash::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().toLatin1(); + return integerFromString(ba.constData(), ba.size(), radix); +} + + +Engine::Engine() + : _lexer(0), _nodePool(0) +{ } + +Engine::~Engine() +{ } + +QSet Engine::literals() const +{ return _literals; } + +void Engine::addComment(int pos, int len, int line, int col) +{ if (len > 0) _comments.append(QDeclarativeJS::AST::SourceLocation(pos, len, line, col)); } + +QList Engine::comments() const +{ return _comments; } + +NameId *Engine::intern(const QChar *u, int s) +{ return const_cast(&*_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 QDeclarativeJS + +QT_QML_END_NAMESPACE diff --git a/src/declarative/qml/parser/qdeclarativejsengine_p.h b/src/declarative/qml/parser/qdeclarativejsengine_p.h new file mode 100644 index 00000000..2603e7d2 --- /dev/null +++ b/src/declarative/qml/parser/qdeclarativejsengine_p.h @@ -0,0 +1,167 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEJSENGINE_P_H +#define QDECLARATIVEJSENGINE_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 "private/qdeclarativejsglobal_p.h" +#include "private/qdeclarativejsastfwd_p.h" + +#include +#include + +QT_QML_BEGIN_NAMESPACE + +namespace QDeclarativeJS { +class QML_PARSER_EXPORT 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 QDeclarativeJS::NameId &id); + +} // end of namespace QDeclarativeJS + +namespace QDeclarativeJS { + +class Lexer; +class NodePool; + +namespace Ecma { + +class QML_PARSER_EXPORT 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 QML_PARSER_EXPORT 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 QML_PARSER_EXPORT Engine +{ + Lexer *_lexer; + NodePool *_nodePool; + QSet _literals; + QList _comments; + +public: + Engine(); + ~Engine(); + + QSet literals() const; + + void addComment(int pos, int len, int line, int col); + QList 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 QDeclarativeJS + +QT_QML_END_NAMESPACE + +#endif // QDECLARATIVEJSENGINE_P_H diff --git a/src/declarative/qml/parser/qdeclarativejsglobal_p.h b/src/declarative/qml/parser/qdeclarativejsglobal_p.h new file mode 100644 index 00000000..014d78ab --- /dev/null +++ b/src/declarative/qml/parser/qdeclarativejsglobal_p.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QDECLARATIVEJSGLOBAL_P_H +#define QDECLARATIVEJSGLOBAL_P_H + +#include + +#ifdef QT_CREATOR +# define QT_QML_BEGIN_NAMESPACE +# define QT_QML_END_NAMESPACE + +# ifdef QDECLARATIVEJS_BUILD_DIR +# define QML_PARSER_EXPORT Q_DECL_EXPORT +# elif QML_BUILD_STATIC_LIB +# define QML_PARSER_EXPORT +# else +# define QML_PARSER_EXPORT Q_DECL_IMPORT +# endif // QDECLARATIVEJS_BUILD_DIR + +#else // !QT_CREATOR +# define QT_QML_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE +# define QT_QML_END_NAMESPACE QT_END_NAMESPACE +# define QML_PARSER_EXPORT +#endif // QT_CREATOR + +#endif // QDECLARATIVEJSGLOBAL_P_H diff --git a/src/declarative/qml/parser/qdeclarativejsgrammar.cpp b/src/declarative/qml/parser/qdeclarativejsgrammar.cpp new file mode 100644 index 00000000..02b23663 --- /dev/null +++ b/src/declarative/qml/parser/qdeclarativejsgrammar.cpp @@ -0,0 +1,989 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// This file was generated by qlalr - DO NOT EDIT! +#include "qdeclarativejsgrammar_p.h" + +QT_BEGIN_NAMESPACE + +const char *const QDeclarativeJSGrammar::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", "on", 0, 0, 0, 0, 0, 0, 0, + 0}; + +const short QDeclarativeJSGrammar::lhs [] = { + 101, 101, 101, 101, 101, 101, 102, 108, 108, 111, + 111, 113, 112, 112, 112, 112, 112, 112, 112, 112, + 115, 110, 109, 118, 118, 119, 119, 120, 120, 117, + 106, 106, 106, 106, 106, 106, 106, 106, 126, 126, + 126, 127, 127, 128, 128, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 116, 116, 116, 116, 116, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 121, 133, + 133, 133, 133, 132, 132, 135, 135, 137, 137, 137, + 137, 137, 137, 138, 138, 138, 138, 138, 138, 138, + 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, + 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, + 138, 138, 138, 138, 139, 139, 114, 114, 114, 114, + 114, 142, 142, 143, 143, 143, 143, 141, 141, 144, + 144, 145, 145, 146, 146, 146, 147, 147, 147, 147, + 147, 147, 147, 147, 147, 147, 148, 148, 148, 148, + 149, 149, 149, 150, 150, 150, 150, 151, 151, 151, + 151, 151, 151, 151, 152, 152, 152, 152, 152, 152, + 153, 153, 153, 153, 153, 154, 154, 154, 154, 154, + 155, 155, 156, 156, 157, 157, 158, 158, 159, 159, + 160, 160, 161, 161, 162, 162, 163, 163, 164, 164, + 165, 165, 166, 166, 136, 136, 167, 167, 168, 168, + 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, + 104, 104, 169, 169, 170, 170, 171, 171, 103, 103, + 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, + 103, 103, 103, 122, 183, 183, 182, 182, 130, 130, + 184, 184, 185, 185, 187, 187, 186, 188, 191, 189, + 189, 192, 190, 190, 123, 124, 124, 125, 125, 172, + 172, 172, 172, 172, 172, 172, 173, 173, 173, 173, + 174, 174, 174, 174, 175, 175, 176, 178, 193, 193, + 196, 196, 194, 194, 197, 195, 177, 177, 177, 179, + 179, 180, 180, 180, 198, 199, 181, 181, 129, 140, + 203, 203, 200, 200, 201, 201, 204, 107, 205, 205, + 105, 105, 202, 202, 134, 134, 206}; + +const short QDeclarativeJSGrammar::rhs [] = { + 2, 2, 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, 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, 10, 6, 1, 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, 1, 2, + 1, 1, 0, 1, 0, 1, 2}; + +const short QDeclarativeJSGrammar::action_default [] = { + 0, 0, 0, 0, 0, 0, 22, 0, 174, 241, + 205, 213, 209, 153, 225, 201, 3, 138, 72, 154, + 217, 221, 142, 171, 152, 157, 137, 191, 178, 0, + 79, 80, 75, 343, 66, 345, 0, 0, 0, 0, + 77, 0, 0, 73, 76, 70, 0, 0, 67, 69, + 68, 78, 71, 0, 74, 0, 0, 167, 0, 0, + 154, 173, 156, 155, 0, 0, 0, 169, 170, 168, + 172, 0, 202, 0, 0, 0, 0, 192, 0, 0, + 0, 0, 0, 0, 182, 0, 0, 0, 176, 177, + 175, 180, 184, 183, 181, 179, 194, 193, 195, 0, + 210, 0, 206, 0, 0, 148, 135, 147, 136, 104, + 105, 106, 131, 107, 132, 108, 109, 110, 111, 112, + 113, 114, 115, 116, 117, 118, 119, 120, 133, 121, + 122, 123, 124, 125, 126, 127, 128, 129, 130, 134, + 0, 0, 146, 242, 149, 0, 150, 0, 151, 145, + 0, 238, 231, 229, 236, 237, 235, 234, 240, 233, + 232, 230, 239, 226, 0, 214, 0, 0, 218, 0, + 0, 222, 0, 0, 148, 140, 0, 139, 0, 144, + 158, 0, 344, 333, 334, 0, 331, 0, 332, 0, + 335, 249, 256, 255, 263, 251, 0, 252, 336, 0, + 342, 253, 254, 259, 257, 339, 337, 341, 260, 0, + 271, 0, 0, 0, 0, 343, 66, 0, 345, 67, + 243, 285, 68, 0, 0, 0, 272, 0, 0, 261, + 262, 0, 250, 258, 286, 287, 330, 340, 0, 301, + 302, 303, 304, 0, 297, 298, 299, 300, 327, 328, + 0, 0, 0, 0, 0, 290, 291, 247, 245, 207, + 215, 211, 227, 203, 248, 0, 154, 219, 223, 196, + 185, 0, 0, 204, 0, 0, 0, 0, 197, 0, + 0, 0, 0, 0, 189, 187, 190, 188, 186, 199, + 198, 200, 0, 212, 0, 208, 0, 246, 154, 0, + 228, 243, 244, 0, 243, 0, 0, 293, 0, 0, + 0, 295, 0, 216, 0, 0, 220, 0, 0, 224, + 283, 0, 275, 284, 278, 0, 282, 0, 243, 276, + 0, 243, 0, 0, 294, 0, 0, 0, 296, 344, + 333, 0, 0, 335, 0, 329, 0, 319, 0, 0, + 0, 289, 0, 288, 0, 346, 0, 103, 265, 268, + 0, 104, 271, 107, 132, 109, 110, 75, 114, 115, + 66, 116, 119, 73, 76, 67, 243, 68, 78, 122, + 71, 124, 74, 126, 127, 272, 129, 130, 134, 0, + 96, 0, 0, 98, 102, 100, 87, 99, 101, 0, + 97, 86, 266, 264, 142, 143, 148, 0, 141, 0, + 318, 0, 305, 306, 0, 317, 0, 0, 0, 308, + 313, 311, 314, 0, 0, 312, 313, 0, 309, 0, + 310, 267, 316, 0, 267, 315, 0, 320, 321, 0, + 267, 322, 323, 0, 0, 324, 0, 0, 0, 325, + 326, 160, 159, 0, 0, 0, 292, 0, 0, 0, + 307, 280, 273, 0, 281, 277, 0, 279, 269, 0, + 270, 274, 90, 0, 0, 94, 81, 0, 83, 92, + 0, 84, 93, 95, 85, 91, 82, 0, 88, 164, + 162, 166, 163, 161, 165, 6, 338, 4, 2, 64, + 89, 0, 0, 67, 69, 68, 31, 5, 0, 65, + 0, 41, 40, 39, 0, 0, 54, 0, 55, 0, + 60, 61, 0, 41, 0, 0, 0, 0, 0, 50, + 0, 51, 0, 0, 26, 0, 0, 62, 27, 0, + 30, 28, 24, 0, 29, 25, 0, 52, 0, 53, + 0, 142, 0, 56, 57, 63, 0, 0, 0, 0, + 0, 58, 59, 0, 48, 42, 49, 43, 0, 0, + 0, 0, 45, 0, 46, 47, 44, 0, 0, 35, + 36, 37, 38, 142, 267, 0, 0, 104, 271, 107, + 132, 109, 110, 75, 114, 115, 66, 116, 119, 73, + 76, 67, 243, 68, 78, 122, 71, 124, 74, 126, + 127, 272, 129, 130, 134, 0, 32, 33, 0, 34, + 8, 0, 10, 0, 9, 0, 1, 21, 12, 0, + 13, 0, 14, 0, 19, 20, 0, 15, 16, 0, + 17, 18, 11, 23, 7, 347}; + +const short QDeclarativeJSGrammar::goto_default [] = { + 7, 626, 207, 196, 205, 507, 495, 625, 644, 620, + 624, 622, 627, 22, 623, 18, 506, 543, 533, 540, + 535, 191, 195, 197, 201, 524, 568, 567, 200, 232, + 26, 474, 473, 356, 355, 9, 354, 357, 107, 17, + 145, 24, 13, 144, 19, 25, 57, 23, 8, 28, + 27, 269, 15, 263, 10, 259, 12, 261, 11, 260, + 20, 267, 21, 268, 14, 262, 258, 299, 411, 264, + 265, 202, 193, 192, 204, 233, 203, 208, 229, 230, + 194, 360, 359, 231, 463, 462, 321, 322, 465, 324, + 464, 323, 419, 423, 426, 422, 421, 441, 442, 185, + 199, 181, 184, 198, 206, 0}; + +const short QDeclarativeJSGrammar::action_index [] = { + 421, 1288, 2322, 2322, 2419, 1016, -52, 37, 140, -101, + 35, -13, -40, 190, -101, 272, 34, -101, -101, 658, + 42, 103, 194, 201, -101, -101, -101, 439, 256, 1288, + -101, -101, -101, 282, -101, 2128, 1751, 1288, 1288, 1288, + -101, 917, 1288, -101, -101, -101, 1288, 1288, -101, -101, + -101, -101, -101, 1288, -101, 1288, 1288, -101, 1288, 1288, + 109, 245, -101, -101, 1288, 1288, 1288, -101, -101, -101, + 185, 1288, 295, 1288, 1288, 1288, 1288, 461, 1288, 1288, + 1288, 1288, 1288, 1288, 256, 1288, 1288, 1288, 155, 119, + 114, 176, 256, 332, 202, 332, 560, 560, 471, 1288, + -23, 1288, 53, 2031, 1288, 1288, -101, -101, -101, -101, + -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, + -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, + -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, + 100, 1288, -101, -101, 70, 59, -101, 1288, -101, -101, + 1288, -101, -101, -101, -101, -101, -101, -101, -101, -101, + -101, -101, -101, -101, 1288, 41, 1288, 1288, 98, 91, + 1288, -101, 2031, 1288, 1288, -101, 121, -101, 73, -101, + -101, 39, -101, 385, 180, 78, -101, 391, -101, 64, + 2322, -101, -101, -101, -101, -101, 208, -101, -101, 82, + -101, -101, -101, -101, -101, -101, 2322, -101, -101, 538, + -101, 495, 128, 2419, 54, 358, 62, 44, 2613, 67, + 1288, -101, 76, 63, 1288, 58, -101, 60, 46, -101, + -101, 309, -101, -101, -101, -101, -101, -101, 86, -101, + -101, -101, -101, 107, -101, -101, -101, -101, -101, -101, + 28, 52, 1288, 101, 102, -101, -101, 1472, -101, 83, + 75, 79, -101, 287, 84, 80, 585, 69, 89, 321, + 177, 482, 1288, 297, 1288, 1288, 1288, 1288, 331, 1288, + 1288, 1288, 1288, 1288, 332, 222, 332, 332, 332, 410, + 410, 410, 1288, 57, 1288, 72, 1288, -101, 658, 1288, + -101, 1288, 71, 45, 1288, 61, 2419, -101, 1288, 132, + 2419, -101, 1288, 47, 1288, 1288, 66, 65, 1288, -101, + 68, 112, 81, -101, -101, 1288, -101, 369, 1288, -101, + 85, 1288, 74, 2419, -101, 1288, 122, 2419, -101, 77, + 294, 16, -29, 2322, -53, -101, 2419, -101, 1288, 127, + 2419, -15, 2419, -101, 10, 11, -34, -101, -101, 2419, + -48, 504, 4, 476, 113, 1288, 2419, 2, -28, 420, + 6, -21, 719, 7, 87, -101, 1382, -101, -4, -16, + 5, 1288, 3, -27, 1288, 9, 1288, -18, -31, 1288, + -101, 2225, -7, -101, -101, -101, -101, -101, -101, 1288, + -101, -101, -101, -101, 246, -101, 1288, -38, -101, 2419, + -101, 88, -101, -101, 2419, -101, 1288, 106, 26, -101, + 55, -101, 50, 105, 1288, -101, 48, 38, -101, -8, + -101, 2419, -101, 94, 2419, -101, 238, -101, -101, 104, + 2419, 31, -101, 21, 19, -101, 305, 1, 30, -101, + -101, -101, -101, 1288, 136, 2419, -101, 1288, 134, 2419, + -101, 49, -101, 173, -101, -101, 1288, -101, -101, 363, + -101, -101, -101, 137, 1565, -101, -101, 1658, -101, -101, + 1844, -101, -101, -101, -101, -101, -101, 95, -101, -101, + -101, -101, -101, -101, -101, -101, 2322, -101, -101, -101, + 92, 15, 925, 169, 27, -6, -101, -101, 212, -101, + 191, -101, -101, -101, 323, 211, -101, 1288, -101, 214, + -101, -101, 216, 40, 317, 210, 43, 259, 236, -101, + 36, -101, 747, 96, -101, 29, 747, -101, -101, 1198, + -101, -101, -101, 1107, -101, -101, 231, -101, 1288, -101, + 217, 286, 32, -101, -101, -101, 188, 340, 51, 1288, + 175, -101, -101, 171, -101, 179, -101, 56, -11, 351, + 181, 336, -101, 110, -101, -101, -101, 1934, 647, -101, + -101, -101, -101, 253, 2516, 1751, -5, 460, 22, 468, + 138, 1288, 2419, 24, -2, 412, 23, -3, 836, 20, + 87, -101, 1382, -101, 17, -10, 18, 1288, 25, 8, + 1288, 33, 1288, 12, 14, 120, -101, -101, 13, -101, + -101, 747, -101, 248, -47, 828, -101, -101, 152, 482, + -101, 150, -101, 123, -101, -101, 398, -101, -101, 117, + -101, -101, -101, -101, -101, -101, + + -106, 6, -92, 10, 5, 278, -106, -106, -106, -106, + -106, -106, -106, -106, -106, -106, -106, -106, -106, -42, + -106, -106, -106, -106, -106, -106, -106, -106, -106, 109, + -106, -106, -106, -10, -106, -106, -35, 24, 73, 90, + -106, 219, 181, -106, -106, -106, 171, 120, -106, -106, + -106, -106, -106, 174, -106, 170, 167, -106, 175, 163, + -106, -106, -106, -106, 184, 177, 180, -106, -106, -106, + -106, 125, -106, 132, 134, 162, 130, -106, 121, 124, + 123, 141, 142, 152, -106, 154, 161, 160, -106, -106, + -106, -106, -106, -106, -106, -106, -106, -106, -106, 139, + -106, 143, -106, 156, 91, 55, -106, -106, -106, -106, + -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, + -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, + -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, + -106, 32, -106, -106, -106, -106, -106, 33, -106, -106, + 26, -106, -106, -106, -106, -106, -106, -106, -106, -106, + -106, -106, -106, -106, 96, -106, 119, 52, -106, -106, + 66, -106, 220, 69, 71, -106, -106, -106, -106, -106, + -106, -106, -106, 25, -106, -106, -106, 64, -106, -106, + -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, + -106, -106, -106, -106, -106, -106, 70, -106, -106, 61, + -106, 41, -106, 39, -106, 37, -106, -106, 42, -106, + 79, -106, -106, -106, 81, 72, -106, -106, -106, -106, + -106, -5, -106, -106, -106, -106, -106, -106, -106, -106, + -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, + -106, -106, 21, -106, -106, -106, -106, 112, -106, -106, + -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, + -106, 17, 237, -106, 192, 236, 224, 225, -106, 97, + 98, 101, 99, 113, -106, -106, -106, -106, -106, -106, + -106, -106, 204, -106, 223, -106, 235, -106, -106, 239, + -106, 197, -106, -106, 228, -106, 27, -106, 13, -106, + 2, -106, 233, -106, 190, 198, -106, -106, 196, -106, + -106, -106, -106, -106, -106, 200, -106, 107, 135, -106, + -106, 186, -106, 84, -106, 80, -106, 76, -106, -106, + 89, -106, -106, -49, -106, -106, 47, -106, 40, -106, + 44, -106, 68, -106, -106, -106, -106, -106, -106, 53, + -106, 35, -106, 49, -106, 87, 63, -106, -106, 30, + -106, -106, 103, -106, -106, -106, 51, -106, -106, -106, + -106, 86, -106, 67, 114, -106, 74, -106, -106, 65, + -106, 56, -106, -106, -106, -106, -106, -106, -106, 62, + -106, -106, -106, -106, -106, -106, 95, -106, -106, 78, + -106, -106, -106, -106, 75, -106, 88, -106, -106, -106, + -106, -106, -54, -106, 45, -106, -40, -106, -106, -106, + -106, 94, -106, -106, 100, -106, -106, -106, -106, -106, + 150, -41, -106, -106, 54, -106, 43, -106, 48, -106, + -106, -106, -106, 59, -106, 57, -106, 60, -106, 58, + -106, -106, -106, -106, -106, -106, 38, -106, -106, 144, + -106, -106, -106, -106, 31, -106, -106, 202, -106, -106, + 50, -106, -106, -106, -106, -106, -106, -106, -106, -106, + -106, -106, -106, -106, -106, -106, 77, -106, -106, -106, + -106, -106, 82, -106, -106, -106, -106, -106, -106, -106, + -17, -106, -106, -106, -4, -106, -106, 12, -106, -106, + -106, -106, -106, -106, -14, 46, -106, -13, -106, -106, + -106, -106, 108, -106, -106, -106, 243, -106, -106, 295, + -106, -106, -106, 290, -106, -106, -106, -106, 346, -106, + -106, -106, 16, -106, -106, -106, 22, 23, -106, 34, + -106, -106, -106, -106, -106, 11, -106, -106, -106, 7, + 1, 8, -106, -106, -106, -106, -106, 307, 179, -106, + -106, -106, -106, -106, 18, 281, 9, 15, -106, 4, + -106, 83, 29, -106, -106, -2, -106, -106, 85, -106, + -106, -106, 3, -106, -106, -106, -106, 14, -106, 0, + 105, -106, 93, -106, -106, -106, -106, -106, -1, -106, + -106, 20, -106, -106, 28, 92, -106, -106, -106, 19, + -106, -106, -106, -106, -106, -106, -12, -106, -106, -106, + -106, -106, -106, -106, -106, -106}; + +const short QDeclarativeJSGrammar::action_info [] = { + 399, 352, 345, -101, 343, 457, 440, 403, 257, -112, + -125, -131, -123, 346, -120, 348, -128, 389, 453, 391, + 416, 401, 408, 563, -101, -123, 416, -120, 539, -131, + 346, -112, -125, 348, 257, 99, 71, 645, 621, 101, + -128, 440, 141, 621, 164, 431, 539, 430, 453, 573, + 457, 444, 440, 424, 71, 424, 101, 446, 559, 420, + 424, 448, 539, 440, 570, 539, 466, 527, 312, 346, + 532, 312, 318, 272, 409, 183, 342, 525, 147, 141, + 348, 510, 457, 414, 272, 325, 0, 0, 252, 99, + 257, 440, 296, 556, -102, 292, 453, 190, 170, 416, + 164, 434, 141, 141, 536, 251, 304, 172, 141, 141, + 443, 0, 335, 340, 141, 427, 0, 0, 0, 149, + 327, 306, 0, 292, 444, 0, 173, 0, 536, 141, + 141, 0, 0, 179, 333, 141, 294, 236, 189, 314, + 141, 301, 141, 315, 141, 477, 331, 242, 241, 413, + 412, 62, 537, 166, 58, 488, 142, 167, 294, 58, + 428, 254, 63, 256, 255, 59, 418, 172, 247, 246, + 59, 575, 574, 328, 249, 248, 616, 177, 641, 640, + 58, 469, 337, 141, 635, 634, 173, 350, 187, 249, + 248, 59, 310, 478, 459, 58, 455, 64, 523, 249, + 248, 85, 85, 86, 86, 103, 59, 565, 511, 172, + 511, 638, 637, 64, 87, 87, 141, 511, 517, 577, + 511, 0, 141, 0, 104, 141, 105, 85, 173, 86, + 174, 172, 566, 564, 470, 468, 562, 561, 548, 511, + 87, 636, 65, 530, 513, 539, 141, 85, 66, 86, + 173, 0, 406, 0, 513, 512, 513, 64, 65, 0, + 87, 172, 0, 513, 66, 512, 513, 512, 172, 235, + 234, 0, 518, 516, 512, 521, 520, 512, 554, 553, + 173, 85, 406, 86, 0, 513, -89, 173, 34, 174, + 73, 74, 549, 547, 87, 631, 512, 531, 529, 438, + 437, 172, 65, 0, 578, 274, 275, 0, 66, 632, + 630, 34, 0, 73, 74, 274, 275, 75, 76, -89, + 173, 0, 174, 34, 0, 48, 50, 49, 0, 0, + 0, 0, 276, 277, 34, 0, 0, 0, 34, 629, + 75, 76, 276, 277, 279, 280, 34, 0, 48, 50, + 49, 45, 34, 281, 279, 280, 282, 85, 283, 86, + 48, 50, 49, 281, 0, 34, 282, 0, 283, 34, + 87, 48, 50, 49, 45, 48, 50, 49, 0, 0, + 34, 0, 0, 48, 50, 49, 45, 34, 0, 48, + 50, 49, 34, 0, 0, 0, 0, 45, 34, 0, + 0, 45, 48, 50, 49, 0, 48, 50, 49, 45, + 0, 0, 0, 0, 34, 45, 0, 48, 50, 49, + 34, 0, 0, 0, 48, 50, 49, 34, 45, 48, + 50, 49, 45, 279, 280, 48, 50, 49, 0, 0, + 0, 34, 281, 45, 0, 282, 0, 283, -343, 34, + 45, 48, 50, 49, 0, 45, -343, 48, 50, 49, + 0, 45, 78, 79, 48, 50, 49, 0, 0, 0, + 80, 81, 0, 0, 82, 0, 83, 45, 48, 50, + 49, 0, 0, 45, 78, 79, 48, 50, 49, 34, + 45, 0, 80, 81, 78, 79, 82, 34, 83, 0, + 0, 0, 80, 81, 45, 34, 82, 0, 83, 0, + 0, 34, 45, 0, 6, 5, 4, 1, 3, 2, + 0, 240, 239, 0, 34, 0, 48, 50, 49, 245, + 244, 0, 0, 34, 48, 50, 49, 245, 244, 0, + 0, 0, 48, 50, 49, 0, 0, 0, 48, 50, + 49, 0, 45, 0, 0, 0, 245, 244, 0, 0, + 45, 48, 50, 49, 0, 240, 239, 34, 45, 0, + 48, 50, 49, 0, 45, 0, 0, 0, 0, 0, + 0, 0, 0, 78, 79, 0, 0, 45, 151, 0, + 0, 80, 81, 0, 0, 82, 45, 83, 152, 240, + 239, 0, 153, 0, 48, 50, 49, 0, 0, 0, + 0, 154, 0, 155, 0, 0, 308, 0, 0, 0, + 0, 0, 0, 0, 156, 0, 157, 62, 0, 0, + 45, 0, 0, 0, 158, 0, 0, 159, 63, 0, + 0, 0, 0, 160, 0, 0, 0, 0, 0, 161, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, + 31, 151, 0, 0, 0, 162, 0, 0, 0, 33, + 0, 152, 0, 0, 0, 153, 34, 0, 0, 0, + 35, 36, 0, 37, 154, 0, 155, 0, 0, 0, + 502, 0, 0, 0, 44, 0, 0, 156, 0, 157, + 62, 0, 0, 0, 0, 0, 0, 158, 0, 0, + 159, 63, 51, 48, 50, 49, 160, 52, 0, 0, + 0, 0, 161, 0, 0, 0, 0, 0, 43, 54, + 32, 30, 31, 0, 40, 0, 0, 0, 162, 45, + 0, 33, 0, 0, 0, 0, 0, 0, 34, 0, + 0, 0, 35, 36, 0, 37, 0, 0, 0, 30, + 31, 0, 41, 0, 0, 0, 44, 0, 0, 33, + 0, 0, 0, 0, 0, 0, 34, 0, 0, 0, + 35, 36, 0, 37, 51, 48, 50, 49, 0, 52, + 502, 0, 0, 0, 44, 0, 0, 0, 0, 0, + 43, 54, 32, 0, 0, 0, 40, 0, 0, 0, + 0, 45, 51, 48, 50, 49, 0, 52, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 43, 54, + 32, 0, 0, 0, 40, 0, 0, 0, 0, 45, + 30, 31, 0, 0, 0, 0, 0, 0, 30, 31, + 33, 0, 0, 0, 0, 0, 0, 34, 33, 0, + 0, 35, 36, 0, 37, 34, 0, 0, 0, 35, + 36, 502, 37, 0, 0, 44, 0, 0, 0, 41, + 0, 0, 0, 44, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 51, 48, 50, 49, 0, 52, 0, + 0, 51, 48, 50, 49, 0, 52, 0, 0, 43, + 54, 32, 0, 0, 0, 40, 0, 43, 54, 32, + 45, 0, 0, 40, 0, 0, 0, 0, 45, 30, + 31, 0, 0, 0, 0, 0, 0, 30, 31, 33, + 0, 0, 0, 0, 0, 0, 34, 33, 0, 0, + 35, 36, 0, 37, 34, 0, 0, 0, 35, 36, + 41, 37, 0, 0, 44, 0, 0, 0, 502, 0, + 0, 0, 44, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 51, 48, 50, 49, 0, 52, 0, 0, + 51, 48, 50, 49, 0, 52, 0, 0, 43, 54, + 32, 0, 0, 0, 40, 0, 43, 54, 32, 45, + 0, 0, 40, 0, 0, 0, 0, 45, 0, 0, + 0, 0, 0, 0, 0, 0, 501, 0, 30, 31, + 0, 0, 0, 0, 0, 0, 0, 0, 215, 0, + 0, 0, 0, 0, 0, 34, 0, 0, 0, 35, + 36, 0, 37, 0, 0, 0, 0, 0, 0, 502, + 0, 0, 0, 44, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 51, 503, 505, 504, 0, 52, 0, 0, 0, + 0, 226, 0, 0, 0, 0, 0, 43, 54, 32, + 210, 0, 0, 40, 0, 0, 0, 0, 45, 0, + 0, 0, 0, 0, 0, 0, 0, 501, 0, 30, + 31, 0, 0, 0, 0, 0, 0, 0, 0, 215, + 0, 0, 0, 0, 0, 0, 34, 0, 0, 0, + 35, 36, 0, 37, 0, 0, 0, 0, 0, 0, + 502, 0, 0, 0, 44, 0, 0, 0, 0, 0, + 0, 0, 544, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 51, 503, 505, 504, 0, 52, 0, 0, + 0, 0, 226, 0, 0, 0, 0, 0, 43, 54, + 32, 210, 0, 0, 40, 0, 0, 0, 0, 45, + 0, 0, 0, 0, 0, 0, 0, 0, 501, 0, + 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, + 215, 0, 0, 0, 0, 0, 0, 34, 0, 0, + 0, 35, 36, 0, 37, 0, 0, 0, 0, 0, + 0, 502, 0, 0, 0, 44, 0, 0, 0, 0, + 0, 0, 0, 541, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 51, 503, 505, 504, 0, 52, 0, + 0, 0, 0, 226, 0, 0, 0, 0, 0, 43, + 54, 32, 210, 0, 0, 40, 0, 0, 0, 0, + 45, 0, 0, 0, 0, 0, 0, 0, 0, 29, + 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, + 33, 0, 0, 0, 0, 0, 0, 34, 0, 0, + 0, 35, 36, 0, 37, 0, 0, 0, 38, 0, + 39, 41, 42, 0, 0, 44, 0, 0, 0, 46, + 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 51, 48, 50, 49, 0, 52, 0, + 53, 0, 55, 0, 56, 0, 0, 0, 0, 43, + 54, 32, 0, 0, 0, 40, 0, 0, 0, 0, + 45, 0, 0, 0, 0, 0, 0, 0, 0, -121, + 0, 0, 0, 29, 30, 31, 0, 0, 0, 0, + 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, + 0, 34, 0, 0, 0, 35, 36, 0, 37, 0, + 0, 0, 38, 0, 39, 41, 42, 0, 0, 44, + 0, 0, 0, 46, 0, 47, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 51, 48, 50, + 49, 0, 52, 0, 53, 0, 55, 0, 56, 0, + 0, 0, 0, 43, 54, 32, 0, 0, 0, 40, + 0, 0, 0, 0, 45, 0, 0, 0, 0, 0, + 0, 0, 0, 29, 30, 31, 0, 0, 0, 0, + 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, + 0, 34, 0, 0, 0, 35, 36, 0, 37, 0, + 0, 0, 38, 0, 39, 41, 42, 0, 0, 44, + 0, 0, 0, 46, 0, 47, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 51, 48, 50, + 49, 0, 52, 0, 53, 0, 55, 271, 56, 0, + 0, 0, 0, 43, 54, 32, 0, 0, 0, 40, + 0, 0, 0, 0, 45, 0, 0, 0, 0, 0, + 0, 0, 0, 483, 0, 0, 29, 30, 31, 0, + 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, + 0, 0, 0, 0, 34, 0, 0, 0, 35, 36, + 0, 37, 0, 0, 0, 38, 0, 39, 41, 42, + 0, 0, 44, 0, 0, 0, 46, 0, 47, 0, + 0, 486, 0, 0, 0, 0, 0, 0, 0, 0, + 51, 48, 50, 49, 0, 52, 0, 53, 0, 55, + 0, 56, 0, 0, 0, 0, 43, 54, 32, 0, + 0, 0, 40, 0, 0, 0, 0, 45, 0, 0, + 0, 0, 0, 0, 0, 0, 475, 0, 0, 29, + 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, + 33, 0, 0, 0, 0, 0, 0, 34, 0, 0, + 0, 35, 36, 0, 37, 0, 0, 0, 38, 0, + 39, 41, 42, 0, 0, 44, 0, 0, 0, 46, + 0, 47, 0, 0, 481, 0, 0, 0, 0, 0, + 0, 0, 0, 51, 48, 50, 49, 0, 52, 0, + 53, 0, 55, 0, 56, 0, 0, 0, 0, 43, + 54, 32, 0, 0, 0, 40, 0, 0, 0, 0, + 45, 0, 0, 0, 0, 0, 0, 0, 0, 475, + 0, 0, 29, 30, 31, 0, 0, 0, 0, 0, + 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, + 34, 0, 0, 0, 35, 36, 0, 37, 0, 0, + 0, 38, 0, 39, 41, 42, 0, 0, 44, 0, + 0, 0, 46, 0, 47, 0, 0, 476, 0, 0, + 0, 0, 0, 0, 0, 0, 51, 48, 50, 49, + 0, 52, 0, 53, 0, 55, 0, 56, 0, 0, + 0, 0, 43, 54, 32, 0, 0, 0, 40, 0, + 0, 0, 0, 45, 0, 0, 0, 0, 0, 0, + 0, 0, 483, 0, 0, 29, 30, 31, 0, 0, + 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, + 0, 0, 0, 34, 0, 0, 0, 35, 36, 0, + 37, 0, 0, 0, 38, 0, 39, 41, 42, 0, + 0, 44, 0, 0, 0, 46, 0, 47, 0, 0, + 484, 0, 0, 0, 0, 0, 0, 0, 0, 51, + 48, 50, 49, 0, 52, 0, 53, 0, 55, 0, + 56, 0, 0, 0, 0, 43, 54, 32, 0, 0, + 0, 40, 0, 0, 0, 0, 45, 0, 0, 0, + 0, 0, 0, 0, 0, 29, 30, 31, 0, 0, + 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, + 0, 0, 0, 34, 217, 0, 0, 584, 585, 0, + 37, 0, 0, 0, 38, 0, 39, 41, 42, 0, + 0, 44, 0, 0, 0, 46, 0, 47, 0, 0, + 0, 0, 0, 0, 0, 221, 0, 0, 0, 51, + 48, 50, 49, 0, 52, 0, 53, 0, 55, 0, + 56, 0, 0, 0, 0, 43, 54, 32, 0, 0, + 0, 40, 0, 0, 0, 0, 45, 0, 0, 0, + 0, 0, 0, 0, 0, 109, 110, 111, 0, 0, + 113, 115, 116, 0, 0, 117, 0, 118, 0, 0, + 0, 120, 121, 122, 0, 0, 0, 0, 0, 0, + 34, 123, 124, 125, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 126, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 129, 0, 0, 0, 0, 0, 0, 48, 50, 49, + 130, 131, 132, 0, 134, 135, 136, 137, 138, 139, + 0, 0, 127, 133, 119, 112, 114, 128, 0, 0, + 0, 0, 0, 45, 0, 0, 0, 0, 0, 0, + 0, 0, 109, 110, 111, 0, 0, 113, 115, 116, + 0, 0, 117, 0, 118, 0, 0, 0, 120, 121, + 122, 0, 0, 0, 0, 0, 0, 393, 123, 124, + 125, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 126, 0, 0, 0, 394, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 129, 0, 0, + 0, 0, 0, 398, 395, 397, 0, 130, 131, 132, + 0, 134, 135, 136, 137, 138, 139, 0, 0, 127, + 133, 119, 112, 114, 128, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 109, + 110, 111, 0, 0, 113, 115, 116, 0, 0, 117, + 0, 118, 0, 0, 0, 120, 121, 122, 0, 0, + 0, 0, 0, 0, 393, 123, 124, 125, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 126, 0, + 0, 0, 394, 0, 0, 0, 0, 0, 0, 0, + 396, 0, 0, 0, 129, 0, 0, 0, 0, 0, + 398, 395, 397, 0, 130, 131, 132, 0, 134, 135, + 136, 137, 138, 139, 0, 0, 127, 133, 119, 112, + 114, 128, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 209, 0, 0, 0, + 0, 211, 0, 29, 30, 31, 213, 0, 0, 0, + 0, 0, 0, 214, 215, 0, 0, 0, 0, 0, + 0, 216, 217, 0, 0, 218, 36, 0, 37, 0, + 0, 0, 38, 0, 39, 41, 42, 0, 0, 44, + 0, 0, 0, 46, 0, 47, 0, 0, 0, 0, + 0, 220, 0, 221, 0, 0, 0, 51, 219, 222, + 49, 223, 52, 224, 53, 225, 55, 226, 56, 227, + 228, 0, 0, 43, 54, 32, 210, 212, 0, 40, + 0, 0, 0, 0, 45, 0, 0, 0, 0, 0, + 0, 0, 0, 209, 0, 0, 0, 0, 211, 0, + 29, 30, 31, 213, 0, 0, 0, 0, 0, 0, + 214, 33, 0, 0, 0, 0, 0, 0, 216, 217, + 0, 0, 218, 36, 0, 37, 0, 0, 0, 38, + 0, 39, 41, 42, 0, 0, 44, 0, 0, 0, + 46, 0, 47, 0, 0, 0, 0, 0, 220, 0, + 221, 0, 0, 0, 51, 219, 222, 49, 223, 52, + 224, 53, 225, 55, 226, 56, 227, 228, 0, 0, + 43, 54, 32, 210, 212, 0, 40, 0, 0, 0, + 0, 45, 0, 0, 0, 0, 0, 0, 0, 0, + 587, 110, 111, 0, 0, 589, 115, 591, 30, 31, + 592, 0, 118, 0, 0, 0, 120, 594, 595, 0, + 0, 0, 0, 0, 0, 596, 597, 124, 125, 218, + 36, 0, 37, 0, 0, 0, 38, 0, 39, 598, + 42, 0, 0, 600, 0, 0, 0, 46, 0, 47, + 0, 0, 0, 0, 0, 602, 0, 221, 0, 0, + 0, 604, 601, 603, 49, 605, 606, 607, 53, 609, + 610, 611, 612, 613, 614, 0, 0, 599, 608, 593, + 588, 590, 128, 40, 0, 0, 0, 0, 45, 0, + 0, 0, 0, 0, 0, 0, 0, 361, 110, 111, + 0, 0, 363, 115, 365, 30, 31, 366, 0, 118, + 0, 0, 0, 120, 368, 369, 0, 0, 0, 0, + 0, 0, 370, 371, 124, 125, 218, 36, 0, 37, + 0, 0, 0, 38, 0, 39, 372, 42, 0, 0, + 374, 0, 0, 0, 46, 0, 47, 0, -267, 0, + 0, 0, 376, 0, 221, 0, 0, 0, 378, 375, + 377, 49, 379, 380, 381, 53, 383, 384, 385, 386, + 387, 388, 0, 0, 373, 382, 367, 362, 364, 128, + 40, 0, 0, 0, 0, 45, 0, 0, 0, 0, + 0, 0, 0, 0, + + 472, 546, 528, 639, 311, 182, 302, 498, 514, 16, + 461, 515, 496, 182, 497, 519, 309, 436, 619, 243, + 358, 439, 576, 572, 253, 150, 571, 487, 617, 307, + 238, 250, 320, 628, 633, 555, 569, 560, 558, 642, + 186, 250, 425, 349, 358, 182, 351, 557, 433, 347, + 238, 344, 339, 429, 302, 402, 243, 445, 447, 456, + 460, 163, 454, 458, 243, 250, 485, 143, 148, 449, + 353, 526, 176, 467, 237, 450, 238, 415, 338, 188, + 410, 237, 302, 336, 436, 482, 334, 169, 439, 436, + 146, 417, 392, 439, 140, 522, 358, 400, 404, 0, + 390, 171, 358, 0, 186, 500, 146, 0, 643, 0, + 0, 178, 0, 0, 0, 0, 404, 60, 60, 489, + 452, 500, 320, 0, 534, 0, 405, 60, 0, 180, + 146, 60, 0, 180, 60, 407, 490, 60, 302, 452, + 60, 60, 60, 60, 405, 60, 284, 285, 287, 60, + 286, 451, 358, 60, 165, 180, 266, 60, 60, 461, + 451, 270, 288, 60, 60, 60, 493, 60, 60, 60, + 84, 106, 92, 91, 60, 432, 60, 72, 60, 168, + 98, 435, 77, 60, 96, 60, 60, 60, 341, 302, + 93, 94, 500, 108, 329, 100, 60, 102, 60, 618, + 302, 95, 88, 330, 60, 60, 60, 60, 90, 89, + 70, 60, 97, 452, 60, 60, 451, 492, 60, 60, + 494, 60, 61, 68, 60, 60, 69, 491, 60, 471, + 67, 302, 404, 480, 60, 106, 60, 479, 0, 270, + 298, 270, 298, 278, 298, 270, 0, 270, 60, 270, + 0, 316, 0, 270, 332, 0, 500, 108, 175, 538, + 405, 293, 319, 0, 317, 303, 326, 60, 60, 60, + 0, 0, 270, 270, 270, 290, 291, 60, 295, 298, + 60, 60, 270, 298, 270, 270, 270, 289, 270, 0, + 273, 500, 313, 0, 551, 545, 305, 534, 508, 615, + 542, 297, 0, 500, 0, 300, 499, 509, 500, 0, + 508, 0, 0, 0, 0, 508, 472, 0, 499, 509, + 583, 0, 0, 499, 509, 0, 0, 586, 579, 580, + 581, 582, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 550, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 551, + 0, 0, 0, 0, 0, 0, 552, 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 short QDeclarativeJSGrammar::action_check [] = { + 7, 16, 55, 7, 33, 36, 33, 55, 36, 7, + 7, 7, 7, 7, 7, 36, 7, 7, 36, 8, + 36, 55, 60, 29, 7, 7, 36, 7, 33, 7, + 7, 7, 7, 36, 36, 48, 1, 0, 90, 79, + 7, 33, 8, 90, 2, 7, 33, 55, 36, 60, + 36, 20, 33, 5, 1, 5, 79, 36, 7, 33, + 5, 60, 33, 33, 8, 33, 17, 24, 2, 7, + 34, 2, 7, 1, 7, 36, 60, 37, 8, 8, + 36, 66, 36, 7, 1, 17, -1, -1, 36, 48, + 36, 33, 8, 66, 7, 48, 36, 33, 7, 36, + 2, 7, 8, 8, 8, 77, 61, 15, 8, 8, + 6, -1, 31, 36, 8, 10, -1, -1, -1, 60, + 8, 60, -1, 48, 20, -1, 34, -1, 8, 8, + 8, -1, -1, 60, 60, 8, 79, 55, 60, 50, + 8, 61, 8, 54, 8, 8, 61, 61, 62, 61, + 62, 42, 56, 50, 40, 60, 56, 54, 79, 40, + 55, 60, 53, 61, 62, 51, 60, 15, 61, 62, + 51, 61, 62, 61, 61, 62, 56, 56, 61, 62, + 40, 8, 60, 8, 61, 62, 34, 60, 8, 61, + 62, 51, 60, 56, 60, 40, 60, 12, 29, 61, + 62, 25, 25, 27, 27, 15, 51, 36, 29, 15, + 29, 61, 62, 12, 38, 38, 8, 29, 7, 7, + 29, -1, 8, -1, 34, 8, 36, 25, 34, 27, + 36, 15, 61, 62, 61, 62, 61, 62, 7, 29, + 38, 91, 57, 7, 75, 33, 8, 25, 63, 27, + 34, -1, 36, -1, 75, 86, 75, 12, 57, -1, + 38, 15, -1, 75, 63, 86, 75, 86, 15, 61, + 62, -1, 61, 62, 86, 61, 62, 86, 61, 62, + 34, 25, 36, 27, -1, 75, 33, 34, 29, 36, + 18, 19, 61, 62, 38, 47, 86, 61, 62, 61, + 62, 15, 57, -1, 92, 18, 19, -1, 63, 61, + 62, 29, -1, 18, 19, 18, 19, 45, 46, 33, + 34, -1, 36, 29, -1, 66, 67, 68, -1, -1, + -1, -1, 45, 46, 29, -1, -1, -1, 29, 91, + 45, 46, 45, 46, 23, 24, 29, -1, 66, 67, + 68, 92, 29, 32, 23, 24, 35, 25, 37, 27, + 66, 67, 68, 32, -1, 29, 35, -1, 37, 29, + 38, 66, 67, 68, 92, 66, 67, 68, -1, -1, + 29, -1, -1, 66, 67, 68, 92, 29, -1, 66, + 67, 68, 29, -1, -1, -1, -1, 92, 29, -1, + -1, 92, 66, 67, 68, -1, 66, 67, 68, 92, + -1, -1, -1, -1, 29, 92, -1, 66, 67, 68, + 29, -1, -1, -1, 66, 67, 68, 29, 92, 66, + 67, 68, 92, 23, 24, 66, 67, 68, -1, -1, + -1, 29, 32, 92, -1, 35, -1, 37, 36, 29, + 92, 66, 67, 68, -1, 92, 36, 66, 67, 68, + -1, 92, 23, 24, 66, 67, 68, -1, -1, -1, + 31, 32, -1, -1, 35, -1, 37, 92, 66, 67, + 68, -1, -1, 92, 23, 24, 66, 67, 68, 29, + 92, -1, 31, 32, 23, 24, 35, 29, 37, -1, + -1, -1, 31, 32, 92, 29, 35, -1, 37, -1, + -1, 29, 92, -1, 93, 94, 95, 96, 97, 98, + -1, 61, 62, -1, 29, -1, 66, 67, 68, 61, + 62, -1, -1, 29, 66, 67, 68, 61, 62, -1, + -1, -1, 66, 67, 68, -1, -1, -1, 66, 67, + 68, -1, 92, -1, -1, -1, 61, 62, -1, -1, + 92, 66, 67, 68, -1, 61, 62, 29, 92, -1, + 66, 67, 68, -1, 92, -1, -1, -1, -1, -1, + -1, -1, -1, 23, 24, -1, -1, 92, 3, -1, + -1, 31, 32, -1, -1, 35, 92, 37, 13, 61, + 62, -1, 17, -1, 66, 67, 68, -1, -1, -1, + -1, 26, -1, 28, -1, -1, 31, -1, -1, -1, + -1, -1, -1, -1, 39, -1, 41, 42, -1, -1, + 92, -1, -1, -1, 49, -1, -1, 52, 53, -1, + -1, -1, -1, 58, -1, -1, -1, -1, -1, 64, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 12, + 13, 3, -1, -1, -1, 80, -1, -1, -1, 22, + -1, 13, -1, -1, -1, 17, 29, -1, -1, -1, + 33, 34, -1, 36, 26, -1, 28, -1, -1, -1, + 43, -1, -1, -1, 47, -1, -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, 12, 13, -1, 87, -1, -1, -1, 80, 92, + -1, 22, -1, -1, -1, -1, -1, -1, 29, -1, + -1, -1, 33, 34, -1, 36, -1, -1, -1, 12, + 13, -1, 43, -1, -1, -1, 47, -1, -1, 22, + -1, -1, -1, -1, -1, -1, 29, -1, -1, -1, + 33, 34, -1, 36, 65, 66, 67, 68, -1, 70, + 43, -1, -1, -1, 47, -1, -1, -1, -1, -1, + 81, 82, 83, -1, -1, -1, 87, -1, -1, -1, + -1, 92, 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, 92, + 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, + 92, -1, -1, 87, -1, -1, -1, -1, 92, 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, 92, + -1, -1, 87, -1, -1, -1, -1, 92, -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, 92, -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, 92, + -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, + 92, -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, + 92, -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, 92, -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, 92, -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, 92, -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, + 92, -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, 92, -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, 92, -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, 92, -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, 92, -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, -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, -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, 92, -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, 92, -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, 92, -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, 92, -1, -1, -1, -1, + -1, -1, -1, -1, + + 35, 15, 15, 15, 2, 15, 3, 2, 25, 3, + 15, 15, 104, 15, 4, 3, 3, 3, 19, 15, + 2, 21, 15, 15, 3, 67, 25, 3, 19, 2, + 15, 2, 15, 13, 15, 19, 25, 3, 15, 11, + 15, 2, 96, 3, 2, 15, 2, 25, 3, 2, + 15, 100, 15, 93, 3, 2, 15, 98, 15, 2, + 2, 35, 3, 3, 15, 2, 35, 35, 35, 21, + 2, 25, 3, 35, 4, 21, 15, 2, 2, 15, + 2, 4, 3, 3, 3, 35, 2, 35, 21, 3, + 35, 3, 36, 21, 3, 13, 2, 35, 13, -1, + 35, 35, 2, -1, 15, 13, 35, -1, 16, -1, + -1, 40, -1, -1, -1, -1, 13, 44, 44, 46, + 46, 13, 15, -1, 16, -1, 41, 44, -1, 46, + 35, 44, -1, 46, 44, 40, 46, 44, 3, 46, + 44, 44, 44, 44, 41, 44, 49, 49, 49, 44, + 49, 46, 2, 44, 58, 46, 44, 44, 44, 15, + 46, 49, 49, 44, 44, 44, 46, 44, 44, 44, + 49, 15, 49, 49, 44, 81, 44, 52, 44, 60, + 50, 81, 50, 44, 50, 44, 44, 44, 99, 3, + 49, 49, 13, 37, 87, 56, 44, 54, 44, 20, + 3, 49, 48, 68, 44, 44, 44, 44, 48, 48, + 47, 44, 50, 46, 44, 44, 46, 46, 44, 44, + 46, 44, 47, 46, 44, 44, 46, 46, 44, 85, + 46, 3, 13, 31, 44, 15, 44, 35, -1, 49, + 44, 49, 44, 51, 44, 49, -1, 49, 44, 49, + -1, 61, -1, 49, 68, -1, 13, 37, 38, 16, + 41, 57, 66, -1, 66, 68, 66, 44, 44, 44, + -1, -1, 49, 49, 49, 51, 51, 44, 55, 44, + 44, 44, 49, 44, 49, 49, 49, 51, 49, -1, + 53, 13, 59, -1, 13, 5, 68, 16, 20, 18, + 5, 66, -1, 13, -1, 66, 28, 29, 13, -1, + 20, -1, -1, -1, -1, 20, 35, -1, 28, 29, + 13, -1, -1, 28, 29, -1, -1, 20, 21, 22, + 23, 24, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 3, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 13, + -1, -1, -1, -1, -1, -1, 20, -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}; + +QT_END_NAMESPACE diff --git a/src/declarative/qml/parser/qdeclarativejsgrammar_p.h b/src/declarative/qml/parser/qdeclarativejsgrammar_p.h new file mode 100644 index 00000000..70570766 --- /dev/null +++ b/src/declarative/qml/parser/qdeclarativejsgrammar_p.h @@ -0,0 +1,210 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $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. +// + +// This file was generated by qlalr - DO NOT EDIT! +#ifndef QDECLARATIVEJSGRAMMAR_P_H +#define QDECLARATIVEJSGRAMMAR_P_H + +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeJSGrammar +{ +public: + enum VariousConstants { + EOF_SYMBOL = 0, + REDUCE_HERE = 100, + SHIFT_THERE = 99, + 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 = 96, + T_FEED_JS_PROGRAM = 98, + T_FEED_JS_SOURCE_ELEMENT = 97, + T_FEED_JS_STATEMENT = 95, + T_FEED_UI_OBJECT_MEMBER = 94, + T_FEED_UI_PROGRAM = 93, + 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_ON = 92, + 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 = 645, + RULE_COUNT = 347, + STATE_COUNT = 646, + TERMINAL_COUNT = 101, + NON_TERMINAL_COUNT = 106, + + GOTO_INDEX_OFFSET = 646, + GOTO_INFO_OFFSET = 2714, + GOTO_CHECK_OFFSET = 2714 + }; + + static const char *const spell []; + static const short lhs []; + static const short rhs []; + static const short goto_default []; + static const short action_default []; + static const short action_index []; + static const short action_info []; + static const short action_check []; + + static inline int nt_action (int state, int nt) + { + const int yyn = action_index [GOTO_INDEX_OFFSET + state] + nt; + if (yyn < 0 || action_check [GOTO_CHECK_OFFSET + yyn] != nt) + return goto_default [nt]; + + return action_info [GOTO_INFO_OFFSET + 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]; + } +}; + + +QT_END_NAMESPACE +#endif // QDECLARATIVEJSGRAMMAR_P_H + diff --git a/src/declarative/qml/parser/qdeclarativejslexer.cpp b/src/declarative/qml/parser/qdeclarativejslexer.cpp new file mode 100644 index 00000000..0fa18be7 --- /dev/null +++ b/src/declarative/qml/parser/qdeclarativejslexer.cpp @@ -0,0 +1,1258 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "private/qdeclarativejslexer_p.h" + +#include "private/qdeclarativejsglobal_p.h" +#include "private/qdeclarativejsengine_p.h" +#include "private/qdeclarativejsgrammar_p.h" + +#include + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE +Q_CORE_EXPORT double qstrtod(const char *s00, char const **se, bool *ok); +QT_END_NAMESPACE + +QT_QML_BEGIN_NAMESPACE + +#define shiftWindowsLineBreak() \ + do { \ + if (((current == '\r') && (next1 == '\n')) \ + || ((current == '\n') && (next1 == '\r'))) { \ + shift(1); \ + } \ + } \ + while (0) + +namespace QDeclarativeJS { +extern double integerFromString(const char *buf, int size, int radix); +} + +using namespace QDeclarativeJS; + +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) +{ + if (driver) 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.clear(); + 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 QDeclarativeJSGrammar::T_DO; + else if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('f')) + return QDeclarativeJSGrammar::T_IF; + else if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('n')) + return QDeclarativeJSGrammar::T_IN; + else if (c[0] == QLatin1Char('a') && c[1] == QLatin1Char('s')) + return QDeclarativeJSGrammar::T_AS; + else if (c[0] == QLatin1Char('o') && c[1] == QLatin1Char('n')) + return QDeclarativeJSGrammar::T_ON; + } break; + + case 3: { + if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('o') && c[2] == QLatin1Char('r')) + return QDeclarativeJSGrammar::T_FOR; + else if (c[0] == QLatin1Char('n') && c[1] == QLatin1Char('e') && c[2] == QLatin1Char('w')) + return QDeclarativeJSGrammar::T_NEW; + else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('r') && c[2] == QLatin1Char('y')) + return QDeclarativeJSGrammar::T_TRY; + else if (c[0] == QLatin1Char('v') && c[1] == QLatin1Char('a') && c[2] == QLatin1Char('r')) + return QDeclarativeJSGrammar::T_VAR; + else if (check_reserved) { + if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('n') && c[2] == QLatin1Char('t')) + return QDeclarativeJSGrammar::T_RESERVED_WORD; + } + } break; + + case 4: { + if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('a') + && c[2] == QLatin1Char('s') && c[3] == QLatin1Char('e')) + return QDeclarativeJSGrammar::T_CASE; + else if (c[0] == QLatin1Char('e') && c[1] == QLatin1Char('l') + && c[2] == QLatin1Char('s') && c[3] == QLatin1Char('e')) + return QDeclarativeJSGrammar::T_ELSE; + else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('h') + && c[2] == QLatin1Char('i') && c[3] == QLatin1Char('s')) + return QDeclarativeJSGrammar::T_THIS; + else if (c[0] == QLatin1Char('v') && c[1] == QLatin1Char('o') + && c[2] == QLatin1Char('i') && c[3] == QLatin1Char('d')) + return QDeclarativeJSGrammar::T_VOID; + else if (c[0] == QLatin1Char('w') && c[1] == QLatin1Char('i') + && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('h')) + return QDeclarativeJSGrammar::T_WITH; + else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('r') + && c[2] == QLatin1Char('u') && c[3] == QLatin1Char('e')) + return QDeclarativeJSGrammar::T_TRUE; + else if (c[0] == QLatin1Char('n') && c[1] == QLatin1Char('u') + && c[2] == QLatin1Char('l') && c[3] == QLatin1Char('l')) + return QDeclarativeJSGrammar::T_NULL; + else if (check_reserved) { + if (c[0] == QLatin1Char('e') && c[1] == QLatin1Char('n') + && c[2] == QLatin1Char('u') && c[3] == QLatin1Char('m')) + return QDeclarativeJSGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('b') && c[1] == QLatin1Char('y') + && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('e')) + return QDeclarativeJSGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('l') && c[1] == QLatin1Char('o') + && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('g')) + return QDeclarativeJSGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('h') + && c[2] == QLatin1Char('a') && c[3] == QLatin1Char('r')) + return QDeclarativeJSGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('g') && c[1] == QLatin1Char('o') + && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('o')) + return QDeclarativeJSGrammar::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 QDeclarativeJSGrammar::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 QDeclarativeJSGrammar::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 QDeclarativeJSGrammar::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 QDeclarativeJSGrammar::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 QDeclarativeJSGrammar::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 QDeclarativeJSGrammar::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 QDeclarativeJSGrammar::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 QDeclarativeJSGrammar::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 QDeclarativeJSGrammar::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 QDeclarativeJSGrammar::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 QDeclarativeJSGrammar::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 QDeclarativeJSGrammar::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 QDeclarativeJSGrammar::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 QDeclarativeJSGrammar::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 QDeclarativeJSGrammar::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 QDeclarativeJSGrammar::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 QDeclarativeJSGrammar::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 QDeclarativeJSGrammar::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 QDeclarativeJSGrammar::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 QDeclarativeJSGrammar::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 QDeclarativeJSGrammar::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 QDeclarativeJSGrammar::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 QDeclarativeJSGrammar::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 QDeclarativeJSGrammar::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 QDeclarativeJSGrammar::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 QDeclarativeJSGrammar::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 QDeclarativeJSGrammar::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 QDeclarativeJSGrammar::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 QDeclarativeJSGrammar::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 QDeclarativeJSGrammar::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 QDeclarativeJSGrammar::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 QDeclarativeJSGrammar::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 QDeclarativeJSGrammar::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 QDeclarativeJSGrammar::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 QDeclarativeJSGrammar::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 QDeclarativeJSGrammar::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 QDeclarativeJSGrammar::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 QDeclarativeJSGrammar::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 QDeclarativeJSGrammar::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 QDeclarativeJSGrammar::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 QDeclarativeJSGrammar::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 QDeclarativeJSGrammar::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 QDeclarativeJSGrammar::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; + } + + bool identifierWithEscapedUnicode = false; + + 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 = QDeclarativeJSGrammar::T_SEMICOLON; + stackToken = 0; + setDone(Other); + } else { + setDone(Eof); + } + } else if (isLineTerminator()) { + if (restrKeyword) { + // automatic semicolon insertion + recordStartPos(); + token = QDeclarativeJSGrammar::T_SEMICOLON; + setDone(Other); + } else { + shiftWindowsLineBreak(); + yylineno++; + yycolumn = 0; + bol = true; + terminator = true; + syncProhibitAutomaticSemicolon(); + } + } else if (current == '"' || current == '\'') { + recordStartPos(); + state = InString; + multiLineString = false; + stringType = current; + } else if (current == '\\' && next1 == 'u') { + identifierWithEscapedUnicode = true; + recordStartPos(); + + shift(2); // skip the unicode escape prefix `\u' + + if (isHexDigit(current) && isHexDigit(next1) && + isHexDigit(next2) && isHexDigit(next3)) { + record16(convertUnicode(current, next1, next2, next3)); + shift(3); + state = InIdentifier; + } else { + setDone(Bad); + err = IllegalUnicodeEscapeSequence; + errmsg = QCoreApplication::translate("QDeclarativeParser", "Illegal unicode escape sequence"); + break; + } + + } else if (isIdentLetter(current)) { + identifierWithEscapedUnicode = false; + 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 == QDeclarativeJSGrammar::T_PLUS_PLUS + || token == QDeclarativeJSGrammar::T_MINUS_MINUS)) { + // automatic semicolon insertion + stackToken = token; + token = QDeclarativeJSGrammar::T_SEMICOLON; + } + setDone(Other); + } + else { + setDone(Bad); + err = IllegalCharacter; + errmsg = QCoreApplication::translate("QDeclarativeParser", "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 = QCoreApplication::translate("QDeclarativeParser", "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 = QCoreApplication::translate("QDeclarativeParser", "Illegal escape sequence"); + } + } 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 = QCoreApplication::translate("QDeclarativeParser", "Illegal unicode escape sequence"); + } + break; + case InSingleLineComment: + if (isLineTerminator()) { + shiftWindowsLineBreak(); + yylineno++; + yycolumn = 0; + terminator = true; + bol = true; + if (restrKeyword) { + token = QDeclarativeJSGrammar::T_SEMICOLON; + setDone(Other); + } else + state = Start; + if (driver) driver->addComment(startpos+2, tokenLength()-2, startlineno, startcolumn+2); + } else if (current == 0) { + if (driver) driver->addComment(startpos+2, tokenLength()-2, startlineno, startcolumn+2); + setDone(Eof); + } + + break; + case InMultiLineComment: + if (current == 0) { + setDone(Bad); + err = UnclosedComment; + errmsg = QCoreApplication::translate("QDeclarativeParser", "Unclosed comment at end of file"); + if (driver) driver->addComment(startpos+2, tokenLength()-2, startlineno, startcolumn+2); + } else if (isLineTerminator()) { + shiftWindowsLineBreak(); + yylineno++; + } else if (current == '*' && next1 == '/') { + state = Start; + shift(1); + if (driver) driver->addComment(startpos+2, tokenLength()-3, startlineno, startcolumn+2); + } + + break; + case InIdentifier: + if (isIdentLetter(current) || isDecimalDigit(current)) { + record16(current); + break; + } else if (current == '\\' && next1 == 'u') { + identifierWithEscapedUnicode = true; + shift(2); // skip the unicode escape prefix `\u' + + if (isHexDigit(current) && isHexDigit(next1) && + isHexDigit(next2) && isHexDigit(next3)) { + record16(convertUnicode(current, next1, next2, next3)); + shift(3); + break; + } else { + setDone(Bad); + err = IllegalUnicodeEscapeSequence; + errmsg = QCoreApplication::translate("QDeclarativeParser", "Illegal unicode escape sequence"); + 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 = QCoreApplication::translate("QDeclarativeParser", "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 = QCoreApplication::translate("QDeclarativeParser", "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 == QDeclarativeJSGrammar::T_RPAREN) { + --parenthesesCount; + if (parenthesesCount == 0) + parenthesesState = BalancedParentheses; + } else if (token == QDeclarativeJSGrammar::T_LPAREN) { + ++parenthesesCount; + } + break; + case BalancedParentheses: + parenthesesState = IgnoreParentheses; + break; + } + + switch (state) { + case Eof: + return 0; + case Other: + if (token == QDeclarativeJSGrammar::T_RBRACE || token == QDeclarativeJSGrammar::T_SEMICOLON) + delimited = true; + return token; + case Identifier: + token = -1; + if (! identifierWithEscapedUnicode) + token = findReservedWord(buffer16, pos16); + + if (token < 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 QDeclarativeJSGrammar::T_IDENTIFIER; + } + if (token == QDeclarativeJSGrammar::T_CONTINUE || token == QDeclarativeJSGrammar::T_BREAK + || token == QDeclarativeJSGrammar::T_RETURN || token == QDeclarativeJSGrammar::T_THROW) { + restrKeyword = true; + } else if (token == QDeclarativeJSGrammar::T_IF || token == QDeclarativeJSGrammar::T_FOR + || token == QDeclarativeJSGrammar::T_WHILE || token == QDeclarativeJSGrammar::T_WITH) { + parenthesesState = CountParentheses; + parenthesesCount = 0; + } else if (token == QDeclarativeJSGrammar::T_DO) { + parenthesesState = BalancedParentheses; + } + return token; + case String: + if (driver) + qsyylval.ustr = driver->intern(buffer16, pos16); + else + qsyylval.ustr = 0; + return multiLineString?QDeclarativeJSGrammar::T_MULTILINE_STRING_LITERAL:QDeclarativeJSGrammar::T_STRING_LITERAL; + case Number: + qsyylval.dval = dval; + return QDeclarativeJSGrammar::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) +{ + // ASCII-biased, since all reserved words are ASCII, aand hence the + // bulk of content to be parsed. + if ((c >= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z') + || c == '$' + || c == '_') + return true; + if (c < 128) + return false; + return QChar(c).isLetterOrNumber(); +} + +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 QDeclarativeJSGrammar::T_GT_GT_GT_EQ; + } else if (c1 == '=' && c2 == '=' && c3 == '=') { + shift(3); + return QDeclarativeJSGrammar::T_EQ_EQ_EQ; + } else if (c1 == '!' && c2 == '=' && c3 == '=') { + shift(3); + return QDeclarativeJSGrammar::T_NOT_EQ_EQ; + } else if (c1 == '>' && c2 == '>' && c3 == '>') { + shift(3); + return QDeclarativeJSGrammar::T_GT_GT_GT; + } else if (c1 == '<' && c2 == '<' && c3 == '=') { + shift(3); + return QDeclarativeJSGrammar::T_LT_LT_EQ; + } else if (c1 == '>' && c2 == '>' && c3 == '=') { + shift(3); + return QDeclarativeJSGrammar::T_GT_GT_EQ; + } else if (c1 == '<' && c2 == '=') { + shift(2); + return QDeclarativeJSGrammar::T_LE; + } else if (c1 == '>' && c2 == '=') { + shift(2); + return QDeclarativeJSGrammar::T_GE; + } else if (c1 == '!' && c2 == '=') { + shift(2); + return QDeclarativeJSGrammar::T_NOT_EQ; + } else if (c1 == '+' && c2 == '+') { + shift(2); + return QDeclarativeJSGrammar::T_PLUS_PLUS; + } else if (c1 == '-' && c2 == '-') { + shift(2); + return QDeclarativeJSGrammar::T_MINUS_MINUS; + } else if (c1 == '=' && c2 == '=') { + shift(2); + return QDeclarativeJSGrammar::T_EQ_EQ; + } else if (c1 == '+' && c2 == '=') { + shift(2); + return QDeclarativeJSGrammar::T_PLUS_EQ; + } else if (c1 == '-' && c2 == '=') { + shift(2); + return QDeclarativeJSGrammar::T_MINUS_EQ; + } else if (c1 == '*' && c2 == '=') { + shift(2); + return QDeclarativeJSGrammar::T_STAR_EQ; + } else if (c1 == '/' && c2 == '=') { + shift(2); + return QDeclarativeJSGrammar::T_DIVIDE_EQ; + } else if (c1 == '&' && c2 == '=') { + shift(2); + return QDeclarativeJSGrammar::T_AND_EQ; + } else if (c1 == '^' && c2 == '=') { + shift(2); + return QDeclarativeJSGrammar::T_XOR_EQ; + } else if (c1 == '%' && c2 == '=') { + shift(2); + return QDeclarativeJSGrammar::T_REMAINDER_EQ; + } else if (c1 == '|' && c2 == '=') { + shift(2); + return QDeclarativeJSGrammar::T_OR_EQ; + } else if (c1 == '<' && c2 == '<') { + shift(2); + return QDeclarativeJSGrammar::T_LT_LT; + } else if (c1 == '>' && c2 == '>') { + shift(2); + return QDeclarativeJSGrammar::T_GT_GT; + } else if (c1 == '&' && c2 == '&') { + shift(2); + return QDeclarativeJSGrammar::T_AND_AND; + } else if (c1 == '|' && c2 == '|') { + shift(2); + return QDeclarativeJSGrammar::T_OR_OR; + } + + switch(c1) { + case '=': shift(1); return QDeclarativeJSGrammar::T_EQ; + case '>': shift(1); return QDeclarativeJSGrammar::T_GT; + case '<': shift(1); return QDeclarativeJSGrammar::T_LT; + case ',': shift(1); return QDeclarativeJSGrammar::T_COMMA; + case '!': shift(1); return QDeclarativeJSGrammar::T_NOT; + case '~': shift(1); return QDeclarativeJSGrammar::T_TILDE; + case '?': shift(1); return QDeclarativeJSGrammar::T_QUESTION; + case ':': shift(1); return QDeclarativeJSGrammar::T_COLON; + case '.': shift(1); return QDeclarativeJSGrammar::T_DOT; + case '+': shift(1); return QDeclarativeJSGrammar::T_PLUS; + case '-': shift(1); return QDeclarativeJSGrammar::T_MINUS; + case '*': shift(1); return QDeclarativeJSGrammar::T_STAR; + case '/': shift(1); return QDeclarativeJSGrammar::T_DIVIDE_; + case '&': shift(1); return QDeclarativeJSGrammar::T_AND; + case '|': shift(1); return QDeclarativeJSGrammar::T_OR; + case '^': shift(1); return QDeclarativeJSGrammar::T_XOR; + case '%': shift(1); return QDeclarativeJSGrammar::T_REMAINDER; + case '(': shift(1); return QDeclarativeJSGrammar::T_LPAREN; + case ')': shift(1); return QDeclarativeJSGrammar::T_RPAREN; + case '{': shift(1); return QDeclarativeJSGrammar::T_LBRACE; + case '}': shift(1); return QDeclarativeJSGrammar::T_RBRACE; + case '[': shift(1); return QDeclarativeJSGrammar::T_LBRACKET; + case ']': shift(1); return QDeclarativeJSGrammar::T_RBRACKET; + case ';': shift(1); return QDeclarativeJSGrammar::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; + pattern = 0; + + if (prefix == EqualPrefix) + record16(QLatin1Char('=')); + + while (true) { + switch (current) { + + case 0: // eof + case '\n': case '\r': // line terminator + errmsg = QCoreApplication::translate("QDeclarativeParser", "Unterminated regular expression literal"); + return false; + + case '/': + shift(1); + + if (driver) // create the pattern + pattern = driver->intern(buffer16, pos16); + + // scan the flags + pos16 = 0; + flags = 0; + while (isIdentLetter(current)) { + int flag = Ecma::RegExp::flagFromChar(current); + if (flag == 0) { + errmsg = QCoreApplication::translate("QDeclarativeParser", "Invalid regular expression flag '%0'") + .arg(QChar(current)); + return false; + } + flags |= flag; + record16(current); + shift(1); + } + return true; + + case '\\': + // regular expression backslash sequence + record16(current); + shift(1); + + if (! current || isLineTerminator()) { + errmsg = QCoreApplication::translate("QDeclarativeParser", "Unterminated regular expression backslash sequence"); + return false; + } + + record16(current); + shift(1); + break; + + case '[': + // regular expression class + record16(current); + shift(1); + + while (current && ! isLineTerminator()) { + if (current == ']') + break; + else if (current == '\\') { + // regular expression backslash sequence + record16(current); + shift(1); + + if (! current || isLineTerminator()) { + errmsg = QCoreApplication::translate("QDeclarativeParser", "Unterminated regular expression backslash sequence"); + return false; + } + + record16(current); + shift(1); + } else { + record16(current); + shift(1); + } + } + + if (current != ']') { + errmsg = QCoreApplication::translate("QDeclarativeParser", "Unterminated regular expression class"); + return false; + } + + record16(current); + shift(1); // skip ] + break; + + default: + record16(current); + shift(1); + } // switch + } // while + + return false; +} + +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/declarative/qml/parser/qdeclarativejslexer_p.h b/src/declarative/qml/parser/qdeclarativejslexer_p.h new file mode 100644 index 00000000..10621e54 --- /dev/null +++ b/src/declarative/qml/parser/qdeclarativejslexer_p.h @@ -0,0 +1,249 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEJSLEXER_P_H +#define QDECLARATIVEJSLEXER_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 "private/qdeclarativejsglobal_p.h" + +#include + +QT_QML_BEGIN_NAMESPACE + +namespace QDeclarativeJS { + +class Engine; +class NameId; + +class QML_PARSER_EXPORT 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 QDeclarativeJS + +QT_QML_END_NAMESPACE + +#endif diff --git a/src/declarative/qml/parser/qdeclarativejsmemorypool_p.h b/src/declarative/qml/parser/qdeclarativejsmemorypool_p.h new file mode 100644 index 00000000..79941ecc --- /dev/null +++ b/src/declarative/qml/parser/qdeclarativejsmemorypool_p.h @@ -0,0 +1,133 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEJSMEMORYPOOL_P_H +#define QDECLARATIVEJSMEMORYPOOL_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 "private/qdeclarativejsglobal_p.h" + +#include +#include + +#include + +QT_QML_BEGIN_NAMESPACE + +namespace QDeclarativeJS { + +class QML_PARSER_EXPORT 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(qRealloc(m_storage, sizeof(char*) * (1 + m_blockIndex))); + m_currentBlock = m_storage[m_blockIndex] = reinterpret_cast(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 + (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 QDeclarativeJS + +QT_QML_END_NAMESPACE + +#endif diff --git a/src/declarative/qml/parser/qdeclarativejsnodepool_p.h b/src/declarative/qml/parser/qdeclarativejsnodepool_p.h new file mode 100644 index 00000000..f9160bd4 --- /dev/null +++ b/src/declarative/qml/parser/qdeclarativejsnodepool_p.h @@ -0,0 +1,139 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEJSNODEPOOL_P_H +#define QDECLARATIVEJSNODEPOOL_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 "private/qdeclarativejsglobal_p.h" +#include "private/qdeclarativejsmemorypool_p.h" + +#include +#include + +QT_QML_BEGIN_NAMESPACE + +namespace QDeclarativeJS { + +namespace AST { +class Node; +} // namespace AST + +class Code; +class CompilationUnit; +class Engine; + +template +inline NodeType *makeAstNode(MemoryPool *storage) +{ + NodeType *node = new (storage->allocate(sizeof(NodeType))) NodeType(); + return node; +} + +template +inline NodeType *makeAstNode(MemoryPool *storage, Arg1 arg1) +{ + NodeType *node = new (storage->allocate(sizeof(NodeType))) NodeType(arg1); + return node; +} + +template +inline NodeType *makeAstNode(MemoryPool *storage, Arg1 arg1, Arg2 arg2) +{ + NodeType *node = new (storage->allocate(sizeof(NodeType))) NodeType(arg1, arg2); + return node; +} + +template +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 +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 QML_PARSER_EXPORT 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 m_codeCache; + QString m_fileName; + Engine *m_engine; +#ifndef J_SCRIPT_NO_EVENT_NOTIFY + qint64 m_id; +#endif + +private: + Q_DISABLE_COPY(NodePool) +}; + +} // namespace QDeclarativeJS + +QT_QML_END_NAMESPACE + +#endif diff --git a/src/declarative/qml/parser/qdeclarativejsparser.cpp b/src/declarative/qml/parser/qdeclarativejsparser.cpp new file mode 100644 index 00000000..da2dd7e5 --- /dev/null +++ b/src/declarative/qml/parser/qdeclarativejsparser.cpp @@ -0,0 +1,1904 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include + +#include "private/qdeclarativejsengine_p.h" +#include "private/qdeclarativejslexer_p.h" +#include "private/qdeclarativejsast_p.h" +#include "private/qdeclarativejsnodepool_p.h" + + + +#include "private/qdeclarativejsparser_p.h" +#include + +// +// This file is automatically generated from qmljs.g. +// Changes will be lost. +// + +using namespace QDeclarativeJS; + +QT_QML_BEGIN_NAMESPACE + +void Parser::reallocateStack() +{ + if (! stack_size) + stack_size = 128; + else + stack_size <<= 1; + + sym_stack = reinterpret_cast (qRealloc(sym_stack, stack_size * sizeof(Value))); + state_stack = reinterpret_cast (qRealloc(state_stack, stack_size * sizeof(int))); + location_stack = reinterpret_cast (qRealloc(location_stack, stack_size * sizeof(AST::SourceLocation))); +} + +inline static bool automatic(Engine *driver, int token) +{ + return token == QDeclarativeJSGrammar::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 nameIds; + QVarLengthArray locations; + + AST::ExpressionNode *it = expr; + while (AST::FieldMemberExpression *m = AST::cast(it)) { + nameIds.append(m->name); + locations.append(m->identifierToken); + it = m->base; + } + + if (AST::IdentifierExpression *idExpr = AST::cast(it)) { + AST::UiQualifiedId *q = makeAstNode(driver->nodePool(), idExpr->name); + q->identifierToken = idExpr->identifierToken; + + AST::UiQualifiedId *currentId = q; + for (int i = nameIds.size() - 1; i != -1; --i) { + currentId = makeAstNode(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).Node = sym(2).Node; + program = sym(1).Node; +} break; + +case 5: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; + +case 6: { + sym(1).UiProgram = makeAstNode (driver->nodePool(), sym(1).UiImportList, + sym(2).UiObjectMemberList->finish()); +} break; + +case 8: { + sym(1).Node = sym(1).UiImportList->finish(); +} break; + +case 9: { + sym(1).Node = makeAstNode (driver->nodePool(), sym(1).UiImport); +} break; + +case 10: { + sym(1).Node = makeAstNode (driver->nodePool(), + sym(1).UiImportList, sym(2).UiImport); +} break; + +case 13: { + sym(1).UiImport->semicolonToken = loc(2); +} break; + +case 15: { + sym(1).UiImport->versionToken = loc(2); + sym(1).UiImport->semicolonToken = loc(3); +} break; + +case 17: { + 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 19: { + 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 20: { + AST::UiImport *node = 0; + + if (AST::StringLiteral *importIdLiteral = AST::cast(sym(2).Expression)) { + node = makeAstNode(driver->nodePool(), importIdLiteral->value); + node->fileNameToken = loc(2); + } else if (AST::UiQualifiedId *qualifiedId = reparseAsQualifiedId(sym(2).Expression)) { + node = makeAstNode(driver->nodePool(), qualifiedId); + node->fileNameToken = loc(2); + } + + sym(1).Node = node; + + if (node) { + node->importToken = loc(1); + } else { + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1), + QLatin1String("Expected a qualified name id or a string literal"))); + + return false; // ### remove me + } +} break; + +case 21: { + sym(1).Node = 0; +} break; + +case 22: { + sym(1).Node = makeAstNode (driver->nodePool(), sym(1).UiObjectMember); +} break; + +case 23: { + sym(1).Node = makeAstNode (driver->nodePool(), sym(1).UiObjectMember); +} break; + +case 24: { + AST::UiObjectMemberList *node = makeAstNode (driver->nodePool(), + sym(1).UiObjectMemberList, sym(2).UiObjectMember); + sym(1).Node = node; +} break; + +case 25: { + sym(1).Node = makeAstNode (driver->nodePool(), sym(1).UiObjectMember); +} break; + +case 26: { + AST::UiArrayMemberList *node = makeAstNode (driver->nodePool(), + sym(1).UiArrayMemberList, sym(3).UiObjectMember); + node->commaToken = loc(2); + sym(1).Node = node; +} break; + +case 27: { + AST::UiObjectInitializer *node = makeAstNode (driver->nodePool(), (AST::UiObjectMemberList*)0); + node->lbraceToken = loc(1); + node->rbraceToken = loc(2); + sym(1).Node = node; +} break; + +case 28: { + AST::UiObjectInitializer *node = makeAstNode (driver->nodePool(), sym(2).UiObjectMemberList->finish()); + node->lbraceToken = loc(1); + node->rbraceToken = loc(3); + sym(1).Node = node; +} break; + +case 29: { + AST::UiObjectDefinition *node = makeAstNode (driver->nodePool(), sym(1).UiQualifiedId, + sym(2).UiObjectInitializer); + sym(1).Node = node; +} break; + +case 31: { + AST::UiArrayBinding *node = makeAstNode (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 32: { + AST::UiObjectBinding *node = makeAstNode (driver->nodePool(), + sym(1).UiQualifiedId, sym(3).UiQualifiedId, sym(4).UiObjectInitializer); + node->colonToken = loc(2); + sym(1).Node = node; +} break; + +case 33: { + AST::UiObjectBinding *node = makeAstNode (driver->nodePool(), + sym(3).UiQualifiedId, sym(1).UiQualifiedId, sym(4).UiObjectInitializer); + node->colonToken = loc(2); + node->hasOnToken = true; + sym(1).Node = node; +} break; +case 34:case 35:case 36:case 37: +{ + AST::UiScriptBinding *node = makeAstNode (driver->nodePool(), + sym(1).UiQualifiedId, sym(3).Statement); + node->colonToken = loc(2); + sym(1).Node = node; +} break; + +case 38: + +case 39: { + sym(1).sval = driver->intern(lexer->characterBuffer(), lexer->characterCount()); + break; +} + +case 41: { + sym(1).Node = 0; +} break; + +case 42: { + sym(1).Node = sym(1).UiParameterList->finish (); +} break; + +case 43: { + AST::UiParameterList *node = makeAstNode (driver->nodePool(), sym(1).sval, sym(2).sval); + node->identifierToken = loc(2); + sym(1).Node = node; +} break; + +case 44: { + AST::UiParameterList *node = makeAstNode (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 46: { + AST::UiPublicMember *node = makeAstNode (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 48: { + AST::UiPublicMember *node = makeAstNode (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 50: { + AST::UiPublicMember *node = makeAstNode (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 52: { + AST::UiPublicMember *node = makeAstNode (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 54: { + AST::UiPublicMember *node = makeAstNode (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 56: { + AST::UiPublicMember *node = makeAstNode (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 58: { + AST::UiPublicMember *node = makeAstNode (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 60: { + AST::UiPublicMember *node = makeAstNode (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 61: { + AST::UiPublicMember *node = makeAstNode (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); // insert a fake ';' before ':' + + AST::UiQualifiedId *propertyName = makeAstNode(driver->nodePool(), sym(6).sval); + propertyName->identifierToken = loc(6); + propertyName->next = 0; + + AST::UiArrayBinding *binding = makeAstNode (driver->nodePool(), + propertyName, sym(9).UiArrayMemberList->finish()); + binding->colonToken = loc(7); + binding->lbracketToken = loc(8); + binding->rbracketToken = loc(10); + + node->binding = binding; + + sym(1).Node = node; +} break; + +case 62: { + AST::UiPublicMember *node = makeAstNode (driver->nodePool(), sym(2).sval, sym(3).sval); + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(3); + node->semicolonToken = loc(4); // insert a fake ';' before ':' + + AST::UiQualifiedId *propertyName = makeAstNode(driver->nodePool(), sym(3).sval); + propertyName->identifierToken = loc(3); + propertyName->next = 0; + + AST::UiObjectBinding *binding = makeAstNode (driver->nodePool(), + propertyName, sym(5).UiQualifiedId, sym(6).UiObjectInitializer); + binding->colonToken = loc(4); + + node->binding = binding; + + sym(1).Node = node; +} break; + +case 63: { + sym(1).Node = makeAstNode(driver->nodePool(), sym(1).Node); +} break; + +case 64: { + sym(1).Node = makeAstNode(driver->nodePool(), sym(1).Node); +} break; + +case 66: { + QString s = QLatin1String(QDeclarativeJSGrammar::spell[T_PROPERTY]); + sym(1).sval = driver->intern(s.constData(), s.length()); + break; +} + +case 67: { + QString s = QLatin1String(QDeclarativeJSGrammar::spell[T_SIGNAL]); + sym(1).sval = driver->intern(s.constData(), s.length()); + break; +} + +case 68: { + QString s = QLatin1String(QDeclarativeJSGrammar::spell[T_READONLY]); + sym(1).sval = driver->intern(s.constData(), s.length()); + break; +} + +case 69: { + QString s = QLatin1String(QDeclarativeJSGrammar::spell[T_ON]); + sym(1).sval = driver->intern(s.constData(), s.length()); + break; +} + +case 70: { + AST::ThisExpression *node = makeAstNode (driver->nodePool()); + node->thisToken = loc(1); + sym(1).Node = node; +} break; + +case 71: { + AST::IdentifierExpression *node = makeAstNode (driver->nodePool(), sym(1).sval); + node->identifierToken = loc(1); + sym(1).Node = node; +} break; + +case 72: { + AST::NullExpression *node = makeAstNode (driver->nodePool()); + node->nullToken = loc(1); + sym(1).Node = node; +} break; + +case 73: { + AST::TrueLiteral *node = makeAstNode (driver->nodePool()); + node->trueToken = loc(1); + sym(1).Node = node; +} break; + +case 74: { + AST::FalseLiteral *node = makeAstNode (driver->nodePool()); + node->falseToken = loc(1); + sym(1).Node = node; +} break; + +case 75: { + AST::NumericLiteral *node = makeAstNode (driver->nodePool(), sym(1).dval); + node->literalToken = loc(1); + sym(1).Node = node; +} break; +case 76: +case 77: { + AST::StringLiteral *node = makeAstNode (driver->nodePool(), sym(1).sval); + node->literalToken = loc(1); + sym(1).Node = node; +} break; + +case 78: { + bool rx = lexer->scanRegExp(Lexer::NoPrefix); + if (!rx) { + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage())); + return false; // ### remove me + } + + loc(1).length = lexer->tokenLength(); + + AST::RegExpLiteral *node = makeAstNode (driver->nodePool(), lexer->pattern, lexer->flags); + node->literalToken = loc(1); + sym(1).Node = node; +} break; + +case 79: { + bool rx = lexer->scanRegExp(Lexer::EqualPrefix); + if (!rx) { + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage())); + return false; + } + + loc(1).length = lexer->tokenLength(); + + AST::RegExpLiteral *node = makeAstNode (driver->nodePool(), lexer->pattern, lexer->flags); + node->literalToken = loc(1); + sym(1).Node = node; +} break; + +case 80: { + AST::ArrayLiteral *node = makeAstNode (driver->nodePool(), (AST::Elision *) 0); + node->lbracketToken = loc(1); + node->rbracketToken = loc(2); + sym(1).Node = node; +} break; + +case 81: { + AST::ArrayLiteral *node = makeAstNode (driver->nodePool(), sym(2).Elision->finish()); + node->lbracketToken = loc(1); + node->rbracketToken = loc(3); + sym(1).Node = node; +} break; + +case 82: { + AST::ArrayLiteral *node = makeAstNode (driver->nodePool(), sym(2).ElementList->finish ()); + node->lbracketToken = loc(1); + node->rbracketToken = loc(3); + sym(1).Node = node; +} break; + +case 83: { + AST::ArrayLiteral *node = makeAstNode (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 84: { + AST::ArrayLiteral *node = makeAstNode (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 85: { + AST::ObjectLiteral *node = 0; + if (sym(2).Node) + node = makeAstNode (driver->nodePool(), + sym(2).PropertyNameAndValueList->finish ()); + else + node = makeAstNode (driver->nodePool()); + node->lbraceToken = loc(1); + node->rbraceToken = loc(3); + sym(1).Node = node; +} break; + +case 86: { + AST::ObjectLiteral *node = makeAstNode (driver->nodePool(), + sym(2).PropertyNameAndValueList->finish ()); + node->lbraceToken = loc(1); + node->rbraceToken = loc(4); + sym(1).Node = node; +} break; + +case 87: { + AST::NestedExpression *node = makeAstNode(driver->nodePool(), sym(2).Expression); + node->lparenToken = loc(1); + node->rparenToken = loc(3); + sym(1).Node = node; +} break; + +case 88: { + if (AST::ArrayMemberExpression *mem = AST::cast(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 89: { + sym(1).Node = makeAstNode (driver->nodePool(), (AST::Elision *) 0, sym(1).Expression); +} break; + +case 90: { + sym(1).Node = makeAstNode (driver->nodePool(), sym(1).Elision->finish(), sym(2).Expression); +} break; + +case 91: { + AST::ElementList *node = makeAstNode (driver->nodePool(), sym(1).ElementList, + (AST::Elision *) 0, sym(3).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; + +case 92: { + AST::ElementList *node = makeAstNode (driver->nodePool(), sym(1).ElementList, sym(3).Elision->finish(), + sym(4).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; + +case 93: { + AST::Elision *node = makeAstNode (driver->nodePool()); + node->commaToken = loc(1); + sym(1).Node = node; +} break; + +case 94: { + AST::Elision *node = makeAstNode (driver->nodePool(), sym(1).Elision); + node->commaToken = loc(2); + sym(1).Node = node; +} break; + +case 95: { + AST::PropertyNameAndValueList *node = makeAstNode (driver->nodePool(), + sym(1).PropertyName, sym(3).Expression); + node->colonToken = loc(2); + sym(1).Node = node; +} break; + +case 96: { + AST::PropertyNameAndValueList *node = makeAstNode (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 97: { + AST::IdentifierPropertyName *node = makeAstNode (driver->nodePool(), sym(1).sval); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; +case 98: +case 99: { + AST::IdentifierPropertyName *node = makeAstNode (driver->nodePool(), driver->intern(lexer->characterBuffer(), lexer->characterCount())); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; + +case 100: { + AST::StringLiteralPropertyName *node = makeAstNode (driver->nodePool(), sym(1).sval); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; + +case 101: { + AST::NumericLiteralPropertyName *node = makeAstNode (driver->nodePool(), sym(1).dval); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; + +case 102: { + AST::IdentifierPropertyName *node = makeAstNode (driver->nodePool(), sym(1).sval); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; + +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: + +case 128: + +case 129: + +case 130: + +case 131: + +case 132: + +case 133: +{ + sym(1).sval = driver->intern(lexer->characterBuffer(), lexer->characterCount()); +} break; + +case 138: { + AST::ArrayMemberExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, sym(3).Expression); + node->lbracketToken = loc(2); + node->rbracketToken = loc(4); + sym(1).Node = node; +} break; + +case 139: { + AST::FieldMemberExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, sym(3).sval); + node->dotToken = loc(2); + node->identifierToken = loc(3); + sym(1).Node = node; +} break; + +case 140: { + AST::NewMemberExpression *node = makeAstNode (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 142: { + AST::NewExpression *node = makeAstNode (driver->nodePool(), sym(2).Expression); + node->newToken = loc(1); + sym(1).Node = node; +} break; + +case 143: { + AST::CallExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, sym(3).ArgumentList); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; + +case 144: { + AST::CallExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, sym(3).ArgumentList); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; + +case 145: { + AST::ArrayMemberExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, sym(3).Expression); + node->lbracketToken = loc(2); + node->rbracketToken = loc(4); + sym(1).Node = node; +} break; + +case 146: { + AST::FieldMemberExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, sym(3).sval); + node->dotToken = loc(2); + node->identifierToken = loc(3); + sym(1).Node = node; +} break; + +case 147: { + sym(1).Node = 0; +} break; + +case 148: { + sym(1).Node = sym(1).ArgumentList->finish(); +} break; + +case 149: { + sym(1).Node = makeAstNode (driver->nodePool(), sym(1).Expression); +} break; + +case 150: { + AST::ArgumentList *node = makeAstNode (driver->nodePool(), sym(1).ArgumentList, sym(3).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; + +case 154: { + AST::PostIncrementExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression); + node->incrementToken = loc(2); + sym(1).Node = node; +} break; + +case 155: { + AST::PostDecrementExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression); + node->decrementToken = loc(2); + sym(1).Node = node; +} break; + +case 157: { + AST::DeleteExpression *node = makeAstNode (driver->nodePool(), sym(2).Expression); + node->deleteToken = loc(1); + sym(1).Node = node; +} break; + +case 158: { + AST::VoidExpression *node = makeAstNode (driver->nodePool(), sym(2).Expression); + node->voidToken = loc(1); + sym(1).Node = node; +} break; + +case 159: { + AST::TypeOfExpression *node = makeAstNode (driver->nodePool(), sym(2).Expression); + node->typeofToken = loc(1); + sym(1).Node = node; +} break; + +case 160: { + AST::PreIncrementExpression *node = makeAstNode (driver->nodePool(), sym(2).Expression); + node->incrementToken = loc(1); + sym(1).Node = node; +} break; + +case 161: { + AST::PreDecrementExpression *node = makeAstNode (driver->nodePool(), sym(2).Expression); + node->decrementToken = loc(1); + sym(1).Node = node; +} break; + +case 162: { + AST::UnaryPlusExpression *node = makeAstNode (driver->nodePool(), sym(2).Expression); + node->plusToken = loc(1); + sym(1).Node = node; +} break; + +case 163: { + AST::UnaryMinusExpression *node = makeAstNode (driver->nodePool(), sym(2).Expression); + node->minusToken = loc(1); + sym(1).Node = node; +} break; + +case 164: { + AST::TildeExpression *node = makeAstNode (driver->nodePool(), sym(2).Expression); + node->tildeToken = loc(1); + sym(1).Node = node; +} break; + +case 165: { + AST::NotExpression *node = makeAstNode (driver->nodePool(), sym(2).Expression); + node->notToken = loc(1); + sym(1).Node = node; +} break; + +case 167: { + AST::BinaryExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, + QSOperator::Mul, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 168: { + AST::BinaryExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, + QSOperator::Div, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 169: { + AST::BinaryExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, + QSOperator::Mod, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 171: { + AST::BinaryExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, + QSOperator::Add, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 172: { + AST::BinaryExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, + QSOperator::Sub, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 174: { + AST::BinaryExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, + QSOperator::LShift, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 175: { + AST::BinaryExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, + QSOperator::RShift, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 176: { + AST::BinaryExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, + QSOperator::URShift, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 178: { + AST::BinaryExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, + QSOperator::Lt, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 179: { + AST::BinaryExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, + QSOperator::Gt, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 180: { + AST::BinaryExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, + QSOperator::Le, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 181: { + AST::BinaryExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, + QSOperator::Ge, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 182: { + AST::BinaryExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, + QSOperator::InstanceOf, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 183: { + AST::BinaryExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, + QSOperator::In, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 185: { + AST::BinaryExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, + QSOperator::Lt, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 186: { + AST::BinaryExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, + QSOperator::Gt, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 187: { + AST::BinaryExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, + QSOperator::Le, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 188: { + AST::BinaryExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, + QSOperator::Ge, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 189: { + AST::BinaryExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, + QSOperator::InstanceOf, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 191: { + AST::BinaryExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, + QSOperator::Equal, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 192: { + AST::BinaryExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, + QSOperator::NotEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 193: { + AST::BinaryExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, + QSOperator::StrictEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 194: { + AST::BinaryExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, + QSOperator::StrictNotEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 196: { + AST::BinaryExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, + QSOperator::Equal, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 197: { + AST::BinaryExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, + QSOperator::NotEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 198: { + AST::BinaryExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, + QSOperator::StrictEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 199: { + AST::BinaryExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, + QSOperator::StrictNotEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 201: { + AST::BinaryExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, + QSOperator::BitAnd, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 203: { + AST::BinaryExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, + QSOperator::BitAnd, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 205: { + AST::BinaryExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, + QSOperator::BitXor, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 207: { + AST::BinaryExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, + QSOperator::BitXor, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 209: { + AST::BinaryExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, + QSOperator::BitOr, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 211: { + AST::BinaryExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, + QSOperator::BitOr, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 213: { + AST::BinaryExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, + QSOperator::And, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 215: { + AST::BinaryExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, + QSOperator::And, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 217: { + AST::BinaryExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, + QSOperator::Or, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 219: { + AST::BinaryExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, + QSOperator::Or, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 221: { + AST::ConditionalExpression *node = makeAstNode (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 223: { + AST::ConditionalExpression *node = makeAstNode (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 225: { + AST::BinaryExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, + sym(2).ival, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 227: { + AST::BinaryExpression *node = makeAstNode (driver->nodePool(), sym(1).Expression, + sym(2).ival, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 228: { + sym(1).ival = QSOperator::Assign; +} break; + +case 229: { + sym(1).ival = QSOperator::InplaceMul; +} break; + +case 230: { + sym(1).ival = QSOperator::InplaceDiv; +} break; + +case 231: { + sym(1).ival = QSOperator::InplaceMod; +} break; + +case 232: { + sym(1).ival = QSOperator::InplaceAdd; +} break; + +case 233: { + sym(1).ival = QSOperator::InplaceSub; +} break; + +case 234: { + sym(1).ival = QSOperator::InplaceLeftShift; +} break; + +case 235: { + sym(1).ival = QSOperator::InplaceRightShift; +} break; + +case 236: { + sym(1).ival = QSOperator::InplaceURightShift; +} break; + +case 237: { + sym(1).ival = QSOperator::InplaceAnd; +} break; + +case 238: { + sym(1).ival = QSOperator::InplaceXor; +} break; + +case 239: { + sym(1).ival = QSOperator::InplaceOr; +} break; + +case 241: { + AST::Expression *node = makeAstNode (driver->nodePool(), sym(1).Expression, sym(3).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; + +case 242: { + sym(1).Node = 0; +} break; + +case 245: { + AST::Expression *node = makeAstNode (driver->nodePool(), sym(1).Expression, sym(3).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; + +case 246: { + sym(1).Node = 0; +} break; + +case 263: { + AST::Block *node = makeAstNode (driver->nodePool(), sym(2).StatementList); + node->lbraceToken = loc(1); + node->rbraceToken = loc(3); + sym(1).Node = node; +} break; + +case 264: { + sym(1).Node = makeAstNode (driver->nodePool(), sym(1).Statement); +} break; + +case 265: { + sym(1).Node = makeAstNode (driver->nodePool(), sym(1).StatementList, sym(2).Statement); +} break; + +case 266: { + sym(1).Node = 0; +} break; + +case 267: { + sym(1).Node = sym(1).StatementList->finish (); +} break; + +case 269: { + AST::VariableStatement *node = makeAstNode (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 270: { + sym(1).ival = T_CONST; +} break; + +case 271: { + sym(1).ival = T_VAR; +} break; + +case 272: { + sym(1).Node = makeAstNode (driver->nodePool(), sym(1).VariableDeclaration); +} break; + +case 273: { + AST::VariableDeclarationList *node = makeAstNode (driver->nodePool(), + sym(1).VariableDeclarationList, sym(3).VariableDeclaration); + node->commaToken = loc(2); + sym(1).Node = node; +} break; + +case 274: { + sym(1).Node = makeAstNode (driver->nodePool(), sym(1).VariableDeclaration); +} break; + +case 275: { + sym(1).Node = makeAstNode (driver->nodePool(), sym(1).VariableDeclarationList, sym(3).VariableDeclaration); +} break; + +case 276: { + AST::VariableDeclaration *node = makeAstNode (driver->nodePool(), sym(1).sval, sym(2).Expression); + node->identifierToken = loc(1); + sym(1).Node = node; +} break; + +case 277: { + AST::VariableDeclaration *node = makeAstNode (driver->nodePool(), sym(1).sval, sym(2).Expression); + node->identifierToken = loc(1); + sym(1).Node = node; +} break; + +case 278: { + // ### TODO: AST for initializer + sym(1) = sym(2); +} break; + +case 279: { + sym(1).Node = 0; +} break; + +case 281: { + // ### TODO: AST for initializer + sym(1) = sym(2); +} break; + +case 282: { + sym(1).Node = 0; +} break; + +case 284: { + AST::EmptyStatement *node = makeAstNode (driver->nodePool()); + node->semicolonToken = loc(1); + sym(1).Node = node; +} break; + +case 286: { + AST::ExpressionStatement *node = makeAstNode (driver->nodePool(), sym(1).Expression); + node->semicolonToken = loc(2); + sym(1).Node = node; +} break; + +case 287: { + AST::IfStatement *node = makeAstNode (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 288: { + AST::IfStatement *node = makeAstNode (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 290: { + AST::DoWhileStatement *node = makeAstNode (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 291: { + AST::WhileStatement *node = makeAstNode (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 292: { + AST::ForStatement *node = makeAstNode (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 293: { + AST::LocalForStatement *node = makeAstNode (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 294: { + AST:: ForEachStatement *node = makeAstNode (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 295: { + AST::LocalForEachStatement *node = makeAstNode (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 297: { + AST::ContinueStatement *node = makeAstNode (driver->nodePool()); + node->continueToken = loc(1); + node->semicolonToken = loc(2); + sym(1).Node = node; +} break; + +case 299: { + AST::ContinueStatement *node = makeAstNode (driver->nodePool(), sym(2).sval); + node->continueToken = loc(1); + node->identifierToken = loc(2); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; + +case 301: { + AST::BreakStatement *node = makeAstNode (driver->nodePool()); + node->breakToken = loc(1); + node->semicolonToken = loc(2); + sym(1).Node = node; +} break; + +case 303: { + AST::BreakStatement *node = makeAstNode (driver->nodePool(), sym(2).sval); + node->breakToken = loc(1); + node->identifierToken = loc(2); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; + +case 305: { + AST::ReturnStatement *node = makeAstNode (driver->nodePool(), sym(2).Expression); + node->returnToken = loc(1); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; + +case 306: { + AST::WithStatement *node = makeAstNode (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 307: { + AST::SwitchStatement *node = makeAstNode (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 308: { + AST::CaseBlock *node = makeAstNode (driver->nodePool(), sym(2).CaseClauses); + node->lbraceToken = loc(1); + node->rbraceToken = loc(3); + sym(1).Node = node; +} break; + +case 309: { + AST::CaseBlock *node = makeAstNode (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 310: { + sym(1).Node = makeAstNode (driver->nodePool(), sym(1).CaseClause); +} break; + +case 311: { + sym(1).Node = makeAstNode (driver->nodePool(), sym(1).CaseClauses, sym(2).CaseClause); +} break; + +case 312: { + sym(1).Node = 0; +} break; + +case 313: { + sym(1).Node = sym(1).CaseClauses->finish (); +} break; + +case 314: { + AST::CaseClause *node = makeAstNode (driver->nodePool(), sym(2).Expression, sym(4).StatementList); + node->caseToken = loc(1); + node->colonToken = loc(3); + sym(1).Node = node; +} break; + +case 315: { + AST::DefaultClause *node = makeAstNode (driver->nodePool(), sym(3).StatementList); + node->defaultToken = loc(1); + node->colonToken = loc(2); + sym(1).Node = node; +} break; +case 316: +case 317: { + AST::LabelledStatement *node = makeAstNode (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 318: { + AST::LabelledStatement *node = makeAstNode (driver->nodePool(), sym(1).sval, sym(3).Statement); + node->identifierToken = loc(1); + node->colonToken = loc(2); + sym(1).Node = node; +} break; + +case 320: { + AST::ThrowStatement *node = makeAstNode (driver->nodePool(), sym(2).Expression); + node->throwToken = loc(1); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; + +case 321: { + AST::TryStatement *node = makeAstNode (driver->nodePool(), sym(2).Statement, sym(3).Catch); + node->tryToken = loc(1); + sym(1).Node = node; +} break; + +case 322: { + AST::TryStatement *node = makeAstNode (driver->nodePool(), sym(2).Statement, sym(3).Finally); + node->tryToken = loc(1); + sym(1).Node = node; +} break; + +case 323: { + AST::TryStatement *node = makeAstNode (driver->nodePool(), sym(2).Statement, sym(3).Catch, sym(4).Finally); + node->tryToken = loc(1); + sym(1).Node = node; +} break; + +case 324: { + AST::Catch *node = makeAstNode (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 325: { + AST::Finally *node = makeAstNode (driver->nodePool(), sym(2).Block); + node->finallyToken = loc(1); + sym(1).Node = node; +} break; + +case 327: { + AST::DebuggerStatement *node = makeAstNode (driver->nodePool()); + node->debuggerToken = loc(1); + node->semicolonToken = loc(2); + sym(1).Node = node; +} break; + +case 328: { + AST::FunctionDeclaration *node = makeAstNode (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 329: { + AST::FunctionExpression *node = makeAstNode (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 330: { + AST::FormalParameterList *node = makeAstNode (driver->nodePool(), sym(1).sval); + node->identifierToken = loc(1); + sym(1).Node = node; +} break; + +case 331: { + AST::FormalParameterList *node = makeAstNode (driver->nodePool(), sym(1).FormalParameterList, sym(3).sval); + node->commaToken = loc(2); + node->identifierToken = loc(3); + sym(1).Node = node; +} break; + +case 332: { + sym(1).Node = 0; +} break; + +case 333: { + sym(1).Node = sym(1).FormalParameterList->finish (); +} break; + +case 334: { + sym(1).Node = 0; +} break; + +case 336: { + sym(1).Node = makeAstNode (driver->nodePool(), sym(1).SourceElements->finish ()); +} break; + +case 337: { + sym(1).Node = makeAstNode (driver->nodePool(), sym(1).SourceElements->finish ()); +} break; + +case 338: { + sym(1).Node = makeAstNode (driver->nodePool(), sym(1).SourceElement); +} break; + +case 339: { + sym(1).Node = makeAstNode (driver->nodePool(), sym(1).SourceElements, sym(2).SourceElement); +} break; + +case 340: { + sym(1).Node = makeAstNode (driver->nodePool(), sym(1).Statement); +} break; + +case 341: { + sym(1).Node = makeAstNode (driver->nodePool(), sym(1).FunctionDeclaration); +} break; + +case 342: { + sym(1).sval = 0; +} break; + +case 344: { + 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 (yytoken != -1 && 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 = qApp->translate("QDeclarativeParser", "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)) { + QString msg; + int token = token_buffer[0].token; + if (token < 0 || token >= TERMINAL_COUNT) + msg = qApp->translate("QDeclarativeParser", "Syntax error"); + else + msg = qApp->translate("QDeclarativeParser", "Unexpected token `%1'").arg(QLatin1String(spell[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 = qApp->translate("QDeclarativeParser", "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 || + tk == T_FEED_JS_SOURCE_ELEMENT) + continue; + + int a = t_action(errorState, tk); + if (a > 0 && t_action(a, yytoken)) { + const QString msg = qApp->translate("QDeclarativeParser", "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 = qApp->translate("QDeclarativeParser", "Syntax error"); + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); + } + + return false; +} + +QT_QML_END_NAMESPACE + + diff --git a/src/declarative/qml/parser/qdeclarativejsparser_p.h b/src/declarative/qml/parser/qdeclarativejsparser_p.h new file mode 100644 index 00000000..c390bcd5 --- /dev/null +++ b/src/declarative/qml/parser/qdeclarativejsparser_p.h @@ -0,0 +1,246 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +// +// 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 QDECLARATIVEJSPARSER_P_H +#define QDECLARATIVEJSPARSER_P_H + +#include "private/qdeclarativejsglobal_p.h" +#include "private/qdeclarativejsgrammar_p.h" +#include "private/qdeclarativejsast_p.h" +#include "private/qdeclarativejsengine_p.h" + +#include +#include + +QT_QML_BEGIN_NAMESPACE + +namespace QDeclarativeJS { + +class Engine; +class NameId; + +class QML_PARSER_EXPORT Parser: protected QDeclarativeJSGrammar +{ +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 parseSourceElement() { return parse(T_FEED_JS_SOURCE_ELEMENT); } + bool parseUiObjectMember() { return parse(T_FEED_UI_OBJECT_MEMBER); } + bool parseProgram() { return parse(T_FEED_JS_PROGRAM); } + + AST::UiProgram *ast() const + { return AST::cast(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(); + } + + AST::Node *rootNode() const + { return program; } + + QList 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 diagnostic_messages; +}; + +} // end of namespace QDeclarativeJS + + + +#define J_SCRIPT_REGEXPLITERAL_RULE1 78 + +#define J_SCRIPT_REGEXPLITERAL_RULE2 79 + +QT_QML_END_NAMESPACE + + + +#endif // QDECLARATIVEJSPARSER_P_H diff --git a/src/declarative/qml/qbitfield_p.h b/src/declarative/qml/qbitfield_p.h new file mode 100644 index 00000000..fb5efe0e --- /dev/null +++ b/src/declarative/qml/qbitfield_p.h @@ -0,0 +1,165 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBITFIELD_P_H +#define QBITFIELD_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 + +QT_BEGIN_NAMESPACE + +class QBitField +{ +public: + inline QBitField(); + inline QBitField(const quint32 *, int bits); + inline QBitField(const QBitField &); + inline ~QBitField(); + + inline QBitField &operator=(const QBitField &); + + inline quint32 size() const; + inline QBitField united(const QBitField &); + inline bool testBit(int) const; + +private: + quint32 bits:31; + quint32 *ownData; + const quint32 *data; +}; + +QBitField::QBitField() +: bits(0), ownData(0), data(0) +{ +} + +QBitField::QBitField(const quint32 *bitData, int bitCount) +: bits((quint32)bitCount), ownData(0), data(bitData) +{ +} + +QBitField::QBitField(const QBitField &other) +: bits(other.bits), ownData(other.ownData), data(other.data) +{ + if (ownData) + ++(*ownData); +} + +QBitField::~QBitField() +{ + if (ownData) + if(0 == --(*ownData)) delete [] ownData; +} + +QBitField &QBitField::operator=(const QBitField &other) +{ + if (other.data == data) + return *this; + + if (ownData) + if(0 == --(*ownData)) delete [] ownData; + + bits = other.bits; + ownData = other.ownData; + data = other.data; + + if (ownData) + ++(*ownData); + + return *this; +} + +inline quint32 QBitField::size() const +{ + return bits; +} + +QBitField QBitField::united(const QBitField &o) +{ + if (o.bits == 0) { + return *this; + } else if (bits == 0) { + return o; + } else { + int max = (bits > o.bits)?bits:o.bits; + int length = (max + 31) / 32; + QBitField rv; + rv.bits = max; + rv.ownData = new quint32[length + 1]; + *(rv.ownData) = 1; + rv.data = rv.ownData + 1; + if (bits > o.bits) { + ::memcpy((quint32 *)rv.data, data, length * sizeof(quint32)); + for (quint32 ii = 0; ii < (o.bits + quint32(31)) / 32; ++ii) + ((quint32 *)rv.data)[ii] |= o.data[ii]; + } else { + ::memcpy((quint32 *)rv.data, o.data, length * sizeof(quint32)); + for (quint32 ii = 0; ii < (bits + quint32(31)) / 32; ++ii) + ((quint32 *)rv.data)[ii] |= data[ii]; + } + return rv; + } +} + +bool QBitField::testBit(int b) const +{ + Q_ASSERT(b >= 0); + if ((quint32)b < bits) { + return data[b / 32] & (1 << (b % 32)); + } else { + return false; + } +} + +QT_END_NAMESPACE + +#endif // QBITFIELD_P_H diff --git a/src/declarative/qml/qdeclarative.h b/src/declarative/qml/qdeclarative.h new file mode 100644 index 00000000..f0893b2e --- /dev/null +++ b/src/declarative/qml/qdeclarative.h @@ -0,0 +1,415 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVE_H +#define QDECLARATIVE_H + +#include +#include +#include +#include +#include + +#include +#include + +QT_BEGIN_HEADER + +#define QML_DECLARE_TYPE(TYPE) \ + Q_DECLARE_METATYPE(TYPE *) \ + Q_DECLARE_METATYPE(QDeclarativeListProperty) + +#define QML_DECLARE_TYPE_HASMETATYPE(TYPE) \ + Q_DECLARE_METATYPE(QDeclarativeListProperty) + +#define QML_DECLARE_INTERFACE(INTERFACE) \ + QML_DECLARE_TYPE(INTERFACE) + +#define QML_DECLARE_INTERFACE_HASMETATYPE(INTERFACE) \ + QML_DECLARE_TYPE_HASMETATYPE(INTERFACE) + +enum { /* TYPEINFO flags */ + QML_HAS_ATTACHED_PROPERTIES = 0x01 +}; + +#define QML_DECLARE_TYPEINFO(TYPE, FLAGS) \ +QT_BEGIN_NAMESPACE \ +template <> \ +class QDeclarativeTypeInfo \ +{ \ +public: \ + enum { \ + hasAttachedProperties = (((FLAGS) & QML_HAS_ATTACHED_PROPERTIES) == QML_HAS_ATTACHED_PROPERTIES) \ + }; \ +}; \ +QT_END_NAMESPACE + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +template +int qmlRegisterType() +{ + QByteArray name(T::staticMetaObject.className()); + + QByteArray pointerName(name + '*'); + QByteArray listName("QDeclarativeListProperty<" + name + ">"); + + QDeclarativePrivate::RegisterType type = { + 0, + + qRegisterMetaType(pointerName.constData()), + qRegisterMetaType >(listName.constData()), + 0, 0, + QString(), + + 0, 0, 0, 0, &T::staticMetaObject, + + QDeclarativePrivate::attachedPropertiesFunc(), + QDeclarativePrivate::attachedPropertiesMetaObject(), + + QDeclarativePrivate::StaticCastSelector::cast(), + QDeclarativePrivate::StaticCastSelector::cast(), + QDeclarativePrivate::StaticCastSelector::cast(), + + 0, 0, + + 0, + 0 + }; + + return QDeclarativePrivate::qmlregister(QDeclarativePrivate::TypeRegistration, &type); +} + +int Q_AUTOTEST_EXPORT qmlRegisterTypeNotAvailable(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& message); + +template +int qmlRegisterUncreatableType(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& reason) +{ + QByteArray name(T::staticMetaObject.className()); + + QByteArray pointerName(name + '*'); + QByteArray listName("QDeclarativeListProperty<" + name + ">"); + + QDeclarativePrivate::RegisterType type = { + 0, + + qRegisterMetaType(pointerName.constData()), + qRegisterMetaType >(listName.constData()), + 0, 0, + reason, + + uri, versionMajor, versionMinor, qmlName, &T::staticMetaObject, + + QDeclarativePrivate::attachedPropertiesFunc(), + QDeclarativePrivate::attachedPropertiesMetaObject(), + + QDeclarativePrivate::StaticCastSelector::cast(), + QDeclarativePrivate::StaticCastSelector::cast(), + QDeclarativePrivate::StaticCastSelector::cast(), + + 0, 0, + + 0, + 0 + }; + + return QDeclarativePrivate::qmlregister(QDeclarativePrivate::TypeRegistration, &type); +} + +template +int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName) +{ + QByteArray name(T::staticMetaObject.className()); + + QByteArray pointerName(name + '*'); + QByteArray listName("QDeclarativeListProperty<" + name + ">"); + + QDeclarativePrivate::RegisterType type = { + 0, + + qRegisterMetaType(pointerName.constData()), + qRegisterMetaType >(listName.constData()), + sizeof(T), QDeclarativePrivate::createInto, + QString(), + + uri, versionMajor, versionMinor, qmlName, &T::staticMetaObject, + + QDeclarativePrivate::attachedPropertiesFunc(), + QDeclarativePrivate::attachedPropertiesMetaObject(), + + QDeclarativePrivate::StaticCastSelector::cast(), + QDeclarativePrivate::StaticCastSelector::cast(), + QDeclarativePrivate::StaticCastSelector::cast(), + + 0, 0, + + 0, + 0 + }; + + return QDeclarativePrivate::qmlregister(QDeclarativePrivate::TypeRegistration, &type); +} + +template +int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName) +{ + QByteArray name(T::staticMetaObject.className()); + + QByteArray pointerName(name + '*'); + QByteArray listName("QDeclarativeListProperty<" + name + ">"); + + QDeclarativePrivate::RegisterType type = { + 1, + + qRegisterMetaType(pointerName.constData()), + qRegisterMetaType >(listName.constData()), + sizeof(T), QDeclarativePrivate::createInto, + QString(), + + uri, versionMajor, versionMinor, qmlName, &T::staticMetaObject, + + QDeclarativePrivate::attachedPropertiesFunc(), + QDeclarativePrivate::attachedPropertiesMetaObject(), + + QDeclarativePrivate::StaticCastSelector::cast(), + QDeclarativePrivate::StaticCastSelector::cast(), + QDeclarativePrivate::StaticCastSelector::cast(), + + 0, 0, + + 0, + metaObjectRevision + }; + + return QDeclarativePrivate::qmlregister(QDeclarativePrivate::TypeRegistration, &type); +} + +template +int qmlRegisterRevision(const char *uri, int versionMajor, int versionMinor) +{ + QByteArray name(T::staticMetaObject.className()); + + QByteArray pointerName(name + '*'); + QByteArray listName("QDeclarativeListProperty<" + name + ">"); + + QDeclarativePrivate::RegisterType type = { + 1, + + qRegisterMetaType(pointerName.constData()), + qRegisterMetaType >(listName.constData()), + sizeof(T), QDeclarativePrivate::createInto, + QString(), + + uri, versionMajor, versionMinor, 0, &T::staticMetaObject, + + QDeclarativePrivate::attachedPropertiesFunc(), + QDeclarativePrivate::attachedPropertiesMetaObject(), + + QDeclarativePrivate::StaticCastSelector::cast(), + QDeclarativePrivate::StaticCastSelector::cast(), + QDeclarativePrivate::StaticCastSelector::cast(), + + 0, 0, + + 0, + metaObjectRevision + }; + + return QDeclarativePrivate::qmlregister(QDeclarativePrivate::TypeRegistration, &type); +} + + +template +int qmlRegisterExtendedType() +{ + QByteArray name(T::staticMetaObject.className()); + + QByteArray pointerName(name + '*'); + QByteArray listName("QDeclarativeListProperty<" + name + ">"); + + QDeclarativePrivate::RegisterType type = { + 0, + + qRegisterMetaType(pointerName.constData()), + qRegisterMetaType >(listName.constData()), + 0, 0, + QString(), + + 0, 0, 0, 0, &T::staticMetaObject, + + QDeclarativePrivate::attachedPropertiesFunc(), + QDeclarativePrivate::attachedPropertiesMetaObject(), + + QDeclarativePrivate::StaticCastSelector::cast(), + QDeclarativePrivate::StaticCastSelector::cast(), + QDeclarativePrivate::StaticCastSelector::cast(), + + QDeclarativePrivate::createParent, &E::staticMetaObject, + + 0, + 0 + }; + + return QDeclarativePrivate::qmlregister(QDeclarativePrivate::TypeRegistration, &type); +} + +template +int qmlRegisterExtendedType(const char *uri, int versionMajor, int versionMinor, + const char *qmlName) +{ + QByteArray name(T::staticMetaObject.className()); + + QByteArray pointerName(name + '*'); + QByteArray listName("QDeclarativeListProperty<" + name + ">"); + + QDeclarativeAttachedPropertiesFunc attached = QDeclarativePrivate::attachedPropertiesFunc(); + const QMetaObject * attachedMetaObject = QDeclarativePrivate::attachedPropertiesMetaObject(); + if (!attached) { + attached = QDeclarativePrivate::attachedPropertiesFunc(); + attachedMetaObject = QDeclarativePrivate::attachedPropertiesMetaObject(); + } + + QDeclarativePrivate::RegisterType type = { + 0, + + qRegisterMetaType(pointerName.constData()), + qRegisterMetaType >(listName.constData()), + sizeof(T), QDeclarativePrivate::createInto, + QString(), + + uri, versionMajor, versionMinor, qmlName, &T::staticMetaObject, + + attached, + attachedMetaObject, + + QDeclarativePrivate::StaticCastSelector::cast(), + QDeclarativePrivate::StaticCastSelector::cast(), + QDeclarativePrivate::StaticCastSelector::cast(), + + QDeclarativePrivate::createParent, &E::staticMetaObject, + + 0, + 0 + }; + + return QDeclarativePrivate::qmlregister(QDeclarativePrivate::TypeRegistration, &type); +} + +template +int qmlRegisterInterface(const char *typeName) +{ + QByteArray name(typeName); + + QByteArray pointerName(name + '*'); + QByteArray listName("QDeclarativeListProperty<" + name + ">"); + + QDeclarativePrivate::RegisterInterface qmlInterface = { + 0, + + qRegisterMetaType(pointerName.constData()), + qRegisterMetaType >(listName.constData()), + + qobject_interface_iid() + }; + + return QDeclarativePrivate::qmlregister(QDeclarativePrivate::InterfaceRegistration, &qmlInterface); +} + +template +int qmlRegisterCustomType(const char *uri, int versionMajor, int versionMinor, + const char *qmlName, QDeclarativeCustomParser *parser) +{ + QByteArray name(T::staticMetaObject.className()); + + QByteArray pointerName(name + '*'); + QByteArray listName("QDeclarativeListProperty<" + name + ">"); + + QDeclarativePrivate::RegisterType type = { + 0, + + qRegisterMetaType(pointerName.constData()), + qRegisterMetaType >(listName.constData()), + sizeof(T), QDeclarativePrivate::createInto, + QString(), + + uri, versionMajor, versionMinor, qmlName, &T::staticMetaObject, + + QDeclarativePrivate::attachedPropertiesFunc(), + QDeclarativePrivate::attachedPropertiesMetaObject(), + + QDeclarativePrivate::StaticCastSelector::cast(), + QDeclarativePrivate::StaticCastSelector::cast(), + QDeclarativePrivate::StaticCastSelector::cast(), + + 0, 0, + + parser, + 0 + }; + + return QDeclarativePrivate::qmlregister(QDeclarativePrivate::TypeRegistration, &type); +} + +class QDeclarativeContext; +class QDeclarativeEngine; +Q_DECLARATIVE_EXPORT void qmlExecuteDeferred(QObject *); +Q_DECLARATIVE_EXPORT QDeclarativeContext *qmlContext(const QObject *); +Q_DECLARATIVE_EXPORT QDeclarativeEngine *qmlEngine(const QObject *); +Q_DECLARATIVE_EXPORT QObject *qmlAttachedPropertiesObjectById(int, const QObject *, bool create = true); +Q_DECLARATIVE_EXPORT QObject *qmlAttachedPropertiesObject(int *, const QObject *, const QMetaObject *, bool create); + +template +QObject *qmlAttachedPropertiesObject(const QObject *obj, bool create = true) +{ + static int idx = -1; + return qmlAttachedPropertiesObject(&idx, obj, &T::staticMetaObject, create); +} + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QObject) +Q_DECLARE_METATYPE(QVariant) + +QT_END_HEADER + +#endif // QDECLARATIVE_H diff --git a/src/declarative/qml/qdeclarativebinding.cpp b/src/declarative/qml/qdeclarativebinding.cpp new file mode 100644 index 00000000..31bc436e --- /dev/null +++ b/src/declarative/qml/qdeclarativebinding.cpp @@ -0,0 +1,573 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativebinding_p.h" +#include "private/qdeclarativebinding_p_p.h" + +#include "qdeclarative.h" +#include "qdeclarativecontext.h" +#include "qdeclarativeinfo.h" +#include "private/qdeclarativecontext_p.h" +#include "private/qdeclarativecompiler_p.h" +#include "private/qdeclarativedata_p.h" +#include "private/qdeclarativestringconverters_p.h" +#include "private/qdeclarativestate_p_p.h" +#include "private/qdeclarativedebugtrace_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +QDeclarativeAbstractBinding::QDeclarativeAbstractBinding() +: m_object(0), m_propertyIndex(-1), m_mePtr(0), m_prevBinding(0), m_nextBinding(0) +{ +} + +QDeclarativeAbstractBinding::~QDeclarativeAbstractBinding() +{ + Q_ASSERT(m_prevBinding == 0); + Q_ASSERT(m_mePtr == 0); +} + +/*! +Destroy the binding. Use this instead of calling delete. + +Bindings are free to implement their own memory management, so the delete operator is not +necessarily safe. The default implementation clears the binding, removes it from the object +and calls delete. +*/ +void QDeclarativeAbstractBinding::destroy() +{ + removeFromObject(); + clear(); + + delete this; +} + +/*! +Add this binding to \a object. + +This transfers ownership of the binding to the object, marks the object's property as +being bound. + +However, it does not enable the binding itself or call update() on it. +*/ +void QDeclarativeAbstractBinding::addToObject(QObject *object, int index) +{ + Q_ASSERT(object); + + if (m_object == object && m_propertyIndex == index) + return; + + removeFromObject(); + + Q_ASSERT(!m_prevBinding); + + m_object = object; + m_propertyIndex = index; + + QDeclarativeData *data = QDeclarativeData::get(object, true); + + if (index & 0xFF000000) { + // Value type + + int coreIndex = index & 0xFFFFFF; + + // Find the value type proxy (if there is one) + QDeclarativeValueTypeProxyBinding *proxy = 0; + if (data->hasBindingBit(coreIndex)) { + QDeclarativeAbstractBinding *b = data->bindings; + while (b && b->propertyIndex() != coreIndex) + b = b->m_nextBinding; + Q_ASSERT(b && b->bindingType() == QDeclarativeAbstractBinding::ValueTypeProxy); + proxy = static_cast(b); + } + + if (!proxy) { + proxy = new QDeclarativeValueTypeProxyBinding(object, coreIndex); + proxy->addToObject(object, coreIndex); + } + + m_nextBinding = proxy->m_bindings; + if (m_nextBinding) m_nextBinding->m_prevBinding = &m_nextBinding; + m_prevBinding = &proxy->m_bindings; + proxy->m_bindings = this; + + } else { + m_nextBinding = data->bindings; + if (m_nextBinding) m_nextBinding->m_prevBinding = &m_nextBinding; + m_prevBinding = &data->bindings; + data->bindings = this; + + data->setBindingBit(m_object, index); + } +} + +/*! +Remove the binding from the object. +*/ +void QDeclarativeAbstractBinding::removeFromObject() +{ + if (m_prevBinding) { + int index = propertyIndex(); + + *m_prevBinding = m_nextBinding; + if (m_nextBinding) m_nextBinding->m_prevBinding = m_prevBinding; + m_prevBinding = 0; + m_nextBinding = 0; + + if (index & 0xFF000000) { + // Value type - we don't remove the proxy from the object. It will sit their happily + // doing nothing until it is removed by a write, a binding change or it is reused + // to hold more sub-bindings. + } else if (m_object) { + QDeclarativeData *data = QDeclarativeData::get(m_object, false); + if (data) data->clearBindingBit(index); + } + + m_object = 0; + m_propertyIndex = -1; + } +} + +static void bindingDummyDeleter(QDeclarativeAbstractBinding *) +{ +} + +QDeclarativeAbstractBinding::Pointer QDeclarativeAbstractBinding::weakPointer() +{ + if (m_selfPointer.isNull()) + m_selfPointer = QSharedPointer(this, bindingDummyDeleter); + + return m_selfPointer.toWeakRef(); +} + +void QDeclarativeAbstractBinding::clear() +{ + if (m_mePtr) { + *m_mePtr = 0; + m_mePtr = 0; + } +} + +QString QDeclarativeAbstractBinding::expression() const +{ + return QLatin1String(""); +} + +QObject *QDeclarativeAbstractBinding::object() const +{ + return m_object; +} + +int QDeclarativeAbstractBinding::propertyIndex() const +{ + return m_propertyIndex; +} + +void QDeclarativeAbstractBinding::setEnabled(bool enabled, QDeclarativePropertyPrivate::WriteFlags flags) +{ + if (enabled) update(flags); +} + +QDeclarativeBinding::Identifier QDeclarativeBinding::Invalid = -1; + +void QDeclarativeBindingPrivate::refresh() +{ + Q_Q(QDeclarativeBinding); + q->update(); +} + +QDeclarativeBindingPrivate::QDeclarativeBindingPrivate() +: updating(false), enabled(false), deleted(0) +{ +} + +QDeclarativeBindingPrivate::~QDeclarativeBindingPrivate() +{ + if (deleted) *deleted = true; +} + +QDeclarativeBinding::QDeclarativeBinding(void *data, QDeclarativeRefCount *rc, QObject *obj, + QDeclarativeContextData *ctxt, const QString &url, int lineNumber, + QObject *parent) +: QDeclarativeExpression(ctxt, data, rc, obj, url, lineNumber, *new QDeclarativeBindingPrivate) +{ + setParent(parent); + setNotifyOnValueChanged(true); +} + +QDeclarativeBinding * +QDeclarativeBinding::createBinding(Identifier id, QObject *obj, QDeclarativeContext *ctxt, + const QString &url, int lineNumber, QObject *parent) +{ + if (id < 0) + return 0; + + Q_ASSERT(ctxt); + QDeclarativeContextData *ctxtdata = QDeclarativeContextData::get(ctxt); + + QDeclarativeEnginePrivate *engine = QDeclarativeEnginePrivate::get(ctxtdata->engine); + QDeclarativeCompiledData *cdata = 0; + QDeclarativeTypeData *typeData = 0; + if (!ctxtdata->url.isEmpty()) { + typeData = engine->typeLoader.get(ctxtdata->url); + cdata = typeData->compiledData(); + } + QDeclarativeBinding *rv = cdata ? new QDeclarativeBinding((void*)cdata->datas.at(id).constData(), cdata, obj, ctxtdata, url, lineNumber, parent) : 0; + if (cdata) + cdata->release(); + if (typeData) + typeData->release(); + return rv; +} + +QDeclarativeBinding::QDeclarativeBinding(const QString &str, QObject *obj, QDeclarativeContext *ctxt, + QObject *parent) +: QDeclarativeExpression(QDeclarativeContextData::get(ctxt), obj, str, *new QDeclarativeBindingPrivate) +{ + setParent(parent); + setNotifyOnValueChanged(true); +} + +QDeclarativeBinding::QDeclarativeBinding(const QString &str, QObject *obj, QDeclarativeContextData *ctxt, + QObject *parent) +: QDeclarativeExpression(ctxt, obj, str, *new QDeclarativeBindingPrivate) +{ + setParent(parent); + setNotifyOnValueChanged(true); +} + +QDeclarativeBinding::QDeclarativeBinding(const QScriptValue &func, QObject *obj, QDeclarativeContextData *ctxt, QObject *parent) +: QDeclarativeExpression(ctxt, obj, func, *new QDeclarativeBindingPrivate) +{ + setParent(parent); + setNotifyOnValueChanged(true); +} + +QDeclarativeBinding::~QDeclarativeBinding() +{ +} + +void QDeclarativeBinding::setTarget(const QDeclarativeProperty &prop) +{ + Q_D(QDeclarativeBinding); + d->property = prop; + + update(); +} + +QDeclarativeProperty QDeclarativeBinding::property() const +{ + Q_D(const QDeclarativeBinding); + return d->property; +} + +void QDeclarativeBinding::setEvaluateFlags(EvaluateFlags flags) +{ + Q_D(QDeclarativeBinding); + d->setEvaluateFlags(QDeclarativeQtScriptExpression::EvaluateFlags(static_cast(flags))); +} + +QDeclarativeBinding::EvaluateFlags QDeclarativeBinding::evaluateFlags() const +{ + Q_D(const QDeclarativeBinding); + return QDeclarativeBinding::EvaluateFlags(static_cast(d->evaluateFlags())); +} + + +class QDeclarativeBindingProfiler { +public: + QDeclarativeBindingProfiler(QDeclarativeBinding *bind) + { + QDeclarativeDebugTrace::startRange(QDeclarativeDebugTrace::Binding); + QDeclarativeDebugTrace::rangeData(QDeclarativeDebugTrace::Binding, bind->expression()); + QDeclarativeDebugTrace::rangeLocation(QDeclarativeDebugTrace::Binding, bind->sourceFile(), bind->lineNumber()); + } + + ~QDeclarativeBindingProfiler() + { + QDeclarativeDebugTrace::endRange(QDeclarativeDebugTrace::Binding); + } +}; + +void QDeclarativeBinding::update(QDeclarativePropertyPrivate::WriteFlags flags) +{ + Q_D(QDeclarativeBinding); + + if (!d->enabled || !d->context() || !d->context()->isValid()) + return; + + if (!d->updating) { + QDeclarativeBindingProfiler prof(this); + d->updating = true; + bool wasDeleted = false; + d->deleted = &wasDeleted; + + if (d->property.propertyType() == qMetaTypeId()) { + + int idx = d->property.index(); + Q_ASSERT(idx != -1); + + QDeclarativeBinding *t = this; + int status = -1; + void *a[] = { &t, 0, &status, &flags }; + QMetaObject::metacall(d->property.object(), + QMetaObject::WriteProperty, + idx, a); + + if (wasDeleted) + return; + + } else { + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(d->context()->engine); + + bool isUndefined = false; + QVariant value; + + QScriptValue scriptValue = d->scriptValue(0, &isUndefined); + if (wasDeleted) + return; + + if (d->property.propertyTypeCategory() == QDeclarativeProperty::List) { + value = ep->scriptValueToVariant(scriptValue, qMetaTypeId >()); + } else if (scriptValue.isNull() && + d->property.propertyTypeCategory() == QDeclarativeProperty::Object) { + value = QVariant::fromValue((QObject *)0); + } else { + value = ep->scriptValueToVariant(scriptValue, d->property.propertyType()); + if (value.userType() == QMetaType::QObjectStar && !qvariant_cast(value)) { + // If the object is null, we extract the predicted type. While this isn't + // 100% reliable, in many cases it gives us better error messages if we + // assign this null-object to an incompatible property + int type = ep->objectClass->objectType(scriptValue); + QObject *o = 0; + value = QVariant(type, (void *)&o); + } + } + + + if (d->error.isValid()) { + + } else if (isUndefined && d->property.isResettable()) { + + d->property.reset(); + + } else if (isUndefined && d->property.propertyType() == qMetaTypeId()) { + + QDeclarativePropertyPrivate::write(d->property, QVariant(), flags); + + } else if (isUndefined) { + + QUrl url = QUrl(d->url); + int line = d->line; + if (url.isEmpty()) url = QUrl(QLatin1String("")); + + d->error.setUrl(url); + d->error.setLine(line); + d->error.setColumn(-1); + d->error.setDescription(QLatin1String("Unable to assign [undefined] to ") + + QLatin1String(QMetaType::typeName(d->property.propertyType())) + + QLatin1String(" ") + d->property.name()); + + } else if (!scriptValue.isRegExp() && scriptValue.isFunction()) { + + QUrl url = QUrl(d->url); + int line = d->line; + if (url.isEmpty()) url = QUrl(QLatin1String("")); + + d->error.setUrl(url); + d->error.setLine(line); + d->error.setColumn(-1); + d->error.setDescription(QLatin1String("Unable to assign a function to a property.")); + + } else if (d->property.object() && + !QDeclarativePropertyPrivate::write(d->property, value, flags)) { + + if (wasDeleted) + return; + + QUrl url = QUrl(d->url); + int line = d->line; + if (url.isEmpty()) url = QUrl(QLatin1String("")); + + const char *valueType = 0; + if (value.userType() == QVariant::Invalid) valueType = "null"; + else valueType = QMetaType::typeName(value.userType()); + + d->error.setUrl(url); + d->error.setLine(line); + d->error.setColumn(-1); + d->error.setDescription(QLatin1String("Unable to assign ") + + QLatin1String(valueType) + + QLatin1String(" to ") + + QLatin1String(QMetaType::typeName(d->property.propertyType()))); + } + + if (wasDeleted) + return; + + if (d->error.isValid()) { + if (!d->addError(ep)) ep->warning(this->error()); + } else { + d->removeError(); + } + } + + d->updating = false; + d->deleted = 0; + } else { + qmlInfo(d->property.object()) << tr("Binding loop detected for property \"%1\"").arg(d->property.name()); + } +} + +void QDeclarativeBindingPrivate::emitValueChanged() +{ + Q_Q(QDeclarativeBinding); + q->update(); +} + +void QDeclarativeBinding::setEnabled(bool e, QDeclarativePropertyPrivate::WriteFlags flags) +{ + Q_D(QDeclarativeBinding); + d->enabled = e; + setNotifyOnValueChanged(e); + + if (e) + update(flags); +} + +bool QDeclarativeBinding::enabled() const +{ + Q_D(const QDeclarativeBinding); + + return d->enabled; +} + +QString QDeclarativeBinding::expression() const +{ + return QDeclarativeExpression::expression(); +} + +QDeclarativeValueTypeProxyBinding::QDeclarativeValueTypeProxyBinding(QObject *o, int index) +: m_object(o), m_index(index), m_bindings(0) +{ +} + +QDeclarativeValueTypeProxyBinding::~QDeclarativeValueTypeProxyBinding() +{ + while (m_bindings) { + QDeclarativeAbstractBinding *binding = m_bindings; + binding->setEnabled(false, 0); + binding->destroy(); + } +} + +void QDeclarativeValueTypeProxyBinding::setEnabled(bool e, QDeclarativePropertyPrivate::WriteFlags flags) +{ + if (e) { + QDeclarativeAbstractBinding *bindings = m_bindings; + recursiveEnable(bindings, flags); + } else { + QDeclarativeAbstractBinding *bindings = m_bindings; + recursiveDisable(bindings); + } +} + +void QDeclarativeValueTypeProxyBinding::recursiveEnable(QDeclarativeAbstractBinding *b, QDeclarativePropertyPrivate::WriteFlags flags) +{ + if (!b) + return; + + recursiveEnable(b->m_nextBinding, flags); + + if (b) + b->setEnabled(true, flags); +} + +void QDeclarativeValueTypeProxyBinding::recursiveDisable(QDeclarativeAbstractBinding *b) +{ + if (!b) + return; + + recursiveDisable(b->m_nextBinding); + + if (b) + b->setEnabled(false, 0); +} + +void QDeclarativeValueTypeProxyBinding::update(QDeclarativePropertyPrivate::WriteFlags) +{ +} + +QDeclarativeAbstractBinding *QDeclarativeValueTypeProxyBinding::binding(int propertyIndex) +{ + QDeclarativeAbstractBinding *binding = m_bindings; + + while (binding && binding->propertyIndex() != propertyIndex) + binding = binding->m_nextBinding; + + return binding; +} + +/*! +Removes a collection of bindings, corresponding to the set bits in \a mask. +*/ +void QDeclarativeValueTypeProxyBinding::removeBindings(quint32 mask) +{ + QDeclarativeAbstractBinding *binding = m_bindings; + while (binding) { + if (mask & (1 << (binding->propertyIndex() >> 24))) { + QDeclarativeAbstractBinding *remove = binding; + binding = remove->m_nextBinding; + *remove->m_prevBinding = remove->m_nextBinding; + if (remove->m_nextBinding) remove->m_nextBinding->m_prevBinding = remove->m_prevBinding; + remove->m_prevBinding = 0; + remove->m_nextBinding = 0; + remove->destroy(); + } else { + binding = binding->m_nextBinding; + } + } +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativebinding_p.h b/src/declarative/qml/qdeclarativebinding_p.h new file mode 100644 index 00000000..042b45d0 --- /dev/null +++ b/src/declarative/qml/qdeclarativebinding_p.h @@ -0,0 +1,193 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEBINDING_P_H +#define QDECLARATIVEBINDING_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 "qdeclarative.h" +#include "qdeclarativepropertyvaluesource.h" +#include "qdeclarativeexpression.h" +#include "qdeclarativeproperty.h" +#include "private/qdeclarativeproperty_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeAbstractBinding +{ +public: + typedef QWeakPointer Pointer; + + QDeclarativeAbstractBinding(); + + virtual void destroy(); + + virtual QString expression() const; + + enum Type { PropertyBinding, ValueTypeProxy }; + virtual Type bindingType() const { return PropertyBinding; } + + QObject *object() const; + int propertyIndex() const; + + void setEnabled(bool e) { setEnabled(e, QDeclarativePropertyPrivate::DontRemoveBinding); } + virtual void setEnabled(bool, QDeclarativePropertyPrivate::WriteFlags) = 0; + + void update() { update(QDeclarativePropertyPrivate::DontRemoveBinding); } + virtual void update(QDeclarativePropertyPrivate::WriteFlags) = 0; + + void addToObject(QObject *, int); + void removeFromObject(); + + static Pointer getPointer(QDeclarativeAbstractBinding *p) { return p ? p->weakPointer() : Pointer(); } + +protected: + virtual ~QDeclarativeAbstractBinding(); + void clear(); + +private: + Pointer weakPointer(); + + friend class QDeclarativeData; + friend class QDeclarativeComponentPrivate; + friend class QDeclarativeValueTypeProxyBinding; + friend class QDeclarativePropertyPrivate; + friend class QDeclarativeVME; + friend class QtSharedPointer::ExternalRefCount; + + QObject *m_object; + int m_propertyIndex; + QDeclarativeAbstractBinding **m_mePtr; + QDeclarativeAbstractBinding **m_prevBinding; + QDeclarativeAbstractBinding *m_nextBinding; + QSharedPointer m_selfPointer; +}; + +class QDeclarativeValueTypeProxyBinding : public QDeclarativeAbstractBinding +{ +public: + QDeclarativeValueTypeProxyBinding(QObject *o, int coreIndex); + + virtual Type bindingType() const { return ValueTypeProxy; } + + virtual void setEnabled(bool, QDeclarativePropertyPrivate::WriteFlags); + virtual void update(QDeclarativePropertyPrivate::WriteFlags); + + QDeclarativeAbstractBinding *binding(int propertyIndex); + + void removeBindings(quint32 mask); + +protected: + ~QDeclarativeValueTypeProxyBinding(); + +private: + void recursiveEnable(QDeclarativeAbstractBinding *, QDeclarativePropertyPrivate::WriteFlags); + void recursiveDisable(QDeclarativeAbstractBinding *); + + friend class QDeclarativeAbstractBinding; + QObject *m_object; + int m_index; + QDeclarativeAbstractBinding *m_bindings; +}; + +class QDeclarativeContext; +class QDeclarativeBindingPrivate; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeBinding : public QDeclarativeExpression, public QDeclarativeAbstractBinding +{ +Q_OBJECT +public: + enum EvaluateFlag { RequiresThisObject = 0x01 }; + Q_DECLARE_FLAGS(EvaluateFlags, EvaluateFlag) + + QDeclarativeBinding(const QString &, QObject *, QDeclarativeContext *, QObject *parent=0); + QDeclarativeBinding(const QString &, QObject *, QDeclarativeContextData *, QObject *parent=0); + QDeclarativeBinding(void *, QDeclarativeRefCount *, QObject *, QDeclarativeContextData *, + const QString &, int, QObject *parent); + QDeclarativeBinding(const QScriptValue &, QObject *, QDeclarativeContextData *, QObject *parent=0); + + void setTarget(const QDeclarativeProperty &); + QDeclarativeProperty property() const; + + void setEvaluateFlags(EvaluateFlags flags); + EvaluateFlags evaluateFlags() const; + + bool enabled() const; + + // Inherited from QDeclarativeAbstractBinding + virtual void setEnabled(bool, QDeclarativePropertyPrivate::WriteFlags flags); + virtual void update(QDeclarativePropertyPrivate::WriteFlags flags); + virtual QString expression() const; + + typedef int Identifier; + static Identifier Invalid; + static QDeclarativeBinding *createBinding(Identifier, QObject *, QDeclarativeContext *, const QString &, int, QObject *parent=0); + +public Q_SLOTS: + void update() { update(QDeclarativePropertyPrivate::DontRemoveBinding); } + +protected: + ~QDeclarativeBinding(); + void emitValueChanged(); + +private: + Q_DECLARE_PRIVATE(QDeclarativeBinding) +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QDeclarativeBinding::EvaluateFlags) + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QDeclarativeBinding*) + +#endif // QDECLARATIVEBINDING_P_H diff --git a/src/declarative/qml/qdeclarativebinding_p_p.h b/src/declarative/qml/qdeclarativebinding_p_p.h new file mode 100644 index 00000000..0c99d013 --- /dev/null +++ b/src/declarative/qml/qdeclarativebinding_p_p.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEBINDING_P_P_H +#define QDECLARATIVEBINDING_P_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 "private/qdeclarativebinding_p.h" + +#include "qdeclarativeproperty.h" +#include "private/qdeclarativeexpression_p.h" + +QT_BEGIN_NAMESPACE + +class QDeclarativeBindingPrivate : public QDeclarativeExpressionPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeBinding) +public: + QDeclarativeBindingPrivate(); + ~QDeclarativeBindingPrivate(); + + virtual void emitValueChanged(); + +protected: + virtual void refresh(); + +private: + bool updating:1; + bool enabled:1; + QDeclarativeProperty property; + + bool *deleted; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVEBINDING_P_P_H diff --git a/src/declarative/qml/qdeclarativeboundsignal.cpp b/src/declarative/qml/qdeclarativeboundsignal.cpp new file mode 100644 index 00000000..a8aece9f --- /dev/null +++ b/src/declarative/qml/qdeclarativeboundsignal.cpp @@ -0,0 +1,306 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativeboundsignal_p.h" + +#include "private/qmetaobjectbuilder_p.h" +#include "private/qdeclarativeengine_p.h" +#include "private/qdeclarativeexpression_p.h" +#include "private/qdeclarativecontext_p.h" +#include "private/qdeclarativemetatype_p.h" +#include "qdeclarative.h" +#include "qdeclarativecontext.h" +#include "private/qdeclarativeglobal_p.h" +#include "private/qdeclarativedebugtrace_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeBoundSignalParameters : public QObject +{ +Q_OBJECT +public: + QDeclarativeBoundSignalParameters(const QMetaMethod &, QObject * = 0); + ~QDeclarativeBoundSignalParameters(); + + void setValues(void **); + void clearValues(); + +private: + friend class MetaObject; + int metaCall(QMetaObject::Call, int _id, void **); + struct MetaObject : public QAbstractDynamicMetaObject { + MetaObject(QDeclarativeBoundSignalParameters *b) + : parent(b) {} + + int metaCall(QMetaObject::Call c, int id, void **a) { + return parent->metaCall(c, id, a); + } + QDeclarativeBoundSignalParameters *parent; + }; + + int *types; + void **values; + QMetaObject *myMetaObject; +}; + +static int evaluateIdx = -1; + +QDeclarativeAbstractBoundSignal::QDeclarativeAbstractBoundSignal(QObject *parent) +: QObject(parent) +{ +} + +QDeclarativeAbstractBoundSignal::~QDeclarativeAbstractBoundSignal() +{ +} + +QDeclarativeBoundSignal::QDeclarativeBoundSignal(QObject *scope, const QMetaMethod &signal, + QObject *parent) +: m_expression(0), m_signal(signal), m_paramsValid(false), m_isEvaluating(false), m_params(0) +{ + // This is thread safe. Although it may be updated by two threads, they + // will both set it to the same value - so the worst thing that can happen + // is that they both do the work to figure it out. Boo hoo. + if (evaluateIdx == -1) evaluateIdx = metaObject()->methodCount(); + + QDeclarative_setParent_noEvent(this, parent); + QDeclarativePropertyPrivate::connect(scope, m_signal.methodIndex(), this, evaluateIdx); +} + +QDeclarativeBoundSignal::QDeclarativeBoundSignal(QDeclarativeContext *ctxt, const QString &val, + QObject *scope, const QMetaMethod &signal, + QObject *parent) +: m_expression(0), m_signal(signal), m_paramsValid(false), m_isEvaluating(false), m_params(0) +{ + // This is thread safe. Although it may be updated by two threads, they + // will both set it to the same value - so the worst thing that can happen + // is that they both do the work to figure it out. Boo hoo. + if (evaluateIdx == -1) evaluateIdx = metaObject()->methodCount(); + + QDeclarative_setParent_noEvent(this, parent); + QDeclarativePropertyPrivate::connect(scope, m_signal.methodIndex(), this, evaluateIdx); + + m_expression = new QDeclarativeExpression(ctxt, scope, val); +} + +QDeclarativeBoundSignal::~QDeclarativeBoundSignal() +{ + delete m_expression; + m_expression = 0; +} + +int QDeclarativeBoundSignal::index() const +{ + return m_signal.methodIndex(); +} + +/*! + Returns the signal expression. +*/ +QDeclarativeExpression *QDeclarativeBoundSignal::expression() const +{ + return m_expression; +} + +/*! + Sets the signal expression to \a e. Returns the current signal expression, + or null if there is no signal expression. + + The QDeclarativeBoundSignal instance takes ownership of \a e. The caller is + assumes ownership of the returned QDeclarativeExpression. +*/ +QDeclarativeExpression *QDeclarativeBoundSignal::setExpression(QDeclarativeExpression *e) +{ + QDeclarativeExpression *rv = m_expression; + m_expression = e; + if (m_expression) m_expression->setNotifyOnValueChanged(false); + return rv; +} + +QDeclarativeBoundSignal *QDeclarativeBoundSignal::cast(QObject *o) +{ + QDeclarativeAbstractBoundSignal *s = qobject_cast(o); + return static_cast(s); +} + +int QDeclarativeBoundSignal::qt_metacall(QMetaObject::Call c, int id, void **a) +{ + if (c == QMetaObject::InvokeMetaMethod && id == evaluateIdx) { + if (!m_expression) + return -1; + if (QDeclarativeDebugService::isDebuggingEnabled()) { + QDeclarativeDebugTrace::startRange(QDeclarativeDebugTrace::HandlingSignal); + QDeclarativeDebugTrace::rangeData(QDeclarativeDebugTrace::HandlingSignal, QLatin1String(m_signal.signature()) % QLatin1String(": ") % m_expression->expression()); + QDeclarativeDebugTrace::rangeLocation(QDeclarativeDebugTrace::HandlingSignal, m_expression->sourceFile(), m_expression->lineNumber()); + } + m_isEvaluating = true; + if (!m_paramsValid) { + if (!m_signal.parameterTypes().isEmpty()) + m_params = new QDeclarativeBoundSignalParameters(m_signal, this); + m_paramsValid = true; + } + + if (m_params) m_params->setValues(a); + if (m_expression && m_expression->engine()) { + QDeclarativeExpressionPrivate::get(m_expression)->value(m_params); + if (m_expression && m_expression->hasError()) + QDeclarativeEnginePrivate::warning(m_expression->engine(), m_expression->error()); + } + if (m_params) m_params->clearValues(); + m_isEvaluating = false; + QDeclarativeDebugTrace::endRange(QDeclarativeDebugTrace::HandlingSignal); + return -1; + } else { + return QObject::qt_metacall(c, id, a); + } +} + +QDeclarativeBoundSignalParameters::QDeclarativeBoundSignalParameters(const QMetaMethod &method, + QObject *parent) +: QObject(parent), types(0), values(0) +{ + MetaObject *mo = new MetaObject(this); + + // ### Optimize! + QMetaObjectBuilder mob; + mob.setSuperClass(&QDeclarativeBoundSignalParameters::staticMetaObject); + mob.setClassName("QDeclarativeBoundSignalParameters"); + + QList paramTypes = method.parameterTypes(); + QList paramNames = method.parameterNames(); + types = new int[paramTypes.count()]; + for (int ii = 0; ii < paramTypes.count(); ++ii) { + const QByteArray &type = paramTypes.at(ii); + const QByteArray &name = paramNames.at(ii); + + if (name.isEmpty() || type.isEmpty()) { + types[ii] = 0; + continue; + } + + QVariant::Type t = (QVariant::Type)QMetaType::type(type.constData()); + if (QDeclarativeMetaType::isQObject(t)) { + types[ii] = QMetaType::QObjectStar; + QMetaPropertyBuilder prop = mob.addProperty(name, "QObject*"); + prop.setWritable(false); + } else { + QByteArray propType = type; + if (t >= QVariant::UserType || t == QVariant::Invalid) { + //copy of QDeclarativeObjectScriptClass::enumType() + QByteArray scope; + QByteArray name; + int scopeIdx = propType.lastIndexOf("::"); + if (scopeIdx != -1) { + scope = propType.left(scopeIdx); + name = propType.mid(scopeIdx + 2); + } else { + name = propType; + } + const QMetaObject *meta; + if (scope == "Qt") + meta = &QObject::staticQtMetaObject; + else + meta = parent->parent()->metaObject(); //### assumes parent->parent() + for (int i = meta->enumeratorCount() - 1; i >= 0; --i) { + QMetaEnum m = meta->enumerator(i); + if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope))) { + t = QVariant::Int; + propType = "int"; + break; + } + } + } + if (QDeclarativeMetaType::canCopy(t)) { + types[ii] = t; + QMetaPropertyBuilder prop = mob.addProperty(name, propType); + prop.setWritable(false); + } else { + types[ii] = 0x80000000 | t; + QMetaPropertyBuilder prop = mob.addProperty(name, "QVariant"); + prop.setWritable(false); + } + } + } + myMetaObject = mob.toMetaObject(); + *static_cast(mo) = *myMetaObject; + + d_ptr->metaObject = mo; +} + +QDeclarativeBoundSignalParameters::~QDeclarativeBoundSignalParameters() +{ + delete [] types; + qFree(myMetaObject); +} + +void QDeclarativeBoundSignalParameters::setValues(void **v) +{ + values = v; +} + +void QDeclarativeBoundSignalParameters::clearValues() +{ + values = 0; +} + +int QDeclarativeBoundSignalParameters::metaCall(QMetaObject::Call c, int id, void **a) +{ + if (!values) + return -1; + + if (c == QMetaObject::ReadProperty && id >= 1) { + if (types[id - 1] & 0x80000000) { + *((QVariant *)a[0]) = QVariant(types[id - 1] & 0x7FFFFFFF, values[id]); + } else { + QDeclarativeMetaType::copy(types[id - 1], a[0], values[id]); + } + return -1; + } else { + return qt_metacall(c, id, a); + } +} + +QT_END_NAMESPACE + +#include diff --git a/src/declarative/qml/qdeclarativeboundsignal_p.h b/src/declarative/qml/qdeclarativeboundsignal_p.h new file mode 100644 index 00000000..8a46179e --- /dev/null +++ b/src/declarative/qml/qdeclarativeboundsignal_p.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEBOUNDSIGNAL_P_H +#define QDECLARATIVEBOUNDSIGNAL_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 "qdeclarativeexpression.h" + +#include + +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeAbstractBoundSignal : public QObject +{ + Q_OBJECT +public: + QDeclarativeAbstractBoundSignal(QObject *parent = 0); + virtual ~QDeclarativeAbstractBoundSignal() = 0; +}; + +class QDeclarativeBoundSignalParameters; +class QDeclarativeBoundSignal : public QDeclarativeAbstractBoundSignal +{ +public: + QDeclarativeBoundSignal(QObject *scope, const QMetaMethod &signal, QObject *parent); + QDeclarativeBoundSignal(QDeclarativeContext *ctxt, const QString &val, QObject *scope, + const QMetaMethod &signal, QObject *parent); + virtual ~QDeclarativeBoundSignal(); + + int index() const; + + QDeclarativeExpression *expression() const; + QDeclarativeExpression *setExpression(QDeclarativeExpression *); + + bool isEvaluating() const { return m_isEvaluating; } + + static QDeclarativeBoundSignal *cast(QObject *); + +protected: + virtual int qt_metacall(QMetaObject::Call c, int id, void **a); + +private: + QDeclarativeExpression *m_expression; + QMetaMethod m_signal; + bool m_paramsValid : 1; + bool m_isEvaluating : 1; + QDeclarativeBoundSignalParameters *m_params; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVEBOUNDSIGNAL_P_H diff --git a/src/declarative/qml/qdeclarativecleanup.cpp b/src/declarative/qml/qdeclarativecleanup.cpp new file mode 100644 index 00000000..0af72694 --- /dev/null +++ b/src/declarative/qml/qdeclarativecleanup.cpp @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativecleanup_p.h" + +#include "private/qdeclarativeengine_p.h" + +QT_BEGIN_NAMESPACE + +/*! +\internal +\class QDeclarativeCleanup +\brief The QDeclarativeCleanup provides a callback when a QDeclarativeEngine is deleted. + +Any object that needs cleanup to occur before the QDeclarativeEngine's QScriptEngine is +destroyed should inherit from QDeclarativeCleanup. The clear() virtual method will be +called by QDeclarativeEngine just before it deletes the QScriptEngine. +*/ + +/*! +\internal + +Create a QDeclarativeCleanup for \a engine +*/ +QDeclarativeCleanup::QDeclarativeCleanup(QDeclarativeEngine *engine) +: prev(0), next(0) +{ + if (!engine) + return; + + QDeclarativeEnginePrivate *p = QDeclarativeEnginePrivate::get(engine); + + if (p->cleanup) next = p->cleanup; + p->cleanup = this; + prev = &p->cleanup; + if (next) next->prev = &next; +} + +/*! +\internal +*/ +QDeclarativeCleanup::~QDeclarativeCleanup() +{ + if (prev) *prev = next; + if (next) next->prev = prev; + prev = 0; + next = 0; +} +QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativecleanup_p.h b/src/declarative/qml/qdeclarativecleanup_p.h new file mode 100644 index 00000000..65688c81 --- /dev/null +++ b/src/declarative/qml/qdeclarativecleanup_p.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVECLEANUP_P_H +#define QDECLARATIVECLEANUP_P_H + +#include + +// +// 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_BEGIN_NAMESPACE + +class QDeclarativeEngine; +class QDeclarativeCleanup +{ +public: + QDeclarativeCleanup(QDeclarativeEngine *); + virtual ~QDeclarativeCleanup(); + +protected: + virtual void clear() = 0; + +private: + friend class QDeclarativeEnginePrivate; + QDeclarativeCleanup **prev; + QDeclarativeCleanup *next; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVECLEANUP_P_H + diff --git a/src/declarative/qml/qdeclarativecompiledbindings.cpp b/src/declarative/qml/qdeclarativecompiledbindings.cpp new file mode 100644 index 00000000..798c97f2 --- /dev/null +++ b/src/declarative/qml/qdeclarativecompiledbindings.cpp @@ -0,0 +1,2921 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// #define COMPILEDBINDINGS_DEBUG +// #define REGISTER_CLEANUP_DEBUG + +#include "private/qdeclarativecompiledbindings_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +DEFINE_BOOL_CONFIG_OPTION(qmlExperimental, QML_EXPERIMENTAL); +DEFINE_BOOL_CONFIG_OPTION(qmlDisableOptimizer, QML_DISABLE_OPTIMIZER); +DEFINE_BOOL_CONFIG_OPTION(qmlDisableFastProperties, QML_DISABLE_FAST_PROPERTIES); +DEFINE_BOOL_CONFIG_OPTION(bindingsDump, QML_BINDINGS_DUMP); + +Q_GLOBAL_STATIC(QDeclarativeFastProperties, fastProperties); + +#if defined(Q_CC_GNU) && (!defined(Q_CC_INTEL) || __INTEL_COMPILER >= 1200) +# define QML_THREADED_INTERPRETER +#endif + +#define FOR_EACH_QML_INSTR(F) \ + F(Noop) /* Nop */ \ + F(BindingId) /* id */ \ + F(Subscribe) /* subscribe */ \ + F(SubscribeId) /* subscribe */ \ + F(FetchAndSubscribe) /* fetchAndSubscribe */ \ + F(LoadId) /* load */ \ + F(LoadScope) /* load */ \ + F(LoadRoot) /* load */ \ + F(LoadAttached) /* attached */ \ + F(ConvertIntToReal) /* unaryop */ \ + F(ConvertRealToInt) /* unaryop */ \ + F(Real) /* real_value */ \ + F(Int) /* int_value */ \ + F(Bool) /* bool_value */ \ + F(String) /* string_value */ \ + F(AddReal) /* binaryop */ \ + F(AddInt) /* binaryop */ \ + F(AddString) /* binaryop */ \ + F(MinusReal) /* binaryop */ \ + F(MinusInt) /* binaryop */ \ + F(CompareReal) /* binaryop */ \ + F(CompareString) /* binaryop */ \ + F(NotCompareReal) /* binaryop */ \ + F(NotCompareString) /* binaryop */ \ + F(GreaterThanReal) /* binaryop */ \ + F(MaxReal) /* binaryop */ \ + F(MinReal) /* binaryop */ \ + F(NewString) /* construct */ \ + F(NewUrl) /* construct */ \ + F(CleanupUrl) /* cleanup */ \ + F(CleanupString) /* cleanup */ \ + F(Copy) /* copy */ \ + F(Fetch) /* fetch */ \ + F(Store) /* store */ \ + F(Skip) /* skip */ \ + F(Done) /* done */ \ + /* Speculative property resolution */ \ + F(InitString) /* initstring */ \ + F(FindGeneric) /* find */ \ + F(FindGenericTerminal) /* find */ \ + F(FindProperty) /* find */ \ + F(FindPropertyTerminal) /* find */ \ + F(CleanupGeneric) /* cleanup */ \ + F(ConvertGenericToReal) /* unaryop */ \ + F(ConvertGenericToBool) /* unaryop */ \ + F(ConvertGenericToString) /* unaryop */ \ + F(ConvertGenericToUrl) /* unaryop */ + +#define QML_INSTR_ENUM(I) I, +#define QML_INSTR_ADDR(I) &&op_##I, + +#ifdef QML_THREADED_INTERPRETER +# define QML_BEGIN_INSTR(I) op_##I: +# define QML_END_INSTR(I) ++instr; goto *instr->common.code; +# define QML_INSTR_HEADER void *code; +#else +# define QML_BEGIN_INSTR(I) case Instr::I: +# define QML_END_INSTR(I) break; +# define QML_INSTR_HEADER +#endif + + +using namespace QDeclarativeJS; + +namespace { +// Supported types: int, qreal, QString (needs constr/destr), QObject*, bool +struct Register { + void setUndefined() { type = 0; } + void setUnknownButDefined() { type = -1; } + void setNaN() { setqreal(qSNaN()); } + bool isUndefined() const { return type == 0; } + + void setQObject(QObject *o) { qobjectValue = o; type = QMetaType::QObjectStar; } + QObject *getQObject() const { return qobjectValue; } + + void setqreal(qreal v) { qrealValue = v; type = QMetaType::QReal; } + qreal getqreal() const { return qrealValue; } + + void setint(int v) { intValue = v; type = QMetaType::Int; } + int getint() const { return intValue; } + + void setbool(bool v) { boolValue = v; type = QMetaType::Bool; } + bool getbool() const { return boolValue; } + + QVariant *getvariantptr() { return (QVariant *)typeDataPtr(); } + QString *getstringptr() { return (QString *)typeDataPtr(); } + QUrl *geturlptr() { return (QUrl *)typeDataPtr(); } + const QVariant *getvariantptr() const { return (QVariant *)typeDataPtr(); } + const QString *getstringptr() const { return (QString *)typeDataPtr(); } + const QUrl *geturlptr() const { return (QUrl *)typeDataPtr(); } + + void *typeDataPtr() { return (void *)&data; } + void *typeMemory() { return (void *)data; } + const void *typeDataPtr() const { return (void *)&data; } + const void *typeMemory() const { return (void *)data; } + + int gettype() const { return type; } + void settype(int t) { type = t; } + + int type; // Optional type + union { + QObject *qobjectValue; + qreal qrealValue; + int intValue; + bool boolValue; + char data[sizeof(QVariant)]; + qint64 q_for_alignment_1; + double q_for_alignment_2; + }; + +#ifdef REGISTER_CLEANUP_DEBUG + Register() { + type = 0; + } + + ~Register() { + int allowedTypes[] = { QMetaType::QObjectStar, QMetaType::QReal, QMetaType::Int, QMetaType::Bool, 0 }; + bool found = (type == 0); + int *ctype = allowedTypes; + while (!found && *ctype) { + found = (*ctype == type); + ++ctype; + } + if (!found) + qWarning("Register leaked of type %d", type); + } +#endif +}; +} + +class QDeclarativeCompiledBindingsPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeCompiledBindings) + +public: + QDeclarativeCompiledBindingsPrivate(); + virtual ~QDeclarativeCompiledBindingsPrivate(); + + struct Binding : public QDeclarativeAbstractBinding, public QDeclarativeDelayedError { + Binding() : enabled(false), updating(0), property(0), + scope(0), target(0), parent(0) {} + + // Inherited from QDeclarativeAbstractBinding + virtual void setEnabled(bool, QDeclarativePropertyPrivate::WriteFlags flags); + virtual void update(QDeclarativePropertyPrivate::WriteFlags flags); + virtual void destroy(); + + int index:30; + bool enabled:1; + bool updating:1; + int property; + QObject *scope; + QObject *target; + + QDeclarativeCompiledBindingsPrivate *parent; + }; + + typedef QDeclarativeNotifierEndpoint Subscription; + Subscription *subscriptions; + QScriptDeclarativeClass::PersistentIdentifier *identifiers; + + void run(Binding *, QDeclarativePropertyPrivate::WriteFlags flags); + + const char *programData; + QDeclarativeRefCount *dataRef; + Binding *m_bindings; + quint32 *m_signalTable; + + static int methodCount; + + void init(); + void run(int instr, QDeclarativeContextData *context, + QDeclarativeDelayedError *error, QObject *scope, QObject *output, QDeclarativePropertyPrivate::WriteFlags storeFlags); + + + inline void unsubscribe(int subIndex); + inline void subscribeId(QDeclarativeContextData *p, int idIndex, int subIndex); + inline void subscribe(QObject *o, int notifyIndex, int subIndex); + + QDeclarativePropertyCache::Data *findproperty(QObject *obj, + const QScriptDeclarativeClass::Identifier &name, + QDeclarativeEnginePrivate *enginePriv, + QDeclarativePropertyCache::Data &local); + bool findproperty(QObject *obj, + Register *output, + QDeclarativeEnginePrivate *enginePriv, + int subIdx, + const QScriptDeclarativeClass::Identifier &name, + bool isTerminal); + void findgeneric(Register *output, // value output + int subIdx, // Subscription index in config + QDeclarativeContextData *context, // Context to search in + const QScriptDeclarativeClass::Identifier &name, + bool isTerminal); +}; + +QDeclarativeCompiledBindingsPrivate::QDeclarativeCompiledBindingsPrivate() +: subscriptions(0), identifiers(0), programData(0), dataRef(0), m_bindings(0), m_signalTable(0) +{ +} + +QDeclarativeCompiledBindingsPrivate::~QDeclarativeCompiledBindingsPrivate() +{ + delete [] subscriptions; subscriptions = 0; + delete [] identifiers; identifiers = 0; + if (dataRef) { + dataRef->release(); + dataRef = 0; + } +} + +int QDeclarativeCompiledBindingsPrivate::methodCount = -1; + +QDeclarativeCompiledBindings::QDeclarativeCompiledBindings(const char *program, QDeclarativeContextData *context, + QDeclarativeRefCount *dataRef) +: QObject(*(new QDeclarativeCompiledBindingsPrivate)) +{ + Q_D(QDeclarativeCompiledBindings); + + if (d->methodCount == -1) + d->methodCount = QDeclarativeCompiledBindings::staticMetaObject.methodCount(); + + d->programData = program; + d->dataRef = dataRef; + if (d->dataRef) d->dataRef->addref(); + + d->init(); + + QDeclarativeAbstractExpression::setContext(context); +} + +QDeclarativeCompiledBindings::~QDeclarativeCompiledBindings() +{ + Q_D(QDeclarativeCompiledBindings); + + delete [] d->m_bindings; +} + +QDeclarativeAbstractBinding *QDeclarativeCompiledBindings::configBinding(int index, QObject *target, + QObject *scope, int property) +{ + Q_D(QDeclarativeCompiledBindings); + + QDeclarativeCompiledBindingsPrivate::Binding *rv = d->m_bindings + index; + + rv->index = index; + rv->property = property; + rv->target = target; + rv->scope = scope; + rv->parent = d; + + addref(); // This is decremented in Binding::destroy() + + return rv; +} + +void QDeclarativeCompiledBindingsPrivate::Binding::setEnabled(bool e, QDeclarativePropertyPrivate::WriteFlags flags) +{ + if (enabled != e) { + enabled = e; + + if (e) update(flags); + } +} + +void QDeclarativeCompiledBindingsPrivate::Binding::update(QDeclarativePropertyPrivate::WriteFlags flags) +{ + QDeclarativeDebugTrace::startRange(QDeclarativeDebugTrace::Binding); + parent->run(this, flags); + QDeclarativeDebugTrace::endRange(QDeclarativeDebugTrace::Binding); +} + +void QDeclarativeCompiledBindingsPrivate::Binding::destroy() +{ + enabled = false; + removeFromObject(); + clear(); + parent->q_func()->release(); +} + +int QDeclarativeCompiledBindings::qt_metacall(QMetaObject::Call c, int id, void **) +{ + Q_D(QDeclarativeCompiledBindings); + + if (c == QMetaObject::InvokeMetaMethod && id >= d->methodCount) { + id -= d->methodCount; + + quint32 *reeval = d->m_signalTable + d->m_signalTable[id]; + quint32 count = *reeval; + ++reeval; + for (quint32 ii = 0; ii < count; ++ii) { + d->run(d->m_bindings + reeval[ii], QDeclarativePropertyPrivate::DontRemoveBinding); + } + } + return -1; +} + +void QDeclarativeCompiledBindingsPrivate::run(Binding *binding, QDeclarativePropertyPrivate::WriteFlags flags) +{ + Q_Q(QDeclarativeCompiledBindings); + + if (!binding->enabled) + return; + + QDeclarativeContextData *context = q->QDeclarativeAbstractExpression::context(); + if (!context || !context->isValid()) + return; + + if (binding->updating) { + QString name; + if (binding->property & 0xFFFF0000) { + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(context->engine); + + QDeclarativeValueType *vt = ep->valueTypes[(binding->property >> 16) & 0xFF]; + Q_ASSERT(vt); + + name = QLatin1String(binding->target->metaObject()->property(binding->property & 0xFFFF).name()); + name.append(QLatin1String(".")); + name.append(QLatin1String(vt->metaObject()->property(binding->property >> 24).name())); + } else { + name = QLatin1String(binding->target->metaObject()->property(binding->property).name()); + } + qmlInfo(binding->target) << QCoreApplication::translate("QDeclarativeCompiledBindings", "Binding loop detected for property \"%1\"").arg(name); + return; + } + + binding->updating = true; + if (binding->property & 0xFFFF0000) { + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(context->engine); + + QDeclarativeValueType *vt = ep->valueTypes[(binding->property >> 16) & 0xFF]; + Q_ASSERT(vt); + vt->read(binding->target, binding->property & 0xFFFF); + + QObject *target = vt; + run(binding->index, context, binding, binding->scope, target, flags); + + vt->write(binding->target, binding->property & 0xFFFF, flags); + } else { + run(binding->index, context, binding, binding->scope, binding->target, flags); + } + binding->updating = false; +} + +namespace { +// This structure is exactly 8-bytes in size +struct Instr { + enum { + FOR_EACH_QML_INSTR(QML_INSTR_ENUM) + }; + + union { + struct { + QML_INSTR_HEADER + quint8 type; + quint8 packing[7]; + } common; + struct { + QML_INSTR_HEADER + quint8 type; + quint8 packing; + quint16 column; + quint32 line; + } id; + struct { + QML_INSTR_HEADER + quint8 type; + quint8 packing[3]; + quint16 subscriptions; + quint16 identifiers; + } init; + struct { + QML_INSTR_HEADER + quint8 type; + qint8 reg; + quint16 offset; + quint32 index; + } subscribe; + struct { + QML_INSTR_HEADER + quint8 type; + qint8 reg; + quint8 packing[2]; + quint32 index; + } load; + struct { + QML_INSTR_HEADER + quint8 type; + qint8 output; + qint8 reg; + quint8 exceptionId; + quint32 id; + } attached; + struct { + QML_INSTR_HEADER + quint8 type; + qint8 output; + qint8 reg; + quint8 exceptionId; + quint32 index; + } store; + struct { + QML_INSTR_HEADER + quint8 type; + qint8 output; + qint8 objectReg; + quint8 exceptionId; + quint16 subscription; + quint16 function; + } fetchAndSubscribe; + struct { + QML_INSTR_HEADER + quint8 type; + qint8 output; + qint8 objectReg; + quint8 exceptionId; + quint32 index; + } fetch; + struct { + QML_INSTR_HEADER + quint8 type; + qint8 reg; + qint8 src; + quint8 packing[5]; + } copy; + struct { + QML_INSTR_HEADER + quint8 type; + qint8 reg; + quint8 packing[6]; + } construct; + struct { + QML_INSTR_HEADER + quint8 type; + qint8 reg; + quint8 packing[2]; + float value; + } real_value; + struct { + QML_INSTR_HEADER + quint8 type; + qint8 reg; + quint8 packing[2]; + int value; + } int_value; + struct { + QML_INSTR_HEADER + quint8 type; + qint8 reg; + bool value; + quint8 packing[5]; + } bool_value; + struct { + QML_INSTR_HEADER + quint8 type; + qint8 reg; + quint16 length; + quint32 offset; + } string_value; + struct { + QML_INSTR_HEADER + quint8 type; + qint8 output; + qint8 src1; + qint8 src2; + quint8 packing[4]; + } binaryop; + struct { + QML_INSTR_HEADER + quint8 type; + qint8 output; + qint8 src; + quint8 packing[5]; + } unaryop; + struct { + QML_INSTR_HEADER + quint8 type; + qint8 reg; + quint8 packing[2]; + quint32 count; + } skip; + struct { + QML_INSTR_HEADER + quint8 type; + qint8 reg; + qint8 src; + quint8 exceptionId; + quint16 name; + quint16 subscribeIndex; + } find; + struct { + QML_INSTR_HEADER + quint8 type; + qint8 reg; + quint8 packing[6]; + } cleanup; + struct { + QML_INSTR_HEADER + quint8 type; + quint8 packing[1]; + quint16 offset; + quint32 dataIdx; + } initstring; + }; +}; + +struct Program { + quint32 bindings; + quint32 dataLength; + quint32 signalTableOffset; + quint32 exceptionDataOffset; + quint16 subscriptions; + quint16 identifiers; + quint16 instructionCount; + quint16 compiled; + + const char *data() const { return ((const char *)this) + sizeof(Program); } + const Instr *instructions() const { return (const Instr *)(data() + dataLength); } +}; +} + +struct QDeclarativeBindingCompilerPrivate +{ + struct Result { + Result() : unknownType(false), metaObject(0), type(-1), reg(-1) {} + bool operator==(const Result &o) const { + return unknownType == o.unknownType && + metaObject == o.metaObject && + type == o.type && + reg == o.reg; + } + bool operator!=(const Result &o) const { + return !(*this == o); + } + bool unknownType; + const QMetaObject *metaObject; + int type; + int reg; + + QSet subscriptionSet; + }; + + QDeclarativeBindingCompilerPrivate() : registers(0) {} + + void resetInstanceState(); + int commitCompile(); + + QDeclarativeParser::Object *context; + QDeclarativeParser::Object *component; + QDeclarativeParser::Property *destination; + QHash ids; + QDeclarativeImports imports; + QDeclarativeEnginePrivate *engine; + + QString contextName() const { return QLatin1String("$$$SCOPE_") + QString::number((quintptr)context, 16); } + + bool compile(QDeclarativeJS::AST::Node *); + + bool parseExpression(QDeclarativeJS::AST::Node *, Result &); + + bool tryName(QDeclarativeJS::AST::Node *); + bool parseName(QDeclarativeJS::AST::Node *, Result &); + + bool tryArith(QDeclarativeJS::AST::Node *); + bool parseArith(QDeclarativeJS::AST::Node *, Result &); + bool numberArith(Result &, const Result &, const Result &, QSOperator::Op op); + bool stringArith(Result &, const Result &, const Result &, QSOperator::Op op); + + bool tryLogic(QDeclarativeJS::AST::Node *); + bool parseLogic(QDeclarativeJS::AST::Node *, Result &); + + bool tryConditional(QDeclarativeJS::AST::Node *); + bool parseConditional(QDeclarativeJS::AST::Node *, Result &); + + bool tryConstant(QDeclarativeJS::AST::Node *); + bool parseConstant(QDeclarativeJS::AST::Node *, Result &); + + bool tryMethod(QDeclarativeJS::AST::Node *); + bool parseMethod(QDeclarativeJS::AST::Node *, Result &); + + bool buildName(QStringList &, QDeclarativeJS::AST::Node *, QList *nodes = 0); + bool fetch(Result &type, const QMetaObject *, int reg, int idx, const QStringList &, QDeclarativeJS::AST::ExpressionNode *); + + quint32 registers; + QHash > registerCleanups; + int acquireReg(int cleanup = Instr::Noop, int cleanupType = 0); + void registerCleanup(int reg, int cleanup, int cleanupType = 0); + void releaseReg(int); + + int registerLiteralString(const QString &); + int registerString(const QString &); + QHash > registeredStrings; + QByteArray data; + + bool subscription(const QStringList &, Result *); + int subscriptionIndex(const QStringList &); + bool subscriptionNeutral(const QSet &base, const QSet &lhs, const QSet &rhs); + + quint8 exceptionId(QDeclarativeJS::AST::ExpressionNode *); + QVector exceptions; + + QSet usedSubscriptionIds; + QSet subscriptionSet; + QHash subscriptionIds; + QVector bytecode; + + // Committed binding data + struct { + QList offsets; + QList > dependencies; + + QVector bytecode; + QByteArray data; + QHash subscriptionIds; + QVector exceptions; + + QHash > registeredStrings; + + int count() const { return offsets.count(); } + } committed; + + QByteArray buildSignalTable() const; + QByteArray buildExceptionData() const; +}; + +void QDeclarativeCompiledBindingsPrivate::unsubscribe(int subIndex) +{ + QDeclarativeCompiledBindingsPrivate::Subscription *sub = (subscriptions + subIndex); + sub->disconnect(); +} + +void QDeclarativeCompiledBindingsPrivate::subscribeId(QDeclarativeContextData *p, int idIndex, int subIndex) +{ + Q_Q(QDeclarativeCompiledBindings); + + unsubscribe(subIndex); + + if (p->idValues[idIndex]) { + QDeclarativeCompiledBindingsPrivate::Subscription *sub = (subscriptions + subIndex); + sub->target = q; + sub->targetMethod = methodCount + subIndex; + sub->connect(&p->idValues[idIndex].bindings); + } +} + +void QDeclarativeCompiledBindingsPrivate::subscribe(QObject *o, int notifyIndex, int subIndex) +{ + Q_Q(QDeclarativeCompiledBindings); + + QDeclarativeCompiledBindingsPrivate::Subscription *sub = (subscriptions + subIndex); + sub->target = q; + sub->targetMethod = methodCount + subIndex; + if (o) + sub->connect(o, notifyIndex); + else + sub->disconnect(); +} + +// Conversion functions - these MUST match the QtScript expression path +inline static qreal toReal(Register *reg, int type, bool *ok = 0) +{ + if (ok) *ok = true; + + if (type == QMetaType::QReal) { + return reg->getqreal(); + } else if (type == qMetaTypeId()) { + return reg->getvariantptr()->toReal(); + } else { + if (ok) *ok = false; + return 0; + } +} + +inline static QString toString(Register *reg, int type, bool *ok = 0) +{ + if (ok) *ok = true; + + if (type == QMetaType::QReal) { + return QString::number(reg->getqreal()); + } else if (type == QMetaType::Int) { + return QString::number(reg->getint()); + } else if (type == qMetaTypeId()) { + return reg->getvariantptr()->toString(); + } else if (type == QMetaType::QString) { + return *reg->getstringptr(); + } else { + if (ok) *ok = false; + return QString(); + } +} + +inline static bool toBool(Register *reg, int type, bool *ok = 0) +{ + if (ok) *ok = true; + + if (type == QMetaType::Bool) { + return reg->getbool(); + } else if (type == qMetaTypeId()) { + return reg->getvariantptr()->toBool(); + } else { + if (ok) *ok = false; + return false; + } +} + +inline static QUrl toUrl(Register *reg, int type, QDeclarativeContextData *context, bool *ok = 0) +{ + if (ok) *ok = true; + + QUrl base; + if (type == qMetaTypeId()) { + QVariant *var = reg->getvariantptr(); + int vt = var->type(); + if (vt == QVariant::Url) { + base = var->toUrl(); + } else if (vt == QVariant::ByteArray) { + base = QUrl(QString::fromUtf8(var->toByteArray())); + } else if (vt == QVariant::String) { + base = QUrl(var->toString()); + } else { + if (ok) *ok = false; + return QUrl(); + } + } else if (type == QMetaType::QString) { + base = QUrl(*reg->getstringptr()); + } else { + if (ok) *ok = false; + return QUrl(); + } + + if (!base.isEmpty() && base.isRelative()) + return context->url.resolved(base); + else + return base; +} + +static QObject *variantToQObject(const QVariant &value, bool *ok) +{ + if (ok) *ok = true; + + if (value.userType() == QMetaType::QObjectStar) { + return qvariant_cast(value); + } else { + if (ok) *ok = false; + return 0; + } +} + +bool QDeclarativeCompiledBindingsPrivate::findproperty(QObject *obj, Register *output, + QDeclarativeEnginePrivate *enginePriv, + int subIdx, const QScriptDeclarativeClass::Identifier &name, + bool isTerminal) +{ + if (!obj) { + output->setUndefined(); + return false; + } + + QDeclarativePropertyCache::Data local; + QDeclarativePropertyCache::Data *property = + QDeclarativePropertyCache::property(QDeclarativeEnginePrivate::get(enginePriv), obj, name, local); + + if (property) { + if (subIdx != -1) + subscribe(obj, property->notifyIndex, subIdx); + + if (property->flags & QDeclarativePropertyCache::Data::IsQObjectDerived) { + void *args[] = { output->typeDataPtr(), 0 }; + QMetaObject::metacall(obj, QMetaObject::ReadProperty, property->coreIndex, args); + output->settype(QMetaType::QObjectStar); + } else if (property->propType == qMetaTypeId()) { + QVariant v; + void *args[] = { &v, 0 }; + QMetaObject::metacall(obj, QMetaObject::ReadProperty, property->coreIndex, args); + + if (isTerminal) { + new (output->typeDataPtr()) QVariant(v); + output->settype(qMetaTypeId()); + } else { + bool ok; + output->setQObject(variantToQObject(v, &ok)); + if (!ok) + output->setUndefined(); + else + output->settype(QMetaType::QObjectStar); + } + + } else { + if (!isTerminal) { + output->setUndefined(); + } else if (property->propType == QMetaType::QReal) { + void *args[] = { output->typeDataPtr(), 0 }; + QMetaObject::metacall(obj, QMetaObject::ReadProperty, property->coreIndex, args); + output->settype(QMetaType::QReal); + } else if (property->propType == QMetaType::Int) { + void *args[] = { output->typeDataPtr(), 0 }; + QMetaObject::metacall(obj, QMetaObject::ReadProperty, property->coreIndex, args); + output->settype(QMetaType::Int); + } else if (property->propType == QMetaType::Bool) { + void *args[] = { output->typeDataPtr(), 0 }; + QMetaObject::metacall(obj, QMetaObject::ReadProperty, property->coreIndex, args); + output->settype(QMetaType::Bool); + } else if (property->propType == QMetaType::QString) { + new (output->typeDataPtr()) QString(); + void *args[] = { output->typeDataPtr(), 0 }; + QMetaObject::metacall(obj, QMetaObject::ReadProperty, property->coreIndex, args); + output->settype(QMetaType::QString); + } else { + new (output->typeDataPtr()) + QVariant(obj->metaObject()->property(property->coreIndex).read(obj)); + output->settype(qMetaTypeId()); + } + } + + return true; + } else { + output->setUndefined(); + return false; + } +} + +void QDeclarativeCompiledBindingsPrivate::findgeneric(Register *output, + int subIdx, + QDeclarativeContextData *context, + const QScriptDeclarativeClass::Identifier &name, + bool isTerminal) +{ + QDeclarativeEnginePrivate *enginePriv = QDeclarativeEnginePrivate::get(context->engine); + + while (context) { + + int contextPropertyIndex = context->propertyNames?context->propertyNames->value(name):-1; + + + if (contextPropertyIndex != -1) { + + if (contextPropertyIndex < context->idValueCount) { + output->setQObject(context->idValues[contextPropertyIndex]); + output->settype(QMetaType::QObjectStar); + + if (subIdx != -1) + subscribeId(context, contextPropertyIndex, subIdx); + + } else { + QDeclarativeContextPrivate *cp = context->asQDeclarativeContextPrivate(); + const QVariant &value = cp->propertyValues.at(contextPropertyIndex); + + if (isTerminal) { + new (output->typeDataPtr()) QVariant(value); + output->settype(qMetaTypeId()); + } else { + bool ok; + output->setQObject(variantToQObject(value, &ok)); + if (!ok) { output->setUndefined(); } + else { output->settype(QMetaType::QObjectStar); } + return; + } + + if (subIdx != -1) + subscribe(context->asQDeclarativeContext(), contextPropertyIndex + cp->notifyIndex, subIdx); + + + } + + return; + } + + if (QObject *root = context->contextObject) { + + if (findproperty(root, output, enginePriv, subIdx, name, isTerminal)) + return; + + } + + context = context->parent; + } + + output->setUndefined(); +} + +void QDeclarativeCompiledBindingsPrivate::init() +{ + Program *program = (Program *)programData; + if (program->subscriptions) + subscriptions = new QDeclarativeCompiledBindingsPrivate::Subscription[program->subscriptions]; + if (program->identifiers) + identifiers = new QScriptDeclarativeClass::PersistentIdentifier[program->identifiers]; + + m_signalTable = (quint32 *)(program->data() + program->signalTableOffset); + m_bindings = new QDeclarativeCompiledBindingsPrivate::Binding[program->bindings]; +} + +static void throwException(int id, QDeclarativeDelayedError *error, + Program *program, QDeclarativeContextData *context, + const QString &description = QString()) +{ + error->error.setUrl(context->url); + if (description.isEmpty()) + error->error.setDescription(QLatin1String("TypeError: Result of expression is not an object")); + else + error->error.setDescription(description); + if (id != 0xFF) { + quint64 e = *((quint64 *)(program->data() + program->exceptionDataOffset) + id); + error->error.setLine((e >> 32) & 0xFFFFFFFF); + error->error.setColumn(e & 0xFFFFFFFF); + } else { + error->error.setLine(-1); + error->error.setColumn(-1); + } + if (!context->engine || !error->addError(QDeclarativeEnginePrivate::get(context->engine))) + QDeclarativeEnginePrivate::warning(context->engine, error->error); +} + +static void dumpInstruction(const Instr *instr) +{ + switch (instr->common.type) { + case Instr::Noop: + qWarning().nospace() << "\t" << "Noop"; + break; + case Instr::BindingId: + qWarning().nospace() << instr->id.line << ":" << instr->id.column << ":"; + break; + case Instr::Subscribe: + qWarning().nospace() << "\t" << "Subscribe" << "\t\t" << instr->subscribe.offset << "\t" << instr->subscribe.reg << "\t" << instr->subscribe.index; + break; + case Instr::SubscribeId: + qWarning().nospace() << "\t" << "SubscribeId" << "\t\t" << instr->subscribe.offset << "\t" << instr->subscribe.reg << "\t" << instr->subscribe.index; + break; + case Instr::FetchAndSubscribe: + qWarning().nospace() << "\t" << "FetchAndSubscribe" << "\t" << instr->fetchAndSubscribe.output << "\t" << instr->fetchAndSubscribe.objectReg << "\t" << instr->fetchAndSubscribe.subscription; + break; + case Instr::LoadId: + qWarning().nospace() << "\t" << "LoadId" << "\t\t\t" << instr->load.index << "\t" << instr->load.reg; + break; + case Instr::LoadScope: + qWarning().nospace() << "\t" << "LoadScope" << "\t\t" << instr->load.index << "\t" << instr->load.reg; + break; + case Instr::LoadRoot: + qWarning().nospace() << "\t" << "LoadRoot" << "\t\t" << instr->load.index << "\t" << instr->load.reg; + break; + case Instr::LoadAttached: + qWarning().nospace() << "\t" << "LoadAttached" << "\t\t" << instr->attached.output << "\t" << instr->attached.reg << "\t" << instr->attached.id; + break; + case Instr::ConvertIntToReal: + qWarning().nospace() << "\t" << "ConvertIntToReal" << "\t" << instr->unaryop.output << "\t" << instr->unaryop.src; + break; + case Instr::ConvertRealToInt: + qWarning().nospace() << "\t" << "ConvertRealToInt" << "\t" << instr->unaryop.output << "\t" << instr->unaryop.src; + break; + case Instr::Real: + qWarning().nospace() << "\t" << "Real" << "\t\t\t" << instr->real_value.reg << "\t" << instr->real_value.value; + break; + case Instr::Int: + qWarning().nospace() << "\t" << "Int" << "\t\t\t" << instr->int_value.reg << "\t" << instr->int_value.value; + break; + case Instr::Bool: + qWarning().nospace() << "\t" << "Bool" << "\t\t\t" << instr->bool_value.reg << "\t" << instr->bool_value.value; + break; + case Instr::String: + qWarning().nospace() << "\t" << "String" << "\t\t\t" << instr->string_value.reg << "\t" << instr->string_value.offset << "\t" << instr->string_value.length; + break; + case Instr::AddReal: + qWarning().nospace() << "\t" << "AddReal" << "\t\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; + break; + case Instr::AddInt: + qWarning().nospace() << "\t" << "AddInt" << "\t\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; + break; + case Instr::AddString: + qWarning().nospace() << "\t" << "AddString" << "\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; + break; + case Instr::MinusReal: + qWarning().nospace() << "\t" << "MinusReal" << "\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; + break; + case Instr::MinusInt: + qWarning().nospace() << "\t" << "MinusInt" << "\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; + break; + case Instr::CompareReal: + qWarning().nospace() << "\t" << "CompareReal" << "\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; + break; + case Instr::CompareString: + qWarning().nospace() << "\t" << "CompareString" << "\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; + break; + case Instr::NotCompareReal: + qWarning().nospace() << "\t" << "NotCompareReal" << "\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; + break; + case Instr::NotCompareString: + qWarning().nospace() << "\t" << "NotCompareString" << "\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; + break; + case Instr::GreaterThanReal: + qWarning().nospace() << "\t" << "GreaterThanReal" << "\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; + break; + case Instr::MaxReal: + qWarning().nospace() << "\t" << "MaxReal" << "\t\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; + break; + case Instr::MinReal: + qWarning().nospace() << "\t" << "MinReal" << "\t\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; + break; + case Instr::NewString: + qWarning().nospace() << "\t" << "NewString" << "\t\t" << instr->construct.reg; + break; + case Instr::NewUrl: + qWarning().nospace() << "\t" << "NewUrl" << "\t\t\t" << instr->construct.reg; + break; + case Instr::CleanupString: + qWarning().nospace() << "\t" << "CleanupString" << "\t\t" << instr->cleanup.reg; + break; + case Instr::CleanupUrl: + qWarning().nospace() << "\t" << "CleanupUrl" << "\t\t" << instr->cleanup.reg; + break; + case Instr::Fetch: + qWarning().nospace() << "\t" << "Fetch" << "\t\t\t" << instr->fetch.output << "\t" << instr->fetch.index << "\t" << instr->fetch.objectReg; + break; + case Instr::Store: + qWarning().nospace() << "\t" << "Store" << "\t\t\t" << instr->store.output << "\t" << instr->store.index << "\t" << instr->store.reg; + break; + case Instr::Copy: + qWarning().nospace() << "\t" << "Copy" << "\t\t\t" << instr->copy.reg << "\t" << instr->copy.src; + break; + case Instr::Skip: + qWarning().nospace() << "\t" << "Skip" << "\t\t\t" << instr->skip.reg << "\t" << instr->skip.count; + break; + case Instr::Done: + qWarning().nospace() << "\t" << "Done"; + break; + case Instr::InitString: + qWarning().nospace() << "\t" << "InitString" << "\t\t" << instr->initstring.offset << "\t" << instr->initstring.dataIdx; + break; + case Instr::FindGeneric: + qWarning().nospace() << "\t" << "FindGeneric" << "\t\t" << instr->find.reg << "\t" << instr->find.name; + break; + case Instr::FindGenericTerminal: + qWarning().nospace() << "\t" << "FindGenericTerminal" << "\t" << instr->find.reg << "\t" << instr->find.name; + break; + case Instr::FindProperty: + qWarning().nospace() << "\t" << "FindProperty" << "\t\t" << instr->find.reg << "\t" << instr->find.src << "\t" << instr->find.name; + break; + case Instr::FindPropertyTerminal: + qWarning().nospace() << "\t" << "FindPropertyTerminal" << "\t" << instr->find.reg << "\t" << instr->find.src << "\t" << instr->find.name; + break; + case Instr::CleanupGeneric: + qWarning().nospace() << "\t" << "CleanupGeneric" << "\t\t" << instr->cleanup.reg; + break; + case Instr::ConvertGenericToReal: + qWarning().nospace() << "\t" << "ConvertGenericToReal" << "\t" << instr->unaryop.output << "\t" << instr->unaryop.src; + break; + case Instr::ConvertGenericToBool: + qWarning().nospace() << "\t" << "ConvertGenericToBool" << "\t" << instr->unaryop.output << "\t" << instr->unaryop.src; + break; + case Instr::ConvertGenericToString: + qWarning().nospace() << "\t" << "ConvertGenericToString" << "\t" << instr->unaryop.output << "\t" << instr->unaryop.src; + break; + case Instr::ConvertGenericToUrl: + qWarning().nospace() << "\t" << "ConvertGenericToUrl" << "\t" << instr->unaryop.output << "\t" << instr->unaryop.src; + break; + default: + qWarning().nospace() << "\t" << "Unknown"; + break; + } +} + +void QDeclarativeCompiledBindingsPrivate::run(int instrIndex, + QDeclarativeContextData *context, QDeclarativeDelayedError *error, + QObject *scope, QObject *output, QDeclarativePropertyPrivate::WriteFlags storeFlags) +{ + Q_Q(QDeclarativeCompiledBindings); + + error->removeError(); + + Register registers[32]; + + QDeclarativeEnginePrivate *engine = QDeclarativeEnginePrivate::get(context->engine); + Program *program = (Program *)programData; + const Instr *instr = program->instructions(); + instr += instrIndex; + const char *data = program->data(); + +#ifdef QML_THREADED_INTERPRETER + static void *decode_instr[] = { + FOR_EACH_QML_INSTR(QML_INSTR_ADDR) + }; + + if (!program->compiled) { + program->compiled = true; + const Instr *inop = program->instructions(); + for (int i = 0; i < program->instructionCount; ++i) { + Instr *op = (Instr *) inop++; + op->common.code = decode_instr[op->common.type]; + } + } + + goto *instr->common.code; +#else + // return; + +#ifdef COMPILEDBINDINGS_DEBUG + qWarning().nospace() << "Begin binding run"; +#endif + + while (instr) { + switch (instr->common.type) { + +#ifdef COMPILEDBINDINGS_DEBUG + dumpInstruction(instr); +#endif + +#endif + + QML_BEGIN_INSTR(Noop) + QML_END_INSTR(Noop) + + QML_BEGIN_INSTR(BindingId) + QML_END_INSTR(BindingId) + + QML_BEGIN_INSTR(SubscribeId) + subscribeId(context, instr->subscribe.index, instr->subscribe.offset); + QML_END_INSTR(SubscribeId) + + QML_BEGIN_INSTR(Subscribe) + { + QObject *o = 0; + const Register &object = registers[instr->subscribe.reg]; + if (!object.isUndefined()) o = object.getQObject(); + subscribe(o, instr->subscribe.index, instr->subscribe.offset); + } + QML_END_INSTR(Subscribe) + + QML_BEGIN_INSTR(FetchAndSubscribe) + { + const Register &input = registers[instr->fetchAndSubscribe.objectReg]; + Register &output = registers[instr->fetchAndSubscribe.output]; + + if (input.isUndefined()) { + throwException(instr->fetchAndSubscribe.exceptionId, error, program, context); + return; + } + + QObject *object = input.getQObject(); + if (!object) { + output.setUndefined(); + } else { + int subIdx = instr->fetchAndSubscribe.subscription; + QDeclarativeCompiledBindingsPrivate::Subscription *sub = 0; + if (subIdx != -1) { + sub = (subscriptions + subIdx); + sub->target = q; + sub->targetMethod = methodCount + subIdx; + } + fastProperties()->accessor(instr->fetchAndSubscribe.function)(object, output.typeDataPtr(), sub); + } + } + QML_END_INSTR(FetchAndSubscribe) + + QML_BEGIN_INSTR(LoadId) + registers[instr->load.reg].setQObject(context->idValues[instr->load.index].data()); + QML_END_INSTR(LoadId) + + QML_BEGIN_INSTR(LoadScope) + registers[instr->load.reg].setQObject(scope); + QML_END_INSTR(LoadScope) + + QML_BEGIN_INSTR(LoadRoot) + registers[instr->load.reg].setQObject(context->contextObject); + QML_END_INSTR(LoadRoot) + + QML_BEGIN_INSTR(LoadAttached) + { + const Register &input = registers[instr->attached.reg]; + Register &output = registers[instr->attached.output]; + if (input.isUndefined()) { + throwException(instr->attached.exceptionId, error, program, context); + return; + } + + QObject *object = registers[instr->attached.reg].getQObject(); + if (!object) { + output.setUndefined(); + } else { + QObject *attached = + qmlAttachedPropertiesObjectById(instr->attached.id, + registers[instr->attached.reg].getQObject(), + true); + Q_ASSERT(attached); + output.setQObject(attached); + } + } + QML_END_INSTR(LoadAttached) + + QML_BEGIN_INSTR(ConvertIntToReal) + { + const Register &input = registers[instr->unaryop.src]; + Register &output = registers[instr->unaryop.output]; + if (input.isUndefined()) output.setUndefined(); + else output.setqreal(qreal(input.getint())); + } + QML_END_INSTR(ConvertIntToReal) + + QML_BEGIN_INSTR(ConvertRealToInt) + { + const Register &input = registers[instr->unaryop.src]; + Register &output = registers[instr->unaryop.output]; + if (input.isUndefined()) output.setUndefined(); + else output.setint(qRound(input.getqreal())); + } + QML_END_INSTR(ConvertRealToInt) + + QML_BEGIN_INSTR(Real) + registers[instr->real_value.reg].setqreal(instr->real_value.value); + QML_END_INSTR(Real) + + QML_BEGIN_INSTR(Int) + registers[instr->int_value.reg].setint(instr->int_value.value); + QML_END_INSTR(Int) + + QML_BEGIN_INSTR(Bool) + registers[instr->bool_value.reg].setbool(instr->bool_value.value); + QML_END_INSTR(Bool) + + QML_BEGIN_INSTR(String) + { + Register &output = registers[instr->string_value.reg]; + new (output.getstringptr()) + QString((QChar *)(data + instr->string_value.offset), instr->string_value.length); + output.settype(QMetaType::QString); + } + QML_END_INSTR(String) + + QML_BEGIN_INSTR(AddReal) + { + const Register &lhs = registers[instr->binaryop.src1]; + const Register &rhs = registers[instr->binaryop.src2]; + Register &output = registers[instr->binaryop.output]; + if (lhs.isUndefined() || rhs.isUndefined()) output.setNaN(); + else output.setqreal(lhs.getqreal() + rhs.getqreal()); + } + QML_END_INSTR(AddReal) + + QML_BEGIN_INSTR(AddInt) + { + const Register &lhs = registers[instr->binaryop.src1]; + const Register &rhs = registers[instr->binaryop.src2]; + Register &output = registers[instr->binaryop.output]; + if (lhs.isUndefined() || rhs.isUndefined()) output.setNaN(); + else output.setint(lhs.getint() + rhs.getint()); + } + QML_END_INSTR(AddInt) + + QML_BEGIN_INSTR(AddString) + { + const Register &lhs = registers[instr->binaryop.src1]; + const Register &rhs = registers[instr->binaryop.src2]; + Register &output = registers[instr->binaryop.output]; + if (lhs.isUndefined() && rhs.isUndefined()) { output.setNaN(); } + else { + if (lhs.isUndefined()) + new (output.getstringptr()) + QString(QLatin1String("undefined") + *registers[instr->binaryop.src2].getstringptr()); + else if (rhs.isUndefined()) + new (output.getstringptr()) + QString(*registers[instr->binaryop.src1].getstringptr() + QLatin1String("undefined")); + else + new (output.getstringptr()) + QString(*registers[instr->binaryop.src1].getstringptr() + + *registers[instr->binaryop.src2].getstringptr()); + output.settype(QMetaType::QString); + } + } + QML_END_INSTR(AddString) + + QML_BEGIN_INSTR(MinusReal) + { + const Register &lhs = registers[instr->binaryop.src1]; + const Register &rhs = registers[instr->binaryop.src2]; + Register &output = registers[instr->binaryop.output]; + if (lhs.isUndefined() || rhs.isUndefined()) output.setNaN(); + else output.setqreal(lhs.getqreal() - rhs.getqreal()); + } + QML_END_INSTR(MinusReal) + + QML_BEGIN_INSTR(MinusInt) + { + const Register &lhs = registers[instr->binaryop.src1]; + const Register &rhs = registers[instr->binaryop.src2]; + Register &output = registers[instr->binaryop.output]; + if (lhs.isUndefined() || rhs.isUndefined()) output.setNaN(); + else output.setint(lhs.getint() - rhs.getint()); + } + QML_END_INSTR(MinusInt) + + QML_BEGIN_INSTR(CompareReal) + { + const Register &lhs = registers[instr->binaryop.src1]; + const Register &rhs = registers[instr->binaryop.src2]; + Register &output = registers[instr->binaryop.output]; + if (lhs.isUndefined() || rhs.isUndefined()) output.setbool(lhs.isUndefined() == rhs.isUndefined()); + else output.setbool(lhs.getqreal() == rhs.getqreal()); + } + QML_END_INSTR(CompareReal) + + QML_BEGIN_INSTR(CompareString) + { + const Register &lhs = registers[instr->binaryop.src1]; + const Register &rhs = registers[instr->binaryop.src2]; + Register &output = registers[instr->binaryop.output]; + if (lhs.isUndefined() || rhs.isUndefined()) output.setbool(lhs.isUndefined() == rhs.isUndefined()); + else output.setbool(*lhs.getstringptr() == *rhs.getstringptr()); + } + QML_END_INSTR(CompareString) + + QML_BEGIN_INSTR(NotCompareReal) + { + const Register &lhs = registers[instr->binaryop.src1]; + const Register &rhs = registers[instr->binaryop.src2]; + Register &output = registers[instr->binaryop.output]; + if (lhs.isUndefined() || rhs.isUndefined()) output.setbool(lhs.isUndefined() != rhs.isUndefined()); + else output.setbool(lhs.getqreal() != rhs.getqreal()); + } + QML_END_INSTR(NotCompareReal) + + QML_BEGIN_INSTR(NotCompareString) + { + const Register &lhs = registers[instr->binaryop.src1]; + const Register &rhs = registers[instr->binaryop.src2]; + Register &output = registers[instr->binaryop.output]; + if (lhs.isUndefined() || rhs.isUndefined()) output.setbool(lhs.isUndefined() != rhs.isUndefined()); + else output.setbool(*lhs.getstringptr() != *rhs.getstringptr()); + } + QML_END_INSTR(NotCompareString) + + QML_BEGIN_INSTR(GreaterThanReal) + { + const Register &lhs = registers[instr->binaryop.src1]; + const Register &rhs = registers[instr->binaryop.src2]; + Register &output = registers[instr->binaryop.output]; + if (lhs.isUndefined() || rhs.isUndefined()) output.setbool(false); + else output.setbool(lhs.getqreal() > rhs.getqreal()); + } + QML_END_INSTR(GreaterThanReal) + + QML_BEGIN_INSTR(MaxReal) + { + const Register &lhs = registers[instr->binaryop.src1]; + const Register &rhs = registers[instr->binaryop.src2]; + Register &output = registers[instr->binaryop.output]; + if (lhs.isUndefined() || rhs.isUndefined()) output.setNaN(); + else output.setqreal(qMax(lhs.getqreal(), rhs.getqreal())); + } + QML_END_INSTR(MaxReal) + + QML_BEGIN_INSTR(MinReal) + { + const Register &lhs = registers[instr->binaryop.src1]; + const Register &rhs = registers[instr->binaryop.src2]; + Register &output = registers[instr->binaryop.output]; + if (lhs.isUndefined() || rhs.isUndefined()) output.setNaN(); + else output.setqreal(qMin(lhs.getqreal(), rhs.getqreal())); + } + QML_END_INSTR(MinReal) + + QML_BEGIN_INSTR(NewString) + { + Register &output = registers[instr->construct.reg]; + new (output.getstringptr()) QString; + output.settype(QMetaType::QString); + } + QML_END_INSTR(NewString) + + QML_BEGIN_INSTR(NewUrl) + { + Register &output = registers[instr->construct.reg]; + new (output.geturlptr()) QUrl; + output.settype(QMetaType::QUrl); + } + QML_END_INSTR(NewUrl) + + QML_BEGIN_INSTR(CleanupString) + registers[instr->cleanup.reg].getstringptr()->~QString(); +#ifdef REGISTER_CLEANUP_DEBUG + registers[instr->cleanup.reg].setUndefined(); +#endif + QML_END_INSTR(CleanupString) + + QML_BEGIN_INSTR(CleanupUrl) + registers[instr->cleanup.reg].geturlptr()->~QUrl(); +#ifdef REGISTER_CLEANUP_DEBUG + registers[instr->cleanup.reg].setUndefined(); +#endif + QML_END_INSTR(CleanupUrl) + + QML_BEGIN_INSTR(Fetch) + { + const Register &input = registers[instr->fetch.objectReg]; + Register &output = registers[instr->fetch.output]; + + if (input.isUndefined()) { + throwException(instr->fetch.exceptionId, error, program, context); + return; + } + + QObject *object = input.getQObject(); + if (!object) { + output.setUndefined(); + } else { + void *argv[] = { output.typeDataPtr(), 0 }; + QMetaObject::metacall(object, QMetaObject::ReadProperty, instr->fetch.index, argv); + } + } + QML_END_INSTR(Fetch) + + QML_BEGIN_INSTR(Store) + { + Register &data = registers[instr->store.reg]; + if (data.isUndefined()) { + throwException(instr->store.exceptionId, error, program, context, + QLatin1String("Unable to assign undefined value")); + return; + } + + int status = -1; + void *argv[] = { data.typeDataPtr(), 0, &status, &storeFlags }; + QMetaObject::metacall(output, QMetaObject::WriteProperty, + instr->store.index, argv); + } + QML_END_INSTR(Store) + + QML_BEGIN_INSTR(Copy) + registers[instr->copy.reg] = registers[instr->copy.src]; + QML_END_INSTR(Copy) + + QML_BEGIN_INSTR(Skip) + if (instr->skip.reg == -1 || !registers[instr->skip.reg].getbool()) + instr += instr->skip.count; + QML_END_INSTR(Skip) + + QML_BEGIN_INSTR(Done) + return; + QML_END_INSTR(Done) + + QML_BEGIN_INSTR(InitString) + if (!identifiers[instr->initstring.offset].identifier) { + quint32 len = *(quint32 *)(data + instr->initstring.dataIdx); + QChar *strdata = (QChar *)(data + instr->initstring.dataIdx + sizeof(quint32)); + + QString str = QString::fromRawData(strdata, len); + + identifiers[instr->initstring.offset] = engine->objectClass->createPersistentIdentifier(str); + } + QML_END_INSTR(InitString) + + QML_BEGIN_INSTR(FindGenericTerminal) + // We start the search in the parent context, as we know that the + // name is not present in the current context or it would have been + // found during the static compile + findgeneric(registers + instr->find.reg, instr->find.subscribeIndex, + context->parent, + identifiers[instr->find.name].identifier, + instr->common.type == Instr::FindGenericTerminal); + QML_END_INSTR(FindGenericTerminal) + + QML_BEGIN_INSTR(FindGeneric) + // We start the search in the parent context, as we know that the + // name is not present in the current context or it would have been + // found during the static compile + findgeneric(registers + instr->find.reg, instr->find.subscribeIndex, + context->parent, + identifiers[instr->find.name].identifier, + instr->common.type == Instr::FindGenericTerminal); + QML_END_INSTR(FindGeneric) + + QML_BEGIN_INSTR(FindPropertyTerminal) + { + const Register &object = registers[instr->find.src]; + if (object.isUndefined()) { + throwException(instr->find.exceptionId, error, program, context); + return; + } + + findproperty(object.getQObject(), registers + instr->find.reg, + QDeclarativeEnginePrivate::get(context->engine), + instr->find.subscribeIndex, identifiers[instr->find.name].identifier, + instr->common.type == Instr::FindPropertyTerminal); + } + QML_END_INSTR(FindPropertyTerminal) + + QML_BEGIN_INSTR(FindProperty) + { + const Register &object = registers[instr->find.src]; + if (object.isUndefined()) { + throwException(instr->find.exceptionId, error, program, context); + return; + } + + findproperty(object.getQObject(), registers + instr->find.reg, + QDeclarativeEnginePrivate::get(context->engine), + instr->find.subscribeIndex, identifiers[instr->find.name].identifier, + instr->common.type == Instr::FindPropertyTerminal); + } + QML_END_INSTR(FindProperty) + + QML_BEGIN_INSTR(CleanupGeneric) + { + int type = registers[instr->cleanup.reg].gettype(); + if (type == qMetaTypeId()) { + registers[instr->cleanup.reg].getvariantptr()->~QVariant(); +#ifdef REGISTER_CLEANUP_DEBUG + registers[instr->cleanup.reg].setUndefined(); +#endif + } else if (type == QMetaType::QString) { + registers[instr->cleanup.reg].getstringptr()->~QString(); +#ifdef REGISTER_CLEANUP_DEBUG + registers[instr->cleanup.reg].setUndefined(); +#endif + } else if (type == QMetaType::QUrl) { + registers[instr->cleanup.reg].geturlptr()->~QUrl(); +#ifdef REGISTER_CLEANUP_DEBUG + registers[instr->cleanup.reg].setUndefined(); +#endif + } + } + QML_END_INSTR(CleanupGeneric) + + QML_BEGIN_INSTR(ConvertGenericToReal) + { + Register &output = registers[instr->unaryop.output]; + Register &input = registers[instr->unaryop.src]; + bool ok = true; + output.setqreal(toReal(&input, input.gettype(), &ok)); + if (!ok) output.setUndefined(); + } + QML_END_INSTR(ConvertGenericToReal) + + QML_BEGIN_INSTR(ConvertGenericToBool) + { + Register &output = registers[instr->unaryop.output]; + Register &input = registers[instr->unaryop.src]; + bool ok = true; + output.setbool(toBool(&input, input.gettype(), &ok)); + if (!ok) output.setUndefined(); + } + QML_END_INSTR(ConvertGenericToBool) + + QML_BEGIN_INSTR(ConvertGenericToString) + { + Register &output = registers[instr->unaryop.output]; + Register &input = registers[instr->unaryop.src]; + bool ok = true; + QString str = toString(&input, input.gettype(), &ok); + if (ok) { new (output.getstringptr()) QString(str); output.settype(QMetaType::QString); } + else { output.setUndefined(); } + } + QML_END_INSTR(ConvertGenericToString) + + QML_BEGIN_INSTR(ConvertGenericToUrl) + { + Register &output = registers[instr->unaryop.output]; + Register &input = registers[instr->unaryop.src]; + bool ok = true; + QUrl url = toUrl(&input, input.gettype(), context, &ok); + if (ok) { new (output.geturlptr()) QUrl(url); output.settype(QMetaType::QUrl); } + else { output.setUndefined(); } + } + QML_END_INSTR(ConvertGenericToUrl) + +#ifdef QML_THREADED_INTERPRETER + // nothing to do +#else + default: + qFatal("EEK"); + break; + } // switch + + ++instr; + } // while +#endif +} + +void QDeclarativeBindingCompiler::dump(const QByteArray &programData) +{ + const Program *program = (const Program *)programData.constData(); + + qWarning() << "Program.bindings:" << program->bindings; + qWarning() << "Program.dataLength:" << program->dataLength; + qWarning() << "Program.subscriptions:" << program->subscriptions; + qWarning() << "Program.indentifiers:" << program->identifiers; + + int count = program->instructionCount; + const Instr *instr = program->instructions(); + + while (count--) { + + dumpInstruction(instr); + ++instr; + } +} + +/*! +Clear the state associated with attempting to compile a specific binding. +This does not clear the global "committed binding" states. +*/ +void QDeclarativeBindingCompilerPrivate::resetInstanceState() +{ + registers = 0; + registerCleanups.clear(); + data = committed.data; + exceptions = committed.exceptions; + usedSubscriptionIds.clear(); + subscriptionSet.clear(); + subscriptionIds = committed.subscriptionIds; + registeredStrings = committed.registeredStrings; + bytecode.clear(); +} + +/*! +Mark the last compile as successful, and add it to the "committed data" +section. + +Returns the index for the committed binding. +*/ +int QDeclarativeBindingCompilerPrivate::commitCompile() +{ + int rv = committed.count(); + committed.offsets << committed.bytecode.count(); + committed.dependencies << usedSubscriptionIds; + committed.bytecode << bytecode; + committed.data = data; + committed.exceptions = exceptions; + committed.subscriptionIds = subscriptionIds; + committed.registeredStrings = registeredStrings; + return rv; +} + +bool QDeclarativeBindingCompilerPrivate::compile(QDeclarativeJS::AST::Node *node) +{ + resetInstanceState(); + + if (destination->type == -1) + return false; + + if (bindingsDump()) { + QDeclarativeJS::AST::ExpressionNode *n = node->expressionCast(); + if (n) { + Instr id; + id.common.type = Instr::BindingId; + id.id.column = n->firstSourceLocation().startColumn; + id.id.line = n->firstSourceLocation().startLine; + bytecode << id; + } + } + + Result type; + + if (!parseExpression(node, type)) + return false; + + if (subscriptionSet.count() > 0xFFFF || + registeredStrings.count() > 0xFFFF) + return false; + + if (type.unknownType) { + if (!qmlExperimental()) + return false; + + if (destination->type != QMetaType::QReal && + destination->type != QVariant::String && + destination->type != QMetaType::Bool && + destination->type != QVariant::Url) + return false; + + int convertReg = acquireReg(); + if (convertReg == -1) + return false; + + if (destination->type == QMetaType::QReal) { + Instr convert; + convert.common.type = Instr::ConvertGenericToReal; + convert.unaryop.output = convertReg; + convert.unaryop.src = type.reg; + bytecode << convert; + } else if (destination->type == QVariant::String) { + Instr convert; + convert.common.type = Instr::ConvertGenericToString; + convert.unaryop.output = convertReg; + convert.unaryop.src = type.reg; + bytecode << convert; + } else if (destination->type == QMetaType::Bool) { + Instr convert; + convert.common.type = Instr::ConvertGenericToBool; + convert.unaryop.output = convertReg; + convert.unaryop.src = type.reg; + bytecode << convert; + } else if (destination->type == QVariant::Url) { + Instr convert; + convert.common.type = Instr::ConvertGenericToUrl; + convert.unaryop.output = convertReg; + convert.unaryop.src = type.reg; + bytecode << convert; + } + + Instr cleanup; + cleanup.common.type = Instr::CleanupGeneric; + cleanup.cleanup.reg = type.reg; + bytecode << cleanup; + + Instr instr; + instr.common.type = Instr::Store; + instr.store.output = 0; + instr.store.index = destination->index; + instr.store.reg = convertReg; + instr.store.exceptionId = exceptionId(node->expressionCast()); + bytecode << instr; + + if (destination->type == QVariant::String) { + Instr cleanup; + cleanup.common.type = Instr::CleanupString; + cleanup.cleanup.reg = convertReg; + bytecode << cleanup; + } else if (destination->type == QVariant::Url) { + Instr cleanup; + cleanup.common.type = Instr::CleanupUrl; + cleanup.cleanup.reg = convertReg; + bytecode << cleanup; + } + + releaseReg(convertReg); + + Instr done; + done.common.type = Instr::Done; + bytecode << done; + + } else { + // Can we store the final value? + if (type.type == QVariant::Int && + destination->type == QMetaType::QReal) { + Instr instr; + instr.common.type = Instr::ConvertIntToReal; + instr.unaryop.output = type.reg; + instr.unaryop.src = type.reg; + bytecode << instr; + type.type = QMetaType::QReal; + } else if (type.type == QMetaType::QReal && + destination->type == QVariant::Int) { + Instr instr; + instr.common.type = Instr::ConvertRealToInt; + instr.unaryop.output = type.reg; + instr.unaryop.src = type.reg; + bytecode << instr; + type.type = QVariant::Int; + } else if (type.type == destination->type) { + } else { + const QMetaObject *from = type.metaObject; + const QMetaObject *to = engine->rawMetaObjectForType(destination->type); + + if (QDeclarativePropertyPrivate::canConvert(from, to)) + type.type = destination->type; + } + + if (type.type == destination->type) { + Instr instr; + instr.common.type = Instr::Store; + instr.store.output = 0; + instr.store.index = destination->index; + instr.store.reg = type.reg; + instr.store.exceptionId = exceptionId(node->expressionCast()); + bytecode << instr; + + releaseReg(type.reg); + + Instr done; + done.common.type = Instr::Done; + bytecode << done; + } else { + return false; + } + } + + return true; +} + +bool QDeclarativeBindingCompilerPrivate::parseExpression(QDeclarativeJS::AST::Node *node, Result &type) +{ + while (node->kind == AST::Node::Kind_NestedExpression) + node = static_cast(node)->expression; + + if (tryArith(node)) { + if (!parseArith(node, type)) return false; + } else if (tryLogic(node)) { + if (!parseLogic(node, type)) return false; + } else if (tryConditional(node)) { + if (!parseConditional(node, type)) return false; + } else if (tryName(node)) { + if (!parseName(node, type)) return false; + } else if (tryConstant(node)) { + if (!parseConstant(node, type)) return false; + } else if (tryMethod(node)) { + if (!parseMethod(node, type)) return false; + } else { + return false; + } + return true; +} + +bool QDeclarativeBindingCompilerPrivate::tryName(QDeclarativeJS::AST::Node *node) +{ + return node->kind == AST::Node::Kind_IdentifierExpression || + node->kind == AST::Node::Kind_FieldMemberExpression; +} + +bool QDeclarativeBindingCompilerPrivate::parseName(AST::Node *node, Result &type) +{ + QStringList nameParts; + QList nameNodes; + if (!buildName(nameParts, node, &nameNodes)) + return false; + + int reg = acquireReg(); + if (reg == -1) + return false; + type.reg = reg; + + QDeclarativeParser::Object *absType = 0; + + QStringList subscribeName; + + bool wasAttachedObject = false; + + for (int ii = 0; ii < nameParts.count(); ++ii) { + const QString &name = nameParts.at(ii); + + // We don't handle signal properties or attached properties + if (name.length() > 2 && name.startsWith(QLatin1String("on")) && + name.at(2).isUpper()) + return false; + + QDeclarativeType *attachType = 0; + if (name.at(0).isUpper()) { + // Could be an attached property + if (ii == nameParts.count() - 1) + return false; + if (nameParts.at(ii + 1).at(0).isUpper()) + return false; + + QDeclarativeImportedNamespace *ns = 0; + if (!imports.resolveType(name.toUtf8(), &attachType, 0, 0, 0, &ns)) + return false; + if (ns || !attachType || !attachType->attachedPropertiesType()) + return false; + + wasAttachedObject = true; + } + + if (ii == 0) { + + if (attachType) { + Instr instr; + instr.common.type = Instr::LoadScope; + instr.load.index = 0; + instr.load.reg = reg; + bytecode << instr; + + Instr attach; + attach.common.type = Instr::LoadAttached; + attach.attached.output = reg; + attach.attached.reg = reg; + attach.attached.id = attachType->attachedPropertiesId(); + attach.attached.exceptionId = exceptionId(nameNodes.at(ii)); + bytecode << attach; + + subscribeName << contextName(); + subscribeName << QLatin1String("$$$ATTACH_") + name; + + absType = 0; + type.metaObject = attachType->attachedPropertiesType(); + + continue; + } else if (ids.contains(name)) { + QDeclarativeParser::Object *idObject = ids.value(name); + absType = idObject; + type.metaObject = absType->metaObject(); + + // We check if the id object is the root or + // scope object to avoid a subscription + if (idObject == component) { + Instr instr; + instr.common.type = Instr::LoadRoot; + instr.load.index = 0; + instr.load.reg = reg; + bytecode << instr; + } else if (idObject == context) { + Instr instr; + instr.common.type = Instr::LoadScope; + instr.load.index = 0; + instr.load.reg = reg; + bytecode << instr; + } else { + Instr instr; + instr.common.type = Instr::LoadId; + instr.load.index = idObject->idIndex; + instr.load.reg = reg; + bytecode << instr; + + subscribeName << QLatin1String("$$$ID_") + name; + + if (subscription(subscribeName, &type)) { + Instr sub; + sub.common.type = Instr::SubscribeId; + sub.subscribe.offset = subscriptionIndex(subscribeName); + sub.subscribe.reg = reg; + sub.subscribe.index = instr.load.index; + bytecode << sub; + } + } + + } else { + + QByteArray utf8Name = name.toUtf8(); + const char *cname = utf8Name.constData(); + + int d0Idx = (context == component)?-1:context->metaObject()->indexOfProperty(cname); + int d1Idx = -1; + if (d0Idx == -1) + d1Idx = component->metaObject()->indexOfProperty(cname); + + if (d0Idx != -1) { + Instr instr; + instr.common.type = Instr::LoadScope; + instr.load.index = 0; + instr.load.reg = reg; + bytecode << instr; + + subscribeName << contextName(); + subscribeName << name; + + if (!fetch(type, context->metaObject(), reg, d0Idx, subscribeName, nameNodes.at(ii))) + return false; + } else if(d1Idx != -1) { + Instr instr; + instr.common.type = Instr::LoadRoot; + instr.load.index = 0; + instr.load.reg = reg; + bytecode << instr; + + subscribeName << QLatin1String("$$$ROOT"); + subscribeName << name; + + if (!fetch(type, component->metaObject(), reg, d1Idx, subscribeName, nameNodes.at(ii))) + return false; + } else if (qmlExperimental()) { + Instr find; + if (nameParts.count() == 1) + find.common.type = Instr::FindGenericTerminal; + else + find.common.type = Instr::FindGeneric; + + find.find.reg = reg; + find.find.src = -1; + find.find.name = registerString(name); + find.find.exceptionId = exceptionId(nameNodes.at(ii)); + + subscribeName << QString(QLatin1String("$$$Generic_") + name); + if (subscription(subscribeName, &type)) + find.find.subscribeIndex = subscriptionIndex(subscribeName); + else + find.find.subscribeIndex = -1; + + bytecode << find; + type.unknownType = true; + } + + if (!type.unknownType && type.type == -1) + return false; // Couldn't fetch that type + } + + } else { + + if (attachType) { + Instr attach; + attach.common.type = Instr::LoadAttached; + attach.attached.output = reg; + attach.attached.reg = reg; + attach.attached.id = attachType->attachedPropertiesId(); + bytecode << attach; + + absType = 0; + type.metaObject = attachType->attachedPropertiesType(); + + subscribeName << QLatin1String("$$$ATTACH_") + name; + continue; + } + + const QMetaObject *mo = 0; + if (absType) + mo = absType->metaObject(); + else if (type.metaObject) + mo = type.metaObject; + + QByteArray utf8Name = name.toUtf8(); + const char *cname = utf8Name.constData(); + int idx = mo?mo->indexOfProperty(cname):-1; + if (absType && idx == -1) + return false; + + subscribeName << name; + + if (absType || (wasAttachedObject && idx != -1) || (mo && mo->property(idx).isFinal())) { + absType = 0; + if (!fetch(type, mo, reg, idx, subscribeName, nameNodes.at(ii))) + return false; + } else { + + Instr prop; + if (ii == nameParts.count() -1 ) + prop.common.type = Instr::FindPropertyTerminal; + else + prop.common.type = Instr::FindProperty; + + prop.find.reg = reg; + prop.find.src = reg; + prop.find.name = registerString(name); + prop.find.exceptionId = exceptionId(nameNodes.at(ii)); + + if (subscription(subscribeName, &type)) + prop.find.subscribeIndex = subscriptionIndex(subscribeName); + else + prop.find.subscribeIndex = -1; + + type.unknownType = true; + type.metaObject = 0; + type.type = -1; + type.reg = reg; + bytecode << prop; + } + } + + wasAttachedObject = false; + } + + return true; +} + +bool QDeclarativeBindingCompilerPrivate::tryArith(QDeclarativeJS::AST::Node *node) +{ + if (node->kind != AST::Node::Kind_BinaryExpression) + return false; + + AST::BinaryExpression *expression = static_cast(node); + if (expression->op == QSOperator::Add || + expression->op == QSOperator::Sub) + return true; + else + return false; +} + +bool QDeclarativeBindingCompilerPrivate::parseArith(QDeclarativeJS::AST::Node *node, Result &type) +{ + AST::BinaryExpression *expression = static_cast(node); + + type.reg = acquireReg(); + if (type.reg == -1) + return false; + + Result lhs; + Result rhs; + + if (!parseExpression(expression->left, lhs)) return false; + if (!parseExpression(expression->right, rhs)) return false; + + if ((lhs.type == QVariant::Int || lhs.type == QMetaType::QReal) && + (rhs.type == QVariant::Int || rhs.type == QMetaType::QReal)) + return numberArith(type, lhs, rhs, (QSOperator::Op)expression->op); + else if(expression->op == QSOperator::Sub) + return numberArith(type, lhs, rhs, (QSOperator::Op)expression->op); + else if ((lhs.type == QMetaType::QString || lhs.unknownType) && + (rhs.type == QMetaType::QString || rhs.unknownType) && + (lhs.type == QMetaType::QString || rhs.type == QMetaType::QString)) + return stringArith(type, lhs, rhs, (QSOperator::Op)expression->op); + else + return false; +} + +bool QDeclarativeBindingCompilerPrivate::numberArith(Result &type, const Result &lhs, const Result &rhs, QSOperator::Op op) +{ + bool nativeReal = rhs.type == QMetaType::QReal || + lhs.type == QMetaType::QReal || + lhs.unknownType || + rhs.unknownType; + + if (nativeReal && lhs.type == QMetaType::Int) { + Instr convert; + convert.common.type = Instr::ConvertIntToReal; + convert.unaryop.output = lhs.reg; + convert.unaryop.src = lhs.reg; + bytecode << convert; + } + + if (nativeReal && rhs.type == QMetaType::Int) { + Instr convert; + convert.common.type = Instr::ConvertIntToReal; + convert.unaryop.output = rhs.reg; + convert.unaryop.src = rhs.reg; + bytecode << convert; + } + + int lhsTmp = -1; + int rhsTmp = -1; + + if (lhs.unknownType) { + if (!qmlExperimental()) + return false; + + lhsTmp = acquireReg(); + if (lhsTmp == -1) + return false; + + Instr conv; + conv.common.type = Instr::ConvertGenericToReal; + conv.unaryop.output = lhsTmp; + conv.unaryop.src = lhs.reg; + bytecode << conv; + } + + if (rhs.unknownType) { + if (!qmlExperimental()) + return false; + + rhsTmp = acquireReg(); + if (rhsTmp == -1) + return false; + + Instr conv; + conv.common.type = Instr::ConvertGenericToReal; + conv.unaryop.output = rhsTmp; + conv.unaryop.src = rhs.reg; + bytecode << conv; + } + + Instr arith; + if (op == QSOperator::Add) { + arith.common.type = nativeReal?Instr::AddReal:Instr::AddInt; + } else if (op == QSOperator::Sub) { + arith.common.type = nativeReal?Instr::MinusReal:Instr::MinusInt; + } else { + qFatal("Unsupported arithmetic operator"); + } + + arith.binaryop.output = type.reg; + arith.binaryop.src1 = (lhsTmp == -1)?lhs.reg:lhsTmp; + arith.binaryop.src2 = (rhsTmp == -1)?rhs.reg:rhsTmp; + bytecode << arith; + + type.metaObject = 0; + type.type = nativeReal?QMetaType::QReal:QMetaType::Int; + type.subscriptionSet.unite(lhs.subscriptionSet); + type.subscriptionSet.unite(rhs.subscriptionSet); + + if (lhsTmp != -1) releaseReg(lhsTmp); + if (rhsTmp != -1) releaseReg(rhsTmp); + releaseReg(lhs.reg); + releaseReg(rhs.reg); + + return true; +} + +bool QDeclarativeBindingCompilerPrivate::stringArith(Result &type, const Result &lhs, const Result &rhs, QSOperator::Op op) +{ + if (op != QSOperator::Add) + return false; + + int lhsTmp = -1; + int rhsTmp = -1; + + if (lhs.unknownType) { + if (!qmlExperimental()) + return false; + + lhsTmp = acquireReg(Instr::CleanupString); + if (lhsTmp == -1) + return false; + + Instr convert; + convert.common.type = Instr::ConvertGenericToString; + convert.unaryop.output = lhsTmp; + convert.unaryop.src = lhs.reg; + bytecode << convert; + } + + if (rhs.unknownType) { + if (!qmlExperimental()) + return false; + + rhsTmp = acquireReg(Instr::CleanupString); + if (rhsTmp == -1) + return false; + + Instr convert; + convert.common.type = Instr::ConvertGenericToString; + convert.unaryop.output = rhsTmp; + convert.unaryop.src = rhs.reg; + bytecode << convert; + } + + type.reg = acquireReg(Instr::CleanupString); + if (type.reg == -1) + return false; + + type.type = QMetaType::QString; + + Instr add; + add.common.type = Instr::AddString; + add.binaryop.output = type.reg; + add.binaryop.src1 = (lhsTmp == -1)?lhs.reg:lhsTmp; + add.binaryop.src2 = (rhsTmp == -1)?rhs.reg:rhsTmp; + bytecode << add; + + if (lhsTmp != -1) releaseReg(lhsTmp); + if (rhsTmp != -1) releaseReg(rhsTmp); + releaseReg(lhs.reg); + releaseReg(rhs.reg); + + return true; +} + +bool QDeclarativeBindingCompilerPrivate::tryLogic(QDeclarativeJS::AST::Node *node) +{ + if (node->kind != AST::Node::Kind_BinaryExpression) + return false; + + AST::BinaryExpression *expression = static_cast(node); + if (expression->op == QSOperator::Gt || + expression->op == QSOperator::Equal || + expression->op == QSOperator::NotEqual) + return true; + else + return false; +} + +bool QDeclarativeBindingCompilerPrivate::parseLogic(QDeclarativeJS::AST::Node *node, Result &type) +{ + AST::BinaryExpression *expression = static_cast(node); + + Result lhs; + Result rhs; + + if (!parseExpression(expression->left, lhs)) return false; + if (!parseExpression(expression->right, rhs)) return false; + + type.reg = acquireReg(); + if (type.reg == -1) + return false; + + type.metaObject = 0; + type.type = QVariant::Bool; + + if (lhs.type == QMetaType::QReal && rhs.type == QMetaType::QReal) { + + Instr op; + if (expression->op == QSOperator::Gt) + op.common.type = Instr::GreaterThanReal; + else if (expression->op == QSOperator::Equal) + op.common.type = Instr::CompareReal; + else if (expression->op == QSOperator::NotEqual) + op.common.type = Instr::NotCompareReal; + else + return false; + op.binaryop.output = type.reg; + op.binaryop.src1 = lhs.reg; + op.binaryop.src2 = rhs.reg; + bytecode << op; + + + } else if (lhs.type == QMetaType::QString && rhs.type == QMetaType::QString) { + + Instr op; + if (expression->op == QSOperator::Equal) + op.common.type = Instr::CompareString; + else if (expression->op == QSOperator::NotEqual) + op.common.type = Instr::NotCompareString; + else + return false; + op.binaryop.output = type.reg; + op.binaryop.src1 = lhs.reg; + op.binaryop.src2 = rhs.reg; + bytecode << op; + + } else { + return false; + } + + releaseReg(lhs.reg); + releaseReg(rhs.reg); + + return true; +} + +bool QDeclarativeBindingCompilerPrivate::tryConditional(QDeclarativeJS::AST::Node *node) +{ + return (node->kind == AST::Node::Kind_ConditionalExpression); +} + +bool QDeclarativeBindingCompilerPrivate::parseConditional(QDeclarativeJS::AST::Node *node, Result &type) +{ + AST::ConditionalExpression *expression = static_cast(node); + + AST::Node *test = expression->expression; + if (test->kind == AST::Node::Kind_NestedExpression) + test = static_cast(test)->expression; + + Result etype; + if (!parseExpression(test, etype)) return false; + + if (etype.type != QVariant::Bool) + return false; + + Instr skip; + skip.common.type = Instr::Skip; + skip.skip.reg = etype.reg; + skip.skip.count = 0; + int skipIdx = bytecode.count(); + bytecode << skip; + + // Release to allow reuse of reg + releaseReg(etype.reg); + + QSet preSubSet = subscriptionSet; + + // int preConditionalSubscriptions = subscriptionSet.count(); + + Result ok; + if (!parseExpression(expression->ok, ok)) return false; + if (ok.unknownType) return false; + + int skipIdx2 = bytecode.count(); + skip.skip.reg = -1; + bytecode << skip; + + // Release to allow reuse of reg in else path + releaseReg(ok.reg); + bytecode[skipIdx].skip.count = bytecode.count() - skipIdx - 1; + + subscriptionSet = preSubSet; + + Result ko; + if (!parseExpression(expression->ko, ko)) return false; + if (ko.unknownType) return false; + + // Do not release reg here, so that its ownership passes to the caller + bytecode[skipIdx2].skip.count = bytecode.count() - skipIdx2 - 1; + + if (ok != ko) + return false; // Must be same type and in same register + + subscriptionSet = preSubSet; + + if (!subscriptionNeutral(subscriptionSet, ok.subscriptionSet, ko.subscriptionSet)) + return false; // Conditionals cannot introduce new subscriptions + + type = ok; + + return true; +} + +bool QDeclarativeBindingCompilerPrivate::tryConstant(QDeclarativeJS::AST::Node *node) +{ + return node->kind == AST::Node::Kind_TrueLiteral || + node->kind == AST::Node::Kind_FalseLiteral || + node->kind == AST::Node::Kind_NumericLiteral || + node->kind == AST::Node::Kind_StringLiteral; +} + +bool QDeclarativeBindingCompilerPrivate::parseConstant(QDeclarativeJS::AST::Node *node, Result &type) +{ + type.metaObject = 0; + type.type = -1; + type.reg = acquireReg(); + if (type.reg == -1) + return false; + + if (node->kind == AST::Node::Kind_TrueLiteral) { + type.type = QVariant::Bool; + Instr instr; + instr.common.type = Instr::Bool; + instr.bool_value.reg = type.reg; + instr.bool_value.value = true; + bytecode << instr; + return true; + } else if (node->kind == AST::Node::Kind_FalseLiteral) { + type.type = QVariant::Bool; + Instr instr; + instr.common.type = Instr::Bool; + instr.bool_value.reg = type.reg; + instr.bool_value.value = false; + bytecode << instr; + return true; + } else if (node->kind == AST::Node::Kind_NumericLiteral) { + qreal value = qreal(static_cast(node)->value); + + if (qreal(float(value)) != value) + return false; + + type.type = QMetaType::QReal; + Instr instr; + instr.common.type = Instr::Real; + instr.real_value.reg = type.reg; + instr.real_value.value = float(value); + bytecode << instr; + return true; + } else if (node->kind == AST::Node::Kind_StringLiteral) { + QString str = static_cast(node)->value->asString(); + type.type = QMetaType::QString; + type.reg = registerLiteralString(str); + return true; + } else { + return false; + } +} + +bool QDeclarativeBindingCompilerPrivate::tryMethod(QDeclarativeJS::AST::Node *node) +{ + return node->kind == AST::Node::Kind_CallExpression; +} + +bool QDeclarativeBindingCompilerPrivate::parseMethod(QDeclarativeJS::AST::Node *node, Result &result) +{ + AST::CallExpression *expr = static_cast(node); + + QStringList name; + if (!buildName(name, expr->base)) + return false; + + if (name.count() != 2 || name.at(0) != QLatin1String("Math")) + return false; + + QString method = name.at(1); + + AST::ArgumentList *args = expr->arguments; + if (!args) return false; + AST::ExpressionNode *arg0 = args->expression; + args = args->next; + if (!args) return false; + AST::ExpressionNode *arg1 = args->expression; + if (args->next != 0) return false; + if (!arg0 || !arg1) return false; + + Result r0; + if (!parseExpression(arg0, r0)) return false; + Result r1; + if (!parseExpression(arg1, r1)) return false; + + if (r0.type != QMetaType::QReal || r1.type != QMetaType::QReal) + return false; + + Instr op; + if (method == QLatin1String("max")) { + op.common.type = Instr::MaxReal; + } else if (method == QLatin1String("min")) { + op.common.type = Instr::MinReal; + } else { + return false; + } + // We release early to reuse registers + releaseReg(r0.reg); + releaseReg(r1.reg); + + op.binaryop.output = acquireReg(); + if (op.binaryop.output == -1) + return false; + + op.binaryop.src1 = r0.reg; + op.binaryop.src2 = r1.reg; + bytecode << op; + + result.type = QMetaType::QReal; + result.reg = op.binaryop.output; + + return true; +} + +bool QDeclarativeBindingCompilerPrivate::buildName(QStringList &name, + QDeclarativeJS::AST::Node *node, + QList *nodes) +{ + if (node->kind == AST::Node::Kind_IdentifierExpression) { + name << static_cast(node)->name->asString(); + if (nodes) *nodes << static_cast(node); + } else if (node->kind == AST::Node::Kind_FieldMemberExpression) { + AST::FieldMemberExpression *expr = + static_cast(node); + + if (!buildName(name, expr->base, nodes)) + return false; + + name << expr->name->asString(); + if (nodes) *nodes << expr; + } else { + return false; + } + + return true; +} + +bool QDeclarativeBindingCompilerPrivate::fetch(Result &rv, const QMetaObject *mo, int reg, + int idx, const QStringList &subName, + QDeclarativeJS::AST::ExpressionNode *node) +{ + QMetaProperty prop = mo->property(idx); + rv.metaObject = 0; + rv.type = 0; + + //XXX binding optimizer doesn't handle properties with a revision + if (prop.revision() > 0) + return false; + + int fastFetchIndex = fastProperties()->accessorIndexForProperty(mo, idx); + + Instr fetch; + + if (!qmlDisableFastProperties() && fastFetchIndex != -1) { + fetch.common.type = Instr::FetchAndSubscribe; + fetch.fetchAndSubscribe.objectReg = reg; + fetch.fetchAndSubscribe.output = reg; + fetch.fetchAndSubscribe.function = fastFetchIndex; + fetch.fetchAndSubscribe.subscription = subscriptionIndex(subName); + fetch.fetchAndSubscribe.exceptionId = exceptionId(node); + } else { + if (subscription(subName, &rv) && prop.hasNotifySignal() && prop.notifySignalIndex() != -1) { + Instr sub; + sub.common.type = Instr::Subscribe; + sub.subscribe.offset = subscriptionIndex(subName); + sub.subscribe.reg = reg; + sub.subscribe.index = prop.notifySignalIndex(); + bytecode << sub; + } + + fetch.common.type = Instr::Fetch; + fetch.fetch.objectReg = reg; + fetch.fetch.index = idx; + fetch.fetch.output = reg; + fetch.fetch.exceptionId = exceptionId(node); + } + + rv.type = prop.userType(); + rv.metaObject = engine->metaObjectForType(rv.type); + rv.reg = reg; + + if (rv.type == QMetaType::QString) { + int tmp = acquireReg(); + if (tmp == -1) + return false; + Instr copy; + copy.common.type = Instr::Copy; + copy.copy.reg = tmp; + copy.copy.src = reg; + bytecode << copy; + releaseReg(tmp); + fetch.fetch.objectReg = tmp; + + Instr setup; + setup.common.type = Instr::NewString; + setup.construct.reg = reg; + bytecode << setup; + registerCleanup(reg, Instr::CleanupString); + } + + bytecode << fetch; + + if (!rv.metaObject && + rv.type != QMetaType::QReal && + rv.type != QMetaType::Int && + rv.type != QMetaType::Bool && + rv.type != qMetaTypeId() && + rv.type != QMetaType::QString) { + rv.metaObject = 0; + rv.type = 0; + return false; // Unsupported type (string not supported yet); + } + + return true; +} + +void QDeclarativeBindingCompilerPrivate::registerCleanup(int reg, int cleanup, int cleanupType) +{ + registerCleanups.insert(reg, qMakePair(cleanup, cleanupType)); +} + +int QDeclarativeBindingCompilerPrivate::acquireReg(int cleanup, int cleanupType) +{ + for (int ii = 0; ii < 32; ++ii) { + if (!(registers & (1 << ii))) { + registers |= (1 << ii); + + if (cleanup != Instr::Noop) + registerCleanup(ii, cleanup, cleanupType); + + return ii; + } + } + return -1; +} + +void QDeclarativeBindingCompilerPrivate::releaseReg(int reg) +{ + Q_ASSERT(reg >= 0 && reg <= 31); + + if (registerCleanups.contains(reg)) { + QPair c = registerCleanups[reg]; + registerCleanups.remove(reg); + Instr cleanup; + cleanup.common.type = (quint8)c.first; + cleanup.cleanup.reg = reg; + bytecode << cleanup; + } + + quint32 mask = 1 << reg; + registers &= ~mask; +} + +// Returns a reg +int QDeclarativeBindingCompilerPrivate::registerLiteralString(const QString &str) +{ + QByteArray strdata((const char *)str.constData(), str.length() * sizeof(QChar)); + int offset = data.count(); + data += strdata; + + int reg = acquireReg(Instr::CleanupString); + if (reg == -1) + return false; + + Instr string; + string.common.type = Instr::String; + string.string_value.reg = reg; + string.string_value.offset = offset; + string.string_value.length = str.length(); + bytecode << string; + + return reg; +} + +// Returns an identifier offset +int QDeclarativeBindingCompilerPrivate::registerString(const QString &string) +{ + Q_ASSERT(!string.isEmpty()); + + QHash >::ConstIterator iter = registeredStrings.find(string); + + if (iter == registeredStrings.end()) { + quint32 len = string.length(); + QByteArray lendata((const char *)&len, sizeof(quint32)); + QByteArray strdata((const char *)string.constData(), string.length() * sizeof(QChar)); + strdata.prepend(lendata); + int rv = data.count(); + data += strdata; + + iter = registeredStrings.insert(string, qMakePair(registeredStrings.count(), rv)); + } + + Instr reg; + reg.common.type = Instr::InitString; + reg.initstring.offset = iter->first; + reg.initstring.dataIdx = iter->second; + bytecode << reg; + return reg.initstring.offset; +} + +bool QDeclarativeBindingCompilerPrivate::subscription(const QStringList &sub, Result *result) +{ + QString str = sub.join(QLatin1String(".")); + result->subscriptionSet.insert(str); + + if (subscriptionSet.contains(str)) { + return false; + } else { + subscriptionSet.insert(str); + return true; + } +} + +int QDeclarativeBindingCompilerPrivate::subscriptionIndex(const QStringList &sub) +{ + QString str = sub.join(QLatin1String(".")); + QHash::ConstIterator iter = subscriptionIds.find(str); + if (iter == subscriptionIds.end()) + iter = subscriptionIds.insert(str, subscriptionIds.count()); + usedSubscriptionIds.insert(*iter); + return *iter; +} + +/* + Returns true if lhs contains no subscriptions that aren't also in base or rhs AND + rhs contains no subscriptions that aren't also in base or lhs. +*/ +bool QDeclarativeBindingCompilerPrivate::subscriptionNeutral(const QSet &base, + const QSet &lhs, + const QSet &rhs) +{ + QSet difflhs = lhs; + difflhs.subtract(rhs); + QSet diffrhs = rhs; + diffrhs.subtract(lhs); + + difflhs.unite(diffrhs); + difflhs.subtract(base); + + return difflhs.isEmpty(); +} + +quint8 QDeclarativeBindingCompilerPrivate::exceptionId(QDeclarativeJS::AST::ExpressionNode *n) +{ + quint8 rv = 0xFF; + if (n && exceptions.count() < 0xFF) { + rv = (quint8)exceptions.count(); + QDeclarativeJS::AST::SourceLocation l = n->firstSourceLocation(); + quint64 e = l.startLine; + e <<= 32; + e |= l.startColumn; + exceptions.append(e); + } + return rv; +} + +QDeclarativeBindingCompiler::QDeclarativeBindingCompiler() +: d(new QDeclarativeBindingCompilerPrivate) +{ +} + +QDeclarativeBindingCompiler::~QDeclarativeBindingCompiler() +{ + delete d; d = 0; +} + +/* +Returns true if any bindings were compiled. +*/ +bool QDeclarativeBindingCompiler::isValid() const +{ + return !d->committed.bytecode.isEmpty(); +} + +/* +-1 on failure, otherwise the binding index to use. +*/ +int QDeclarativeBindingCompiler::compile(const Expression &expression, QDeclarativeEnginePrivate *engine) +{ + if (!expression.expression.asAST()) return false; + + if (!qmlExperimental() && expression.property->isValueTypeSubProperty) + return -1; + + if (qmlDisableOptimizer()) + return -1; + + d->context = expression.context; + d->component = expression.component; + d->destination = expression.property; + d->ids = expression.ids; + d->imports = expression.imports; + d->engine = engine; + + if (d->compile(expression.expression.asAST())) { + return d->commitCompile(); + } else { + return -1; + } +} + + +QByteArray QDeclarativeBindingCompilerPrivate::buildSignalTable() const +{ + QHash > table; + + for (int ii = 0; ii < committed.count(); ++ii) { + const QSet &deps = committed.dependencies.at(ii); + for (QSet::ConstIterator iter = deps.begin(); iter != deps.end(); ++iter) + table[*iter].append(ii); + } + + QVector header; + QVector data; + for (int ii = 0; ii < committed.subscriptionIds.count(); ++ii) { + header.append(committed.subscriptionIds.count() + data.count()); + const QList &bindings = table[ii]; + data.append(bindings.count()); + for (int jj = 0; jj < bindings.count(); ++jj) + data.append(bindings.at(jj)); + } + header << data; + + return QByteArray((const char *)header.constData(), header.count() * sizeof(quint32)); +} + +QByteArray QDeclarativeBindingCompilerPrivate::buildExceptionData() const +{ + QByteArray rv; + rv.resize(committed.exceptions.count() * sizeof(quint64)); + ::memcpy(rv.data(), committed.exceptions.constData(), rv.size()); + return rv; +} + +/* +Returns the compiled program. +*/ +QByteArray QDeclarativeBindingCompiler::program() const +{ + QByteArray programData; + + if (isValid()) { + Program prog; + prog.bindings = d->committed.count(); + + QVector bytecode; + Instr skip; + skip.common.type = Instr::Skip; + skip.skip.reg = -1; + for (int ii = 0; ii < d->committed.count(); ++ii) { + skip.skip.count = d->committed.count() - ii - 1; + skip.skip.count+= d->committed.offsets.at(ii); + bytecode << skip; + } + bytecode << d->committed.bytecode; + + QByteArray data = d->committed.data; + while (data.count() % 4) data.append('\0'); + prog.signalTableOffset = data.count(); + data += d->buildSignalTable(); + while (data.count() % 4) data.append('\0'); + prog.exceptionDataOffset = data.count(); + data += d->buildExceptionData(); + + prog.dataLength = 4 * ((data.size() + 3) / 4); + prog.subscriptions = d->committed.subscriptionIds.count(); + prog.identifiers = d->committed.registeredStrings.count(); + prog.instructionCount = bytecode.count(); + prog.compiled = false; + int size = sizeof(Program) + bytecode.count() * sizeof(Instr); + size += prog.dataLength; + + programData.resize(size); + memcpy(programData.data(), &prog, sizeof(Program)); + if (prog.dataLength) + memcpy((char *)((Program *)programData.data())->data(), data.constData(), + data.size()); + memcpy((char *)((Program *)programData.data())->instructions(), bytecode.constData(), + bytecode.count() * sizeof(Instr)); + } + + return programData; +} + + + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativecompiledbindings_p.h b/src/declarative/qml/qdeclarativecompiledbindings_p.h new file mode 100644 index 00000000..bc954c7c --- /dev/null +++ b/src/declarative/qml/qdeclarativecompiledbindings_p.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEBINDINGOPTIMIZATIONS_P_H +#define QDECLARATIVEBINDINGOPTIMIZATIONS_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 "private/qdeclarativeexpression_p.h" +#include "private/qdeclarativebinding_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +struct QDeclarativeBindingCompilerPrivate; +class QDeclarativeBindingCompiler +{ +public: + QDeclarativeBindingCompiler(); + ~QDeclarativeBindingCompiler(); + + // Returns true if bindings were compiled + bool isValid() const; + + struct Expression + { + QDeclarativeParser::Object *component; + QDeclarativeParser::Object *context; + QDeclarativeParser::Property *property; + QDeclarativeParser::Variant expression; + QHash ids; + QDeclarativeImports imports; + }; + + // -1 on failure, otherwise the binding index to use + int compile(const Expression &, QDeclarativeEnginePrivate *); + + // Returns the compiled program + QByteArray program() const; + + static void dump(const QByteArray &); +private: + QDeclarativeBindingCompilerPrivate *d; +}; + +class QDeclarativeCompiledBindingsPrivate; +class QDeclarativeCompiledBindings : public QObject, public QDeclarativeAbstractExpression, public QDeclarativeRefCount +{ +public: + QDeclarativeCompiledBindings(const char *program, QDeclarativeContextData *context, QDeclarativeRefCount *); + virtual ~QDeclarativeCompiledBindings(); + + QDeclarativeAbstractBinding *configBinding(int index, QObject *target, QObject *scope, int property); + +protected: + int qt_metacall(QMetaObject::Call, int, void **); + +private: + Q_DISABLE_COPY(QDeclarativeCompiledBindings) + Q_DECLARE_PRIVATE(QDeclarativeCompiledBindings) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEBINDINGOPTIMIZATIONS_P_H + diff --git a/src/declarative/qml/qdeclarativecompileddata.cpp b/src/declarative/qml/qdeclarativecompileddata.cpp new file mode 100644 index 00000000..81d809bf --- /dev/null +++ b/src/declarative/qml/qdeclarativecompileddata.cpp @@ -0,0 +1,255 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativecompiler_p.h" +#include "qdeclarativeengine.h" +#include "qdeclarativecomponent.h" +#include "private/qdeclarativecomponent_p.h" +#include "qdeclarativecontext.h" +#include "private/qdeclarativecontext_p.h" + +#include + +#include + +QT_BEGIN_NAMESPACE + +int QDeclarativeCompiledData::pack(const char *data, size_t size) +{ + const char *p = packData.constData(); + unsigned int ps = packData.size(); + + for (unsigned int ii = 0; (ii + size) <= ps; ii += sizeof(int)) { + if (0 == ::memcmp(p + ii, data, size)) + return ii; + } + + int rv = packData.size(); + packData.append(data, size); + return rv; +} + +int QDeclarativeCompiledData::indexForString(const QString &data) +{ + int idx = primitives.indexOf(data); + if (idx == -1) { + idx = primitives.count(); + primitives << data; + } + return idx; +} + +int QDeclarativeCompiledData::indexForByteArray(const QByteArray &data) +{ + int idx = datas.indexOf(data); + if (idx == -1) { + idx = datas.count(); + datas << data; + } + return idx; +} + +int QDeclarativeCompiledData::indexForUrl(const QUrl &data) +{ + int idx = urls.indexOf(data); + if (idx == -1) { + idx = urls.count(); + urls << data; + } + return idx; +} + +int QDeclarativeCompiledData::indexForFloat(float *data, int count) +{ + Q_ASSERT(count > 0); + + for (int ii = 0; ii <= floatData.count() - count; ++ii) { + bool found = true; + for (int jj = 0; jj < count; ++jj) { + if (floatData.at(ii + jj) != data[jj]) { + found = false; + break; + } + } + + if (found) + return ii; + } + + int idx = floatData.count(); + for (int ii = 0; ii < count; ++ii) + floatData << data[ii]; + + return idx; +} + +int QDeclarativeCompiledData::indexForInt(int *data, int count) +{ + Q_ASSERT(count > 0); + + for (int ii = 0; ii <= intData.count() - count; ++ii) { + bool found = true; + for (int jj = 0; jj < count; ++jj) { + if (intData.at(ii + jj) != data[jj]) { + found = false; + break; + } + } + + if (found) + return ii; + } + + int idx = intData.count(); + for (int ii = 0; ii < count; ++ii) + intData << data[ii]; + + return idx; +} + +int QDeclarativeCompiledData::indexForLocation(const QDeclarativeParser::Location &l) +{ + // ### FIXME + int rv = locations.count(); + locations << l; + return rv; +} + +int QDeclarativeCompiledData::indexForLocation(const QDeclarativeParser::LocationSpan &l) +{ + // ### FIXME + int rv = locations.count(); + locations << l.start << l.end; + return rv; +} + +QDeclarativeCompiledData::QDeclarativeCompiledData(QDeclarativeEngine *engine) +: QDeclarativeCleanup(engine), importCache(0), root(0), rootPropertyCache(0) +{ +} + +QDeclarativeCompiledData::~QDeclarativeCompiledData() +{ + for (int ii = 0; ii < types.count(); ++ii) { + if (types.at(ii).component) + types.at(ii).component->release(); + if (types.at(ii).typePropertyCache) + types.at(ii).typePropertyCache->release(); + } + + for (int ii = 0; ii < propertyCaches.count(); ++ii) + propertyCaches.at(ii)->release(); + + for (int ii = 0; ii < contextCaches.count(); ++ii) + contextCaches.at(ii)->release(); + + if (importCache) + importCache->release(); + + if (rootPropertyCache) + rootPropertyCache->release(); + + qDeleteAll(cachedPrograms); + qDeleteAll(cachedClosures); +} + +void QDeclarativeCompiledData::clear() +{ + qDeleteAll(cachedPrograms); + qDeleteAll(cachedClosures); + for (int ii = 0; ii < cachedClosures.count(); ++ii) + cachedClosures[ii] = 0; + for (int ii = 0; ii < cachedPrograms.count(); ++ii) + cachedPrograms[ii] = 0; +} + +const QMetaObject *QDeclarativeCompiledData::TypeReference::metaObject() const +{ + if (type) { + return type->metaObject(); + } else { + Q_ASSERT(component); + return component->root; + } +} + +/*! +Returns the property cache, if one alread exists. The cache is not referenced. +*/ +QDeclarativePropertyCache *QDeclarativeCompiledData::TypeReference::propertyCache() const +{ + if (type) + return typePropertyCache; + else + return component->rootPropertyCache; +} + +/*! +Returns the property cache, creating one if it doesn't already exist. The cache is not referenced. +*/ +QDeclarativePropertyCache *QDeclarativeCompiledData::TypeReference::createPropertyCache(QDeclarativeEngine *engine) +{ + if (typePropertyCache) { + return typePropertyCache; + } else if (type) { + typePropertyCache = QDeclarativeEnginePrivate::get(engine)->cache(type->metaObject()); + typePropertyCache->addref(); + return typePropertyCache; + } else { + return component->rootPropertyCache; + } +} + + +void QDeclarativeCompiledData::dumpInstructions() +{ + if (!name.isEmpty()) + qWarning() << name; + qWarning().nospace() << "Index\tLine\tOperation\t\tData1\tData2\tData3\tComments"; + qWarning().nospace() << "-------------------------------------------------------------------------------"; + for (int ii = 0; ii < bytecode.count(); ++ii) { + dump(&bytecode[ii], ii); + } + qWarning().nospace() << "-------------------------------------------------------------------------------"; +} + + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativecompiler.cpp b/src/declarative/qml/qdeclarativecompiler.cpp new file mode 100644 index 00000000..25e67ce5 --- /dev/null +++ b/src/declarative/qml/qdeclarativecompiler.cpp @@ -0,0 +1,3129 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativecompiler_p.h" + +#include "private/qdeclarativeparser_p.h" +#include "private/qdeclarativescriptparser_p.h" +#include "qdeclarativepropertyvaluesource.h" +#include "qdeclarativecomponent.h" +#include "private/qmetaobjectbuilder_p.h" +#include "private/qdeclarativestringconverters_p.h" +#include "private/qdeclarativeengine_p.h" +#include "qdeclarativeengine.h" +#include "qdeclarativecontext.h" +#include "private/qdeclarativemetatype_p.h" +#include "private/qdeclarativecustomparser_p_p.h" +#include "private/qdeclarativecontext_p.h" +#include "private/qdeclarativecomponent_p.h" +#include "parser/qdeclarativejsast_p.h" +#include "private/qdeclarativevmemetaobject_p.h" +#include "private/qdeclarativeexpression_p.h" +#include "private/qdeclarativeproperty_p.h" +#include "private/qdeclarativerewrite_p.h" +#include "qdeclarativescriptstring.h" +#include "private/qdeclarativeglobal_p.h" +#include "private/qdeclarativescriptparser_p.h" +#include "private/qdeclarativebinding_p.h" +#include "private/qdeclarativecompiledbindings_p.h" +#include "private/qdeclarativeglobalscriptclass_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +DEFINE_BOOL_CONFIG_OPTION(compilerDump, QML_COMPILER_DUMP); +DEFINE_BOOL_CONFIG_OPTION(compilerStatDump, QML_COMPILER_STATS); +DEFINE_BOOL_CONFIG_OPTION(bindingsDump, QML_BINDINGS_DUMP); + +using namespace QDeclarativeParser; + +/*! + Instantiate a new QDeclarativeCompiler. +*/ +QDeclarativeCompiler::QDeclarativeCompiler() +: output(0), engine(0), unitRoot(0), unit(0) +{ +} + +/*! + Returns true if the last call to compile() caused errors. + + \sa errors() +*/ +bool QDeclarativeCompiler::isError() const +{ + return !exceptions.isEmpty(); +} + +/*! + Return the list of errors from the last call to compile(), or an empty list + if there were no errors. +*/ +QList QDeclarativeCompiler::errors() const +{ + return exceptions; +} + +/*! + Returns true if \a name refers to an attached property, false otherwise. + + Attached property names are those that start with a capital letter. +*/ +bool QDeclarativeCompiler::isAttachedPropertyName(const QByteArray &name) +{ + return !name.isEmpty() && name.at(0) >= 'A' && name.at(0) <= 'Z'; +} + +/*! + Returns true if \a name refers to a signal property, false otherwise. + + Signal property names are those that start with "on", followed by a capital + letter. +*/ +bool QDeclarativeCompiler::isSignalPropertyName(const QByteArray &name) +{ + return name.length() >= 3 && name.startsWith("on") && + 'A' <= name.at(2) && 'Z' >= name.at(2); +} + +/*! + \macro COMPILE_EXCEPTION + \internal + Inserts an error into the QDeclarativeCompiler error list, and returns false + (failure). + + \a token is used to source the error line and column, and \a desc is the + error itself. \a desc can be an expression that can be piped into QDebug. + + For example: + + \code + COMPILE_EXCEPTION(property, tr("Error for property \"%1\"").arg(QString::fromUtf8(property->name))); + \endcode +*/ +#define COMPILE_EXCEPTION(token, desc) \ + { \ + QString exceptionDescription; \ + QDeclarativeError error; \ + error.setUrl(output->url); \ + error.setLine((token)->location.start.line); \ + error.setColumn((token)->location.start.column); \ + error.setDescription(desc.trimmed()); \ + exceptions << error; \ + return false; \ + } + +/*! + \macro COMPILE_CHECK + \internal + Returns false if \a is false, otherwise does nothing. +*/ +#define COMPILE_CHECK(a) \ + { \ + if (!a) return false; \ + } + +/*! + Returns true if literal \a v can be assigned to property \a prop, otherwise + false. + + This test corresponds to action taken by genLiteralAssignment(). Any change + made here, must have a corresponding action in genLiteralAssigment(). +*/ +bool QDeclarativeCompiler::testLiteralAssignment(const QMetaProperty &prop, + QDeclarativeParser::Value *v) +{ + QString string = v->value.asString(); + + if (!prop.isWritable()) + COMPILE_EXCEPTION(v, tr("Invalid property assignment: \"%1\" is a read-only property").arg(QString::fromUtf8(prop.name()))); + + if (prop.isEnumType()) { + int value; + if (prop.isFlagType()) { + value = prop.enumerator().keysToValue(string.toUtf8().constData()); + } else + value = prop.enumerator().keyToValue(string.toUtf8().constData()); + if (value == -1) + COMPILE_EXCEPTION(v, tr("Invalid property assignment: unknown enumeration")); + return true; + } + int type = prop.userType(); + switch(type) { + case -1: + break; + case QVariant::String: + if (!v->value.isString()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: string expected")); + break; + case QVariant::Url: + if (!v->value.isString()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: url expected")); + break; + case QVariant::UInt: + { + bool ok = v->value.isNumber(); + if (ok) { + double n = v->value.asNumber(); + if (double(uint(n)) != n) + ok = false; + } + if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: unsigned int expected")); + } + break; + case QVariant::Int: + { + bool ok = v->value.isNumber(); + if (ok) { + double n = v->value.asNumber(); + if (double(int(n)) != n) + ok = false; + } + if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: int expected")); + } + break; + case QMetaType::Float: + if (!v->value.isNumber()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: number expected")); + break; + case QVariant::Double: + if (!v->value.isNumber()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: number expected")); + break; + case QVariant::Color: + { + bool ok; + QDeclarativeStringConverters::colorFromString(string, &ok); + if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: color expected")); + } + break; +#ifndef QT_NO_DATESTRING + case QVariant::Date: + { + bool ok; + QDeclarativeStringConverters::dateFromString(string, &ok); + if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: date expected")); + } + break; + case QVariant::Time: + { + bool ok; + QDeclarativeStringConverters::timeFromString(string, &ok); + if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: time expected")); + } + break; + case QVariant::DateTime: + { + bool ok; + QDeclarativeStringConverters::dateTimeFromString(string, &ok); + if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: datetime expected")); + } + break; +#endif // QT_NO_DATESTRING + case QVariant::Point: + case QVariant::PointF: + { + bool ok; + QPointF point = QDeclarativeStringConverters::pointFFromString(string, &ok); + if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: point expected")); + } + break; + case QVariant::Size: + case QVariant::SizeF: + { + bool ok; + QSizeF size = QDeclarativeStringConverters::sizeFFromString(string, &ok); + if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: size expected")); + } + break; + case QVariant::Rect: + case QVariant::RectF: + { + bool ok; + QRectF rect = QDeclarativeStringConverters::rectFFromString(string, &ok); + if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: rect expected")); + } + break; + case QVariant::Bool: + { + if (!v->value.isBoolean()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: boolean expected")); + } + break; + case QVariant::Vector3D: + { + bool ok; + QDeclarativeStringConverters::vector3DFromString(string, &ok); + if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: 3D vector expected")); + } + break; + default: + { + int t = prop.userType(); + QDeclarativeMetaType::StringConverter converter = + QDeclarativeMetaType::customStringConverter(t); + if (!converter) + COMPILE_EXCEPTION(v, tr("Invalid property assignment: unsupported type \"%1\"").arg(QString::fromLatin1(QVariant::typeToName(prop.type())))); + } + break; + } + return true; +} + +/*! + Generate a store instruction for assigning literal \a v to property \a prop. + + Any literal assignment that is approved in testLiteralAssignment() must have + a corresponding action in this method. +*/ +void QDeclarativeCompiler::genLiteralAssignment(const QMetaProperty &prop, + QDeclarativeParser::Value *v) +{ + QString string = v->value.asString(); + + QDeclarativeInstruction instr; + instr.line = v->location.start.line; + if (prop.isEnumType()) { + int value; + if (prop.isFlagType()) { + value = prop.enumerator().keysToValue(string.toUtf8().constData()); + } else + value = prop.enumerator().keyToValue(string.toUtf8().constData()); + + instr.type = QDeclarativeInstruction::StoreInteger; + instr.storeInteger.propertyIndex = prop.propertyIndex(); + instr.storeInteger.value = value; + output->bytecode << instr; + return; + } + + int type = prop.userType(); + switch(type) { + case -1: + { + if (v->value.isNumber()) { + double n = v->value.asNumber(); + if (double(int(n)) == n) { + instr.type = QDeclarativeInstruction::StoreVariantInteger; + instr.storeInteger.propertyIndex = prop.propertyIndex(); + instr.storeInteger.value = int(n); + } else { + instr.type = QDeclarativeInstruction::StoreVariantDouble; + instr.storeDouble.propertyIndex = prop.propertyIndex(); + instr.storeDouble.value = n; + } + } else if(v->value.isBoolean()) { + instr.type = QDeclarativeInstruction::StoreVariantBool; + instr.storeBool.propertyIndex = prop.propertyIndex(); + instr.storeBool.value = v->value.asBoolean(); + } else { + instr.type = QDeclarativeInstruction::StoreVariant; + instr.storeString.propertyIndex = prop.propertyIndex(); + instr.storeString.value = output->indexForString(string); + } + } + break; + case QVariant::String: + { + instr.type = QDeclarativeInstruction::StoreString; + instr.storeString.propertyIndex = prop.propertyIndex(); + instr.storeString.value = output->indexForString(string); + } + break; + case QVariant::Url: + { + instr.type = QDeclarativeInstruction::StoreUrl; + QUrl u = string.isEmpty() ? QUrl() : output->url.resolved(QUrl(string)); + instr.storeUrl.propertyIndex = prop.propertyIndex(); + instr.storeUrl.value = output->indexForUrl(u); + } + break; + case QVariant::UInt: + { + instr.type = QDeclarativeInstruction::StoreInteger; + instr.storeInteger.propertyIndex = prop.propertyIndex(); + instr.storeInteger.value = uint(v->value.asNumber()); + } + break; + case QVariant::Int: + { + instr.type = QDeclarativeInstruction::StoreInteger; + instr.storeInteger.propertyIndex = prop.propertyIndex(); + instr.storeInteger.value = int(v->value.asNumber()); + } + break; + case QMetaType::Float: + { + instr.type = QDeclarativeInstruction::StoreFloat; + instr.storeFloat.propertyIndex = prop.propertyIndex(); + instr.storeFloat.value = float(v->value.asNumber()); + } + break; + case QVariant::Double: + { + instr.type = QDeclarativeInstruction::StoreDouble; + instr.storeDouble.propertyIndex = prop.propertyIndex(); + instr.storeDouble.value = v->value.asNumber(); + } + break; + case QVariant::Color: + { + QColor c = QDeclarativeStringConverters::colorFromString(string); + instr.type = QDeclarativeInstruction::StoreColor; + instr.storeColor.propertyIndex = prop.propertyIndex(); + instr.storeColor.value = c.rgba(); + } + break; +#ifndef QT_NO_DATESTRING + case QVariant::Date: + { + QDate d = QDeclarativeStringConverters::dateFromString(string); + instr.type = QDeclarativeInstruction::StoreDate; + instr.storeDate.propertyIndex = prop.propertyIndex(); + instr.storeDate.value = d.toJulianDay(); + } + break; + case QVariant::Time: + { + QTime time = QDeclarativeStringConverters::timeFromString(string); + int data[] = { time.hour(), time.minute(), + time.second(), time.msec() }; + int index = output->indexForInt(data, 4); + instr.type = QDeclarativeInstruction::StoreTime; + instr.storeTime.propertyIndex = prop.propertyIndex(); + instr.storeTime.valueIndex = index; + } + break; + case QVariant::DateTime: + { + QDateTime dateTime = QDeclarativeStringConverters::dateTimeFromString(string); + int data[] = { dateTime.date().toJulianDay(), + dateTime.time().hour(), + dateTime.time().minute(), + dateTime.time().second(), + dateTime.time().msec() }; + int index = output->indexForInt(data, 5); + instr.type = QDeclarativeInstruction::StoreDateTime; + instr.storeDateTime.propertyIndex = prop.propertyIndex(); + instr.storeDateTime.valueIndex = index; + } + break; +#endif // QT_NO_DATESTRING + case QVariant::Point: + case QVariant::PointF: + { + bool ok; + QPointF point = + QDeclarativeStringConverters::pointFFromString(string, &ok); + float data[] = { float(point.x()), float(point.y()) }; + int index = output->indexForFloat(data, 2); + if (type == QVariant::PointF) + instr.type = QDeclarativeInstruction::StorePointF; + else + instr.type = QDeclarativeInstruction::StorePoint; + instr.storeRealPair.propertyIndex = prop.propertyIndex(); + instr.storeRealPair.valueIndex = index; + } + break; + case QVariant::Size: + case QVariant::SizeF: + { + bool ok; + QSizeF size = QDeclarativeStringConverters::sizeFFromString(string, &ok); + float data[] = { float(size.width()), float(size.height()) }; + int index = output->indexForFloat(data, 2); + if (type == QVariant::SizeF) + instr.type = QDeclarativeInstruction::StoreSizeF; + else + instr.type = QDeclarativeInstruction::StoreSize; + instr.storeRealPair.propertyIndex = prop.propertyIndex(); + instr.storeRealPair.valueIndex = index; + } + break; + case QVariant::Rect: + case QVariant::RectF: + { + bool ok; + QRectF rect = QDeclarativeStringConverters::rectFFromString(string, &ok); + float data[] = { float(rect.x()), float(rect.y()), + float(rect.width()), float(rect.height()) }; + int index = output->indexForFloat(data, 4); + if (type == QVariant::RectF) + instr.type = QDeclarativeInstruction::StoreRectF; + else + instr.type = QDeclarativeInstruction::StoreRect; + instr.storeRect.propertyIndex = prop.propertyIndex(); + instr.storeRect.valueIndex = index; + } + break; + case QVariant::Bool: + { + bool b = v->value.asBoolean(); + instr.type = QDeclarativeInstruction::StoreBool; + instr.storeBool.propertyIndex = prop.propertyIndex(); + instr.storeBool.value = b; + } + break; + case QVariant::Vector3D: + { + bool ok; + QVector3D vector = + QDeclarativeStringConverters::vector3DFromString(string, &ok); + float data[] = { float(vector.x()), float(vector.y()), float(vector.z()) }; + int index = output->indexForFloat(data, 3); + instr.type = QDeclarativeInstruction::StoreVector3D; + instr.storeRealPair.propertyIndex = prop.propertyIndex(); + instr.storeRealPair.valueIndex = index; + } + break; + default: + { + int t = prop.userType(); + int index = output->customTypeData.count(); + instr.type = QDeclarativeInstruction::AssignCustomType; + instr.assignCustomType.propertyIndex = prop.propertyIndex(); + instr.assignCustomType.valueIndex = index; + + QDeclarativeCompiledData::CustomTypeData data; + data.index = output->indexForString(string); + data.type = t; + output->customTypeData << data; + } + break; + } + output->bytecode << instr; +} + +/*! + Resets data by clearing the lists that the QDeclarativeCompiler modifies. +*/ +void QDeclarativeCompiler::reset(QDeclarativeCompiledData *data) +{ + data->types.clear(); + data->primitives.clear(); + data->floatData.clear(); + data->intData.clear(); + data->customTypeData.clear(); + data->datas.clear(); + data->bytecode.clear(); +} + +/*! + Compile \a unit, and store the output in \a out. \a engine is the QDeclarativeEngine + with which the QDeclarativeCompiledData will be associated. + + Returns true on success, false on failure. On failure, the compile errors + are available from errors(). + + If the environment variant QML_COMPILER_DUMP is set + (eg. QML_COMPILER_DUMP=1) the compiled instructions will be dumped to stderr + on a successful compiler. +*/ +bool QDeclarativeCompiler::compile(QDeclarativeEngine *engine, + QDeclarativeTypeData *unit, + QDeclarativeCompiledData *out) +{ + exceptions.clear(); + + Q_ASSERT(out); + reset(out); + + output = out; + + // Compile types + const QList &resolvedTypes = unit->resolvedTypes(); + QList referencedTypes = unit->parser().referencedTypes(); + + for (int ii = 0; ii < resolvedTypes.count(); ++ii) { + QDeclarativeCompiledData::TypeReference ref; + + const QDeclarativeTypeData::TypeReference &tref = resolvedTypes.at(ii); + QDeclarativeScriptParser::TypeReference *parserRef = referencedTypes.at(ii); + + if (tref.type) { + ref.type = tref.type; + if (!ref.type->isCreatable()) { + QString err = ref.type->noCreationReason(); + if (err.isEmpty()) + err = tr( "Element is not creatable."); + COMPILE_EXCEPTION(parserRef->refObjects.first(), err); + } + + if (ref.type->containsRevisionedAttributes()) { + QDeclarativeError cacheError; + ref.typePropertyCache = + QDeclarativeEnginePrivate::get(engine)->cache(ref.type, resolvedTypes.at(ii).minorVersion, cacheError); + + if (!ref.typePropertyCache) { + COMPILE_EXCEPTION(parserRef->refObjects.first(), cacheError.description()); + } + ref.typePropertyCache->addref(); + } + + } else if (tref.typeData) { + ref.component = tref.typeData->compiledData(); + } + ref.className = parserRef->name.toUtf8(); + out->types << ref; + } + + QDeclarativeParser::Object *root = unit->parser().tree(); + Q_ASSERT(root); + + this->engine = engine; + this->enginePrivate = QDeclarativeEnginePrivate::get(engine); + this->unit = unit; + this->unitRoot = root; + compileTree(root); + + if (!isError()) { + if (compilerDump()) + out->dumpInstructions(); + if (compilerStatDump()) + dumpStats(); + Q_ASSERT(out->rootPropertyCache); + } else { + reset(out); + } + + compileState = ComponentCompileState(); + savedCompileStates.clear(); + output = 0; + this->engine = 0; + this->enginePrivate = 0; + this->unit = 0; + this->unitRoot = 0; + + return !isError(); +} + +void QDeclarativeCompiler::compileTree(QDeclarativeParser::Object *tree) +{ + compileState.root = tree; + componentStat.lineNumber = tree->location.start.line; + + if (!buildObject(tree, BindingContext()) || !completeComponentBuild()) + return; + + QDeclarativeInstruction init; + init.type = QDeclarativeInstruction::Init; + init.line = 0; + init.init.bindingsSize = compileState.bindings.count(); + init.init.parserStatusSize = compileState.parserStatusCount; + init.init.contextCache = genContextCache(); + if (compileState.compiledBindingData.isEmpty()) + init.init.compiledBinding = -1; + else + init.init.compiledBinding = output->indexForByteArray(compileState.compiledBindingData); + output->bytecode << init; + + // Build global import scripts + QHash importedScripts; + QStringList importedScriptIndexes; + + foreach (const QDeclarativeTypeData::ScriptReference &script, unit->resolvedScripts()) { + QString scriptCode = script.script->scriptSource(); + Object::ScriptBlock::Pragmas pragmas = script.script->pragmas(); + + Q_ASSERT(!importedScripts.contains(script.qualifier)); + + if (!scriptCode.isEmpty()) { + Object::ScriptBlock &scriptBlock = importedScripts[script.qualifier]; + + scriptBlock.code = scriptCode; + scriptBlock.file = script.script->finalUrl().toString(); + scriptBlock.pragmas = pragmas; + } + } + + for (QHash::Iterator iter = importedScripts.begin(); + iter != importedScripts.end(); ++iter) { + + importedScriptIndexes.append(iter.key()); + + QDeclarativeInstruction import; + import.type = QDeclarativeInstruction::StoreImportedScript; + import.line = 0; + import.storeScript.value = output->scripts.count(); + output->scripts << *iter; + output->bytecode << import; + } + + genObject(tree); + + QDeclarativeInstruction def; + init.line = 0; + def.type = QDeclarativeInstruction::SetDefault; + output->bytecode << def; + + output->importCache = new QDeclarativeTypeNameCache(engine); + + for (int ii = 0; ii < importedScriptIndexes.count(); ++ii) + output->importCache->add(importedScriptIndexes.at(ii), ii); + + unit->imports().populateCache(output->importCache, engine); + + Q_ASSERT(tree->metatype); + + if (tree->metadata.isEmpty()) { + output->root = tree->metatype; + } else { + static_cast(output->rootData) = *tree->metaObject(); + output->root = &output->rootData; + } + if (!tree->metadata.isEmpty()) + enginePrivate->registerCompositeType(output); +} + +static bool ValuePtrLessThan(const QDeclarativeParser::Value *t1, const QDeclarativeParser::Value *t2) +{ + return t1->location.start.line < t2->location.start.line || + (t1->location.start.line == t2->location.start.line && + t1->location.start.column < t2->location.start.column); +} + +bool QDeclarativeCompiler::buildObject(QDeclarativeParser::Object *obj, const BindingContext &ctxt) +{ + componentStat.objects++; + + Q_ASSERT (obj->type != -1); + const QDeclarativeCompiledData::TypeReference &tr = + output->types.at(obj->type); + obj->metatype = tr.metaObject(); + + if (tr.component) + obj->url = tr.component->url; + if (tr.type) + obj->typeName = tr.type->qmlTypeName(); + obj->className = tr.className; + + // This object is a "Component" element + if (tr.type && obj->metatype == &QDeclarativeComponent::staticMetaObject) { + COMPILE_CHECK(buildComponent(obj, ctxt)); + return true; + } + + // Object instantiations reset the binding context + BindingContext objCtxt(obj); + + // Create the synthesized meta object, ignoring aliases + COMPILE_CHECK(checkDynamicMeta(obj)); + COMPILE_CHECK(mergeDynamicMetaProperties(obj)); + COMPILE_CHECK(buildDynamicMeta(obj, IgnoreAliases)); + + // Find the native type and check for the QDeclarativeParserStatus interface + QDeclarativeType *type = toQmlType(obj); + Q_ASSERT(type); + obj->parserStatusCast = type->parserStatusCast(); + if (obj->parserStatusCast != -1) + compileState.parserStatusCount++; + + // Check if this is a custom parser type. Custom parser types allow + // assignments to non-existent properties. These assignments are then + // compiled by the type. + bool isCustomParser = output->types.at(obj->type).type && + output->types.at(obj->type).type->customParser() != 0; + QList customProps; + + // Fetch the list of deferred properties + QStringList deferredList = deferredProperties(obj); + + // Must do id property first. This is to ensure that the id given to any + // id reference created matches the order in which the objects are + // instantiated + foreach(Property *prop, obj->properties) { + if (prop->name == "id") { + COMPILE_CHECK(buildProperty(prop, obj, objCtxt)); + break; + } + } + + // Merge + Property *defaultProperty = 0; + Property *skipProperty = 0; + if (obj->defaultProperty) { + const QMetaObject *metaObject = obj->metaObject(); + Q_ASSERT(metaObject); + QMetaProperty p = QDeclarativeMetaType::defaultProperty(metaObject); + if (p.name()) { + Property *explicitProperty = obj->getProperty(p.name(), false); + if (explicitProperty && !explicitProperty->value) { + skipProperty = explicitProperty; + + defaultProperty = new Property; + defaultProperty->parent = obj; + defaultProperty->isDefault = true; + defaultProperty->location = obj->defaultProperty->location; + defaultProperty->listValueRange = obj->defaultProperty->listValueRange; + defaultProperty->listCommaPositions = obj->defaultProperty->listCommaPositions; + + defaultProperty->values = obj->defaultProperty->values; + defaultProperty->values += explicitProperty->values; + foreach(QDeclarativeParser::Value *value, defaultProperty->values) + value->addref(); + qSort(defaultProperty->values.begin(), defaultProperty->values.end(), ValuePtrLessThan); + + } else { + defaultProperty = obj->defaultProperty; + defaultProperty->addref(); + } + } else { + defaultProperty = obj->defaultProperty; + defaultProperty->addref(); + } + } + + QDeclarativeCustomParser *cp = 0; + if (isCustomParser) + cp = output->types.at(obj->type).type->customParser(); + + // Build all explicit properties specified + foreach(Property *prop, obj->properties) { + + if (prop == skipProperty) + continue; + if (prop->name == "id") + continue; + + bool canDefer = false; + if (isCustomParser) { + if (doesPropertyExist(prop, obj) && + (!(cp->flags() & QDeclarativeCustomParser::AcceptsAttachedProperties) || + !isAttachedPropertyName(prop->name))) { + int ids = compileState.ids.count(); + COMPILE_CHECK(buildProperty(prop, obj, objCtxt)); + canDefer = ids == compileState.ids.count(); + } else { + customProps << QDeclarativeCustomParserNodePrivate::fromProperty(prop); + } + } else { + if (isSignalPropertyName(prop->name)) { + COMPILE_CHECK(buildSignal(prop,obj,objCtxt)); + } else { + int ids = compileState.ids.count(); + COMPILE_CHECK(buildProperty(prop, obj, objCtxt)); + canDefer = ids == compileState.ids.count(); + } + } + + if (canDefer && !deferredList.isEmpty() && + deferredList.contains(QString::fromUtf8(prop->name))) + prop->isDeferred = true; + + } + + // Build the default property + if (defaultProperty) { + Property *prop = defaultProperty; + + bool canDefer = false; + if (isCustomParser) { + if (doesPropertyExist(prop, obj)) { + int ids = compileState.ids.count(); + COMPILE_CHECK(buildProperty(prop, obj, objCtxt)); + canDefer = ids == compileState.ids.count(); + } else { + customProps << QDeclarativeCustomParserNodePrivate::fromProperty(prop); + } + } else { + int ids = compileState.ids.count(); + COMPILE_CHECK(buildProperty(prop, obj, objCtxt)); + canDefer = ids == compileState.ids.count(); + } + + if (canDefer && !deferredList.isEmpty() && + deferredList.contains(QString::fromUtf8(prop->name))) + prop->isDeferred = true; + } + + if (defaultProperty) + defaultProperty->release(); + + // Compile custom parser parts + if (isCustomParser && !customProps.isEmpty()) { + cp->clearErrors(); + cp->compiler = this; + cp->object = obj; + obj->custom = cp->compile(customProps); + cp->compiler = 0; + cp->object = 0; + foreach (QDeclarativeError err, cp->errors()) { + err.setUrl(output->url); + exceptions << err; + } + } + + return true; +} + +void QDeclarativeCompiler::genObject(QDeclarativeParser::Object *obj) +{ + QDeclarativeCompiledData::TypeReference &tr = output->types[obj->type]; + if (tr.type && obj->metatype == &QDeclarativeComponent::staticMetaObject) { + genComponent(obj); + return; + } + + // Create the object + if (obj->custom.isEmpty() && output->types.at(obj->type).type && + !output->types.at(obj->type).type->isExtendedType() && obj != compileState.root) { + + QDeclarativeInstruction create; + create.type = QDeclarativeInstruction::CreateSimpleObject; + create.line = obj->location.start.line; + create.createSimple.create = output->types.at(obj->type).type->createFunction(); + create.createSimple.typeSize = output->types.at(obj->type).type->createSize(); + create.createSimple.type = obj->type; + create.createSimple.column = obj->location.start.column; + output->bytecode << create; + + } else { + + QDeclarativeInstruction create; + create.type = QDeclarativeInstruction::CreateObject; + create.line = obj->location.start.line; + create.create.column = obj->location.start.column; + create.create.data = -1; + if (!obj->custom.isEmpty()) + create.create.data = output->indexForByteArray(obj->custom); + create.create.type = obj->type; + if (!output->types.at(create.create.type).type && + !obj->bindingBitmask.isEmpty()) { + Q_ASSERT(obj->bindingBitmask.size() % 4 == 0); + create.create.bindingBits = + output->indexForByteArray(obj->bindingBitmask); + } else { + create.create.bindingBits = -1; + } + output->bytecode << create; + + } + + // Setup the synthesized meta object if necessary + if (!obj->metadata.isEmpty()) { + QDeclarativeInstruction meta; + meta.type = QDeclarativeInstruction::StoreMetaObject; + meta.line = 0; + meta.storeMeta.data = output->indexForByteArray(obj->metadata); + meta.storeMeta.aliasData = output->indexForByteArray(obj->synthdata); + meta.storeMeta.propertyCache = output->propertyCaches.count(); + + QDeclarativePropertyCache *propertyCache = obj->synthCache; + Q_ASSERT(propertyCache); + propertyCache->addref(); + + // Add flag for alias properties + if (!obj->synthdata.isEmpty()) { + const QDeclarativeVMEMetaData *vmeMetaData = + reinterpret_cast(obj->synthdata.constData()); + for (int ii = 0; ii < vmeMetaData->aliasCount; ++ii) { + int index = obj->metaObject()->propertyOffset() + vmeMetaData->propertyCount + ii; + propertyCache->property(index)->flags |= QDeclarativePropertyCache::Data::IsAlias; + } + } + + if (obj == unitRoot) { + propertyCache->addref(); + output->rootPropertyCache = propertyCache; + } + + output->propertyCaches << propertyCache; + output->bytecode << meta; + } else if (obj == unitRoot) { + output->rootPropertyCache = tr.createPropertyCache(engine); + output->rootPropertyCache->addref(); + } + + // Set the object id + if (!obj->id.isEmpty()) { + QDeclarativeInstruction id; + id.type = QDeclarativeInstruction::SetId; + id.line = 0; + id.setId.value = output->indexForString(obj->id); + id.setId.index = obj->idIndex; + output->bytecode << id; + } + + // Begin the class + if (tr.type && obj->parserStatusCast != -1) { + QDeclarativeInstruction begin; + begin.type = QDeclarativeInstruction::BeginObject; + begin.begin.castValue = obj->parserStatusCast; + begin.line = obj->location.start.line; + output->bytecode << begin; + } + + genObjectBody(obj); +} + +void QDeclarativeCompiler::genObjectBody(QDeclarativeParser::Object *obj) +{ + typedef QPair PropPair; + foreach(const PropPair &prop, obj->scriptStringProperties) { + QDeclarativeInstruction ss; + ss.type = QDeclarativeInstruction::StoreScriptString; + ss.storeScriptString.propertyIndex = prop.first->index; + ss.storeScriptString.value = + output->indexForString(prop.first->values.at(0)->value.asScript()); + ss.storeScriptString.scope = prop.second; + output->bytecode << ss; + } + + bool seenDefer = false; + foreach(Property *prop, obj->valueProperties) { + if (prop->isDeferred) { + seenDefer = true; + continue; + } + if (!prop->isAlias) + genValueProperty(prop, obj); + } + if (seenDefer) { + QDeclarativeInstruction defer; + defer.type = QDeclarativeInstruction::Defer; + defer.line = 0; + defer.defer.deferCount = 0; + int deferIdx = output->bytecode.count(); + output->bytecode << defer; + + QDeclarativeInstruction init; + init.type = QDeclarativeInstruction::Init; + init.init.bindingsSize = compileState.bindings.count(); // XXX - bigger than necessary + init.init.parserStatusSize = compileState.parserStatusCount; // XXX - bigger than necessary + init.init.contextCache = -1; + init.init.compiledBinding = -1; + output->bytecode << init; + + foreach(Property *prop, obj->valueProperties) { + if (!prop->isDeferred) + continue; + genValueProperty(prop, obj); + } + + output->bytecode[deferIdx].defer.deferCount = + output->bytecode.count() - deferIdx - 1; + } + + foreach(Property *prop, obj->signalProperties) { + + QDeclarativeParser::Value *v = prop->values.at(0); + + if (v->type == Value::SignalObject) { + + genObject(v->object); + + QDeclarativeInstruction assign; + assign.type = QDeclarativeInstruction::AssignSignalObject; + assign.line = v->location.start.line; + assign.assignSignalObject.signal = + output->indexForByteArray(prop->name); + output->bytecode << assign; + + } else if (v->type == Value::SignalExpression) { + + BindingContext ctxt = compileState.signalExpressions.value(v); + + QDeclarativeInstruction store; + store.type = QDeclarativeInstruction::StoreSignal; + store.line = v->location.start.line; + store.storeSignal.signalIndex = prop->index; + store.storeSignal.value = + output->indexForString(v->value.asScript().trimmed()); + store.storeSignal.context = ctxt.stack; + store.storeSignal.name = output->indexForByteArray(prop->name); + output->bytecode << store; + + } + + } + + foreach(Property *prop, obj->attachedProperties) { + QDeclarativeInstruction fetch; + fetch.type = QDeclarativeInstruction::FetchAttached; + fetch.line = prop->location.start.line; + fetch.fetchAttached.id = prop->index; + output->bytecode << fetch; + + genObjectBody(prop->value); + + QDeclarativeInstruction pop; + pop.type = QDeclarativeInstruction::PopFetchedObject; + pop.line = prop->location.start.line; + output->bytecode << pop; + } + + foreach(Property *prop, obj->groupedProperties) { + QDeclarativeInstruction fetch; + fetch.type = QDeclarativeInstruction::FetchObject; + fetch.fetch.property = prop->index; + fetch.line = prop->location.start.line; + output->bytecode << fetch; + + if (!prop->value->metadata.isEmpty()) { + QDeclarativeInstruction meta; + meta.type = QDeclarativeInstruction::StoreMetaObject; + meta.line = 0; + meta.storeMeta.data = output->indexForByteArray(prop->value->metadata); + meta.storeMeta.aliasData = output->indexForByteArray(prop->value->synthdata); + meta.storeMeta.propertyCache = -1; + output->bytecode << meta; + } + + genObjectBody(prop->value); + + QDeclarativeInstruction pop; + pop.type = QDeclarativeInstruction::PopFetchedObject; + pop.line = prop->location.start.line; + output->bytecode << pop; + } + + foreach(Property *prop, obj->valueTypeProperties) { + if (!prop->isAlias) + genValueTypeProperty(obj, prop); + } + + foreach(Property *prop, obj->valueProperties) { + if (prop->isDeferred) + continue; + if (prop->isAlias) + genValueProperty(prop, obj); + } + + foreach(Property *prop, obj->valueTypeProperties) { + if (prop->isAlias) + genValueTypeProperty(obj, prop); + } +} + +void QDeclarativeCompiler::genValueTypeProperty(QDeclarativeParser::Object *obj,QDeclarativeParser::Property *prop) +{ + QDeclarativeInstruction fetch; + fetch.type = QDeclarativeInstruction::FetchValueType; + fetch.fetchValue.property = prop->index; + fetch.fetchValue.type = prop->type; + fetch.fetchValue.bindingSkipList = 0; + fetch.line = prop->location.start.line; + + if (obj->type == -1 || output->types.at(obj->type).component) { + // We only have to do this if this is a composite type. If it is a builtin + // type it can't possibly already have bindings that need to be cleared. + foreach(Property *vprop, prop->value->valueProperties) { + if (!vprop->values.isEmpty()) { + Q_ASSERT(vprop->index >= 0 && vprop->index < 32); + fetch.fetchValue.bindingSkipList |= (1 << vprop->index); + } + } + } + + output->bytecode << fetch; + + foreach(Property *vprop, prop->value->valueProperties) { + genPropertyAssignment(vprop, prop->value, prop); + } + + QDeclarativeInstruction pop; + pop.type = QDeclarativeInstruction::PopValueType; + pop.fetchValue.property = prop->index; + pop.fetchValue.type = prop->type; + pop.fetchValue.bindingSkipList = 0; + pop.line = prop->location.start.line; + output->bytecode << pop; +} + +void QDeclarativeCompiler::genComponent(QDeclarativeParser::Object *obj) +{ + QDeclarativeParser::Object *root = obj->defaultProperty->values.at(0)->object; + Q_ASSERT(root); + + QDeclarativeInstruction create; + create.type = QDeclarativeInstruction::CreateComponent; + create.line = root->location.start.line; + create.createComponent.column = root->location.start.column; + create.createComponent.endLine = root->location.end.line; + output->bytecode << create; + int count = output->bytecode.count(); + + ComponentCompileState oldCompileState = compileState; + compileState = componentState(root); + + QDeclarativeInstruction init; + init.type = QDeclarativeInstruction::Init; + init.init.bindingsSize = compileState.bindings.count(); + init.init.parserStatusSize = compileState.parserStatusCount; + init.init.contextCache = genContextCache(); + if (compileState.compiledBindingData.isEmpty()) + init.init.compiledBinding = -1; + else + init.init.compiledBinding = output->indexForByteArray(compileState.compiledBindingData); + init.line = obj->location.start.line; + output->bytecode << init; + + genObject(root); + + QDeclarativeInstruction def; + init.line = 0; + def.type = QDeclarativeInstruction::SetDefault; + output->bytecode << def; + + output->bytecode[count - 1].createComponent.count = + output->bytecode.count() - count; + + compileState = oldCompileState; + + if (!obj->id.isEmpty()) { + QDeclarativeInstruction id; + id.type = QDeclarativeInstruction::SetId; + id.line = 0; + id.setId.value = output->indexForString(obj->id); + id.setId.index = obj->idIndex; + output->bytecode << id; + } + + if (obj == unitRoot) { + output->rootPropertyCache = output->types[obj->type].createPropertyCache(engine); + output->rootPropertyCache->addref(); + } +} + +bool QDeclarativeCompiler::buildComponent(QDeclarativeParser::Object *obj, + const BindingContext &ctxt) +{ + // The special "Component" element can only have the id property and a + // default property, that actually defines the component's tree + + // Find, check and set the "id" property (if any) + Property *idProp = 0; + if (obj->properties.count() > 1 || + (obj->properties.count() == 1 && obj->properties.begin().key() != "id")) + COMPILE_EXCEPTION(*obj->properties.begin(), tr("Component elements may not contain properties other than id")); + + if (obj->properties.count()) + idProp = *obj->properties.begin(); + + if (idProp) { + if (idProp->value || idProp->values.count() > 1 || idProp->values.at(0)->object) + COMPILE_EXCEPTION(idProp, tr("Invalid component id specification")); + COMPILE_CHECK(checkValidId(idProp->values.first(), idProp->values.first()->primitive())) + + QString idVal = idProp->values.first()->primitive(); + + if (compileState.ids.contains(idVal)) + COMPILE_EXCEPTION(idProp, tr("id is not unique")); + + obj->id = idVal; + addId(idVal, obj); + } + + // Check the Component tree is well formed + if (obj->defaultProperty && + (obj->defaultProperty->value || obj->defaultProperty->values.count() > 1 || + (obj->defaultProperty->values.count() == 1 && !obj->defaultProperty->values.first()->object))) + COMPILE_EXCEPTION(obj, tr("Invalid component body specification")); + + if (!obj->dynamicProperties.isEmpty()) + COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new properties.")); + if (!obj->dynamicSignals.isEmpty()) + COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new signals.")); + if (!obj->dynamicSlots.isEmpty()) + COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new functions.")); + + QDeclarativeParser::Object *root = 0; + if (obj->defaultProperty && obj->defaultProperty->values.count()) + root = obj->defaultProperty->values.first()->object; + + if (!root) + COMPILE_EXCEPTION(obj, tr("Cannot create empty component specification")); + + // Build the component tree + COMPILE_CHECK(buildComponentFromRoot(root, ctxt)); + + return true; +} + +bool QDeclarativeCompiler::buildComponentFromRoot(QDeclarativeParser::Object *obj, + const BindingContext &ctxt) +{ + ComponentCompileState oldComponentCompileState = compileState; + ComponentStat oldComponentStat = componentStat; + + compileState = ComponentCompileState(); + compileState.root = obj; + + componentStat = ComponentStat(); + componentStat.lineNumber = obj->location.start.line; + + if (obj) + COMPILE_CHECK(buildObject(obj, ctxt)); + + COMPILE_CHECK(completeComponentBuild()); + + compileState = oldComponentCompileState; + componentStat = oldComponentStat; + + return true; +} + + +// Build a sub-object. A sub-object is one that was not created directly by +// QML - such as a grouped property object, or an attached object. Sub-object's +// can't have an id, involve a custom parser, have attached properties etc. +bool QDeclarativeCompiler::buildSubObject(QDeclarativeParser::Object *obj, const BindingContext &ctxt) +{ + Q_ASSERT(obj->metatype); + Q_ASSERT(!obj->defaultProperty); + Q_ASSERT(ctxt.isSubContext()); // sub-objects must always be in a binding + // sub-context + + foreach(Property *prop, obj->properties) { + if (isSignalPropertyName(prop->name)) { + COMPILE_CHECK(buildSignal(prop, obj, ctxt)); + } else { + COMPILE_CHECK(buildProperty(prop, obj, ctxt)); + } + } + + return true; +} + +int QDeclarativeCompiler::componentTypeRef() +{ + QDeclarativeType *t = QDeclarativeMetaType::qmlType("QtQuick/Component",1,0); + for (int ii = output->types.count() - 1; ii >= 0; --ii) { + if (output->types.at(ii).type == t) + return ii; + } + QDeclarativeCompiledData::TypeReference ref; + ref.className = "Component"; + ref.type = t; + output->types << ref; + return output->types.count() - 1; +} + +bool QDeclarativeCompiler::buildSignal(QDeclarativeParser::Property *prop, QDeclarativeParser::Object *obj, + const BindingContext &ctxt) +{ + Q_ASSERT(obj->metaObject()); + + QByteArray name = prop->name; + Q_ASSERT(name.startsWith("on")); + name = name.mid(2); + if(name[0] >= 'A' && name[0] <= 'Z') + name[0] = name[0] - 'A' + 'a'; + + bool notInRevision = false; + int sigIdx = indexOfSignal(obj, name, ¬InRevision); + + if (sigIdx == -1) { + + if (notInRevision && -1 == indexOfProperty(obj, prop->name, 0)) { + Q_ASSERT(obj->type != -1); + const QList &resolvedTypes = unit->resolvedTypes(); + const QDeclarativeTypeData::TypeReference &type = resolvedTypes.at(obj->type); + if (type.type) { + COMPILE_EXCEPTION(prop, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(QString::fromUtf8(obj->className)).arg(QString::fromUtf8(prop->name)).arg(QString::fromUtf8(type.type->module())).arg(type.majorVersion).arg(type.minorVersion)); + } else { + COMPILE_EXCEPTION(prop, tr("\"%1.%2\" is not available due to component versioning.").arg(QString::fromUtf8(obj->className)).arg(QString::fromUtf8(prop->name))); + } + } + + // If the "on" name doesn't resolve into a signal, try it as a + // property. + COMPILE_CHECK(buildProperty(prop, obj, ctxt)); + + } else { + + if (prop->value || prop->values.count() != 1) + COMPILE_EXCEPTION(prop, tr("Incorrectly specified signal assignment")); + + prop->index = sigIdx; + obj->addSignalProperty(prop); + + if (prop->values.at(0)->object) { + COMPILE_CHECK(buildObject(prop->values.at(0)->object, ctxt)); + prop->values.at(0)->type = Value::SignalObject; + } else { + prop->values.at(0)->type = Value::SignalExpression; + + if (!prop->values.at(0)->value.isScript()) + COMPILE_EXCEPTION(prop, tr("Cannot assign a value to a signal (expecting a script to be run)")); + + QString script = prop->values.at(0)->value.asScript().trimmed(); + if (script.isEmpty()) + COMPILE_EXCEPTION(prop, tr("Empty signal assignment")); + + compileState.signalExpressions.insert(prop->values.at(0), ctxt); + } + } + + return true; +} + + +/*! + Returns true if (value) property \a prop exists on obj, false otherwise. +*/ +bool QDeclarativeCompiler::doesPropertyExist(QDeclarativeParser::Property *prop, + QDeclarativeParser::Object *obj) +{ + if(isAttachedPropertyName(prop->name) || prop->name == "id") + return true; + + const QMetaObject *mo = obj->metaObject(); + if (mo) { + if (prop->isDefault) { + QMetaProperty p = QDeclarativeMetaType::defaultProperty(mo); + return p.name() != 0; + } else { + int idx = indexOfProperty(obj, prop->name); + return idx != -1 && mo->property(idx).isScriptable(); + } + } + + return false; +} + +bool QDeclarativeCompiler::buildProperty(QDeclarativeParser::Property *prop, + QDeclarativeParser::Object *obj, + const BindingContext &ctxt) +{ + if (prop->isEmpty()) + COMPILE_EXCEPTION(prop, tr("Empty property assignment")); + + const QMetaObject *metaObject = obj->metaObject(); + Q_ASSERT(metaObject); + + if (isAttachedPropertyName(prop->name)) { + // Setup attached property data + + if (ctxt.isSubContext()) { + // Attached properties cannot be used on sub-objects. Sub-objects + // always exist in a binding sub-context, which is what we test + // for here. + COMPILE_EXCEPTION(prop, tr("Attached properties cannot be used here")); + } + + QDeclarativeType *type = 0; + QDeclarativeImportedNamespace *typeNamespace = 0; + unit->imports().resolveType(prop->name, &type, 0, 0, 0, &typeNamespace); + + if (typeNamespace) { + // ### We might need to indicate that this property is a namespace + // for the DOM API + COMPILE_CHECK(buildPropertyInNamespace(typeNamespace, prop, obj, + ctxt)); + return true; + } else if (!type || !type->attachedPropertiesType()) { + COMPILE_EXCEPTION(prop, tr("Non-existent attached object")); + } + + if (!prop->value) + COMPILE_EXCEPTION(prop, tr("Invalid attached object assignment")); + + Q_ASSERT(type->attachedPropertiesFunction()); + prop->index = type->attachedPropertiesId(); + prop->value->metatype = type->attachedPropertiesType(); + } else { + // Setup regular property data + QMetaProperty p; + + if (prop->isDefault) { + p = QDeclarativeMetaType::defaultProperty(metaObject); + + if (p.name()) { + prop->index = p.propertyIndex(); + prop->name = p.name(); + } + + } else { + bool notInRevision = false; + prop->index = indexOfProperty(obj, prop->name, ¬InRevision); + if (prop->index == -1 && notInRevision) { + const QList &resolvedTypes = unit->resolvedTypes(); + const QDeclarativeTypeData::TypeReference &type = resolvedTypes.at(obj->type); + if (type.type) { + COMPILE_EXCEPTION(prop, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(QString::fromUtf8(obj->className)).arg(QString::fromUtf8(prop->name)).arg(QString::fromUtf8(type.type->module())).arg(type.majorVersion).arg(type.minorVersion)); + } else { + COMPILE_EXCEPTION(prop, tr("\"%1.%2\" is not available due to component versioning.").arg(QString::fromUtf8(obj->className)).arg(QString::fromUtf8(prop->name))); + } + } + + if (prop->index != -1) { + p = metaObject->property(prop->index); + Q_ASSERT(p.name()); + + if (!p.isScriptable()) { + prop->index = -1; + p = QMetaProperty(); + } + } + } + + // We can't error here as the "id" property does not require a + // successful index resolution + if (p.name()) + prop->type = p.userType(); + + // Check if this is an alias + if (prop->index != -1 && + prop->parent && + prop->parent->type != -1 && + output->types.at(prop->parent->type).component) { + + QDeclarativePropertyCache *cache = output->types.at(prop->parent->type).component->rootPropertyCache; + if (cache && cache->property(prop->index) && + cache->property(prop->index)->flags & QDeclarativePropertyCache::Data::IsAlias) + prop->isAlias = true; + } + + if (prop->index != -1 && !prop->values.isEmpty()) + prop->parent->setBindingBit(prop->index); + } + + if (!prop->isDefault && prop->name == "id" && !ctxt.isSubContext()) { + + // The magic "id" behavior doesn't apply when "id" is resolved as a + // default property or to sub-objects (which are always in binding + // sub-contexts) + COMPILE_CHECK(buildIdProperty(prop, obj)); + if (prop->type == QVariant::String && + prop->values.at(0)->value.isString()) + COMPILE_CHECK(buildPropertyAssignment(prop, obj, ctxt)); + + } else if (isAttachedPropertyName(prop->name)) { + + COMPILE_CHECK(buildAttachedProperty(prop, obj, ctxt)); + + } else if (prop->index == -1) { + + if (prop->isDefault) { + COMPILE_EXCEPTION(prop->values.first(), tr("Cannot assign to non-existent default property")); + } else { + COMPILE_EXCEPTION(prop, tr("Cannot assign to non-existent property \"%1\"").arg(QString::fromUtf8(prop->name))); + } + + } else if (prop->value) { + + COMPILE_CHECK(buildGroupedProperty(prop, obj, ctxt)); + + } else if (enginePrivate->isList(prop->type)) { + + COMPILE_CHECK(buildListProperty(prop, obj, ctxt)); + + } else if (prop->type == qMetaTypeId()) { + + COMPILE_CHECK(buildScriptStringProperty(prop, obj, ctxt)); + + } else { + + COMPILE_CHECK(buildPropertyAssignment(prop, obj, ctxt)); + + } + + return true; +} + +bool QDeclarativeCompiler::buildPropertyInNamespace(QDeclarativeImportedNamespace *ns, + QDeclarativeParser::Property *nsProp, + QDeclarativeParser::Object *obj, + const BindingContext &ctxt) +{ + if (!nsProp->value) + COMPILE_EXCEPTION(nsProp, tr("Invalid use of namespace")); + + foreach (Property *prop, nsProp->value->properties) { + + if (!isAttachedPropertyName(prop->name)) + COMPILE_EXCEPTION(prop, tr("Not an attached property name")); + + // Setup attached property data + + QDeclarativeType *type = 0; + unit->imports().resolveType(ns, prop->name, &type, 0, 0, 0); + + if (!type || !type->attachedPropertiesType()) + COMPILE_EXCEPTION(prop, tr("Non-existent attached object")); + + if (!prop->value) + COMPILE_EXCEPTION(prop, tr("Invalid attached object assignment")); + + Q_ASSERT(type->attachedPropertiesFunction()); + prop->index = type->index(); + prop->value->metatype = type->attachedPropertiesType(); + + COMPILE_CHECK(buildAttachedProperty(prop, obj, ctxt)); + } + + return true; +} + +void QDeclarativeCompiler::genValueProperty(QDeclarativeParser::Property *prop, + QDeclarativeParser::Object *obj) +{ + if (enginePrivate->isList(prop->type)) { + genListProperty(prop, obj); + } else { + genPropertyAssignment(prop, obj); + } +} + +void QDeclarativeCompiler::genListProperty(QDeclarativeParser::Property *prop, + QDeclarativeParser::Object *obj) +{ + int listType = enginePrivate->listType(prop->type); + + QDeclarativeInstruction fetch; + fetch.type = QDeclarativeInstruction::FetchQList; + fetch.line = prop->location.start.line; + fetch.fetchQmlList.property = prop->index; + bool listTypeIsInterface = QDeclarativeMetaType::isInterface(listType); + fetch.fetchQmlList.type = listType; + output->bytecode << fetch; + + for (int ii = 0; ii < prop->values.count(); ++ii) { + QDeclarativeParser::Value *v = prop->values.at(ii); + + if (v->type == Value::CreatedObject) { + + genObject(v->object); + if (listTypeIsInterface) { + QDeclarativeInstruction assign; + assign.type = QDeclarativeInstruction::AssignObjectList; + assign.line = prop->location.start.line; + output->bytecode << assign; + } else { + QDeclarativeInstruction store; + store.type = QDeclarativeInstruction::StoreObjectQList; + store.line = prop->location.start.line; + output->bytecode << store; + } + + } else if (v->type == Value::PropertyBinding) { + + genBindingAssignment(v, prop, obj); + + } + + } + + QDeclarativeInstruction pop; + pop.type = QDeclarativeInstruction::PopQList; + pop.line = prop->location.start.line; + output->bytecode << pop; +} + +void QDeclarativeCompiler::genPropertyAssignment(QDeclarativeParser::Property *prop, + QDeclarativeParser::Object *obj, + QDeclarativeParser::Property *valueTypeProperty) +{ + for (int ii = 0; ii < prop->values.count(); ++ii) { + QDeclarativeParser::Value *v = prop->values.at(ii); + + Q_ASSERT(v->type == Value::CreatedObject || + v->type == Value::PropertyBinding || + v->type == Value::Literal); + + if (v->type == Value::CreatedObject) { + + genObject(v->object); + + if (QDeclarativeMetaType::isInterface(prop->type)) { + + QDeclarativeInstruction store; + store.type = QDeclarativeInstruction::StoreInterface; + store.line = v->object->location.start.line; + store.storeObject.propertyIndex = prop->index; + output->bytecode << store; + + } else if (prop->type == -1) { + + QDeclarativeInstruction store; + store.type = QDeclarativeInstruction::StoreVariantObject; + store.line = v->object->location.start.line; + store.storeObject.propertyIndex = prop->index; + output->bytecode << store; + + } else { + + QDeclarativeInstruction store; + store.type = QDeclarativeInstruction::StoreObject; + store.line = v->object->location.start.line; + store.storeObject.propertyIndex = prop->index; + output->bytecode << store; + + } + } else if (v->type == Value::PropertyBinding) { + + genBindingAssignment(v, prop, obj, valueTypeProperty); + + } else if (v->type == Value::Literal) { + + QMetaProperty mp = obj->metaObject()->property(prop->index); + genLiteralAssignment(mp, v); + + } + + } + + for (int ii = 0; ii < prop->onValues.count(); ++ii) { + + QDeclarativeParser::Value *v = prop->onValues.at(ii); + + Q_ASSERT(v->type == Value::ValueSource || + v->type == Value::ValueInterceptor); + + if (v->type == Value::ValueSource) { + genObject(v->object); + + QDeclarativeInstruction store; + store.type = QDeclarativeInstruction::StoreValueSource; + store.line = v->object->location.start.line; + if (valueTypeProperty) { + store.assignValueSource.property = genValueTypeData(prop, valueTypeProperty); + store.assignValueSource.owner = 1; + } else { + store.assignValueSource.property = genPropertyData(prop); + store.assignValueSource.owner = 0; + } + QDeclarativeType *valueType = toQmlType(v->object); + store.assignValueSource.castValue = valueType->propertyValueSourceCast(); + output->bytecode << store; + + } else if (v->type == Value::ValueInterceptor) { + genObject(v->object); + + QDeclarativeInstruction store; + store.type = QDeclarativeInstruction::StoreValueInterceptor; + store.line = v->object->location.start.line; + if (valueTypeProperty) { + store.assignValueInterceptor.property = genValueTypeData(prop, valueTypeProperty); + store.assignValueInterceptor.owner = 1; + } else { + store.assignValueInterceptor.property = genPropertyData(prop); + store.assignValueInterceptor.owner = 0; + } + QDeclarativeType *valueType = toQmlType(v->object); + store.assignValueInterceptor.castValue = valueType->propertyValueInterceptorCast(); + output->bytecode << store; + } + + } +} + +bool QDeclarativeCompiler::buildIdProperty(QDeclarativeParser::Property *prop, + QDeclarativeParser::Object *obj) +{ + if (prop->value || + prop->values.count() > 1 || + prop->values.at(0)->object) + COMPILE_EXCEPTION(prop, tr("Invalid use of id property")); + + QDeclarativeParser::Value *idValue = prop->values.at(0); + QString val = idValue->primitive(); + + COMPILE_CHECK(checkValidId(idValue, val)); + + if (compileState.ids.contains(val)) + COMPILE_EXCEPTION(prop, tr("id is not unique")); + + prop->values.at(0)->type = Value::Id; + + obj->id = val; + addId(val, obj); + + return true; +} + +void QDeclarativeCompiler::addId(const QString &id, QDeclarativeParser::Object *obj) +{ + Q_ASSERT(!compileState.ids.contains(id)); + Q_ASSERT(obj->id == id); + obj->idIndex = compileState.ids.count(); + compileState.ids.insert(id, obj); + compileState.idIndexes.insert(obj->idIndex, obj); +} + +void QDeclarativeCompiler::addBindingReference(const BindingReference &ref) +{ + Q_ASSERT(ref.value && !compileState.bindings.contains(ref.value)); + compileState.bindings.insert(ref.value, ref); +} + +void QDeclarativeCompiler::saveComponentState() +{ + Q_ASSERT(compileState.root); + Q_ASSERT(!savedCompileStates.contains(compileState.root)); + + savedCompileStates.insert(compileState.root, compileState); + savedComponentStats.append(componentStat); +} + +QDeclarativeCompiler::ComponentCompileState +QDeclarativeCompiler::componentState(QDeclarativeParser::Object *obj) +{ + Q_ASSERT(savedCompileStates.contains(obj)); + return savedCompileStates.value(obj); +} + +// Build attached property object. In this example, +// Text { +// GridView.row: 10 +// } +// GridView is an attached property object. +bool QDeclarativeCompiler::buildAttachedProperty(QDeclarativeParser::Property *prop, + QDeclarativeParser::Object *obj, + const BindingContext &ctxt) +{ + Q_ASSERT(prop->value); + Q_ASSERT(prop->index != -1); // This is set in buildProperty() + + obj->addAttachedProperty(prop); + + COMPILE_CHECK(buildSubObject(prop->value, ctxt.incr())); + + return true; +} + + +// Build "grouped" properties. In this example: +// Text { +// font.pointSize: 12 +// font.family: "Helvetica" +// } +// font is a nested property. pointSize and family are not. +bool QDeclarativeCompiler::buildGroupedProperty(QDeclarativeParser::Property *prop, + QDeclarativeParser::Object *obj, + const BindingContext &ctxt) +{ + Q_ASSERT(prop->type != 0); + Q_ASSERT(prop->index != -1); + + if (QDeclarativeValueTypeFactory::isValueType(prop->type)) { + if (prop->type >= 0 /* QVariant == -1 */ && enginePrivate->valueTypes[prop->type]) { + + if (prop->values.count()) { + if (prop->values.at(0)->location < prop->value->location) { + COMPILE_EXCEPTION(prop->value, tr( "Property has already been assigned a value")); + } else { + COMPILE_EXCEPTION(prop->values.at(0), tr( "Property has already been assigned a value")); + } + } + + if (!obj->metaObject()->property(prop->index).isWritable()) { + COMPILE_EXCEPTION(prop, tr( "Invalid property assignment: \"%1\" is a read-only property").arg(QString::fromUtf8(prop->name))); + } + + + if (prop->isAlias) { + foreach (Property *vtProp, prop->value->properties) + vtProp->isAlias = true; + } + + COMPILE_CHECK(buildValueTypeProperty(enginePrivate->valueTypes[prop->type], + prop->value, obj, ctxt.incr())); + obj->addValueTypeProperty(prop); + } else { + COMPILE_EXCEPTION(prop, tr("Invalid grouped property access")); + } + + } else { + // Load the nested property's meta type + prop->value->metatype = enginePrivate->metaObjectForType(prop->type); + if (!prop->value->metatype) + COMPILE_EXCEPTION(prop, tr("Invalid grouped property access")); + + if (prop->values.count()) + COMPILE_EXCEPTION(prop->values.at(0), tr( "Cannot assign a value directly to a grouped property")); + + obj->addGroupedProperty(prop); + + COMPILE_CHECK(buildSubObject(prop->value, ctxt.incr())); + } + + return true; +} + +bool QDeclarativeCompiler::buildValueTypeProperty(QObject *type, + QDeclarativeParser::Object *obj, + QDeclarativeParser::Object *baseObj, + const BindingContext &ctxt) +{ + if (obj->defaultProperty) + COMPILE_EXCEPTION(obj, tr("Invalid property use")); + obj->metatype = type->metaObject(); + + foreach (Property *prop, obj->properties) { + int idx = type->metaObject()->indexOfProperty(prop->name.constData()); + if (idx == -1) + COMPILE_EXCEPTION(prop, tr("Cannot assign to non-existent property \"%1\"").arg(QString::fromUtf8(prop->name))); + QMetaProperty p = type->metaObject()->property(idx); + if (!p.isScriptable()) + COMPILE_EXCEPTION(prop, tr("Cannot assign to non-existent property \"%1\"").arg(QString::fromUtf8(prop->name))); + prop->index = idx; + prop->type = p.userType(); + prop->isValueTypeSubProperty = true; + + if (prop->value) + COMPILE_EXCEPTION(prop, tr("Property assignment expected")); + + if (prop->values.count() > 1) { + COMPILE_EXCEPTION(prop, tr("Single property assignment expected")); + } else if (prop->values.count()) { + QDeclarativeParser::Value *value = prop->values.at(0); + + if (value->object) { + COMPILE_EXCEPTION(prop, tr("Unexpected object assignment")); + } else if (value->value.isScript()) { + // ### Check for writability + + //optimization for . enum assignments + bool isEnumAssignment = false; + COMPILE_CHECK(testQualifiedEnumAssignment(p, obj, value, &isEnumAssignment)); + if (isEnumAssignment) { + value->type = Value::Literal; + } else { + BindingReference reference; + reference.expression = value->value; + reference.property = prop; + reference.value = value; + reference.bindingContext = ctxt; + reference.bindingContext.owner++; + addBindingReference(reference); + value->type = Value::PropertyBinding; + } + } else { + COMPILE_CHECK(testLiteralAssignment(p, value)); + value->type = Value::Literal; + } + } + + for (int ii = 0; ii < prop->onValues.count(); ++ii) { + QDeclarativeParser::Value *v = prop->onValues.at(ii); + Q_ASSERT(v->object); + + COMPILE_CHECK(buildPropertyOnAssignment(prop, obj, baseObj, v, ctxt)); + } + + obj->addValueProperty(prop); + } + + return true; +} + +// Build assignments to QML lists. QML lists are properties of type +// QDeclarativeListProperty. List properties can accept a list of +// objects, or a single binding. +bool QDeclarativeCompiler::buildListProperty(QDeclarativeParser::Property *prop, + QDeclarativeParser::Object *obj, + const BindingContext &ctxt) +{ + Q_ASSERT(enginePrivate->isList(prop->type)); + + int t = prop->type; + + obj->addValueProperty(prop); + + int listType = enginePrivate->listType(t); + bool listTypeIsInterface = QDeclarativeMetaType::isInterface(listType); + + bool assignedBinding = false; + for (int ii = 0; ii < prop->values.count(); ++ii) { + QDeclarativeParser::Value *v = prop->values.at(ii); + if (v->object) { + v->type = Value::CreatedObject; + COMPILE_CHECK(buildObject(v->object, ctxt)); + + // We check object coercian here. We check interface assignment + // at runtime. + if (!listTypeIsInterface) { + if (!canCoerce(listType, v->object)) { + COMPILE_EXCEPTION(v, tr("Cannot assign object to list")); + } + } + + } else if (v->value.isScript()) { + if (assignedBinding) + COMPILE_EXCEPTION(v, tr("Can only assign one binding to lists")); + + assignedBinding = true; + COMPILE_CHECK(buildBinding(v, prop, ctxt)); + v->type = Value::PropertyBinding; + } else { + COMPILE_EXCEPTION(v, tr("Cannot assign primitives to lists")); + } + } + + return true; +} + +// Compiles an assignment to a QDeclarativeScriptString property +bool QDeclarativeCompiler::buildScriptStringProperty(QDeclarativeParser::Property *prop, + QDeclarativeParser::Object *obj, + const BindingContext &ctxt) +{ + if (prop->values.count() > 1) + COMPILE_EXCEPTION(prop->values.at(1), tr( "Cannot assign multiple values to a script property")); + + if (prop->values.at(0)->object) + COMPILE_EXCEPTION(prop->values.at(0), tr( "Invalid property assignment: script expected")); + + obj->addScriptStringProperty(prop, ctxt.stack); + + return true; +} + +// Compile regular property assignments of the form "property: " +bool QDeclarativeCompiler::buildPropertyAssignment(QDeclarativeParser::Property *prop, + QDeclarativeParser::Object *obj, + const BindingContext &ctxt) +{ + obj->addValueProperty(prop); + + if (prop->values.count() > 1) + COMPILE_EXCEPTION(prop->values.at(0), tr( "Cannot assign multiple values to a singular property") ); + + for (int ii = 0; ii < prop->values.count(); ++ii) { + QDeclarativeParser::Value *v = prop->values.at(ii); + if (v->object) { + + COMPILE_CHECK(buildPropertyObjectAssignment(prop, obj, v, ctxt)); + + } else { + + COMPILE_CHECK(buildPropertyLiteralAssignment(prop, obj, v, ctxt)); + + } + } + + for (int ii = 0; ii < prop->onValues.count(); ++ii) { + QDeclarativeParser::Value *v = prop->onValues.at(ii); + + Q_ASSERT(v->object); + COMPILE_CHECK(buildPropertyOnAssignment(prop, obj, obj, v, ctxt)); + } + + return true; +} + +// Compile assigning a single object instance to a regular property +bool QDeclarativeCompiler::buildPropertyObjectAssignment(QDeclarativeParser::Property *prop, + QDeclarativeParser::Object *obj, + QDeclarativeParser::Value *v, + const BindingContext &ctxt) +{ + Q_ASSERT(prop->index != -1); + Q_ASSERT(v->object->type != -1); + + if (!obj->metaObject()->property(prop->index).isWritable()) + COMPILE_EXCEPTION(v, tr("Invalid property assignment: \"%1\" is a read-only property").arg(QString::fromUtf8(prop->name))); + + if (QDeclarativeMetaType::isInterface(prop->type)) { + + // Assigning an object to an interface ptr property + COMPILE_CHECK(buildObject(v->object, ctxt)); + + v->type = Value::CreatedObject; + + } else if (prop->type == -1) { + + // Assigning an object to a QVariant + COMPILE_CHECK(buildObject(v->object, ctxt)); + + v->type = Value::CreatedObject; + } else { + // Normally buildObject() will set this up, but we need the static + // meta object earlier to test for assignability. It doesn't matter + // that there may still be outstanding synthesized meta object changes + // on this type, as they are not relevant for assignability testing + v->object->metatype = output->types.at(v->object->type).metaObject(); + Q_ASSERT(v->object->metaObject()); + + // We want to raw metaObject here as the raw metaobject is the + // actual property type before we applied any extensions that might + // effect the properties on the type, but don't effect assignability + const QMetaObject *propertyMetaObject = enginePrivate->rawMetaObjectForType(prop->type); + + // Will be true if the assgned type inherits propertyMetaObject + bool isAssignable = false; + // Determine isAssignable value + if (propertyMetaObject) { + const QMetaObject *c = v->object->metatype; + while(c) { + isAssignable |= (QDeclarativePropertyPrivate::equal(c, propertyMetaObject)); + c = c->superClass(); + } + } + + if (isAssignable) { + // Simple assignment + COMPILE_CHECK(buildObject(v->object, ctxt)); + + v->type = Value::CreatedObject; + } else if (propertyMetaObject == &QDeclarativeComponent::staticMetaObject) { + // Automatic "Component" insertion + QDeclarativeParser::Object *root = v->object; + QDeclarativeParser::Object *component = new QDeclarativeParser::Object; + component->type = componentTypeRef(); + component->typeName = "Qt/Component"; + component->metatype = &QDeclarativeComponent::staticMetaObject; + component->location = root->location; + QDeclarativeParser::Value *componentValue = new QDeclarativeParser::Value; + componentValue->object = root; + component->getDefaultProperty()->addValue(componentValue); + v->object = component; + COMPILE_CHECK(buildPropertyObjectAssignment(prop, obj, v, ctxt)); + } else { + COMPILE_EXCEPTION(v->object, tr("Cannot assign object to property")); + } + } + + return true; +} + +// Compile assigning a single object instance to a regular property using the "on" syntax. +// +// For example: +// Item { +// NumberAnimation on x { } +// } +bool QDeclarativeCompiler::buildPropertyOnAssignment(QDeclarativeParser::Property *prop, + QDeclarativeParser::Object *obj, + QDeclarativeParser::Object *baseObj, + QDeclarativeParser::Value *v, + const BindingContext &ctxt) +{ + Q_ASSERT(prop->index != -1); + Q_ASSERT(v->object->type != -1); + + if (!obj->metaObject()->property(prop->index).isWritable()) + COMPILE_EXCEPTION(v, tr("Invalid property assignment: \"%1\" is a read-only property").arg(QString::fromUtf8(prop->name))); + + + // Normally buildObject() will set this up, but we need the static + // meta object earlier to test for assignability. It doesn't matter + // that there may still be outstanding synthesized meta object changes + // on this type, as they are not relevant for assignability testing + v->object->metatype = output->types.at(v->object->type).metaObject(); + Q_ASSERT(v->object->metaObject()); + + // Will be true if the assigned type inherits QDeclarativePropertyValueSource + bool isPropertyValue = false; + // Will be true if the assigned type inherits QDeclarativePropertyValueInterceptor + bool isPropertyInterceptor = false; + if (QDeclarativeType *valueType = toQmlType(v->object)) { + isPropertyValue = valueType->propertyValueSourceCast() != -1; + isPropertyInterceptor = valueType->propertyValueInterceptorCast() != -1; + } + + if (isPropertyValue || isPropertyInterceptor) { + // Assign as a property value source + COMPILE_CHECK(buildObject(v->object, ctxt)); + + if (isPropertyInterceptor && prop->parent->synthdata.isEmpty()) + buildDynamicMeta(baseObj, ForceCreation); + v->type = isPropertyValue ? Value::ValueSource : Value::ValueInterceptor; + } else { + COMPILE_EXCEPTION(v, tr("\"%1\" cannot operate on \"%2\"").arg(QString::fromUtf8(v->object->typeName)).arg(QString::fromUtf8(prop->name.constData()))); + } + + return true; +} + +// Compile assigning a literal or binding to a regular property +bool QDeclarativeCompiler::buildPropertyLiteralAssignment(QDeclarativeParser::Property *prop, + QDeclarativeParser::Object *obj, + QDeclarativeParser::Value *v, + const BindingContext &ctxt) +{ + Q_ASSERT(prop->index != -1); + + if (v->value.isScript()) { + + //optimization for . enum assignments + bool isEnumAssignment = false; + COMPILE_CHECK(testQualifiedEnumAssignment(obj->metaObject()->property(prop->index), obj, v, &isEnumAssignment)); + if (isEnumAssignment) { + v->type = Value::Literal; + return true; + } + + COMPILE_CHECK(buildBinding(v, prop, ctxt)); + + v->type = Value::PropertyBinding; + + } else { + + COMPILE_CHECK(testLiteralAssignment(obj->metaObject()->property(prop->index), v)); + + v->type = Value::Literal; + } + + return true; +} + +bool QDeclarativeCompiler::testQualifiedEnumAssignment(const QMetaProperty &prop, + QDeclarativeParser::Object *obj, + QDeclarativeParser::Value *v, + bool *isAssignment) +{ + *isAssignment = false; + if (!prop.isEnumType()) + return true; + + if (!prop.isWritable()) + COMPILE_EXCEPTION(v, tr("Invalid property assignment: \"%1\" is a read-only property").arg(QString::fromUtf8(prop.name()))); + + QString string = v->value.asString(); + if (!string.at(0).isUpper()) + return true; + + QStringList parts = string.split(QLatin1Char('.')); + if (parts.count() != 2) + return true; + + QString typeName = parts.at(0); + QDeclarativeType *type = 0; + unit->imports().resolveType(typeName.toUtf8(), &type, 0, 0, 0, 0); + + //handle enums on value types (where obj->typeName is empty) + QByteArray objTypeName = obj->typeName; + if (objTypeName.isEmpty()) { + QDeclarativeType *objType = toQmlType(obj); + if (objType) + objTypeName = objType->qmlTypeName(); + } + + if (!type || objTypeName != type->qmlTypeName()) + return true; + + QString enumValue = parts.at(1); + int value; + if (prop.isFlagType()) { + value = prop.enumerator().keysToValue(enumValue.toUtf8().constData()); + } else + value = prop.enumerator().keyToValue(enumValue.toUtf8().constData()); + if (value == -1) + return true; + + v->type = Value::Literal; + v->value = QDeclarativeParser::Variant(enumValue); + *isAssignment = true; + + return true; +} + +// Similar logic to above, but not knowing target property. +int QDeclarativeCompiler::evaluateEnum(const QByteArray& script) const +{ + int dot = script.indexOf('.'); + if (dot > 0) { + QDeclarativeType *type = 0; + unit->imports().resolveType(script.left(dot), &type, 0, 0, 0, 0); + if (!type) + return -1; + const QMetaObject *mo = type->metaObject(); + const char *key = script.constData() + dot+1; + int i = mo->enumeratorCount(); + while (i--) { + int v = mo->enumerator(i).keyToValue(key); + if (v >= 0) + return v; + } + } + return -1; +} + +const QMetaObject *QDeclarativeCompiler::resolveType(const QByteArray& name) const +{ + QDeclarativeType *qmltype = 0; + if (!unit->imports().resolveType(name, &qmltype, 0, 0, 0, 0)) + return 0; + if (!qmltype) + return 0; + return qmltype->metaObject(); +} + +// similar to logic of completeComponentBuild, but also sticks data +// into datas at the end +int QDeclarativeCompiler::rewriteBinding(const QString& expression, const QByteArray& name) +{ + QDeclarativeRewrite::RewriteBinding rewriteBinding; + rewriteBinding.setName('$' + name.mid(name.lastIndexOf('.') + 1)); + bool isSharable = false; + QString rewrite = rewriteBinding(expression, 0, &isSharable); + + quint32 length = rewrite.length(); + quint32 pc; + + if (isSharable) { + pc = output->cachedClosures.count(); + pc |= 0x80000000; + output->cachedClosures.append(0); + } else { + pc = output->cachedPrograms.length(); + output->cachedPrograms.append(0); + } + + QByteArray compiledData = + QByteArray((const char *)&pc, sizeof(quint32)) + + QByteArray((const char *)&length, sizeof(quint32)) + + QByteArray((const char *)rewrite.constData(), + rewrite.length() * sizeof(QChar)); + + return output->indexForByteArray(compiledData); +} + +// Ensures that the dynamic meta specification on obj is valid +bool QDeclarativeCompiler::checkDynamicMeta(QDeclarativeParser::Object *obj) +{ + QSet propNames; + QSet methodNames; + bool seenDefaultProperty = false; + + // Check properties + for (int ii = 0; ii < obj->dynamicProperties.count(); ++ii) { + const QDeclarativeParser::Object::DynamicProperty &prop = + obj->dynamicProperties.at(ii); + + if (prop.isDefaultProperty) { + if (seenDefaultProperty) + COMPILE_EXCEPTION(&prop, tr("Duplicate default property")); + seenDefaultProperty = true; + } + + if (propNames.contains(prop.name)) + COMPILE_EXCEPTION(&prop, tr("Duplicate property name")); + + QString propName = QString::fromUtf8(prop.name); + if (propName.at(0).isUpper()) + COMPILE_EXCEPTION(&prop, tr("Property names cannot begin with an upper case letter")); + + if (enginePrivate->globalClass->illegalNames().contains(propName)) + COMPILE_EXCEPTION(&prop, tr("Illegal property name")); + + propNames.insert(prop.name); + } + + for (int ii = 0; ii < obj->dynamicSignals.count(); ++ii) { + QByteArray name = obj->dynamicSignals.at(ii).name; + if (methodNames.contains(name)) + COMPILE_EXCEPTION(obj, tr("Duplicate signal name")); + QString nameStr = QString::fromUtf8(name); + if (nameStr.at(0).isUpper()) + COMPILE_EXCEPTION(obj, tr("Signal names cannot begin with an upper case letter")); + if (enginePrivate->globalClass->illegalNames().contains(nameStr)) + COMPILE_EXCEPTION(obj, tr("Illegal signal name")); + methodNames.insert(name); + } + for (int ii = 0; ii < obj->dynamicSlots.count(); ++ii) { + QByteArray name = obj->dynamicSlots.at(ii).name; + if (methodNames.contains(name)) + COMPILE_EXCEPTION(obj, tr("Duplicate method name")); + QString nameStr = QString::fromUtf8(name); + if (nameStr.at(0).isUpper()) + COMPILE_EXCEPTION(obj, tr("Method names cannot begin with an upper case letter")); + if (enginePrivate->globalClass->illegalNames().contains(nameStr)) + COMPILE_EXCEPTION(obj, tr("Illegal method name")); + methodNames.insert(name); + } + + return true; +} + +bool QDeclarativeCompiler::mergeDynamicMetaProperties(QDeclarativeParser::Object *obj) +{ + for (int ii = 0; ii < obj->dynamicProperties.count(); ++ii) { + const Object::DynamicProperty &p = obj->dynamicProperties.at(ii); + + if (!p.defaultValue || p.type == Object::DynamicProperty::Alias) + continue; + + Property *property = 0; + if (p.isDefaultProperty) { + property = obj->getDefaultProperty(); + } else { + property = obj->getProperty(p.name); + if (!property->values.isEmpty()) + COMPILE_EXCEPTION(property, tr("Property value set multiple times")); + } + + if (property->value) + COMPILE_EXCEPTION(property, tr("Invalid property nesting")); + + for (int ii = 0; ii < p.defaultValue->values.count(); ++ii) { + QDeclarativeParser::Value *v = p.defaultValue->values.at(ii); + v->addref(); + property->values.append(v); + } + } + return true; +} + +Q_GLOBAL_STATIC(QAtomicInt, classIndexCounter) + +bool QDeclarativeCompiler::buildDynamicMeta(QDeclarativeParser::Object *obj, DynamicMetaMode mode) +{ + Q_ASSERT(obj); + Q_ASSERT(obj->metatype); + + if (mode != ForceCreation && + obj->dynamicProperties.isEmpty() && + obj->dynamicSignals.isEmpty() && + obj->dynamicSlots.isEmpty()) + return true; + + QByteArray dynamicData(sizeof(QDeclarativeVMEMetaData), (char)0); + + QByteArray newClassName = obj->metatype->className(); + newClassName.append("_QML_"); + int idx = classIndexCounter()->fetchAndAddRelaxed(1); + newClassName.append(QByteArray::number(idx)); + if (compileState.root == obj) { + QString path = output->url.path(); + int lastSlash = path.lastIndexOf(QLatin1Char('/')); + if (lastSlash > -1) { + QString nameBase = path.mid(lastSlash + 1, path.length()-lastSlash-5); + if (!nameBase.isEmpty() && nameBase.at(0).isUpper()) + newClassName = nameBase.toUtf8() + "_QMLTYPE_" + QByteArray::number(idx); + } + } + + QMetaObjectBuilder builder; + builder.setClassName(newClassName); + builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); + + bool hasAlias = false; + for (int ii = 0; ii < obj->dynamicProperties.count(); ++ii) { + const Object::DynamicProperty &p = obj->dynamicProperties.at(ii); + + int propIdx = obj->metaObject()->indexOfProperty(p.name.constData()); + if (-1 != propIdx) { + QMetaProperty prop = obj->metaObject()->property(propIdx); + if (prop.isFinal()) + COMPILE_EXCEPTION(&p, tr("Cannot override FINAL property")); + } + + if (p.isDefaultProperty && + (p.type != Object::DynamicProperty::Alias || + mode == ResolveAliases)) + builder.addClassInfo("DefaultProperty", p.name); + + QByteArray type; + int propertyType = 0; + bool readonly = false; + switch(p.type) { + case Object::DynamicProperty::Alias: + hasAlias = true; + continue; + break; + case Object::DynamicProperty::CustomList: + case Object::DynamicProperty::Custom: + { + QByteArray customTypeName; + QDeclarativeType *qmltype = 0; + QUrl url; + if (!unit->imports().resolveType(p.customType, &qmltype, &url, 0, 0, 0)) + COMPILE_EXCEPTION(&p, tr("Invalid property type")); + + if (!qmltype) { + QDeclarativeTypeData *tdata = enginePrivate->typeLoader.get(url); + Q_ASSERT(tdata); + Q_ASSERT(tdata->isComplete()); + + QDeclarativeCompiledData *data = tdata->compiledData(); + customTypeName = data->root->className(); + data->release(); + tdata->release(); + } else { + customTypeName = qmltype->typeName(); + } + + if (p.type == Object::DynamicProperty::Custom) { + type = customTypeName + '*'; + propertyType = QMetaType::QObjectStar; + } else { + readonly = true; + type = "QDeclarativeListProperty<"; + type.append(customTypeName); + type.append(">"); + propertyType = qMetaTypeId >(); + } + } + break; + case Object::DynamicProperty::Variant: + propertyType = -1; + type = "QVariant"; + break; + case Object::DynamicProperty::Int: + propertyType = QVariant::Int; + type = "int"; + break; + case Object::DynamicProperty::Bool: + propertyType = QVariant::Bool; + type = "bool"; + break; + case Object::DynamicProperty::Real: + propertyType = QVariant::Double; + type = "double"; + break; + case Object::DynamicProperty::String: + propertyType = QVariant::String; + type = "QString"; + break; + case Object::DynamicProperty::Url: + propertyType = QVariant::Url; + type = "QUrl"; + break; + case Object::DynamicProperty::Color: + propertyType = QVariant::Color; + type = "QColor"; + break; + case Object::DynamicProperty::Time: + propertyType = QVariant::Time; + type = "QTime"; + break; + case Object::DynamicProperty::Date: + propertyType = QVariant::Date; + type = "QDate"; + break; + case Object::DynamicProperty::DateTime: + propertyType = QVariant::DateTime; + type = "QDateTime"; + break; + } + + ((QDeclarativeVMEMetaData *)dynamicData.data())->propertyCount++; + QDeclarativeVMEMetaData::PropertyData propertyData = { propertyType }; + dynamicData.append((char *)&propertyData, sizeof(propertyData)); + + builder.addSignal(p.name + "Changed()"); + QMetaPropertyBuilder propBuilder = + builder.addProperty(p.name, type, builder.methodCount() - 1); + propBuilder.setWritable(!readonly); + } + + for (int ii = 0; ii < obj->dynamicProperties.count(); ++ii) { + const Object::DynamicProperty &p = obj->dynamicProperties.at(ii); + + if (p.type == Object::DynamicProperty::Alias) { + if (mode == ResolveAliases) { + ((QDeclarativeVMEMetaData *)dynamicData.data())->aliasCount++; + COMPILE_CHECK(compileAlias(builder, dynamicData, obj, p)); + } else { + // Need a fake signal so that the metaobject remains consistent across + // the resolve and non-resolve alias runs + builder.addSignal(p.name + "Changed()"); + } + } + } + + for (int ii = 0; ii < obj->dynamicSignals.count(); ++ii) { + const Object::DynamicSignal &s = obj->dynamicSignals.at(ii); + QByteArray sig(s.name + '('); + for (int jj = 0; jj < s.parameterTypes.count(); ++jj) { + if (jj) sig.append(','); + sig.append(s.parameterTypes.at(jj)); + } + sig.append(')'); + QMetaMethodBuilder b = builder.addSignal(sig); + b.setParameterNames(s.parameterNames); + ((QDeclarativeVMEMetaData *)dynamicData.data())->signalCount++; + } + + QStringList funcScripts; + + for (int ii = 0; ii < obj->dynamicSlots.count(); ++ii) { + Object::DynamicSlot &s = obj->dynamicSlots[ii]; + QByteArray sig(s.name + '('); + QString funcScript(QLatin1String("(function ") + s.name + QLatin1Char('(')); + + for (int jj = 0; jj < s.parameterNames.count(); ++jj) { + if (jj) { + sig.append(','); + funcScript.append(QLatin1Char(',')); + } + funcScript.append(QLatin1String(s.parameterNames.at(jj))); + sig.append("QVariant"); + } + sig.append(')'); + funcScript.append(QLatin1Char(')')); + funcScript.append(s.body); + funcScript.append(QLatin1Char(')')); + funcScripts << funcScript; + + QMetaMethodBuilder b = builder.addSlot(sig); + b.setReturnType("QVariant"); + b.setParameterNames(s.parameterNames); + + ((QDeclarativeVMEMetaData *)dynamicData.data())->methodCount++; + QDeclarativeVMEMetaData::MethodData methodData = + { s.parameterNames.count(), 0, funcScript.length(), s.location.start.line }; + + dynamicData.append((char *)&methodData, sizeof(methodData)); + } + + for (int ii = 0; ii < obj->dynamicSlots.count(); ++ii) { + const QString &funcScript = funcScripts.at(ii); + QDeclarativeVMEMetaData::MethodData *data = + ((QDeclarativeVMEMetaData *)dynamicData.data())->methodData() + ii; + + data->bodyOffset = dynamicData.size(); + + dynamicData.append((const char *)funcScript.constData(), + (funcScript.length() * sizeof(QChar))); + } + + obj->metadata = builder.toRelocatableData(); + builder.fromRelocatableData(&obj->extObject, obj->metatype, obj->metadata); + + if (mode == IgnoreAliases && hasAlias) + compileState.aliasingObjects << obj; + + obj->synthdata = dynamicData; + + if (obj->synthCache) { + obj->synthCache->release(); + obj->synthCache = 0; + } + + if (obj->type != -1) { + QDeclarativePropertyCache *cache = output->types[obj->type].createPropertyCache(engine)->copy(); + cache->append(engine, &obj->extObject, QDeclarativePropertyCache::Data::NoFlags, + QDeclarativePropertyCache::Data::IsVMEFunction, + QDeclarativePropertyCache::Data::IsVMESignal); + obj->synthCache = cache; + } + + return true; +} + +bool QDeclarativeCompiler::checkValidId(QDeclarativeParser::Value *v, const QString &val) +{ + if (val.isEmpty()) + COMPILE_EXCEPTION(v, tr( "Invalid empty ID")); + + if (val.at(0).isLetter() && !val.at(0).isLower()) + COMPILE_EXCEPTION(v, tr( "IDs cannot start with an uppercase letter")); + + QChar u(QLatin1Char('_')); + for (int ii = 0; ii < val.count(); ++ii) { + + if (ii == 0 && !val.at(ii).isLetter() && val.at(ii) != u) { + COMPILE_EXCEPTION(v, tr( "IDs must start with a letter or underscore")); + } else if (ii != 0 && !val.at(ii).isLetterOrNumber() && val.at(ii) != u) { + COMPILE_EXCEPTION(v, tr( "IDs must contain only letters, numbers, and underscores")); + } + + } + + if (enginePrivate->globalClass->illegalNames().contains(val)) + COMPILE_EXCEPTION(v, tr( "ID illegally masks global JavaScript property")); + + return true; +} + +#include + +static QStringList astNodeToStringList(QDeclarativeJS::AST::Node *node) +{ + if (node->kind == QDeclarativeJS::AST::Node::Kind_IdentifierExpression) { + QString name = + static_cast(node)->name->asString(); + return QStringList() << name; + } else if (node->kind == QDeclarativeJS::AST::Node::Kind_FieldMemberExpression) { + QDeclarativeJS::AST::FieldMemberExpression *expr = static_cast(node); + + QStringList rv = astNodeToStringList(expr->base); + if (rv.isEmpty()) + return rv; + rv.append(expr->name->asString()); + return rv; + } + return QStringList(); +} + +bool QDeclarativeCompiler::compileAlias(QMetaObjectBuilder &builder, + QByteArray &data, + QDeclarativeParser::Object *obj, + const Object::DynamicProperty &prop) +{ + if (!prop.defaultValue) + COMPILE_EXCEPTION(obj, tr("No property alias location")); + + if (prop.defaultValue->values.count() != 1 || + prop.defaultValue->values.at(0)->object || + !prop.defaultValue->values.at(0)->value.isScript()) + COMPILE_EXCEPTION(prop.defaultValue, tr("Invalid alias location")); + + QDeclarativeJS::AST::Node *node = prop.defaultValue->values.at(0)->value.asAST(); + if (!node) + COMPILE_EXCEPTION(obj, tr("No property alias location")); // ### Can this happen? + + QStringList alias = astNodeToStringList(node); + + if (alias.count() < 1 || alias.count() > 3) + COMPILE_EXCEPTION(prop.defaultValue, tr("Invalid alias reference. An alias reference must be specified as , . or ..")); + + if (!compileState.ids.contains(alias.at(0))) + COMPILE_EXCEPTION(prop.defaultValue, tr("Invalid alias reference. Unable to find id \"%1\"").arg(alias.at(0))); + + QDeclarativeParser::Object *idObject = compileState.ids[alias.at(0)]; + + QByteArray typeName; + + int propIdx = -1; + int flags = 0; + bool writable = false; + if (alias.count() == 2 || alias.count() == 3) { + propIdx = indexOfProperty(idObject, alias.at(1).toUtf8()); + + if (-1 == propIdx) { + COMPILE_EXCEPTION(prop.defaultValue, tr("Invalid alias location")); + } else if (propIdx > 0xFFFF) { + COMPILE_EXCEPTION(prop.defaultValue, tr("Alias property exceeds alias bounds")); + } + + QMetaProperty aliasProperty = idObject->metaObject()->property(propIdx); + if (!aliasProperty.isScriptable()) + COMPILE_EXCEPTION(prop.defaultValue, tr("Invalid alias location")); + + writable = aliasProperty.isWritable(); + + if (alias.count() == 3) { + QDeclarativeValueType *valueType = enginePrivate->valueTypes[aliasProperty.type()]; + if (!valueType) + COMPILE_EXCEPTION(prop.defaultValue, tr("Invalid alias location")); + + propIdx |= ((unsigned int)aliasProperty.type()) << 24; + + int valueTypeIndex = valueType->metaObject()->indexOfProperty(alias.at(2).toUtf8().constData()); + if (valueTypeIndex == -1) + COMPILE_EXCEPTION(prop.defaultValue, tr("Invalid alias location")); + Q_ASSERT(valueTypeIndex <= 0xFF); + + aliasProperty = valueType->metaObject()->property(valueTypeIndex); + propIdx |= (valueTypeIndex << 16); + } + + if (aliasProperty.isEnumType()) + typeName = "int"; // Avoid introducing a dependency on the aliased metaobject + else + typeName = aliasProperty.typeName(); + } else { + typeName = idObject->metaObject()->className(); + + //use the base type since it has been registered with metatype system + int index = typeName.indexOf("_QML_"); + if (index != -1) { + typeName = typeName.left(index); + } else { + index = typeName.indexOf("_QMLTYPE_"); + const QMetaObject *mo = idObject->metaObject(); + while (index != -1 && mo) { + typeName = mo->superClass()->className(); + index = typeName.indexOf("_QMLTYPE_"); + mo = mo->superClass(); + } + } + + typeName += '*'; + } + + if (typeName.endsWith('*')) + flags |= QML_ALIAS_FLAG_PTR; + + data.append((const char *)&idObject->idIndex, sizeof(idObject->idIndex)); + data.append((const char *)&propIdx, sizeof(propIdx)); + data.append((const char *)&flags, sizeof(flags)); + + builder.addSignal(prop.name + "Changed()"); + QMetaPropertyBuilder propBuilder = + builder.addProperty(prop.name, typeName.constData(), builder.methodCount() - 1); + propBuilder.setWritable(writable); + return true; +} + +bool QDeclarativeCompiler::buildBinding(QDeclarativeParser::Value *value, + QDeclarativeParser::Property *prop, + const BindingContext &ctxt) +{ + Q_ASSERT(prop->index != -1); + Q_ASSERT(prop->parent); + Q_ASSERT(prop->parent->metaObject()); + + QMetaProperty mp = prop->parent->metaObject()->property(prop->index); + if (!mp.isWritable() && !QDeclarativeMetaType::isList(prop->type)) + COMPILE_EXCEPTION(prop, tr("Invalid property assignment: \"%1\" is a read-only property").arg(QString::fromUtf8(prop->name))); + + BindingReference reference; + reference.expression = value->value; + reference.property = prop; + reference.value = value; + reference.bindingContext = ctxt; + addBindingReference(reference); + + return true; +} + +void QDeclarativeCompiler::genBindingAssignment(QDeclarativeParser::Value *binding, + QDeclarativeParser::Property *prop, + QDeclarativeParser::Object *obj, + QDeclarativeParser::Property *valueTypeProperty) +{ + Q_UNUSED(obj); + Q_ASSERT(compileState.bindings.contains(binding)); + + const BindingReference &ref = compileState.bindings.value(binding); + if (ref.dataType == BindingReference::Experimental) { + QDeclarativeInstruction store; + store.type = QDeclarativeInstruction::StoreCompiledBinding; + store.assignBinding.value = ref.compiledIndex; + store.assignBinding.context = ref.bindingContext.stack; + store.assignBinding.owner = ref.bindingContext.owner; + if (valueTypeProperty) + store.assignBinding.property = (valueTypeProperty->index & 0xFFFF) | + ((valueTypeProperty->type & 0xFF)) << 16 | + ((prop->index & 0xFF) << 24); + else + store.assignBinding.property = prop->index; + store.line = binding->location.start.line; + output->bytecode << store; + return; + } + + QDeclarativeInstruction store; + if (!prop->isAlias) + store.type = QDeclarativeInstruction::StoreBinding; + else + store.type = QDeclarativeInstruction::StoreBindingOnAlias; + store.assignBinding.value = output->indexForByteArray(ref.compiledData); + store.assignBinding.context = ref.bindingContext.stack; + store.assignBinding.owner = ref.bindingContext.owner; + store.line = binding->location.start.line; + + Q_ASSERT(ref.bindingContext.owner == 0 || + (ref.bindingContext.owner != 0 && valueTypeProperty)); + if (ref.bindingContext.owner) { + store.assignBinding.property = genValueTypeData(prop, valueTypeProperty); + } else { + store.assignBinding.property = genPropertyData(prop); + } + + output->bytecode << store; +} + +int QDeclarativeCompiler::genContextCache() +{ + if (compileState.ids.count() == 0) + return -1; + + QDeclarativeIntegerCache *cache = new QDeclarativeIntegerCache(engine); + + for (QHash::ConstIterator iter = compileState.ids.begin(); + iter != compileState.ids.end(); + ++iter) + cache->add(iter.key(), (*iter)->idIndex); + + output->contextCaches.append(cache); + return output->contextCaches.count() - 1; +} + +int QDeclarativeCompiler::genValueTypeData(QDeclarativeParser::Property *valueTypeProp, + QDeclarativeParser::Property *prop) +{ + QByteArray data = + QDeclarativePropertyPrivate::saveValueType(prop->parent->metaObject(), prop->index, + enginePrivate->valueTypes[prop->type]->metaObject(), + valueTypeProp->index); +// valueTypeProp->index, valueTypeProp->type); + + return output->indexForByteArray(data); +} + +int QDeclarativeCompiler::genPropertyData(QDeclarativeParser::Property *prop) +{ + return output->indexForByteArray(QDeclarativePropertyPrivate::saveProperty(prop->parent->metaObject(), prop->index)); +} + +bool QDeclarativeCompiler::completeComponentBuild() +{ + componentStat.ids = compileState.ids.count(); + + for (int ii = 0; ii < compileState.aliasingObjects.count(); ++ii) { + QDeclarativeParser::Object *aliasObject = compileState.aliasingObjects.at(ii); + COMPILE_CHECK(buildDynamicMeta(aliasObject, ResolveAliases)); + } + + QDeclarativeBindingCompiler::Expression expr; + expr.component = compileState.root; + expr.ids = compileState.ids; + + QDeclarativeBindingCompiler bindingCompiler; + + for (QHash::Iterator iter = compileState.bindings.begin(); + iter != compileState.bindings.end(); ++iter) { + + BindingReference &binding = *iter; + + expr.context = binding.bindingContext.object; + expr.property = binding.property; + expr.expression = binding.expression; + expr.imports = unit->imports(); + + // ### We don't currently optimize for bindings on alias's - because + // of the solution to QTBUG-13719 + if (!binding.property->isAlias) { + int index = bindingCompiler.compile(expr, enginePrivate); + if (index != -1) { + binding.dataType = BindingReference::Experimental; + binding.compiledIndex = index; + componentStat.optimizedBindings.append(iter.key()->location); + continue; + } + } + + binding.dataType = BindingReference::QtScript; + + // Pre-rewrite the expression + QString expression = binding.expression.asScript(); + + QDeclarativeRewrite::RewriteBinding rewriteBinding; + rewriteBinding.setName('$'+binding.property->name); + bool isSharable = false; + expression = rewriteBinding(binding.expression.asAST(), expression, &isSharable); + + quint32 length = expression.length(); + quint32 pc; + + if (isSharable) { + pc = output->cachedClosures.count(); + pc |= 0x80000000; + output->cachedClosures.append(0); + } else { + pc = output->cachedPrograms.length(); + output->cachedPrograms.append(0); + } + + binding.compiledData = + QByteArray((const char *)&pc, sizeof(quint32)) + + QByteArray((const char *)&length, sizeof(quint32)) + + QByteArray((const char *)expression.constData(), + expression.length() * sizeof(QChar)); + + componentStat.scriptBindings.append(iter.key()->location); + } + + if (bindingCompiler.isValid()) { + compileState.compiledBindingData = bindingCompiler.program(); + if (bindingsDump()) + QDeclarativeBindingCompiler::dump(compileState.compiledBindingData); + } + + saveComponentState(); + + return true; +} + +void QDeclarativeCompiler::dumpStats() +{ + qWarning().nospace() << "QML Document: " << output->url.toString(); + for (int ii = 0; ii < savedComponentStats.count(); ++ii) { + const ComponentStat &stat = savedComponentStats.at(ii); + qWarning().nospace() << " Component Line " << stat.lineNumber; + qWarning().nospace() << " Total Objects: " << stat.objects; + qWarning().nospace() << " IDs Used: " << stat.ids; + qWarning().nospace() << " Optimized Bindings: " << stat.optimizedBindings.count(); + + { + QByteArray output; + for (int ii = 0; ii < stat.optimizedBindings.count(); ++ii) { + if (0 == (ii % 10)) { + if (ii) output.append("\n"); + output.append(" "); + } + + output.append("("); + output.append(QByteArray::number(stat.optimizedBindings.at(ii).start.line)); + output.append(":"); + output.append(QByteArray::number(stat.optimizedBindings.at(ii).start.column)); + output.append(") "); + } + if (!output.isEmpty()) + qWarning().nospace() << output.constData(); + } + + qWarning().nospace() << " QScript Bindings: " << stat.scriptBindings.count(); + { + QByteArray output; + for (int ii = 0; ii < stat.scriptBindings.count(); ++ii) { + if (0 == (ii % 10)) { + if (ii) output.append("\n"); + output.append(" "); + } + + output.append("("); + output.append(QByteArray::number(stat.scriptBindings.at(ii).start.line)); + output.append(":"); + output.append(QByteArray::number(stat.scriptBindings.at(ii).start.column)); + output.append(") "); + } + if (!output.isEmpty()) + qWarning().nospace() << output.constData(); + } + } +} + +/*! + Returns true if from can be assigned to a (QObject) property of type + to. +*/ +bool QDeclarativeCompiler::canCoerce(int to, QDeclarativeParser::Object *from) +{ + const QMetaObject *toMo = + enginePrivate->rawMetaObjectForType(to); + const QMetaObject *fromMo = from->metaObject(); + + while (fromMo) { + if (QDeclarativePropertyPrivate::equal(fromMo, toMo)) + return true; + fromMo = fromMo->superClass(); + } + return false; +} + +QDeclarativeType *QDeclarativeCompiler::toQmlType(QDeclarativeParser::Object *from) +{ + // ### Optimize + const QMetaObject *mo = from->metatype; + QDeclarativeType *type = 0; + while (!type && mo) { + type = QDeclarativeMetaType::qmlType(mo); + mo = mo->superClass(); + } + return type; +} + +QStringList QDeclarativeCompiler::deferredProperties(QDeclarativeParser::Object *obj) +{ + const QMetaObject *mo = obj->metatype; + + int idx = mo->indexOfClassInfo("DeferredPropertyNames"); + if (idx == -1) + return QStringList(); + + QMetaClassInfo classInfo = mo->classInfo(idx); + QStringList rv = QString::fromUtf8(classInfo.value()).split(QLatin1Char(',')); + return rv; +} + +// This code must match the semantics of QDeclarativePropertyPrivate::findSignalByName +int QDeclarativeCompiler::indexOfSignal(QDeclarativeParser::Object *object, const QByteArray &name, + bool *notInRevision) +{ + if (notInRevision) *notInRevision = false; + + if (object->synthCache || (object->type != -1 && output->types.at(object->type).propertyCache())) { + // XXX fromUtf8 + QString strName(QString::fromUtf8(name)); + QDeclarativePropertyCache *cache = + object->synthCache?object->synthCache:output->types.at(object->type).propertyCache(); + + QDeclarativePropertyCache::Data *d = cache->property(strName); + if (notInRevision) *notInRevision = false; + + while (d && !(d->flags & QDeclarativePropertyCache::Data::IsFunction)) + d = cache->overrideData(d); + + if (d && !cache->isAllowedInRevision(d)) { + if (notInRevision) *notInRevision = true; + return -1; + } else if (d) { + return d->coreIndex; + } + + if (name.endsWith("Changed")) { + QByteArray propName = name.mid(0, name.length() - 7); + + int propIndex = indexOfProperty(object, propName, notInRevision); + if (propIndex != -1) { + d = cache->property(propIndex); + return d->notifyIndex; + } + } + + return -1; + } else { + return QDeclarativePropertyPrivate::findSignalByName(object->metaObject(), name).methodIndex(); + } + +} + +int QDeclarativeCompiler::indexOfProperty(QDeclarativeParser::Object *object, const QByteArray &name, + bool *notInRevision) +{ + if (notInRevision) *notInRevision = false; + + if (object->synthCache || (object->type != -1 && output->types.at(object->type).propertyCache())) { + // XXX fromUtf8 + QString strName(QString::fromUtf8(name)); + QDeclarativePropertyCache *cache = + object->synthCache?object->synthCache:output->types.at(object->type).propertyCache(); + + QDeclarativePropertyCache::Data *d = cache->property(strName); + // Find the first property + while (d && d->flags & QDeclarativePropertyCache::Data::IsFunction) + d = cache->overrideData(d); + + if (d && !cache->isAllowedInRevision(d)) { + if (notInRevision) *notInRevision = true; + return -1; + } else { + return d?d->coreIndex:-1; + } + } else { + const QMetaObject *mo = object->metaObject(); + return mo->indexOfProperty(name.constData()); + } +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativecompiler_p.h b/src/declarative/qml/qdeclarativecompiler_p.h new file mode 100644 index 00000000..619fa3b3 --- /dev/null +++ b/src/declarative/qml/qdeclarativecompiler_p.h @@ -0,0 +1,351 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVECOMPILER_P_H +#define QDECLARATIVECOMPILER_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 "qdeclarative.h" +#include "qdeclarativeerror.h" +#include "private/qdeclarativeinstruction_p.h" +#include "private/qdeclarativeparser_p.h" +#include "private/qdeclarativeengine_p.h" +#include "private/qbitfield_p.h" +#include "private/qdeclarativepropertycache_p.h" +#include "private/qdeclarativeintegercache_p.h" +#include "private/qdeclarativetypenamecache_p.h" +#include "private/qdeclarativetypeloader_p.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeEngine; +class QDeclarativeComponent; +class QDeclarativeContext; +class QDeclarativeContextData; + +class QScriptProgram; +class Q_AUTOTEST_EXPORT QDeclarativeCompiledData : public QDeclarativeRefCount, public QDeclarativeCleanup +{ +public: + QDeclarativeCompiledData(QDeclarativeEngine *engine); + virtual ~QDeclarativeCompiledData(); + + QString name; + QUrl url; + QDeclarativeTypeNameCache *importCache; + + struct TypeReference + { + TypeReference() + : type(0), typePropertyCache(0), component(0) {} + + QByteArray className; + QDeclarativeType *type; + QDeclarativePropertyCache *typePropertyCache; + QDeclarativeCompiledData *component; + + QObject *createInstance(QDeclarativeContextData *, const QBitField &, QList *) const; + const QMetaObject *metaObject() const; + QDeclarativePropertyCache *propertyCache() const; + QDeclarativePropertyCache *createPropertyCache(QDeclarativeEngine *); + }; + QList types; + struct CustomTypeData + { + int index; + int type; + }; + + const QMetaObject *root; + QAbstractDynamicMetaObject rootData; + QDeclarativePropertyCache *rootPropertyCache; + QList primitives; + QList floatData; + QList intData; + QList customTypeData; + QList datas; + QList locations; + QList bytecode; + QList cachedPrograms; + QList cachedClosures; + QList propertyCaches; + QList contextCaches; + QList scripts; + QList urls; + + void dumpInstructions(); + +protected: + virtual void clear(); // From QDeclarativeCleanup + +private: + void dump(QDeclarativeInstruction *, int idx = -1); + QDeclarativeCompiledData(const QDeclarativeCompiledData &other); + QDeclarativeCompiledData &operator=(const QDeclarativeCompiledData &other); + QByteArray packData; + friend class QDeclarativeCompiler; + int pack(const char *, size_t); + + int indexForString(const QString &); + int indexForByteArray(const QByteArray &); + int indexForFloat(float *, int); + int indexForInt(int *, int); + int indexForLocation(const QDeclarativeParser::Location &); + int indexForLocation(const QDeclarativeParser::LocationSpan &); + int indexForUrl(const QUrl &); +}; + +class QMetaObjectBuilder; +class Q_AUTOTEST_EXPORT QDeclarativeCompiler +{ + Q_DECLARE_TR_FUNCTIONS(QDeclarativeCompiler) +public: + QDeclarativeCompiler(); + + bool compile(QDeclarativeEngine *, QDeclarativeTypeData *, QDeclarativeCompiledData *); + + bool isError() const; + QList errors() const; + + static bool isAttachedPropertyName(const QByteArray &); + static bool isSignalPropertyName(const QByteArray &); + + int evaluateEnum(const QByteArray& script) const; // for QDeclarativeCustomParser::evaluateEnum + const QMetaObject *resolveType(const QByteArray& name) const; // for QDeclarativeCustomParser::resolveType + int rewriteBinding(const QString& expression, const QByteArray& name); // for QDeclarativeCustomParser::rewriteBinding + +private: + static void reset(QDeclarativeCompiledData *); + + struct BindingContext { + BindingContext() + : stack(0), owner(0), object(0) {} + BindingContext(QDeclarativeParser::Object *o) + : stack(0), owner(0), object(o) {} + BindingContext incr() const { + BindingContext rv(object); + rv.stack = stack + 1; + return rv; + } + bool isSubContext() const { return stack != 0; } + int stack; + int owner; + QDeclarativeParser::Object *object; + }; + + void compileTree(QDeclarativeParser::Object *tree); + + + bool buildObject(QDeclarativeParser::Object *obj, const BindingContext &); + bool buildComponent(QDeclarativeParser::Object *obj, const BindingContext &); + bool buildSubObject(QDeclarativeParser::Object *obj, const BindingContext &); + bool buildSignal(QDeclarativeParser::Property *prop, QDeclarativeParser::Object *obj, + const BindingContext &); + bool buildProperty(QDeclarativeParser::Property *prop, QDeclarativeParser::Object *obj, + const BindingContext &); + bool buildPropertyInNamespace(QDeclarativeImportedNamespace *ns, + QDeclarativeParser::Property *prop, + QDeclarativeParser::Object *obj, + const BindingContext &); + bool buildIdProperty(QDeclarativeParser::Property *prop, QDeclarativeParser::Object *obj); + bool buildAttachedProperty(QDeclarativeParser::Property *prop, + QDeclarativeParser::Object *obj, + const BindingContext &ctxt); + bool buildGroupedProperty(QDeclarativeParser::Property *prop, + QDeclarativeParser::Object *obj, + const BindingContext &ctxt); + bool buildValueTypeProperty(QObject *type, + QDeclarativeParser::Object *obj, + QDeclarativeParser::Object *baseObj, + const BindingContext &ctxt); + bool buildListProperty(QDeclarativeParser::Property *prop, + QDeclarativeParser::Object *obj, + const BindingContext &ctxt); + bool buildScriptStringProperty(QDeclarativeParser::Property *prop, + QDeclarativeParser::Object *obj, + const BindingContext &ctxt); + bool buildPropertyAssignment(QDeclarativeParser::Property *prop, + QDeclarativeParser::Object *obj, + const BindingContext &ctxt); + bool buildPropertyObjectAssignment(QDeclarativeParser::Property *prop, + QDeclarativeParser::Object *obj, + QDeclarativeParser::Value *value, + const BindingContext &ctxt); + bool buildPropertyOnAssignment(QDeclarativeParser::Property *prop, + QDeclarativeParser::Object *obj, + QDeclarativeParser::Object *baseObj, + QDeclarativeParser::Value *value, + const BindingContext &ctxt); + bool buildPropertyLiteralAssignment(QDeclarativeParser::Property *prop, + QDeclarativeParser::Object *obj, + QDeclarativeParser::Value *value, + const BindingContext &ctxt); + bool doesPropertyExist(QDeclarativeParser::Property *prop, QDeclarativeParser::Object *obj); + bool testLiteralAssignment(const QMetaProperty &prop, + QDeclarativeParser::Value *value); + bool testQualifiedEnumAssignment(const QMetaProperty &prop, + QDeclarativeParser::Object *obj, + QDeclarativeParser::Value *value, + bool *isAssignment); + enum DynamicMetaMode { IgnoreAliases, ResolveAliases, ForceCreation }; + bool mergeDynamicMetaProperties(QDeclarativeParser::Object *obj); + bool buildDynamicMeta(QDeclarativeParser::Object *obj, DynamicMetaMode mode); + bool checkDynamicMeta(QDeclarativeParser::Object *obj); + bool buildBinding(QDeclarativeParser::Value *, QDeclarativeParser::Property *prop, + const BindingContext &ctxt); + bool buildComponentFromRoot(QDeclarativeParser::Object *obj, const BindingContext &); + bool compileAlias(QMetaObjectBuilder &, + QByteArray &data, + QDeclarativeParser::Object *obj, + const QDeclarativeParser::Object::DynamicProperty &); + bool completeComponentBuild(); + bool checkValidId(QDeclarativeParser::Value *, const QString &); + + + void genObject(QDeclarativeParser::Object *obj); + void genObjectBody(QDeclarativeParser::Object *obj); + void genValueTypeProperty(QDeclarativeParser::Object *obj,QDeclarativeParser::Property *); + void genComponent(QDeclarativeParser::Object *obj); + void genValueProperty(QDeclarativeParser::Property *prop, QDeclarativeParser::Object *obj); + void genListProperty(QDeclarativeParser::Property *prop, QDeclarativeParser::Object *obj); + void genPropertyAssignment(QDeclarativeParser::Property *prop, + QDeclarativeParser::Object *obj, + QDeclarativeParser::Property *valueTypeProperty = 0); + void genLiteralAssignment(const QMetaProperty &prop, + QDeclarativeParser::Value *value); + void genBindingAssignment(QDeclarativeParser::Value *binding, + QDeclarativeParser::Property *prop, + QDeclarativeParser::Object *obj, + QDeclarativeParser::Property *valueTypeProperty = 0); + int genContextCache(); + + int genValueTypeData(QDeclarativeParser::Property *prop, QDeclarativeParser::Property *valueTypeProp); + int genPropertyData(QDeclarativeParser::Property *prop); + + int componentTypeRef(); + + static QDeclarativeType *toQmlType(QDeclarativeParser::Object *from); + bool canCoerce(int to, QDeclarativeParser::Object *from); + + QStringList deferredProperties(QDeclarativeParser::Object *); + int indexOfProperty(QDeclarativeParser::Object *, const QByteArray &, bool *notInRevision = 0); + int indexOfSignal(QDeclarativeParser::Object *, const QByteArray &, bool *notInRevision = 0); + + void addId(const QString &, QDeclarativeParser::Object *); + + void dumpStats(); + + struct BindingReference { + QDeclarativeParser::Variant expression; + QDeclarativeParser::Property *property; + QDeclarativeParser::Value *value; + + enum DataType { QtScript, Experimental }; + DataType dataType; + + int compiledIndex; + + QByteArray compiledData; + BindingContext bindingContext; + }; + void addBindingReference(const BindingReference &); + + struct ComponentCompileState + { + ComponentCompileState() + : parserStatusCount(0), pushedProperties(0), root(0) {} + QHash ids; + QHash idIndexes; + int parserStatusCount; + int pushedProperties; + + QByteArray compiledBindingData; + + QHash bindings; + QHash signalExpressions; + QList aliasingObjects; + QDeclarativeParser::Object *root; + }; + ComponentCompileState compileState; + + struct ComponentStat + { + ComponentStat() : ids(0), objects(0) {} + + int lineNumber; + + int ids; + QList scriptBindings; + QList optimizedBindings; + int objects; + }; + ComponentStat componentStat; + + void saveComponentState(); + + ComponentCompileState componentState(QDeclarativeParser::Object *); + QHash savedCompileStates; + QList savedComponentStats; + + QList exceptions; + QDeclarativeCompiledData *output; + QDeclarativeEngine *engine; + QDeclarativeEnginePrivate *enginePrivate; + QDeclarativeParser::Object *unitRoot; + QDeclarativeTypeData *unit; +}; +QT_END_NAMESPACE + +#endif // QDECLARATIVECOMPILER_P_H diff --git a/src/declarative/qml/qdeclarativecomponent.cpp b/src/declarative/qml/qdeclarativecomponent.cpp new file mode 100644 index 00000000..e427f4e8 --- /dev/null +++ b/src/declarative/qml/qdeclarativecomponent.cpp @@ -0,0 +1,1086 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativecomponent.h" +#include "private/qdeclarativecomponent_p.h" + +#include "private/qdeclarativecompiler_p.h" +#include "private/qdeclarativecontext_p.h" +#include "private/qdeclarativeengine_p.h" +#include "private/qdeclarativevme_p.h" +#include "qdeclarative.h" +#include "qdeclarativeengine.h" +#include "private/qdeclarativebinding_p.h" +#include "private/qdeclarativebinding_p_p.h" +#include "private/qdeclarativeglobal_p.h" +#include "private/qdeclarativescriptparser_p.h" +#include "private/qdeclarativedebugtrace_p.h" +#include "private/qdeclarativeenginedebugservice_p.h" +#include + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QByteArray; + +/*! + \class QDeclarativeComponent + \since 4.7 + \brief The QDeclarativeComponent class encapsulates a QML component definition. + \mainclass + + Components are reusable, encapsulated QML elements with well-defined interfaces. + They are often defined in \l {qdeclarativedocuments.html}{Component Files}. + + A QDeclarativeComponent instance can be created from a QML file. + For example, if there is a \c main.qml file like this: + + \qml + import QtQuick 1.0 + + Item { + width: 200 + height: 200 + } + \endqml + + The following code loads this QML file as a component, creates an instance of + this component using create(), and then queries the \l Item's \l {Item::}{width} + value: + + \code + QDeclarativeEngine *engine = new QDeclarativeEngine; + QDeclarativeComponent component(engine, QUrl::fromLocalFile("main.qml")); + + QObject *myObject = component.create(); + QDeclarativeItem *item = qobject_cast(myObject); + int width = item->width(); // width = 200 + \endcode + + + \section2 Network Components + + If the URL passed to QDeclarativeComponent is a network resource, or if the QML document references a + network resource, the QDeclarativeComponent has to fetch the network data before it is able to create + objects. In this case, the QDeclarativeComponent will have a \l {QDeclarativeComponent::Loading}{Loading} + \l {QDeclarativeComponent::status()}{status}. An application will have to wait until the component + is \l {QDeclarativeComponent::Ready}{Ready} before calling \l {QDeclarativeComponent::create()}. + + The following example shows how to load a QML file from a network resource. After creating + the QDeclarativeComponent, it tests whether the component is loading. If it is, it connects to the + QDeclarativeComponent::statusChanged() signal and otherwise calls the \c {continueLoading()} method + directly. Note that QDeclarativeComponent::isLoading() may be false for a network component if the + component has been cached and is ready immediately. + + \code + MyApplication::MyApplication() + { + // ... + component = new QDeclarativeComponent(engine, QUrl("http://www.example.com/main.qml")); + if (component->isLoading()) + QObject::connect(component, SIGNAL(statusChanged(QDeclarativeComponent::Status)), + this, SLOT(continueLoading())); + else + continueLoading(); + } + + void MyApplication::continueLoading() + { + if (component->isError()) { + qWarning() << component->errors(); + } else { + QObject *myObject = component->create(); + } + } + \endcode + + \sa {Using QML Bindings in C++ Applications}, {Integrating QML Code with Existing Qt UI Code} +*/ + +/*! + \qmlclass Component QDeclarativeComponent + \ingroup qml-utility-elements + \since 4.7 + \brief The Component element encapsulates a QML component definition. + + Components are reusable, encapsulated QML elements with well-defined interfaces. + + Components are often defined by \l {qdeclarativedocuments.html}{component files} - + that is, \c .qml files. The \e Component element essentially allows QML components + to be defined inline, within a \l {QML Document}{QML document}, rather than as a separate QML file. + This may be useful for reusing a small component within a QML file, or for defining + a component that logically belongs with other QML components within a file. + + For example, here is a component that is used by multiple \l Loader objects. + It contains a single item, a \l Rectangle: + + \snippet doc/src/snippets/declarative/component.qml 0 + + Notice that while a \l Rectangle by itself would be automatically + rendered and displayed, this is not the case for the above rectangle + because it is defined inside a \c Component. The component encapsulates the + QML elements within, as if they were defined in a separate QML + file, and is not loaded until requested (in this case, by the + two \l Loader objects). + + Defining a \c Component is similar to defining a \l {QML Document}{QML document}. + A QML document has a single top-level item that defines the behaviors and + properties of that component, and cannot define properties or behaviors outside + of that top-level item. In the same way, a \c Component definition contains a single + top level item (which in the above example is a \l Rectangle) and cannot define any + data outside of this item, with the exception of an \e id (which in the above example + is \e redSquare). + + The \c Component element is commonly used to provide graphical components + for views. For example, the ListView::delegate property requires a \c Component + to specify how each list item is to be displayed. + + \c Component objects can also be created dynamically using + \l{QML:Qt::createComponent()}{Qt.createComponent()}. +*/ + +/*! + \qmlattachedsignal Component::onCompleted() + + Emitted after component "startup" has completed. This can be used to + execute script code at startup, once the full QML environment has been + established. + + The \c {Component::onCompleted} attached property can be applied to + any element. The order of running the \c onCompleted scripts is + undefined. + + \qml + Rectangle { + Component.onCompleted: console.log("Completed Running!") + Rectangle { + Component.onCompleted: console.log("Nested Completed Running!") + } + } + \endqml +*/ + +/*! + \qmlattachedsignal Component::onDestruction() + + Emitted as the component begins destruction. This can be used to undo + work done in the onCompleted signal, or other imperative code in your + application. + + The \c {Component::onDestruction} attached property can be applied to + any element. However, it applies to the destruction of the component as + a whole, and not the destruction of the specific object. The order of + running the \c onDestruction scripts is undefined. + + \qml + Rectangle { + Component.onDestruction: console.log("Destruction Beginning!") + Rectangle { + Component.onDestruction: console.log("Nested Destruction Beginning!") + } + } + \endqml + + \sa QtDeclarative +*/ + +/*! + \enum QDeclarativeComponent::Status + + Specifies the loading status of the QDeclarativeComponent. + + \value Null This QDeclarativeComponent has no data. Call loadUrl() or setData() to add QML content. + \value Ready This QDeclarativeComponent is ready and create() may be called. + \value Loading This QDeclarativeComponent is loading network data. + \value Error An error has occurred. Call errors() to retrieve a list of \{QDeclarativeError}{errors}. +*/ + +void QDeclarativeComponentPrivate::typeDataReady(QDeclarativeTypeData *) +{ + Q_Q(QDeclarativeComponent); + + Q_ASSERT(typeData); + + fromTypeData(typeData); + typeData = 0; + + emit q->statusChanged(q->status()); +} + +void QDeclarativeComponentPrivate::typeDataProgress(QDeclarativeTypeData *, qreal p) +{ + Q_Q(QDeclarativeComponent); + + progress = p; + + emit q->progressChanged(p); +} + +void QDeclarativeComponentPrivate::fromTypeData(QDeclarativeTypeData *data) +{ + url = data->finalUrl(); + QDeclarativeCompiledData *c = data->compiledData(); + + if (!c) { + Q_ASSERT(data->isError()); + state.errors = data->errors(); + } else { + cc = c; + } + + data->release(); +} + +void QDeclarativeComponentPrivate::clear() +{ + if (typeData) { + typeData->unregisterCallback(this); + typeData->release(); + typeData = 0; + } + + if (cc) { + cc->release(); + cc = 0; + } +} + +/*! + \internal +*/ +QDeclarativeComponent::QDeclarativeComponent(QObject *parent) + : QObject(*(new QDeclarativeComponentPrivate), parent) +{ +} + +/*! + Destruct the QDeclarativeComponent. +*/ +QDeclarativeComponent::~QDeclarativeComponent() +{ + Q_D(QDeclarativeComponent); + + if (d->state.completePending) { + qWarning("QDeclarativeComponent: Component destroyed while completion pending"); + d->completeCreate(); + } + + if (d->typeData) { + d->typeData->unregisterCallback(d); + d->typeData->release(); + } + if (d->cc) + d->cc->release(); +} + +/*! + \qmlproperty enumeration Component::status + This property holds the status of component loading. It can be one of: + \list + \o Component.Null - no data is available for the component + \o Component.Ready - the component has been loaded, and can be used to create instances. + \o Component.Loading - the component is currently being loaded + \o Component.Error - an error occurred while loading the component. + Calling errorString() will provide a human-readable description of any errors. + \endlist + */ + +/*! + \property QDeclarativeComponent::status + The component's current \l{QDeclarativeComponent::Status} {status}. + */ +QDeclarativeComponent::Status QDeclarativeComponent::status() const +{ + Q_D(const QDeclarativeComponent); + + if (d->typeData) + return Loading; + else if (!d->state.errors.isEmpty()) + return Error; + else if (d->engine && d->cc) + return Ready; + else + return Null; +} + +/*! + Returns true if status() == QDeclarativeComponent::Null. +*/ +bool QDeclarativeComponent::isNull() const +{ + return status() == Null; +} + +/*! + Returns true if status() == QDeclarativeComponent::Ready. +*/ +bool QDeclarativeComponent::isReady() const +{ + return status() == Ready; +} + +/*! + Returns true if status() == QDeclarativeComponent::Error. +*/ +bool QDeclarativeComponent::isError() const +{ + return status() == Error; +} + +/*! + Returns true if status() == QDeclarativeComponent::Loading. +*/ +bool QDeclarativeComponent::isLoading() const +{ + return status() == Loading; +} + +/*! + \qmlproperty real Component::progress + The progress of loading the component, from 0.0 (nothing loaded) + to 1.0 (finished). +*/ + +/*! + \property QDeclarativeComponent::progress + The progress of loading the component, from 0.0 (nothing loaded) + to 1.0 (finished). +*/ +qreal QDeclarativeComponent::progress() const +{ + Q_D(const QDeclarativeComponent); + return d->progress; +} + +/*! + \fn void QDeclarativeComponent::progressChanged(qreal progress) + + Emitted whenever the component's loading progress changes. \a progress will be the + current progress between 0.0 (nothing loaded) and 1.0 (finished). +*/ + +/*! + \fn void QDeclarativeComponent::statusChanged(QDeclarativeComponent::Status status) + + Emitted whenever the component's status changes. \a status will be the + new status. +*/ + +/*! + Create a QDeclarativeComponent with no data and give it the specified + \a engine and \a parent. Set the data with setData(). +*/ +QDeclarativeComponent::QDeclarativeComponent(QDeclarativeEngine *engine, QObject *parent) + : QObject(*(new QDeclarativeComponentPrivate), parent) +{ + Q_D(QDeclarativeComponent); + d->engine = engine; +} + +/*! + Create a QDeclarativeComponent from the given \a url and give it the + specified \a parent and \a engine. + + Ensure that the URL provided is full and correct, in particular, use + \l QUrl::fromLocalFile() when loading a file from the local filesystem. + + \sa loadUrl() +*/ +QDeclarativeComponent::QDeclarativeComponent(QDeclarativeEngine *engine, const QUrl &url, QObject *parent) +: QObject(*(new QDeclarativeComponentPrivate), parent) +{ + Q_D(QDeclarativeComponent); + d->engine = engine; + loadUrl(url); +} + +/*! + Create a QDeclarativeComponent from the given \a fileName and give it the specified + \a parent and \a engine. + + \sa loadUrl() +*/ +QDeclarativeComponent::QDeclarativeComponent(QDeclarativeEngine *engine, const QString &fileName, + QObject *parent) +: QObject(*(new QDeclarativeComponentPrivate), parent) +{ + Q_D(QDeclarativeComponent); + d->engine = engine; + loadUrl(d->engine->baseUrl().resolved(QUrl::fromLocalFile(fileName))); +} + +/*! + \internal +*/ +QDeclarativeComponent::QDeclarativeComponent(QDeclarativeEngine *engine, QDeclarativeCompiledData *cc, int start, int count, QObject *parent) + : QObject(*(new QDeclarativeComponentPrivate), parent) +{ + Q_D(QDeclarativeComponent); + d->engine = engine; + d->cc = cc; + cc->addref(); + d->start = start; + d->count = count; + d->url = cc->url; + d->progress = 1.0; +} + +/*! + Sets the QDeclarativeComponent to use the given QML \a data. If \a url + is provided, it is used to set the component name and to provide + a base path for items resolved by this component. +*/ +void QDeclarativeComponent::setData(const QByteArray &data, const QUrl &url) +{ + Q_D(QDeclarativeComponent); + + d->clear(); + + d->url = url; + + QDeclarativeTypeData *typeData = QDeclarativeEnginePrivate::get(d->engine)->typeLoader.get(data, url); + + if (typeData->isCompleteOrError()) { + d->fromTypeData(typeData); + } else { + d->typeData = typeData; + d->typeData->registerCallback(d); + } + + d->progress = 1.0; + emit statusChanged(status()); + emit progressChanged(d->progress); +} + +/*! +Returns the QDeclarativeContext the component was created in. This is only +valid for components created directly from QML. +*/ +QDeclarativeContext *QDeclarativeComponent::creationContext() const +{ + Q_D(const QDeclarativeComponent); + if(d->creationContext) + return d->creationContext->asQDeclarativeContext(); + + return qmlContext(this); +} + +/*! + Load the QDeclarativeComponent from the provided \a url. + + Ensure that the URL provided is full and correct, in particular, use + \l QUrl::fromLocalFile() when loading a file from the local filesystem. +*/ +void QDeclarativeComponent::loadUrl(const QUrl &url) +{ + Q_D(QDeclarativeComponent); + + d->clear(); + + if ((url.isRelative() && !url.isEmpty()) + || url.scheme() == QLatin1String("file")) // Workaround QTBUG-11929 + d->url = d->engine->baseUrl().resolved(url); + else + d->url = url; + + if (url.isEmpty()) { + QDeclarativeError error; + error.setDescription(tr("Invalid empty URL")); + d->state.errors << error; + return; + } + + QDeclarativeTypeData *data = QDeclarativeEnginePrivate::get(d->engine)->typeLoader.get(d->url); + + if (data->isCompleteOrError()) { + d->fromTypeData(data); + d->progress = 1.0; + } else { + d->typeData = data; + d->typeData->registerCallback(d); + d->progress = data->progress(); + } + + emit statusChanged(status()); + emit progressChanged(d->progress); +} + +/*! + Return the list of errors that occurred during the last compile or create + operation. An empty list is returned if isError() is not set. +*/ +QList QDeclarativeComponent::errors() const +{ + Q_D(const QDeclarativeComponent); + if (isError()) + return d->state.errors; + else + return QList(); +} + +/*! + \qmlmethod string Component::errorString() + + Returns a human-readable description of any errors. + + The string includes the file, location, and description of each error. + If multiple errors are present they are separated by a newline character. + + If no errors are present, an empty string is returned. +*/ + +/*! + \internal + errorString is only meant as a way to get the errors in script +*/ +QString QDeclarativeComponent::errorString() const +{ + Q_D(const QDeclarativeComponent); + QString ret; + if(!isError()) + return ret; + foreach(const QDeclarativeError &e, d->state.errors) { + ret += e.url().toString() + QLatin1Char(':') + + QString::number(e.line()) + QLatin1Char(' ') + + e.description() + QLatin1Char('\n'); + } + return ret; +} + +/*! + \qmlproperty url Component::url + The component URL. This is the URL that was used to construct the component. +*/ + +/*! + \property QDeclarativeComponent::url + The component URL. This is the URL passed to either the constructor, + or the loadUrl() or setData() methods. +*/ +QUrl QDeclarativeComponent::url() const +{ + Q_D(const QDeclarativeComponent); + return d->url; +} + +/*! + \internal +*/ +QDeclarativeComponent::QDeclarativeComponent(QDeclarativeComponentPrivate &dd, QObject *parent) + : QObject(dd, parent) +{ +} + +/*! + \qmlmethod object Component::createObject(Item parent, object properties) + + Creates and returns an object instance of this component that will have + the given \a parent and \a properties. The \a properties argument is optional. + Returns null if object creation fails. + + The object will be created in the same context as the one in which the component + was created. This function will always return null when called on components + which were not created in QML. + + If you wish to create an object without setting a parent, specify \c null for + the \a parent value. Note that if the returned object is to be displayed, you + must provide a valid \a parent value or set the returned object's \l{Item::parent}{parent} + property, or else the object will not be visible. + + If a \a parent is not provided to createObject(), a reference to the returned object must be held so that + it is not destroyed by the garbage collector. This is true regardless of whether \l{Item::parent} is set afterwards, + since setting the Item parent does not change object ownership; only the graphical parent is changed. + + As of QtQuick 1.1, this method accepts an optional \a properties argument that specifies a + map of initial property values for the created object. These values are applied before object + creation is finalized. (This is more efficient than setting property values after object creation, + particularly where large sets of property values are defined, and also allows property bindings + to be set up before the object is created.) + + The \a properties argument is specified as a map of property-value items. For example, the code + below creates an object with initial \c x and \c y values of 100 and 200, respectively: + + \js + var component = Qt.createComponent("Button.qml"); + if (component.status == Component.Ready) + component.createObject(parent, {"x": 100, "y": 100}); + \endjs + + Dynamically created instances can be deleted with the \c destroy() method. + See \l {Dynamic Object Management in QML} for more information. +*/ + +/*! + \internal + A version of create which returns a scriptObject, for use in script. + This function will only work on components created in QML. + + Sets graphics object parent because forgetting to do this is a frequent + and serious problem. +*/ +QScriptValue QDeclarativeComponent::createObject(QObject* parent) +{ + Q_D(QDeclarativeComponent); + return d->createObject(parent, QScriptValue(QScriptValue::NullValue)); +} + +/*! + \internal + Overloadable method allows properties to be set during creation +*/ +QScriptValue QDeclarativeComponent::createObject(QObject* parent, const QScriptValue& valuemap) +{ + Q_D(QDeclarativeComponent); + + if (!valuemap.isObject() || valuemap.isArray()) { + qmlInfo(this) << tr("createObject: value is not an object"); + return QScriptValue(QScriptValue::NullValue); + } + return d->createObject(parent, valuemap); +} + +QScriptValue QDeclarativeComponentPrivate::createObject(QObject *publicParent, const QScriptValue valuemap) +{ + Q_Q(QDeclarativeComponent); + QDeclarativeContext* ctxt = q->creationContext(); + if(!ctxt && engine) + ctxt = engine->rootContext(); + if (!ctxt) + return QScriptValue(QScriptValue::NullValue); + QObject* ret = q->beginCreate(ctxt); + if (!ret) { + q->completeCreate(); + return QScriptValue(QScriptValue::NullValue); + } + + if (publicParent) { + ret->setParent(publicParent); + QList functions = QDeclarativeMetaType::parentFunctions(); + + bool needParent = false; + + for (int ii = 0; ii < functions.count(); ++ii) { + QDeclarativePrivate::AutoParentResult res = functions.at(ii)(ret, publicParent); + if (res == QDeclarativePrivate::Parented) { + needParent = false; + break; + } else if (res == QDeclarativePrivate::IncompatibleParent) { + needParent = true; + } + } + + if (needParent) + qWarning("QDeclarativeComponent: Created graphical object was not placed in the graphics scene."); + } + + QDeclarativeEnginePrivate *priv = QDeclarativeEnginePrivate::get(engine); + QDeclarativeData::get(ret, true)->setImplicitDestructible(); + QScriptValue newObject = priv->objectClass->newQObject(ret, QMetaType::QObjectStar); + + if (valuemap.isObject() && !valuemap.isArray()) { + //Iterate through and assign properties + QScriptValueIterator it(valuemap); + while (it.hasNext()) { + it.next(); + QScriptValue prop = newObject; + QString propName = it.name(); + int index = propName.indexOf(QLatin1Char('.')); + if (index > 0) { + QString subProp = propName; + int lastIndex = 0; + while (index > 0) { + subProp = propName.mid(lastIndex, index - lastIndex); + prop = prop.property(subProp); + lastIndex = index + 1; + index = propName.indexOf(QLatin1Char('.'), index + 1); + } + prop.setProperty(propName.mid(propName.lastIndexOf(QLatin1Char('.')) + 1), it.value()); + } else { + newObject.setProperty(propName, it.value()); + } + } + } + + q->completeCreate(); + + return newObject; +} + +/*! + Create an object instance from this component. Returns 0 if creation + failed. \a context specifies the context within which to create the object + instance. + + If \a context is 0 (the default), it will create the instance in the + engine' s \l {QDeclarativeEngine::rootContext()}{root context}. +*/ +QObject *QDeclarativeComponent::create(QDeclarativeContext *context) +{ + Q_D(QDeclarativeComponent); + + if (!context) + context = d->engine->rootContext(); + + QObject *rv = beginCreate(context); + completeCreate(); + return rv; +} + +/*! + This method provides more advanced control over component instance creation. + In general, programmers should use QDeclarativeComponent::create() to create a + component. + + Create an object instance from this component. Returns 0 if creation + failed. \a context specifies the context within which to create the object + instance. + + When QDeclarativeComponent constructs an instance, it occurs in three steps: + \list 1 + \i The object hierarchy is created, and constant values are assigned. + \i Property bindings are evaluated for the the first time. + \i If applicable, QDeclarativeParserStatus::componentComplete() is called on objects. + \endlist + QDeclarativeComponent::beginCreate() differs from QDeclarativeComponent::create() in that it + only performs step 1. QDeclarativeComponent::completeCreate() must be called to + complete steps 2 and 3. + + This breaking point is sometimes useful when using attached properties to + communicate information to an instantiated component, as it allows their + initial values to be configured before property bindings take effect. +*/ +QObject *QDeclarativeComponent::beginCreate(QDeclarativeContext *context) +{ + Q_D(QDeclarativeComponent); + QObject *rv = d->beginCreate(context?QDeclarativeContextData::get(context):0, QBitField()); + if (rv) { + QDeclarativeData *ddata = QDeclarativeData::get(rv); + Q_ASSERT(ddata); + ddata->indestructible = true; + } + return rv; +} + +QObject * +QDeclarativeComponentPrivate::beginCreate(QDeclarativeContextData *context, const QBitField &bindings) +{ + Q_Q(QDeclarativeComponent); + if (!context) { + qWarning("QDeclarativeComponent: Cannot create a component in a null context"); + return 0; + } + + if (!context->isValid()) { + qWarning("QDeclarativeComponent: Cannot create a component in an invalid context"); + return 0; + } + + if (context->engine != engine) { + qWarning("QDeclarativeComponent: Must create component in context from the same QDeclarativeEngine"); + return 0; + } + + if (state.completePending) { + qWarning("QDeclarativeComponent: Cannot create new component instance before completing the previous"); + return 0; + } + + if (!q->isReady()) { + qWarning("QDeclarativeComponent: Component is not ready"); + return 0; + } + + return begin(context, creationContext, cc, start, count, &state, 0, bindings); +} + +QObject * QDeclarativeComponentPrivate::begin(QDeclarativeContextData *parentContext, + QDeclarativeContextData *componentCreationContext, + QDeclarativeCompiledData *component, int start, int count, + ConstructionState *state, QList *errors, + const QBitField &bindings) +{ + QDeclarativeEnginePrivate *enginePriv = QDeclarativeEnginePrivate::get(parentContext->engine); + bool isRoot = !enginePriv->inBeginCreate; + + Q_ASSERT(!isRoot || state); // Either this isn't a root component, or a state data must be provided + Q_ASSERT((state != 0) ^ (errors != 0)); // One of state or errors (but not both) must be provided + + if (isRoot) { + QDeclarativeDebugTrace::startRange(QDeclarativeDebugTrace::Creating); + QDeclarativeDebugTrace::rangeData(QDeclarativeDebugTrace::Creating, component->url); + } + + QDeclarativeContextData *ctxt = new QDeclarativeContextData; + ctxt->isInternal = true; + ctxt->url = component->url; + ctxt->imports = component->importCache; + + // Nested global imports + if (componentCreationContext && start != -1) + ctxt->importedScripts = componentCreationContext->importedScripts; + + component->importCache->addref(); + ctxt->setParent(parentContext); + + enginePriv->inBeginCreate = true; + + QDeclarativeVME vme; + QObject *rv = vme.run(ctxt, component, start, count, bindings); + + if (vme.isError()) { + if(errors) *errors = vme.errors(); + else state->errors = vme.errors(); + } + + if (isRoot) { + enginePriv->inBeginCreate = false; + + state->bindValues = enginePriv->bindValues; + state->parserStatus = enginePriv->parserStatus; + state->finalizedParserStatus = enginePriv->finalizedParserStatus; + state->componentAttached = enginePriv->componentAttached; + if (state->componentAttached) + state->componentAttached->prev = &state->componentAttached; + + enginePriv->componentAttached = 0; + enginePriv->bindValues.clear(); + enginePriv->parserStatus.clear(); + enginePriv->finalizedParserStatus.clear(); + state->completePending = true; + enginePriv->inProgressCreations++; + } + + if (enginePriv->isDebugging && rv) { + if (!parentContext->isInternal) + parentContext->asQDeclarativeContextPrivate()->instances.append(rv); + QDeclarativeEngineDebugService::instance()->objectCreated(parentContext->engine, rv); + } + + return rv; +} + +void QDeclarativeComponentPrivate::beginDeferred(QDeclarativeEnginePrivate *enginePriv, + QObject *object, ConstructionState *state) +{ + bool isRoot = !enginePriv->inBeginCreate; + enginePriv->inBeginCreate = true; + + QDeclarativeVME vme; + vme.runDeferred(object); + + if (vme.isError()) + state->errors = vme.errors(); + + if (isRoot) { + enginePriv->inBeginCreate = false; + + state->bindValues = enginePriv->bindValues; + state->parserStatus = enginePriv->parserStatus; + state->finalizedParserStatus = enginePriv->finalizedParserStatus; + state->componentAttached = enginePriv->componentAttached; + if (state->componentAttached) + state->componentAttached->prev = &state->componentAttached; + + enginePriv->componentAttached = 0; + enginePriv->bindValues.clear(); + enginePriv->parserStatus.clear(); + enginePriv->finalizedParserStatus.clear(); + state->completePending = true; + enginePriv->inProgressCreations++; + } +} + +void QDeclarativeComponentPrivate::complete(QDeclarativeEnginePrivate *enginePriv, ConstructionState *state) +{ + if (state->completePending) { + QT_TRY { + for (int ii = 0; ii < state->bindValues.count(); ++ii) { + QDeclarativeEnginePrivate::SimpleList bv = + state->bindValues.at(ii); + for (int jj = 0; jj < bv.count; ++jj) { + if(bv.at(jj)) { + // XXX akennedy + bv.at(jj)->m_mePtr = 0; + bv.at(jj)->setEnabled(true, QDeclarativePropertyPrivate::BypassInterceptor | + QDeclarativePropertyPrivate::DontRemoveBinding); + } + } + QDeclarativeEnginePrivate::clear(bv); + } + + for (int ii = 0; ii < state->parserStatus.count(); ++ii) { + QDeclarativeEnginePrivate::SimpleList ps = + state->parserStatus.at(ii); + + for (int jj = ps.count - 1; jj >= 0; --jj) { + QDeclarativeParserStatus *status = ps.at(jj); + if (status && status->d) { + status->d = 0; + status->componentComplete(); + } + } + QDeclarativeEnginePrivate::clear(ps); + } + + for (int ii = 0; ii < state->finalizedParserStatus.count(); ++ii) { + QPair, int> status = state->finalizedParserStatus.at(ii); + QObject *obj = status.first; + if (obj) { + void *args[] = { 0 }; + QMetaObject::metacall(obj, QMetaObject::InvokeMetaMethod, + status.second, args); + } + } + + //componentComplete() can register additional finalization objects + //that are then never handled. Handle them manually here. + if (1 == enginePriv->inProgressCreations) { + for (int ii = 0; ii < enginePriv->finalizedParserStatus.count(); ++ii) { + QPair, int> status = enginePriv->finalizedParserStatus.at(ii); + QObject *obj = status.first; + if (obj) { + void *args[] = { 0 }; + QMetaObject::metacall(obj, QMetaObject::InvokeMetaMethod, + status.second, args); + } + } + enginePriv->finalizedParserStatus.clear(); + } + + while (state->componentAttached) { + QDeclarativeComponentAttached *a = state->componentAttached; + a->rem(); + QDeclarativeData *d = QDeclarativeData::get(a->parent()); + Q_ASSERT(d); + Q_ASSERT(d->context); + a->add(&d->context->componentAttached); + emit a->completed(); + } + } QT_CATCH(const std::exception&) { + state->bindValues.clear(); + state->parserStatus.clear(); + state->finalizedParserStatus.clear(); + state->completePending = false; + enginePriv->inProgressCreations--; + QT_RETHROW; + } + + state->bindValues.clear(); + state->parserStatus.clear(); + state->finalizedParserStatus.clear(); + state->completePending = false; + + enginePriv->inProgressCreations--; + if (0 == enginePriv->inProgressCreations) { + while (enginePriv->erroredBindings) { + enginePriv->warning(enginePriv->erroredBindings->error); + enginePriv->erroredBindings->removeError(); + } + } + } +} + +/*! + This method provides more advanced control over component instance creation. + In general, programmers should use QDeclarativeComponent::create() to create a + component. + + Complete a component creation begin with QDeclarativeComponent::beginCreate(). +*/ +void QDeclarativeComponent::completeCreate() +{ + Q_D(QDeclarativeComponent); + d->completeCreate(); +} + +void QDeclarativeComponentPrivate::completeCreate() +{ + if (state.completePending) { + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); + complete(ep, &state); + + QDeclarativeDebugTrace::endRange(QDeclarativeDebugTrace::Creating); + } +} + +QDeclarativeComponentAttached::QDeclarativeComponentAttached(QObject *parent) +: QObject(parent), prev(0), next(0) +{ +} + +QDeclarativeComponentAttached::~QDeclarativeComponentAttached() +{ + if (prev) *prev = next; + if (next) next->prev = prev; + prev = 0; + next = 0; +} + +/*! + \internal +*/ +QDeclarativeComponentAttached *QDeclarativeComponent::qmlAttachedProperties(QObject *obj) +{ + QDeclarativeComponentAttached *a = new QDeclarativeComponentAttached(obj); + + QDeclarativeEngine *engine = qmlEngine(obj); + if (!engine) + return a; + + if (QDeclarativeEnginePrivate::get(engine)->inBeginCreate) { + QDeclarativeEnginePrivate *p = QDeclarativeEnginePrivate::get(engine); + a->add(&p->componentAttached); + } else { + QDeclarativeData *d = QDeclarativeData::get(obj); + Q_ASSERT(d); + Q_ASSERT(d->context); + a->add(&d->context->componentAttached); + } + + return a; +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativecomponent.h b/src/declarative/qml/qdeclarativecomponent.h new file mode 100644 index 00000000..50954ad7 --- /dev/null +++ b/src/declarative/qml/qdeclarativecomponent.h @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVECOMPONENT_H +#define QDECLARATIVECOMPONENT_H + +#include +#include + +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeCompiledData; +class QByteArray; +class QDeclarativeComponentPrivate; +class QDeclarativeEngine; +class QDeclarativeComponentAttached; +class Q_DECLARATIVE_EXPORT QDeclarativeComponent : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeComponent) + + Q_PROPERTY(qreal progress READ progress NOTIFY progressChanged) + Q_PROPERTY(Status status READ status NOTIFY statusChanged) + Q_PROPERTY(QUrl url READ url CONSTANT) + +public: + QDeclarativeComponent(QObject *parent = 0); + QDeclarativeComponent(QDeclarativeEngine *, QObject *parent=0); + QDeclarativeComponent(QDeclarativeEngine *, const QString &fileName, QObject *parent = 0); + QDeclarativeComponent(QDeclarativeEngine *, const QUrl &url, QObject *parent = 0); + virtual ~QDeclarativeComponent(); + + Q_ENUMS(Status) + enum Status { Null, Ready, Loading, Error }; + Status status() const; + + bool isNull() const; + bool isReady() const; + bool isError() const; + bool isLoading() const; + + QList errors() const; + Q_INVOKABLE QString errorString() const; + + qreal progress() const; + + QUrl url() const; + + virtual QObject *create(QDeclarativeContext *context = 0); + virtual QObject *beginCreate(QDeclarativeContext *); + virtual void completeCreate(); + + void loadUrl(const QUrl &url); + void setData(const QByteArray &, const QUrl &baseUrl); + + QDeclarativeContext *creationContext() const; + + static QDeclarativeComponentAttached *qmlAttachedProperties(QObject *); + +Q_SIGNALS: + void statusChanged(QDeclarativeComponent::Status); + void progressChanged(qreal); + +protected: + QDeclarativeComponent(QDeclarativeComponentPrivate &dd, QObject* parent); + Q_INVOKABLE QScriptValue createObject(QObject* parent); + Q_INVOKABLE Q_REVISION(1) QScriptValue createObject(QObject* parent, const QScriptValue& valuemap); //XXX Versioning + +private: + QDeclarativeComponent(QDeclarativeEngine *, QDeclarativeCompiledData *, int, int, QObject *parent); + + Q_DISABLE_COPY(QDeclarativeComponent) + friend class QDeclarativeVME; + friend class QDeclarativeCompositeTypeData; + friend class QDeclarativeTypeData; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QDeclarativeComponent::Status) +QML_DECLARE_TYPE(QDeclarativeComponent) +QML_DECLARE_TYPEINFO(QDeclarativeComponent, QML_HAS_ATTACHED_PROPERTIES) + +QT_END_HEADER + +#endif // QDECLARATIVECOMPONENT_H diff --git a/src/declarative/qml/qdeclarativecomponent_p.h b/src/declarative/qml/qdeclarativecomponent_p.h new file mode 100644 index 00000000..f75999b9 --- /dev/null +++ b/src/declarative/qml/qdeclarativecomponent_p.h @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVECOMPONENT_P_H +#define QDECLARATIVECOMPONENT_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 "qdeclarativecomponent.h" + +#include "private/qdeclarativeengine_p.h" +#include "private/qdeclarativetypeloader_p.h" +#include "private/qbitfield_p.h" +#include "qdeclarativeerror.h" +#include "qdeclarative.h" + +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeComponent; +class QDeclarativeEngine; +class QDeclarativeCompiledData; + +class QDeclarativeComponentAttached; +class Q_AUTOTEST_EXPORT QDeclarativeComponentPrivate : public QObjectPrivate, public QDeclarativeTypeData::TypeDataCallback +{ + Q_DECLARE_PUBLIC(QDeclarativeComponent) + +public: + QDeclarativeComponentPrivate() : typeData(0), progress(0.), start(-1), count(-1), cc(0), engine(0), creationContext(0) {} + + QObject *beginCreate(QDeclarativeContextData *, const QBitField &); + void completeCreate(); + + QDeclarativeTypeData *typeData; + virtual void typeDataReady(QDeclarativeTypeData *); + virtual void typeDataProgress(QDeclarativeTypeData *, qreal); + + void fromTypeData(QDeclarativeTypeData *data); + + QUrl url; + qreal progress; + + int start; + int count; + QDeclarativeCompiledData *cc; + + struct ConstructionState { + ConstructionState() : componentAttached(0), completePending(false) {} + QList > bindValues; + QList > parserStatus; + QList, int> > finalizedParserStatus; + QDeclarativeComponentAttached *componentAttached; + QList errors; + bool completePending; + }; + ConstructionState state; + + static QObject *begin(QDeclarativeContextData *parentContext, QDeclarativeContextData *componentCreationContext, + QDeclarativeCompiledData *component, int start, int count, + ConstructionState *state, QList *errors, + const QBitField &bindings = QBitField()); + static void beginDeferred(QDeclarativeEnginePrivate *enginePriv, QObject *object, + ConstructionState *state); + static void complete(QDeclarativeEnginePrivate *enginePriv, ConstructionState *state); + + QScriptValue createObject(QObject *publicParent, const QScriptValue valuemap); + + QDeclarativeEngine *engine; + QDeclarativeGuardedContextData creationContext; + + void clear(); + + static QDeclarativeComponentPrivate *get(QDeclarativeComponent *c) { + return static_cast(QObjectPrivate::get(c)); + } +}; + +class QDeclarativeComponentAttached : public QObject +{ + Q_OBJECT +public: + QDeclarativeComponentAttached(QObject *parent = 0); + ~QDeclarativeComponentAttached(); + + void add(QDeclarativeComponentAttached **a) { + prev = a; next = *a; *a = this; + if (next) next->prev = &next; + } + void rem() { + if (next) next->prev = prev; + *prev = next; + next = 0; prev = 0; + } + QDeclarativeComponentAttached **prev; + QDeclarativeComponentAttached *next; + +Q_SIGNALS: + void completed(); + void destruction(); + +private: + friend class QDeclarativeContextData; + friend class QDeclarativeComponentPrivate; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVECOMPONENT_P_H diff --git a/src/declarative/qml/qdeclarativecontext.cpp b/src/declarative/qml/qdeclarativecontext.cpp new file mode 100644 index 00000000..9132d0e1 --- /dev/null +++ b/src/declarative/qml/qdeclarativecontext.cpp @@ -0,0 +1,774 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativecontext.h" +#include "private/qdeclarativecontext_p.h" + +#include "private/qdeclarativecomponent_p.h" +#include "private/qdeclarativeexpression_p.h" +#include "private/qdeclarativeengine_p.h" +#include "qdeclarativeengine.h" +#include "private/qdeclarativecompiledbindings_p.h" +#include "qdeclarativeinfo.h" +#include "private/qdeclarativeglobalscriptclass_p.h" + +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +QDeclarativeContextPrivate::QDeclarativeContextPrivate() +: data(0), notifyIndex(-1) +{ +} + +/*! + \class QDeclarativeContext + \since 4.7 + \brief The QDeclarativeContext class defines a context within a QML engine. + \mainclass + + Contexts allow data to be exposed to the QML components instantiated by the + QML engine. + + Each QDeclarativeContext contains a set of properties, distinct from its QObject + properties, that allow data to be explicitly bound to a context by name. The + context properties are defined and updated by calling + QDeclarativeContext::setContextProperty(). The following example shows a Qt model + being bound to a context and then accessed from a QML file. + + \code + QDeclarativeEngine engine; + QStringListModel modelData; + QDeclarativeContext *context = new QDeclarativeContext(engine.rootContext()); + context->setContextProperty("myModel", &modelData); + + QDeclarativeComponent component(&engine); + component.setData("import QtQuick 1.0\nListView { model: myModel }", QUrl()); + QObject *window = component.create(context); + \endcode + + Note it is the responsibility of the creator to delete any QDeclarativeContext it + constructs. If the \c context object in the example is no longer needed when the + \c window component instance is destroyed, the \c context must be destroyed explicitly. + The simplest way to ensure this is to set \c window as the parent of \c context. + + To simplify binding and maintaining larger data sets, a context object can be set + on a QDeclarativeContext. All the properties of the context object are available + by name in the context, as though they were all individually added through calls + to QDeclarativeContext::setContextProperty(). Changes to the property's values are + detected through the property's notify signal. Setting a context object is both + faster and easier than manually adding and maintaing context property values. + + The following example has the same effect as the previous one, but it uses a context + object. + + \code + class MyDataSet : ... { + ... + Q_PROPERTY(QAbstractItemModel *myModel READ model NOTIFY modelChanged) + ... + }; + + MyDataSet myDataSet; + QDeclarativeEngine engine; + QDeclarativeContext *context = new QDeclarativeContext(engine.rootContext()); + context->setContextObject(&myDataSet); + + QDeclarativeComponent component(&engine); + component.setData("import QtQuick 1.0\nListView { model: myModel }", QUrl()); + component.create(context); + \endcode + + All properties added explicitly by QDeclarativeContext::setContextProperty() take + precedence over the context object's properties. + + \section2 The Context Hierarchy + + Contexts form a hierarchy. The root of this hierarchy is the QML engine's + \l {QDeclarativeEngine::rootContext()}{root context}. Child contexts inherit + the context properties of their parents; if a child context sets a context property + that already exists in its parent, the new context property overrides that of the + parent. + + The following example defines two contexts - \c context1 and \c context2. The + second context overrides the "b" context property inherited from the first with a + new value. + + \code + QDeclarativeEngine engine; + QDeclarativeContext *context1 = new QDeclarativeContext(engine.rootContext()); + QDeclarativeContext *context2 = new QDeclarativeContext(context1); + + context1->setContextProperty("a", 12); + context1->setContextProperty("b", 12); + + context2->setContextProperty("b", 15); + \endcode + + While QML objects instantiated in a context are not strictly owned by that + context, their bindings are. If a context is destroyed, the property bindings of + outstanding QML objects will stop evaluating. + + \warning Setting the context object or adding new context properties after an object + has been created in that context is an expensive operation (essentially forcing all bindings + to reevaluate). Thus whenever possible you should complete "setup" of the context + before using it to create any objects. + + \sa {Using QML Bindings in C++ Applications} +*/ + +/*! \internal */ +QDeclarativeContext::QDeclarativeContext(QDeclarativeEngine *e, bool) +: QObject(*(new QDeclarativeContextPrivate)) +{ + Q_D(QDeclarativeContext); + d->data = new QDeclarativeContextData(this); + + d->data->engine = e; +} + +/*! + Create a new QDeclarativeContext as a child of \a engine's root context, and the + QObject \a parent. +*/ +QDeclarativeContext::QDeclarativeContext(QDeclarativeEngine *engine, QObject *parent) +: QObject(*(new QDeclarativeContextPrivate), parent) +{ + Q_D(QDeclarativeContext); + d->data = new QDeclarativeContextData(this); + + d->data->setParent(engine?QDeclarativeContextData::get(engine->rootContext()):0); +} + +/*! + Create a new QDeclarativeContext with the given \a parentContext, and the + QObject \a parent. +*/ +QDeclarativeContext::QDeclarativeContext(QDeclarativeContext *parentContext, QObject *parent) +: QObject(*(new QDeclarativeContextPrivate), parent) +{ + Q_D(QDeclarativeContext); + d->data = new QDeclarativeContextData(this); + + d->data->setParent(parentContext?QDeclarativeContextData::get(parentContext):0); +} + +/*! + \internal +*/ +QDeclarativeContext::QDeclarativeContext(QDeclarativeContextData *data) +: QObject(*(new QDeclarativeContextPrivate), 0) +{ + Q_D(QDeclarativeContext); + d->data = data; +} + +/*! + Destroys the QDeclarativeContext. + + Any expressions, or sub-contexts dependent on this context will be + invalidated, but not destroyed (unless they are parented to the QDeclarativeContext + object). + */ +QDeclarativeContext::~QDeclarativeContext() +{ + Q_D(QDeclarativeContext); + + if (!d->data->isInternal) + d->data->destroy(); +} + +/*! + Returns whether the context is valid. + + To be valid, a context must have a engine, and it's contextObject(), if any, + must not have been deleted. +*/ +bool QDeclarativeContext::isValid() const +{ + Q_D(const QDeclarativeContext); + return d->data && d->data->isValid(); +} + +/*! + Return the context's QDeclarativeEngine, or 0 if the context has no QDeclarativeEngine or the + QDeclarativeEngine was destroyed. +*/ +QDeclarativeEngine *QDeclarativeContext::engine() const +{ + Q_D(const QDeclarativeContext); + return d->data->engine; +} + +/*! + Return the context's parent QDeclarativeContext, or 0 if this context has no + parent or if the parent has been destroyed. +*/ +QDeclarativeContext *QDeclarativeContext::parentContext() const +{ + Q_D(const QDeclarativeContext); + return d->data->parent?d->data->parent->asQDeclarativeContext():0; +} + +/*! + Return the context object, or 0 if there is no context object. +*/ +QObject *QDeclarativeContext::contextObject() const +{ + Q_D(const QDeclarativeContext); + return d->data->contextObject; +} + +/*! + Set the context \a object. +*/ +void QDeclarativeContext::setContextObject(QObject *object) +{ + Q_D(QDeclarativeContext); + + QDeclarativeContextData *data = d->data; + + if (data->isInternal) { + qWarning("QDeclarativeContext: Cannot set context object for internal context."); + return; + } + + if (!isValid()) { + qWarning("QDeclarativeContext: Cannot set context object on invalid context."); + return; + } + + data->contextObject = object; +} + +/*! + Set a the \a value of the \a name property on this context. +*/ +void QDeclarativeContext::setContextProperty(const QString &name, const QVariant &value) +{ + Q_D(QDeclarativeContext); + if (d->notifyIndex == -1) + d->notifyIndex = this->metaObject()->methodCount(); + + QDeclarativeContextData *data = d->data; + + if (data->isInternal) { + qWarning("QDeclarativeContext: Cannot set property on internal context."); + return; + } + + if (!isValid()) { + qWarning("QDeclarativeContext: Cannot set property on invalid context."); + return; + } + + if (data->engine) { + bool ok; + QObject *o = QDeclarativeEnginePrivate::get(data->engine)->toQObject(value, &ok); + if (ok) { + setContextProperty(name, o); + return; + } + } + + if (!data->propertyNames) data->propertyNames = new QDeclarativeIntegerCache(data->engine); + + int idx = data->propertyNames->value(name); + if (idx == -1) { + data->propertyNames->add(name, data->idValueCount + d->propertyValues.count()); + d->propertyValues.append(value); + + data->refreshExpressions(); + } else { + d->propertyValues[idx] = value; + QMetaObject::activate(this, idx + d->notifyIndex, 0); + } +} + +/*! + Set the \a value of the \a name property on this context. + + QDeclarativeContext does \bold not take ownership of \a value. +*/ +void QDeclarativeContext::setContextProperty(const QString &name, QObject *value) +{ + Q_D(QDeclarativeContext); + if (d->notifyIndex == -1) + d->notifyIndex = this->metaObject()->methodCount(); + + QDeclarativeContextData *data = d->data; + + if (data->isInternal) { + qWarning("QDeclarativeContext: Cannot set property on internal context."); + return; + } + + if (!isValid()) { + qWarning("QDeclarativeContext: Cannot set property on invalid context."); + return; + } + + if (!data->propertyNames) data->propertyNames = new QDeclarativeIntegerCache(data->engine); + int idx = data->propertyNames->value(name); + + if (idx == -1) { + data->propertyNames->add(name, data->idValueCount + d->propertyValues.count()); + d->propertyValues.append(QVariant::fromValue(value)); + + data->refreshExpressions(); + } else { + d->propertyValues[idx] = QVariant::fromValue(value); + QMetaObject::activate(this, idx + d->notifyIndex, 0); + } +} + +/*! + Returns the value of the \a name property for this context + as a QVariant. + */ +QVariant QDeclarativeContext::contextProperty(const QString &name) const +{ + Q_D(const QDeclarativeContext); + QVariant value; + int idx = -1; + + QDeclarativeContextData *data = d->data; + + if (data->propertyNames) + idx = data->propertyNames->value(name); + + if (idx == -1) { + QByteArray utf8Name = name.toUtf8(); + if (data->contextObject) { + QObject *obj = data->contextObject; + QDeclarativePropertyCache::Data local; + QDeclarativePropertyCache::Data *property = + QDeclarativePropertyCache::property(data->engine, obj, name, local); + + if (property) value = obj->metaObject()->property(property->coreIndex).read(obj); + } + if (!value.isValid() && parentContext()) + value = parentContext()->contextProperty(name); + } else { + if (idx >= d->propertyValues.count()) + value = QVariant::fromValue(data->idValues[idx - d->propertyValues.count()].data()); + else + value = d->propertyValues[idx]; + } + + return value; +} + +/*! + Resolves the URL \a src relative to the URL of the + containing component. + + \sa QDeclarativeEngine::baseUrl(), setBaseUrl() +*/ +QUrl QDeclarativeContext::resolvedUrl(const QUrl &src) +{ + Q_D(QDeclarativeContext); + return d->data->resolvedUrl(src); +} + +QUrl QDeclarativeContextData::resolvedUrl(const QUrl &src) +{ + QDeclarativeContextData *ctxt = this; + + if (src.isRelative() && !src.isEmpty()) { + if (ctxt) { + while(ctxt) { + if(ctxt->url.isValid()) + break; + else + ctxt = ctxt->parent; + } + + if (ctxt) + return ctxt->url.resolved(src); + else if (engine) + return engine->baseUrl().resolved(src); + } + return QUrl(); + } else { + return src; + } +} + + +/*! + Explicitly sets the url resolvedUrl() will use for relative references to \a baseUrl. + + Calling this function will override the url of the containing + component used by default. + + \sa resolvedUrl() +*/ +void QDeclarativeContext::setBaseUrl(const QUrl &baseUrl) +{ + Q_D(QDeclarativeContext); + + d->data->url = baseUrl; +} + +/*! + Returns the base url of the component, or the containing component + if none is set. +*/ +QUrl QDeclarativeContext::baseUrl() const +{ + Q_D(const QDeclarativeContext); + const QDeclarativeContextData* data = d->data; + while (data && data->url.isEmpty()) + data = data->parent; + + if (data) + return data->url; + else + return QUrl(); +} + +int QDeclarativeContextPrivate::context_count(QDeclarativeListProperty *prop) +{ + QDeclarativeContext *context = static_cast(prop->object); + QDeclarativeContextPrivate *d = QDeclarativeContextPrivate::get(context); + int contextProperty = (int)(quintptr)prop->data; + + if (d->propertyValues.at(contextProperty).userType() != qMetaTypeId >()) { + return 0; + } else { + return ((const QList *)d->propertyValues.at(contextProperty).constData())->count(); + } +} + +QObject *QDeclarativeContextPrivate::context_at(QDeclarativeListProperty *prop, int index) +{ + QDeclarativeContext *context = static_cast(prop->object); + QDeclarativeContextPrivate *d = QDeclarativeContextPrivate::get(context); + int contextProperty = (int)(quintptr)prop->data; + + if (d->propertyValues.at(contextProperty).userType() != qMetaTypeId >()) { + return 0; + } else { + return ((const QList *)d->propertyValues.at(contextProperty).constData())->at(index); + } +} + + +QDeclarativeContextData::QDeclarativeContextData() +: parent(0), engine(0), isInternal(false), publicContext(0), propertyNames(0), contextObject(0), + imports(0), childContexts(0), nextChild(0), prevChild(0), expressions(0), contextObjects(0), + contextGuards(0), idValues(0), idValueCount(0), optimizedBindings(0), linkedContext(0), + componentAttached(0) +{ +} + +QDeclarativeContextData::QDeclarativeContextData(QDeclarativeContext *ctxt) +: parent(0), engine(0), isInternal(false), publicContext(ctxt), propertyNames(0), contextObject(0), + imports(0), childContexts(0), nextChild(0), prevChild(0), expressions(0), contextObjects(0), + contextGuards(0), idValues(0), idValueCount(0), optimizedBindings(0), linkedContext(0), + componentAttached(0) +{ +} + +void QDeclarativeContextData::invalidate() +{ + while (childContexts) + childContexts->invalidate(); + + while (componentAttached) { + QDeclarativeComponentAttached *a = componentAttached; + componentAttached = a->next; + if (componentAttached) componentAttached->prev = &componentAttached; + + a->next = 0; + a->prev = 0; + + emit a->destruction(); + } + + if (prevChild) { + *prevChild = nextChild; + if (nextChild) nextChild->prevChild = prevChild; + nextChild = 0; + prevChild = 0; + } + + engine = 0; + parent = 0; +} + +void QDeclarativeContextData::clearContext() +{ + if (engine) { + while (componentAttached) { + QDeclarativeComponentAttached *a = componentAttached; + componentAttached = a->next; + if (componentAttached) componentAttached->prev = &componentAttached; + + a->next = 0; + a->prev = 0; + + emit a->destruction(); + } + } + + QDeclarativeAbstractExpression *expression = expressions; + while (expression) { + QDeclarativeAbstractExpression *nextExpression = expression->m_nextExpression; + + expression->m_context = 0; + expression->m_prevExpression = 0; + expression->m_nextExpression = 0; + + expression = nextExpression; + } + expressions = 0; +} + +void QDeclarativeContextData::destroy() +{ + if (linkedContext) + linkedContext->destroy(); + + if (engine) invalidate(); + + clearContext(); + + while (contextObjects) { + QDeclarativeData *co = contextObjects; + contextObjects = contextObjects->nextContextObject; + + co->context = 0; + co->outerContext = 0; + co->nextContextObject = 0; + co->prevContextObject = 0; + } + + QDeclarativeGuardedContextData *contextGuard = contextGuards; + while (contextGuard) { + QDeclarativeGuardedContextData *next = contextGuard->m_next; + contextGuard->m_next = 0; + contextGuard->m_prev = 0; + contextGuard->m_contextData = 0; + contextGuard = next; + } + contextGuards = 0; + + if (propertyNames) + propertyNames->release(); + + if (imports) + imports->release(); + + if (optimizedBindings) + optimizedBindings->release(); + + delete [] idValues; + + if (isInternal) + delete publicContext; + + delete this; +} + +void QDeclarativeContextData::setParent(QDeclarativeContextData *p) +{ + if (p) { + parent = p; + engine = p->engine; + nextChild = p->childContexts; + if (nextChild) nextChild->prevChild = &nextChild; + prevChild = &p->childContexts; + p->childContexts = this; + } +} + +/* +Refreshes all expressions that could possibly depend on this context. Refreshing flushes all +context-tree dependent caches in the expressions, and should occur every time the context tree + *structure* (not values) changes. +*/ +void QDeclarativeContextData::refreshExpressions() +{ + QDeclarativeContextData *child = childContexts; + while (child) { + child->refreshExpressions(); + child = child->nextChild; + } + + QDeclarativeAbstractExpression *expression = expressions; + while (expression) { + expression->refresh(); + expression = expression->m_nextExpression; + } +} + +void QDeclarativeContextData::addObject(QObject *o) +{ + QDeclarativeData *data = QDeclarativeData::get(o, true); + + Q_ASSERT(data->context == 0); + + data->context = this; + data->outerContext = this; + + data->nextContextObject = contextObjects; + if (data->nextContextObject) + data->nextContextObject->prevContextObject = &data->nextContextObject; + data->prevContextObject = &contextObjects; + contextObjects = data; +} + +void QDeclarativeContextData::addImportedScript(const QDeclarativeParser::Object::ScriptBlock &script) +{ + if (!engine) + return; + + QDeclarativeEnginePrivate *enginePriv = QDeclarativeEnginePrivate::get(engine); + QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine); + + const QString &code = script.code; + const QString &url = script.file; + const QDeclarativeParser::Object::ScriptBlock::Pragmas &pragmas = script.pragmas; + + Q_ASSERT(!url.isEmpty()); + + if (pragmas & QDeclarativeParser::Object::ScriptBlock::Shared) { + + QHash::Iterator iter = enginePriv->m_sharedScriptImports.find(url); + if (iter == enginePriv->m_sharedScriptImports.end()) { + QScriptContext *scriptContext = QScriptDeclarativeClass::pushCleanContext(scriptEngine); + + scriptContext->pushScope(enginePriv->contextClass->newUrlContext(url)); + scriptContext->pushScope(enginePriv->globalClass->staticGlobalObject()); + + QScriptValue scope = QScriptDeclarativeClass::newStaticScopeObject(scriptEngine); + scriptContext->pushScope(scope); + + scriptEngine->evaluate(code, url, 1); + + if (scriptEngine->hasUncaughtException()) { + QDeclarativeError error; + QDeclarativeExpressionPrivate::exceptionToError(scriptEngine, error); + enginePriv->warning(error); + } + + scriptEngine->popContext(); + + iter = enginePriv->m_sharedScriptImports.insert(url, scope); + } + + importedScripts.append(*iter); + + } else { + + QScriptContext *scriptContext = QScriptDeclarativeClass::pushCleanContext(scriptEngine); + + scriptContext->pushScope(enginePriv->contextClass->newUrlContext(this, 0, url)); + scriptContext->pushScope(enginePriv->globalClass->staticGlobalObject()); + + QScriptValue scope = QScriptDeclarativeClass::newStaticScopeObject(scriptEngine); + scriptContext->pushScope(scope); + + scriptEngine->evaluate(code, url, 1); + + if (scriptEngine->hasUncaughtException()) { + QDeclarativeError error; + QDeclarativeExpressionPrivate::exceptionToError(scriptEngine, error); + enginePriv->warning(error); + } + + scriptEngine->popContext(); + + importedScripts.append(scope); + + } +} + +void QDeclarativeContextData::setIdProperty(int idx, QObject *obj) +{ + idValues[idx] = obj; + idValues[idx].context = this; +} + +void QDeclarativeContextData::setIdPropertyData(QDeclarativeIntegerCache *data) +{ + Q_ASSERT(!propertyNames); + propertyNames = data; + propertyNames->addref(); + + idValueCount = data->count(); + idValues = new ContextGuard[idValueCount]; +} + +QString QDeclarativeContextData::findObjectId(const QObject *obj) const +{ + if (!idValues || !propertyNames) + return QString(); + + for (int i=0; ifindId(i); + } + + if (linkedContext) + return linkedContext->findObjectId(obj); + return QString(); +} + +QDeclarativeContext *QDeclarativeContextData::asQDeclarativeContext() +{ + if (!publicContext) + publicContext = new QDeclarativeContext(this); + return publicContext; +} + +QDeclarativeContextPrivate *QDeclarativeContextData::asQDeclarativeContextPrivate() +{ + return QDeclarativeContextPrivate::get(asQDeclarativeContext()); +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativecontext.h b/src/declarative/qml/qdeclarativecontext.h new file mode 100644 index 00000000..d89e1609 --- /dev/null +++ b/src/declarative/qml/qdeclarativecontext.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVECONTEXT_H +#define QDECLARATIVECONTEXT_H + +#include +#include +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QString; +class QDeclarativeEngine; +class QDeclarativeRefCount; +class QDeclarativeContextPrivate; +class QDeclarativeCompositeTypeData; +class QDeclarativeContextData; + +class Q_DECLARATIVE_EXPORT QDeclarativeContext : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeContext) + +public: + QDeclarativeContext(QDeclarativeEngine *parent, QObject *objParent=0); + QDeclarativeContext(QDeclarativeContext *parent, QObject *objParent=0); + virtual ~QDeclarativeContext(); + + bool isValid() const; + + QDeclarativeEngine *engine() const; + QDeclarativeContext *parentContext() const; + + QObject *contextObject() const; + void setContextObject(QObject *); + + QVariant contextProperty(const QString &) const; + void setContextProperty(const QString &, QObject *); + void setContextProperty(const QString &, const QVariant &); + + QUrl resolvedUrl(const QUrl &); + + void setBaseUrl(const QUrl &); + QUrl baseUrl() const; + +private: + friend class QDeclarativeVME; + friend class QDeclarativeEngine; + friend class QDeclarativeEnginePrivate; + friend class QDeclarativeExpression; + friend class QDeclarativeExpressionPrivate; + friend class QDeclarativeContextScriptClass; + friend class QDeclarativeObjectScriptClass; + friend class QDeclarativeComponent; + friend class QDeclarativeComponentPrivate; + friend class QDeclarativeScriptPrivate; + friend class QDeclarativeBoundSignalProxy; + friend class QDeclarativeContextData; + QDeclarativeContext(QDeclarativeContextData *); + QDeclarativeContext(QDeclarativeEngine *, bool); + Q_DISABLE_COPY(QDeclarativeContext) +}; +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QList) + +QT_END_HEADER + +#endif // QDECLARATIVECONTEXT_H diff --git a/src/declarative/qml/qdeclarativecontext_p.h b/src/declarative/qml/qdeclarativecontext_p.h new file mode 100644 index 00000000..a65fc271 --- /dev/null +++ b/src/declarative/qml/qdeclarativecontext_p.h @@ -0,0 +1,292 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVECONTEXT_P_H +#define QDECLARATIVECONTEXT_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 "qdeclarativecontext.h" + +#include "private/qdeclarativedata_p.h" +#include "private/qdeclarativeintegercache_p.h" +#include "private/qdeclarativetypenamecache_p.h" +#include "private/qdeclarativenotifier_p.h" +#include "qdeclarativelist.h" +#include "private/qdeclarativeparser_p.h" + +#include +#include +#include + +#include +#include "private/qdeclarativeguard_p.h" + +QT_BEGIN_NAMESPACE + +class QDeclarativeContext; +class QDeclarativeExpression; +class QDeclarativeEngine; +class QDeclarativeExpression; +class QDeclarativeExpressionPrivate; +class QDeclarativeAbstractExpression; +class QDeclarativeCompiledBindings; +class QDeclarativeContextData; + +class QDeclarativeContextPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeContext) +public: + QDeclarativeContextPrivate(); + + QDeclarativeContextData *data; + + QList propertyValues; + int notifyIndex; + + static QDeclarativeContextPrivate *get(QDeclarativeContext *context) { + return static_cast(QObjectPrivate::get(context)); + } + static QDeclarativeContext *get(QDeclarativeContextPrivate *context) { + return static_cast(context->q_func()); + } + + // Only used for debugging + QList > instances; + + static int context_count(QDeclarativeListProperty *); + static QObject *context_at(QDeclarativeListProperty *, int); +}; + +class QDeclarativeComponentAttached; +class QDeclarativeGuardedContextData; +class Q_AUTOTEST_EXPORT QDeclarativeContextData +{ +public: + QDeclarativeContextData(); + QDeclarativeContextData(QDeclarativeContext *); + void clearContext(); + void destroy(); + void invalidate(); + + inline bool isValid() const { + return engine && (!isInternal || !contextObject || !QObjectPrivate::get(contextObject)->wasDeleted); + } + + // My parent context and engine + QDeclarativeContextData *parent; + QDeclarativeEngine *engine; + + void setParent(QDeclarativeContextData *); + void refreshExpressions(); + + void addObject(QObject *); + + QUrl resolvedUrl(const QUrl &); + + // My containing QDeclarativeContext. If isInternal is true this owns publicContext. + // If internal is false publicContext owns this. + QDeclarativeContext *asQDeclarativeContext(); + QDeclarativeContextPrivate *asQDeclarativeContextPrivate(); + bool isInternal; + QDeclarativeContext *publicContext; + + // Property name cache + QDeclarativeIntegerCache *propertyNames; + + // Context object + QObject *contextObject; + + // Any script blocks that exist on this context + QList importedScripts; + void addImportedScript(const QDeclarativeParser::Object::ScriptBlock &script); + + // Context base url + QUrl url; + + // List of imports that apply to this context + QDeclarativeTypeNameCache *imports; + + // My children + QDeclarativeContextData *childContexts; + + // My peers in parent's childContexts list + QDeclarativeContextData *nextChild; + QDeclarativeContextData **prevChild; + + // Expressions that use this context + QDeclarativeAbstractExpression *expressions; + + // Doubly-linked list of objects that are owned by this context + QDeclarativeData *contextObjects; + + // Doubly-linked list of context guards (XXX merge with contextObjects) + QDeclarativeGuardedContextData *contextGuards; + + // id guards + struct ContextGuard : public QDeclarativeGuard + { + ContextGuard() : context(0) {} + inline ContextGuard &operator=(QObject *obj) + { QDeclarativeGuard::operator=(obj); return *this; } + virtual void objectDestroyed(QObject *) { + if (context->contextObject && !QObjectPrivate::get(context->contextObject)->wasDeleted) bindings.notify(); + } + QDeclarativeContextData *context; + QDeclarativeNotifier bindings; + }; + ContextGuard *idValues; + int idValueCount; + void setIdProperty(int, QObject *); + void setIdPropertyData(QDeclarativeIntegerCache *); + + // Optimized binding pointer + QDeclarativeCompiledBindings *optimizedBindings; + + // Linked contexts. this owns linkedContext. + QDeclarativeContextData *linkedContext; + + // Linked list of uses of the Component attached property in this + // context + QDeclarativeComponentAttached *componentAttached; + + // Return the outermost id for obj, if any. + QString findObjectId(const QObject *obj) const; + + static QDeclarativeContextData *get(QDeclarativeContext *context) { + return QDeclarativeContextPrivate::get(context)->data; + } + +private: + ~QDeclarativeContextData() {} +}; + +class QDeclarativeGuardedContextData +{ +public: + inline QDeclarativeGuardedContextData(); + inline QDeclarativeGuardedContextData(QDeclarativeContextData *); + inline ~QDeclarativeGuardedContextData(); + + inline void setContextData(QDeclarativeContextData *); + + inline QDeclarativeContextData *contextData(); + + inline operator QDeclarativeContextData*() const { return m_contextData; } + inline QDeclarativeContextData* operator->() const { return m_contextData; } + inline QDeclarativeGuardedContextData &operator=(QDeclarativeContextData *d); + +private: + QDeclarativeGuardedContextData &operator=(const QDeclarativeGuardedContextData &); + QDeclarativeGuardedContextData(const QDeclarativeGuardedContextData &); + friend class QDeclarativeContextData; + + inline void clear(); + + QDeclarativeContextData *m_contextData; + QDeclarativeGuardedContextData *m_next; + QDeclarativeGuardedContextData **m_prev; +}; + +QDeclarativeGuardedContextData::QDeclarativeGuardedContextData() +: m_contextData(0), m_next(0), m_prev(0) +{ +} + +QDeclarativeGuardedContextData::QDeclarativeGuardedContextData(QDeclarativeContextData *data) +: m_contextData(0), m_next(0), m_prev(0) +{ + setContextData(data); +} + +QDeclarativeGuardedContextData::~QDeclarativeGuardedContextData() +{ + clear(); +} + +void QDeclarativeGuardedContextData::setContextData(QDeclarativeContextData *contextData) +{ + clear(); + + if (contextData) { + m_contextData = contextData; + m_next = contextData->contextGuards; + if (m_next) m_next->m_prev = &m_next; + m_prev = &contextData->contextGuards; + contextData->contextGuards = this; + } +} + +QDeclarativeContextData *QDeclarativeGuardedContextData::contextData() +{ + return m_contextData; +} + +void QDeclarativeGuardedContextData::clear() +{ + if (m_prev) { + *m_prev = m_next; + if (m_next) m_next->m_prev = m_prev; + m_contextData = 0; + m_next = 0; + m_prev = 0; + } +} + +QDeclarativeGuardedContextData & +QDeclarativeGuardedContextData::operator=(QDeclarativeContextData *d) +{ + setContextData(d); + return *this; +} + +QT_END_NAMESPACE + +#endif // QDECLARATIVECONTEXT_P_H diff --git a/src/declarative/qml/qdeclarativecontextscriptclass.cpp b/src/declarative/qml/qdeclarativecontextscriptclass.cpp new file mode 100644 index 00000000..c3b40273 --- /dev/null +++ b/src/declarative/qml/qdeclarativecontextscriptclass.cpp @@ -0,0 +1,335 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativecontextscriptclass_p.h" + +#include "private/qdeclarativeengine_p.h" +#include "private/qdeclarativecontext_p.h" +#include "private/qdeclarativetypenamescriptclass_p.h" +#include "private/qdeclarativelistscriptclass_p.h" +#include "private/qdeclarativeguard_p.h" + +QT_BEGIN_NAMESPACE + +struct ContextData : public QScriptDeclarativeClass::Object { + ContextData() : overrideObject(0), isSharedContext(true) {} + ContextData(QDeclarativeContextData *c, QObject *o) + : context(c), scopeObject(o), overrideObject(0), isSharedContext(false), isUrlContext(false) {} + QDeclarativeGuardedContextData context; + QDeclarativeGuard scopeObject; + QObject *overrideObject; + bool isSharedContext:1; + bool isUrlContext:1; + + QDeclarativeContextData *getContext(QDeclarativeEngine *engine) { + if (isSharedContext) { + return QDeclarativeEnginePrivate::get(engine)->sharedContext; + } else { + return context.contextData(); + } + } + + QObject *getScope(QDeclarativeEngine *engine) { + if (isSharedContext) { + return QDeclarativeEnginePrivate::get(engine)->sharedScope; + } else { + return scopeObject.data(); + } + } +}; + +struct UrlContextData : public ContextData { + UrlContextData(QDeclarativeContextData *c, QObject *o, const QString &u) + : ContextData(c, o), url(u) { + isUrlContext = true; + } + UrlContextData(const QString &u) + : ContextData(0, 0), url(u) { + isUrlContext = true; + } + QString url; +}; + +/* + The QDeclarativeContextScriptClass handles property access for a QDeclarativeContext + via QtScript. + */ +QDeclarativeContextScriptClass::QDeclarativeContextScriptClass(QDeclarativeEngine *bindEngine) +: QScriptDeclarativeClass(QDeclarativeEnginePrivate::getScriptEngine(bindEngine)), engine(bindEngine), + lastScopeObject(0), lastContext(0), lastData(0), lastPropertyIndex(-1) +{ +} + +QDeclarativeContextScriptClass::~QDeclarativeContextScriptClass() +{ +} + +QScriptValue QDeclarativeContextScriptClass::newContext(QDeclarativeContextData *context, QObject *scopeObject) +{ + QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine); + + return newObject(scriptEngine, this, new ContextData(context, scopeObject)); +} + +QScriptValue QDeclarativeContextScriptClass::newUrlContext(QDeclarativeContextData *context, QObject *scopeObject, + const QString &url) +{ + QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine); + + return newObject(scriptEngine, this, new UrlContextData(context, scopeObject, url)); +} + +QScriptValue QDeclarativeContextScriptClass::newUrlContext(const QString &url) +{ + QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine); + + return newObject(scriptEngine, this, new UrlContextData(url)); +} + +QScriptValue QDeclarativeContextScriptClass::newSharedContext() +{ + QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine); + + return newObject(scriptEngine, this, new ContextData()); +} + +QDeclarativeContextData *QDeclarativeContextScriptClass::contextFromValue(const QScriptValue &v) +{ + if (scriptClass(v) != this) + return 0; + + ContextData *data = (ContextData *)object(v); + return data->getContext(engine); +} + +QUrl QDeclarativeContextScriptClass::urlFromValue(const QScriptValue &v) +{ + if (scriptClass(v) != this) + return QUrl(); + + ContextData *data = (ContextData *)object(v); + if (data->isUrlContext) { + return QUrl(static_cast(data)->url); + } else { + return QUrl(); + } +} + +QObject *QDeclarativeContextScriptClass::setOverrideObject(QScriptValue &v, QObject *override) +{ + if (scriptClass(v) != this) + return 0; + + ContextData *data = (ContextData *)object(v); + QObject *rv = data->overrideObject; + data->overrideObject = override; + return rv; +} + +QScriptClass::QueryFlags +QDeclarativeContextScriptClass::queryProperty(Object *object, const Identifier &name, + QScriptClass::QueryFlags flags) +{ + Q_UNUSED(flags); + + lastScopeObject = 0; + lastContext = 0; + lastData = 0; + lastPropertyIndex = -1; + + QDeclarativeContextData *bindContext = ((ContextData *)object)->getContext(engine); + QObject *scopeObject = ((ContextData *)object)->getScope(engine); + if (!bindContext) + return 0; + + QObject *overrideObject = ((ContextData *)object)->overrideObject; + if (overrideObject) { + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); + QScriptClass::QueryFlags rv = + ep->objectClass->queryProperty(overrideObject, name, flags, bindContext, + QDeclarativeObjectScriptClass::ImplicitObject | + QDeclarativeObjectScriptClass::SkipAttachedProperties); + if (rv) { + lastScopeObject = overrideObject; + lastContext = bindContext; + return rv; + } + } + + bool includeTypes = true; + while (bindContext) { + QScriptClass::QueryFlags rv = + queryProperty(bindContext, scopeObject, name, flags, includeTypes); + scopeObject = 0; // Only applies to the first context + includeTypes = false; // Only applies to the first context + if (rv) return rv; + bindContext = bindContext->parent; + } + + return 0; +} + +QScriptClass::QueryFlags +QDeclarativeContextScriptClass::queryProperty(QDeclarativeContextData *bindContext, QObject *scopeObject, + const Identifier &name, + QScriptClass::QueryFlags flags, + bool includeTypes) +{ + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); + + lastPropertyIndex = bindContext->propertyNames?bindContext->propertyNames->value(name):-1; + if (lastPropertyIndex != -1) { + lastContext = bindContext; + return QScriptClass::HandlesReadAccess; + } + + if (includeTypes && bindContext->imports) { + QDeclarativeTypeNameCache::Data *data = bindContext->imports->data(name); + + if (data) { + lastData = data; + lastContext = bindContext; + lastScopeObject = scopeObject; + return QScriptClass::HandlesReadAccess; + } + } + + if (scopeObject) { + QScriptClass::QueryFlags rv = + ep->objectClass->queryProperty(scopeObject, name, flags, bindContext, + QDeclarativeObjectScriptClass::ImplicitObject | QDeclarativeObjectScriptClass::SkipAttachedProperties); + if (rv) { + lastScopeObject = scopeObject; + lastContext = bindContext; + return rv; + } + } + + if (bindContext->contextObject) { + QScriptClass::QueryFlags rv = + ep->objectClass->queryProperty(bindContext->contextObject, name, flags, bindContext, + QDeclarativeObjectScriptClass::ImplicitObject | QDeclarativeObjectScriptClass::SkipAttachedProperties); + + if (rv) { + lastScopeObject = bindContext->contextObject; + lastContext = bindContext; + return rv; + } + } + + return 0; +} + +QDeclarativeContextScriptClass::Value +QDeclarativeContextScriptClass::property(Object *object, const Identifier &name) +{ + Q_UNUSED(object); + + QDeclarativeContextData *bindContext = lastContext; + Q_ASSERT(bindContext); + + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); + QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine); + + if (lastData) { + + if (lastData->type) { + return Value(scriptEngine, ep->typeNameClass->newObject(lastScopeObject, lastData->type)); + } else if (lastData->typeNamespace) { + return Value(scriptEngine, ep->typeNameClass->newObject(lastScopeObject, lastData->typeNamespace)); + } else { + int index = lastData->importedScriptIndex; + if (index < bindContext->importedScripts.count()) { + return Value(scriptEngine, bindContext->importedScripts.at(index)); + } else { + return Value(); + } + } + + } else if (lastScopeObject) { + + return ep->objectClass->property(lastScopeObject, name); + + } else if (lastPropertyIndex != -1) { + + QScriptValue rv; + if (lastPropertyIndex < bindContext->idValueCount) { + rv = ep->objectClass->newQObject(bindContext->idValues[lastPropertyIndex].data()); + + if (ep->captureProperties) + ep->capturedProperties << QDeclarativeEnginePrivate::CapturedProperty(&bindContext->idValues[lastPropertyIndex].bindings); + } else { + QDeclarativeContextPrivate *cp = bindContext->asQDeclarativeContextPrivate(); + const QVariant &value = cp->propertyValues.at(lastPropertyIndex); + if (value.userType() == qMetaTypeId >()) { + rv = ep->listClass->newList(QDeclarativeListProperty(bindContext->asQDeclarativeContext(), (void*)lastPropertyIndex, 0, QDeclarativeContextPrivate::context_count, QDeclarativeContextPrivate::context_at), qMetaTypeId >()); + } else { + rv = ep->scriptValueFromVariant(value); + } + + if (ep->captureProperties) + ep->capturedProperties << QDeclarativeEnginePrivate::CapturedProperty(bindContext->asQDeclarativeContext(), -1, lastPropertyIndex + cp->notifyIndex); + } + + return Value(scriptEngine, rv); + + } else { + + return Value(scriptEngine, lastFunction); + + } +} + +void QDeclarativeContextScriptClass::setProperty(Object *object, const Identifier &name, + const QScriptValue &value) +{ + Q_UNUSED(object); + Q_ASSERT(lastScopeObject); + + QDeclarativeContextData *bindContext = lastContext; + Q_ASSERT(bindContext); + + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); + + ep->objectClass->setProperty(lastScopeObject, name, value, context(), bindContext); +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativecontextscriptclass_p.h b/src/declarative/qml/qdeclarativecontextscriptclass_p.h new file mode 100644 index 00000000..6b288d69 --- /dev/null +++ b/src/declarative/qml/qdeclarativecontextscriptclass_p.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVECONTEXTSCRIPTCLASS_P_H +#define QDECLARATIVECONTEXTSCRIPTCLASS_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 "private/qdeclarativetypenamecache_p.h" +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeEngine; +class QDeclarativeContext; +class QDeclarativeContextData; +class QDeclarativeContextScriptClass : public QScriptDeclarativeClass +{ +public: + QDeclarativeContextScriptClass(QDeclarativeEngine *); + ~QDeclarativeContextScriptClass(); + + QScriptValue newContext(QDeclarativeContextData *, QObject * = 0); + QScriptValue newUrlContext(QDeclarativeContextData *, QObject *, const QString &); + QScriptValue newUrlContext(const QString &); + QScriptValue newSharedContext(); + + QDeclarativeContextData *contextFromValue(const QScriptValue &); + QUrl urlFromValue(const QScriptValue &); + + QObject *setOverrideObject(QScriptValue &, QObject *); + +protected: + virtual QScriptClass::QueryFlags queryProperty(Object *, const Identifier &, + QScriptClass::QueryFlags flags); + virtual Value property(Object *, const Identifier &); + virtual void setProperty(Object *, const Identifier &name, const QScriptValue &); + +private: + QScriptClass::QueryFlags queryProperty(QDeclarativeContextData *, QObject *scopeObject, + const Identifier &, + QScriptClass::QueryFlags flags, + bool includeTypes); + + QDeclarativeEngine *engine; + + QObject *lastScopeObject; + QDeclarativeContextData *lastContext; + QDeclarativeTypeNameCache::Data *lastData; + int lastPropertyIndex; + QScriptValue lastFunction; + + uint m_id; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVECONTEXTSCRIPTCLASS_P_H + diff --git a/src/declarative/qml/qdeclarativecustomparser.cpp b/src/declarative/qml/qdeclarativecustomparser.cpp new file mode 100644 index 00000000..b6a25830 --- /dev/null +++ b/src/declarative/qml/qdeclarativecustomparser.cpp @@ -0,0 +1,317 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativecustomparser_p.h" +#include "private/qdeclarativecustomparser_p_p.h" + +#include "private/qdeclarativeparser_p.h" +#include "private/qdeclarativecompiler_p.h" + +#include + +QT_BEGIN_NAMESPACE + +using namespace QDeclarativeParser; + +/*! + \class QDeclarativeCustomParser + \brief The QDeclarativeCustomParser class allows you to add new arbitrary types to QML. + \internal + + By subclassing QDeclarativeCustomParser, you can add a parser for + building a particular type. + + The subclass must implement compile() and setCustomData(), and register + itself in the meta type system by calling the macro: + + \code + QML_REGISTER_CUSTOM_TYPE(Module, MajorVersion, MinorVersion, Name, TypeClass, ParserClass) + \endcode +*/ + +/* + \fn QByteArray QDeclarativeCustomParser::compile(const QList & properties) + + The custom parser processes \a properties, and returns + a QByteArray containing data meaningful only to the + custom parser; the type engine will pass this same data to + setCustomData() when making an instance of the data. + + Errors must be reported via the error() functions. + + The QByteArray may be cached between executions of the system, so + it must contain correctly-serialized data (not, for example, + pointers to stack objects). +*/ + +/* + \fn void QDeclarativeCustomParser::setCustomData(QObject *object, const QByteArray &data) + + This function sets \a object to have the properties defined + by \a data, which is a block of data previously returned by a call + to compile(). + + Errors should be reported using qmlInfo(object). + + The \a object will be an instance of the TypeClass specified by QML_REGISTER_CUSTOM_TYPE. +*/ + +QDeclarativeCustomParserNode +QDeclarativeCustomParserNodePrivate::fromObject(QDeclarativeParser::Object *root) +{ + QDeclarativeCustomParserNode rootNode; + rootNode.d->name = root->typeName; + rootNode.d->location = root->location.start; + + for(QHash::Iterator iter = root->properties.begin(); + iter != root->properties.end(); + ++iter) { + + Property *p = *iter; + + rootNode.d->properties << fromProperty(p); + } + + if (root->defaultProperty) + rootNode.d->properties << fromProperty(root->defaultProperty); + + return rootNode; +} + +QDeclarativeCustomParserProperty +QDeclarativeCustomParserNodePrivate::fromProperty(QDeclarativeParser::Property *p) +{ + QDeclarativeCustomParserProperty prop; + prop.d->name = p->name; + prop.d->isList = (p->values.count() > 1); + prop.d->location = p->location.start; + + if (p->value) { + QDeclarativeCustomParserNode node = fromObject(p->value); + QList props = node.properties(); + for (int ii = 0; ii < props.count(); ++ii) + prop.d->values << QVariant::fromValue(props.at(ii)); + } else { + for(int ii = 0; ii < p->values.count(); ++ii) { + QDeclarativeParser::Value *v = p->values.at(ii); + v->type = QDeclarativeParser::Value::Literal; + + if(v->object) { + QDeclarativeCustomParserNode node = fromObject(v->object); + prop.d->values << QVariant::fromValue(node); + } else { + prop.d->values << QVariant::fromValue(v->value); + } + + } + } + + return prop; +} + +QDeclarativeCustomParserNode::QDeclarativeCustomParserNode() +: d(new QDeclarativeCustomParserNodePrivate) +{ +} + +QDeclarativeCustomParserNode::QDeclarativeCustomParserNode(const QDeclarativeCustomParserNode &other) +: d(new QDeclarativeCustomParserNodePrivate) +{ + *this = other; +} + +QDeclarativeCustomParserNode &QDeclarativeCustomParserNode::operator=(const QDeclarativeCustomParserNode &other) +{ + d->name = other.d->name; + d->properties = other.d->properties; + d->location = other.d->location; + return *this; +} + +QDeclarativeCustomParserNode::~QDeclarativeCustomParserNode() +{ + delete d; d = 0; +} + +QByteArray QDeclarativeCustomParserNode::name() const +{ + return d->name; +} + +QList QDeclarativeCustomParserNode::properties() const +{ + return d->properties; +} + +QDeclarativeParser::Location QDeclarativeCustomParserNode::location() const +{ + return d->location; +} + +QDeclarativeCustomParserProperty::QDeclarativeCustomParserProperty() +: d(new QDeclarativeCustomParserPropertyPrivate) +{ +} + +QDeclarativeCustomParserProperty::QDeclarativeCustomParserProperty(const QDeclarativeCustomParserProperty &other) +: d(new QDeclarativeCustomParserPropertyPrivate) +{ + *this = other; +} + +QDeclarativeCustomParserProperty &QDeclarativeCustomParserProperty::operator=(const QDeclarativeCustomParserProperty &other) +{ + d->name = other.d->name; + d->isList = other.d->isList; + d->values = other.d->values; + d->location = other.d->location; + return *this; +} + +QDeclarativeCustomParserProperty::~QDeclarativeCustomParserProperty() +{ + delete d; d = 0; +} + +QByteArray QDeclarativeCustomParserProperty::name() const +{ + return d->name; +} + +bool QDeclarativeCustomParserProperty::isList() const +{ + return d->isList; +} + +QDeclarativeParser::Location QDeclarativeCustomParserProperty::location() const +{ + return d->location; +} + +QList QDeclarativeCustomParserProperty::assignedValues() const +{ + return d->values; +} + +void QDeclarativeCustomParser::clearErrors() +{ + exceptions.clear(); +} + +/*! + Reports an error with the given \a description. + + This can only be used during the compile() step. For errors during setCustomData(), use qmlInfo(). + + An error is generated referring to the position of the element in the source file. +*/ +void QDeclarativeCustomParser::error(const QString& description) +{ + Q_ASSERT(object); + QDeclarativeError error; + QString exceptionDescription; + error.setLine(object->location.start.line); + error.setColumn(object->location.start.column); + error.setDescription(description); + exceptions << error; +} + +/*! + Reports an error in parsing \a prop, with the given \a description. + + An error is generated referring to the position of \a node in the source file. +*/ +void QDeclarativeCustomParser::error(const QDeclarativeCustomParserProperty& prop, const QString& description) +{ + QDeclarativeError error; + QString exceptionDescription; + error.setLine(prop.location().line); + error.setColumn(prop.location().column); + error.setDescription(description); + exceptions << error; +} + +/*! + Reports an error in parsing \a node, with the given \a description. + + An error is generated referring to the position of \a node in the source file. +*/ +void QDeclarativeCustomParser::error(const QDeclarativeCustomParserNode& node, const QString& description) +{ + QDeclarativeError error; + QString exceptionDescription; + error.setLine(node.location().line); + error.setColumn(node.location().column); + error.setDescription(description); + exceptions << error; +} + +/*! + If \a script is a simply enum expression (eg. Text.AlignLeft), + returns the integer equivalent (eg. 1). + + Otherwise, returns -1. +*/ +int QDeclarativeCustomParser::evaluateEnum(const QByteArray& script) const +{ + return compiler->evaluateEnum(script); +} + +/*! + Resolves \a name to a type, or 0 if it is not a type. This can be used + to type-check object nodes. +*/ +const QMetaObject *QDeclarativeCustomParser::resolveType(const QByteArray& name) const +{ + return compiler->resolveType(name); +} + +/*! + Rewrites \a expression and returns an identifier that can be + used to construct the binding later. \a name + is used as the name of the rewritten function. +*/ +QDeclarativeBinding::Identifier QDeclarativeCustomParser::rewriteBinding(const QString& expression, const QByteArray& name) +{ + return compiler->rewriteBinding(expression, name); +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativecustomparser_p.h b/src/declarative/qml/qdeclarativecustomparser_p.h new file mode 100644 index 00000000..41b52258 --- /dev/null +++ b/src/declarative/qml/qdeclarativecustomparser_p.h @@ -0,0 +1,167 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVECUSTOMPARSER_H +#define QDECLARATIVECUSTOMPARSER_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 "private/qdeclarativemetatype_p.h" +#include "qdeclarativeerror.h" +#include "private/qdeclarativeparser_p.h" +#include "private/qdeclarativebinding_p.h" + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeCompiler; + +class QDeclarativeCustomParserPropertyPrivate; +class Q_DECLARATIVE_EXPORT QDeclarativeCustomParserProperty +{ +public: + QDeclarativeCustomParserProperty(); + QDeclarativeCustomParserProperty(const QDeclarativeCustomParserProperty &); + QDeclarativeCustomParserProperty &operator=(const QDeclarativeCustomParserProperty &); + ~QDeclarativeCustomParserProperty(); + + QByteArray name() const; + QDeclarativeParser::Location location() const; + + bool isList() const; + // Will be one of QDeclarativeParser::Variant, QDeclarativeCustomParserProperty or + // QDeclarativeCustomParserNode + QList assignedValues() const; + +private: + friend class QDeclarativeCustomParserNodePrivate; + friend class QDeclarativeCustomParserPropertyPrivate; + QDeclarativeCustomParserPropertyPrivate *d; +}; + +class QDeclarativeCustomParserNodePrivate; +class Q_DECLARATIVE_EXPORT QDeclarativeCustomParserNode +{ +public: + QDeclarativeCustomParserNode(); + QDeclarativeCustomParserNode(const QDeclarativeCustomParserNode &); + QDeclarativeCustomParserNode &operator=(const QDeclarativeCustomParserNode &); + ~QDeclarativeCustomParserNode(); + + QByteArray name() const; + QDeclarativeParser::Location location() const; + + QList properties() const; + +private: + friend class QDeclarativeCustomParserNodePrivate; + QDeclarativeCustomParserNodePrivate *d; +}; + +class Q_DECLARATIVE_EXPORT QDeclarativeCustomParser +{ +public: + enum Flag { + NoFlag = 0x00000000, + AcceptsAttachedProperties = 0x00000001 + }; + Q_DECLARE_FLAGS(Flags, Flag) + + QDeclarativeCustomParser() : compiler(0), object(0), m_flags(NoFlag) {} + QDeclarativeCustomParser(Flags f) : compiler(0), object(0), m_flags(f) {} + virtual ~QDeclarativeCustomParser() {} + + void clearErrors(); + Flags flags() const { return m_flags; } + + virtual QByteArray compile(const QList &)=0; + virtual void setCustomData(QObject *, const QByteArray &)=0; + + QList errors() const { return exceptions; } + +protected: + void error(const QString& description); + void error(const QDeclarativeCustomParserProperty&, const QString& description); + void error(const QDeclarativeCustomParserNode&, const QString& description); + + int evaluateEnum(const QByteArray&) const; + + const QMetaObject *resolveType(const QByteArray&) const; + + QDeclarativeBinding::Identifier rewriteBinding(const QString&, const QByteArray&); + +private: + QList exceptions; + QDeclarativeCompiler *compiler; + QDeclarativeParser::Object *object; + Flags m_flags; + friend class QDeclarativeCompiler; +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(QDeclarativeCustomParser::Flags); + +#if 0 +#define QML_REGISTER_CUSTOM_TYPE(URI, VERSION_MAJ, VERSION_MIN, NAME, TYPE, CUSTOMTYPE) \ + qmlRegisterCustomType(#URI, VERSION_MAJ, VERSION_MIN, #NAME, #TYPE, new CUSTOMTYPE) +#endif + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QDeclarativeCustomParserProperty) +Q_DECLARE_METATYPE(QDeclarativeCustomParserNode) + +QT_END_HEADER + +#endif diff --git a/src/declarative/qml/qdeclarativecustomparser_p_p.h b/src/declarative/qml/qdeclarativecustomparser_p_p.h new file mode 100644 index 00000000..3f96a835 --- /dev/null +++ b/src/declarative/qml/qdeclarativecustomparser_p_p.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVECUSTOMPARSER_P_H +#define QDECLARATIVECUSTOMPARSER_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 "private/qdeclarativecustomparser_p.h" + +#include "private/qdeclarativeparser_p.h" + +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeCustomParserNodePrivate +{ +public: + QByteArray name; + QList properties; + QDeclarativeParser::Location location; + + static QDeclarativeCustomParserNode fromObject(QDeclarativeParser::Object *); + static QDeclarativeCustomParserProperty fromProperty(QDeclarativeParser::Property *); +}; + +class QDeclarativeCustomParserPropertyPrivate +{ +public: + QDeclarativeCustomParserPropertyPrivate() + : isList(false) {} + + QByteArray name; + bool isList; + QDeclarativeParser::Location location; + QList values; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVECUSTOMPARSER_P_H diff --git a/src/declarative/qml/qdeclarativedata_p.h b/src/declarative/qml/qdeclarativedata_p.h new file mode 100644 index 00000000..b26c4d51 --- /dev/null +++ b/src/declarative/qml/qdeclarativedata_p.h @@ -0,0 +1,165 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEDATA_P_H +#define QDECLARATIVEDATA_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 +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeGuardImpl; +class QDeclarativeCompiledData; +class QDeclarativeAbstractBinding; +class QDeclarativeContext; +class QDeclarativePropertyCache; +class QDeclarativeContextData; +class QDeclarativeNotifier; +class QDeclarativeDataExtended; +// This class is structured in such a way, that simply zero'ing it is the +// default state for elemental object allocations. This is crucial in the +// workings of the QDeclarativeInstruction::CreateSimpleObject instruction. +// Don't change anything here without first considering that case! +class Q_AUTOTEST_EXPORT QDeclarativeData : public QAbstractDeclarativeData +{ +public: + QDeclarativeData() + : ownMemory(true), ownContext(false), indestructible(true), explicitIndestructibleSet(false), + context(0), outerContext(0), bindings(0), nextContextObject(0), prevContextObject(0), bindingBitsSize(0), + bindingBits(0), lineNumber(0), columnNumber(0), deferredComponent(0), deferredIdx(0), + scriptValue(0), objectDataRefCount(0), propertyCache(0), guards(0), extendedData(0) { + init(); + } + + static inline void init() { + QAbstractDeclarativeData::destroyed = destroyed; + QAbstractDeclarativeData::parentChanged = parentChanged; + QAbstractDeclarativeData::objectNameChanged = objectNameChanged; + } + + static void destroyed(QAbstractDeclarativeData *, QObject *); + static void parentChanged(QAbstractDeclarativeData *, QObject *, QObject *); + static void objectNameChanged(QAbstractDeclarativeData *, QObject *); + + void destroyed(QObject *); + void parentChanged(QObject *, QObject *); + void objectNameChanged(QObject *); + + void setImplicitDestructible() { + if (!explicitIndestructibleSet) indestructible = false; + } + + quint32 ownMemory:1; + quint32 ownContext:1; + quint32 indestructible:1; + quint32 explicitIndestructibleSet:1; + quint32 dummy:28; + + // The context that created the C++ object + QDeclarativeContextData *context; + // The outermost context in which this object lives + QDeclarativeContextData *outerContext; + + QDeclarativeAbstractBinding *bindings; + + // Linked list for QDeclarativeContext::contextObjects + QDeclarativeData *nextContextObject; + QDeclarativeData**prevContextObject; + + int bindingBitsSize; + quint32 *bindingBits; + bool hasBindingBit(int) const; + void clearBindingBit(int); + void setBindingBit(QObject *obj, int); + + ushort lineNumber; + ushort columnNumber; + + QDeclarativeCompiledData *deferredComponent; // Can't this be found from the context? + unsigned int deferredIdx; + + // ### Can we make this QScriptValuePrivate so we incur no additional allocation + // cost? + QScriptValue *scriptValue; + quint32 objectDataRefCount; + QDeclarativePropertyCache *propertyCache; + + QDeclarativeGuardImpl *guards; + + static QDeclarativeData *get(const QObject *object, bool create = false) { + QObjectPrivate *priv = QObjectPrivate::get(const_cast(object)); + if (priv->wasDeleted) { + Q_ASSERT(!create); + return 0; + } else if (priv->declarativeData) { + return static_cast(priv->declarativeData); + } else if (create) { + priv->declarativeData = new QDeclarativeData; + return static_cast(priv->declarativeData); + } else { + return 0; + } + } + + bool hasExtendedData() const { return extendedData != 0; } + QDeclarativeNotifier *objectNameNotifier() const; + QHash *attachedProperties() const; + +private: + // For objectNameNotifier and attachedProperties + mutable QDeclarativeDataExtended *extendedData; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVEDATA_P_H diff --git a/src/declarative/qml/qdeclarativedirparser.cpp b/src/declarative/qml/qdeclarativedirparser.cpp new file mode 100644 index 00000000..16bc2c3e --- /dev/null +++ b/src/declarative/qml/qdeclarativedirparser.cpp @@ -0,0 +1,285 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativedirparser_p.h" +#include "qdeclarativeerror.h" +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QDeclarativeDirParser::QDeclarativeDirParser() + : _isParsed(false) +{ +} + +QDeclarativeDirParser::~QDeclarativeDirParser() +{ +} + +QUrl QDeclarativeDirParser::url() const +{ + return _url; +} + +void QDeclarativeDirParser::setUrl(const QUrl &url) +{ + _url = url; +} + +QString QDeclarativeDirParser::fileSource() const +{ + return _filePathSouce; +} + +void QDeclarativeDirParser::setFileSource(const QString &filePath) +{ + _filePathSouce = filePath; +} + +QString QDeclarativeDirParser::source() const +{ + return _source; +} + +void QDeclarativeDirParser::setSource(const QString &source) +{ + _isParsed = false; + _source = source; +} + +bool QDeclarativeDirParser::isParsed() const +{ + return _isParsed; +} + +bool QDeclarativeDirParser::parse() +{ + if (_isParsed) + return true; + + _isParsed = true; + _errors.clear(); + _plugins.clear(); + _components.clear(); + + if (_source.isEmpty() && !_filePathSouce.isEmpty()) { + QFile file(_filePathSouce); + if (!QDeclarative_isFileCaseCorrect(_filePathSouce)) { + QDeclarativeError error; + error.setDescription(QString::fromUtf8("cannot load module \"$$URI$$\": File name case mismatch for \"%1\"").arg(_filePathSouce)); + _errors.prepend(error); + return false; + } else if (file.open(QFile::ReadOnly)) { + _source = QString::fromUtf8(file.readAll()); + } else { + QDeclarativeError error; + error.setDescription(QString::fromUtf8("module \"$$URI$$\" definition \"%1\" not readable").arg(_filePathSouce)); + _errors.prepend(error); + return false; + } + } + + QTextStream stream(&_source); + int lineNumber = 0; + + forever { + ++lineNumber; + + const QString line = stream.readLine(); + if (line.isNull()) + break; + + QString sections[3]; + int sectionCount = 0; + + int index = 0; + const int length = line.length(); + + while (index != length) { + const QChar ch = line.at(index); + + if (ch.isSpace()) { + do { ++index; } + while (index != length && line.at(index).isSpace()); + + } else if (ch == QLatin1Char('#')) { + // recognized a comment + break; + + } else { + const int start = index; + + do { ++index; } + while (index != length && !line.at(index).isSpace()); + + const QString lexeme = line.mid(start, index - start); + + if (sectionCount >= 3) { + reportError(lineNumber, start, QLatin1String("unexpected token")); + + } else { + sections[sectionCount++] = lexeme; + } + } + } + + if (sectionCount == 0) { + continue; // no sections, no party. + + } else if (sections[0] == QLatin1String("plugin")) { + if (sectionCount < 2) { + reportError(lineNumber, -1, + QString::fromUtf8("plugin directive requires 2 arguments, but %1 were provided").arg(sectionCount + 1)); + + continue; + } + + const Plugin entry(sections[1], sections[2]); + + _plugins.append(entry); + + } else if (sections[0] == QLatin1String("internal")) { + if (sectionCount != 3) { + reportError(lineNumber, -1, + QString::fromUtf8("internal types require 2 arguments, but %1 were provided").arg(sectionCount + 1)); + continue; + } + Component entry(sections[1], sections[2], -1, -1); + entry.internal = true; + _components.append(entry); + } else if (sections[0] == QLatin1String("typeinfo")) { + if (sectionCount != 2) { + reportError(lineNumber, -1, + QString::fromUtf8("typeinfo requires 1 argument, but %1 were provided").arg(sectionCount - 1)); + continue; + } +#ifdef QT_CREATOR + TypeInfo typeInfo(sections[1]); + _typeInfos.append(typeInfo); +#endif + + } else if (sectionCount == 2) { + // No version specified (should only be used for relative qmldir files) + const Component entry(sections[0], sections[1], -1, -1); + _components.append(entry); + } else if (sectionCount == 3) { + const QString &version = sections[1]; + const int dotIndex = version.indexOf(QLatin1Char('.')); + + if (dotIndex == -1) { + reportError(lineNumber, -1, QLatin1String("expected '.'")); + } else if (version.indexOf(QLatin1Char('.'), dotIndex + 1) != -1) { + reportError(lineNumber, -1, QLatin1String("unexpected '.'")); + } else { + bool validVersionNumber = false; + const int majorVersion = version.left(dotIndex).toInt(&validVersionNumber); + + if (validVersionNumber) { + const int minorVersion = version.mid(dotIndex + 1).toInt(&validVersionNumber); + + if (validVersionNumber) { + const Component entry(sections[0], sections[2], majorVersion, minorVersion); + + _components.append(entry); + } + } + } + } else { + reportError(lineNumber, -1, + QString::fromUtf8("a component declaration requires 3 arguments, but %1 were provided").arg(sectionCount + 1)); + } + } + + return hasError(); +} + +void QDeclarativeDirParser::reportError(int line, int column, const QString &description) +{ + QDeclarativeError error; + error.setUrl(_url); + error.setLine(line); + error.setColumn(column); + error.setDescription(description); + _errors.append(error); +} + +bool QDeclarativeDirParser::hasError() const +{ + if (! _errors.isEmpty()) + return true; + + return false; +} + +QList QDeclarativeDirParser::errors(const QString &uri) const +{ + QList errors = _errors; + for (int i = 0; i < errors.size(); ++i) { + QDeclarativeError &e = errors[i]; + QString description = e.description(); + description.replace(QLatin1String("$$URI$$"), uri); + e.setDescription(description); + } + return errors; +} + +QList QDeclarativeDirParser::plugins() const +{ + return _plugins; +} + +QList QDeclarativeDirParser::components() const +{ + return _components; +} + +#ifdef QT_CREATOR +QList QDeclarativeDirParser::typeInfos() const +{ + return _typeInfos; +} +#endif + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativedirparser_p.h b/src/declarative/qml/qdeclarativedirparser_p.h new file mode 100644 index 00000000..aa1ace5f --- /dev/null +++ b/src/declarative/qml/qdeclarativedirparser_p.h @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEDIRPARSER_P_H +#define QDECLARATIVEDIRPARSER_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 +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeError; +class QDeclarativeDirParser +{ + Q_DISABLE_COPY(QDeclarativeDirParser) + +public: + QDeclarativeDirParser(); + ~QDeclarativeDirParser(); + + QUrl url() const; + void setUrl(const QUrl &url); + + QString source() const; + void setSource(const QString &source); + + QString fileSource() const; + void setFileSource(const QString &filePath); + + bool isParsed() const; + bool parse(); + + bool hasError() const; + QList errors(const QString &uri) const; + + struct Plugin + { + Plugin() {} + + Plugin(const QString &name, const QString &path) + : name(name), path(path) {} + + QString name; + QString path; + }; + + struct Component + { + Component() + : majorVersion(0), minorVersion(0), internal(false) {} + + Component(const QString &typeName, const QString &fileName, int majorVersion, int minorVersion) + : typeName(typeName), fileName(fileName), majorVersion(majorVersion), minorVersion(minorVersion), + internal(false) {} + + QString typeName; + QString fileName; + int majorVersion; + int minorVersion; + bool internal; + }; + + QList components() const; + QList plugins() const; + +#ifdef QT_CREATOR + struct TypeInfo + { + TypeInfo() {} + TypeInfo(const QString &fileName) + : fileName(fileName) {} + + QString fileName; + }; + + QList typeInfos() const; +#endif + +private: + void reportError(int line, int column, const QString &message); + +private: + QList _errors; + QUrl _url; + QString _source; + QString _filePathSouce; + QList _components; + QList _plugins; +#ifdef QT_CREATOR + QList _typeInfos; +#endif + unsigned _isParsed: 1; +}; + +typedef QList QDeclarativeDirComponents; + + +QT_END_NAMESPACE + +#endif // QDECLARATIVEDIRPARSER_P_H diff --git a/src/declarative/qml/qdeclarativedom.cpp b/src/declarative/qml/qdeclarativedom.cpp new file mode 100644 index 00000000..bd68d73d --- /dev/null +++ b/src/declarative/qml/qdeclarativedom.cpp @@ -0,0 +1,1835 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativedom_p.h" +#include "private/qdeclarativedom_p_p.h" + +#include "private/qdeclarativecompiler_p.h" +#include "private/qdeclarativeengine_p.h" +#include "private/qdeclarativescriptparser_p.h" +#include "private/qdeclarativeglobal_p.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QDeclarativeDomDocumentPrivate::QDeclarativeDomDocumentPrivate() +: root(0) +{ +} + +QDeclarativeDomDocumentPrivate::~QDeclarativeDomDocumentPrivate() +{ + if (root) root->release(); +} + +/*! + \class QDeclarativeDomDocument + \internal + \brief The QDeclarativeDomDocument class represents the root of a QML document + + A QML document is a self-contained snippet of QML, usually contained in a + single file. Each document has a root object, accessible through + QDeclarativeDomDocument::rootObject(). + + The QDeclarativeDomDocument class allows the programmer to inspect a QML document by + calling QDeclarativeDomDocument::load(). + + The following example loads a QML file from disk, and prints out its root + object type and the properties assigned in the root object. + \code + QFile file(inputFileName); + file.open(QIODevice::ReadOnly); + QByteArray xmlData = file.readAll(); + + QDeclarativeDomDocument document; + document.load(qmlengine, xmlData); + + QDeclarativeDomObject rootObject = document.rootObject(); + qDebug() << rootObject.objectType(); + foreach(QDeclarativeDomProperty property, rootObject.properties()) + qDebug() << property.propertyName(); + \endcode +*/ + +/*! + Construct an empty QDeclarativeDomDocument. +*/ +QDeclarativeDomDocument::QDeclarativeDomDocument() +: d(new QDeclarativeDomDocumentPrivate) +{ +} + +/*! + Create a copy of \a other QDeclarativeDomDocument. +*/ +QDeclarativeDomDocument::QDeclarativeDomDocument(const QDeclarativeDomDocument &other) +: d(other.d) +{ +} + +/*! + Destroy the QDeclarativeDomDocument +*/ +QDeclarativeDomDocument::~QDeclarativeDomDocument() +{ +} + +/*! + Assign \a other to this QDeclarativeDomDocument. +*/ +QDeclarativeDomDocument &QDeclarativeDomDocument::operator=(const QDeclarativeDomDocument &other) +{ + d = other.d; + return *this; +} + +/*! + Returns all import statements in qml. +*/ +QList QDeclarativeDomDocument::imports() const +{ + return d->imports; +} + +/*! + Loads a QDeclarativeDomDocument from \a data. \a data should be valid QML + data. On success, true is returned. If the \a data is malformed, false + is returned and QDeclarativeDomDocument::errors() contains an error description. + + \sa QDeclarativeDomDocument::loadError() +*/ +bool QDeclarativeDomDocument::load(QDeclarativeEngine *engine, const QByteArray &data, const QUrl &url) +{ + d->errors.clear(); + d->imports.clear(); + + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); + QDeclarativeTypeData *td = ep->typeLoader.get(data, url, QDeclarativeTypeLoader::PreserveParser); + + if(td->isError()) { + d->errors = td->errors(); + td->release(); + return false; + } else if(!td->isCompleteOrError()) { + QDeclarativeError error; + error.setDescription(QLatin1String("QDeclarativeDomDocument supports local types only")); + d->errors << error; + td->release(); + return false; + } + + for (int i = 0; i < td->parser().imports().size(); ++i) { + QDeclarativeScriptParser::Import parserImport = td->parser().imports().at(i); + QDeclarativeDomImport domImport; + domImport.d->type = static_cast(parserImport.type); + domImport.d->uri = parserImport.uri; + domImport.d->qualifier = parserImport.qualifier; + domImport.d->version = parserImport.version; + d->imports += domImport; + } + + if (td->parser().tree()) { + d->root = td->parser().tree(); + d->root->addref(); + } + + td->release(); + return true; +} + +/*! + Returns the last load errors. The load errors will be reset after a + successful call to load(). + + \sa load() +*/ +QList QDeclarativeDomDocument::errors() const +{ + return d->errors; +} + +/*! + Returns the document's root object, or an invalid QDeclarativeDomObject if the + document has no root. + + In the sample QML below, the root object will be the QDeclarativeItem type. + \qml +Item { + Text { + text: "Hello World" + } +} + \endqml +*/ +QDeclarativeDomObject QDeclarativeDomDocument::rootObject() const +{ + QDeclarativeDomObject rv; + rv.d->object = d->root; + if (rv.d->object) rv.d->object->addref(); + return rv; +} + +QDeclarativeDomPropertyPrivate::QDeclarativeDomPropertyPrivate() +: property(0) +{ +} + +QDeclarativeDomPropertyPrivate::~QDeclarativeDomPropertyPrivate() +{ + if (property) property->release(); +} + +QDeclarativeDomDynamicPropertyPrivate::QDeclarativeDomDynamicPropertyPrivate(): + valid(false) +{ +} + +QDeclarativeDomDynamicPropertyPrivate::~QDeclarativeDomDynamicPropertyPrivate() +{ + if (valid && property.defaultValue) property.defaultValue->release(); +} + +/*! + \class QDeclarativeDomProperty + \internal + \brief The QDeclarativeDomProperty class represents one property assignment in the + QML DOM tree + + Properties in QML can be assigned QML \l {QDeclarativeDomValue}{values}. + + \sa QDeclarativeDomObject +*/ + +/*! + Construct an invalid QDeclarativeDomProperty. +*/ +QDeclarativeDomProperty::QDeclarativeDomProperty() +: d(new QDeclarativeDomPropertyPrivate) +{ +} + +/*! + Create a copy of \a other QDeclarativeDomProperty. +*/ +QDeclarativeDomProperty::QDeclarativeDomProperty(const QDeclarativeDomProperty &other) +: d(other.d) +{ +} + +/*! + Destroy the QDeclarativeDomProperty. +*/ +QDeclarativeDomProperty::~QDeclarativeDomProperty() +{ +} + +/*! + Assign \a other to this QDeclarativeDomProperty. +*/ +QDeclarativeDomProperty &QDeclarativeDomProperty::operator=(const QDeclarativeDomProperty &other) +{ + d = other.d; + return *this; +} + +/*! + Returns true if this is a valid QDeclarativeDomProperty, false otherwise. +*/ +bool QDeclarativeDomProperty::isValid() const +{ + return d->property != 0; +} + + +/*! + Return the name of this property. + + \qml +Text { + x: 10 + y: 10 + font.bold: true +} + \endqml + + As illustrated above, a property name can be a simple string, such as "x" or + "y", or a more complex "dot property", such as "font.bold". In both cases + the full name is returned ("x", "y" and "font.bold") by this method. + + For dot properties, a split version of the name can be accessed by calling + QDeclarativeDomProperty::propertyNameParts(). + + \sa QDeclarativeDomProperty::propertyNameParts() +*/ +QByteArray QDeclarativeDomProperty::propertyName() const +{ + return d->propertyName; +} + +/*! + Return the name of this property, split into multiple parts in the case + of dot properties. + + \qml +Text { + x: 10 + y: 10 + font.bold: true +} + \endqml + + For each of the properties shown above, this method would return ("x"), + ("y") and ("font", "bold"). + + \sa QDeclarativeDomProperty::propertyName() +*/ +QList QDeclarativeDomProperty::propertyNameParts() const +{ + if (d->propertyName.isEmpty()) return QList(); + else return d->propertyName.split('.'); +} + +/*! + Return true if this property is used as a default property in the QML + document. + + \code + +hello + \endcode + + The above two examples return the same DOM tree, except that the second has + the default property flag set on the text property. Observe that whether + or not a property has isDefaultProperty set is determined by how the + property is used, and not only by whether the property is the types default + property. +*/ +bool QDeclarativeDomProperty::isDefaultProperty() const +{ + return d->property && d->property->isDefault; +} + +/*! + Returns the QDeclarativeDomValue that is assigned to this property, or an invalid + QDeclarativeDomValue if no value is assigned. +*/ +QDeclarativeDomValue QDeclarativeDomProperty::value() const +{ + QDeclarativeDomValue rv; + if (d->property) { + rv.d->property = d->property; + if (d->property->values.count()) + rv.d->value = d->property->values.at(0); + else + rv.d->value = d->property->onValues.at(0); + rv.d->property->addref(); + rv.d->value->addref(); + } + return rv; +} + +/*! + Returns the position in the input data where the property ID startd, or -1 if + the property is invalid. +*/ +int QDeclarativeDomProperty::position() const +{ + if (d && d->property) { + return d->property->location.range.offset; + } else + return -1; +} + +/*! + Returns the length in the input data from where the property ID started upto + the end of it, or -1 if the property is invalid. +*/ +int QDeclarativeDomProperty::length() const +{ + if (d && d->property) + return d->property->location.range.length; + else + return -1; +} + +/*! + Construct an invalid QDeclarativeDomDynamicProperty. +*/ +QDeclarativeDomDynamicProperty::QDeclarativeDomDynamicProperty(): + d(new QDeclarativeDomDynamicPropertyPrivate) +{ +} + +/*! + Create a copy of \a other QDeclarativeDomDynamicProperty. +*/ +QDeclarativeDomDynamicProperty::QDeclarativeDomDynamicProperty(const QDeclarativeDomDynamicProperty &other): + d(other.d) +{ +} + +/*! + Destroy the QDeclarativeDomDynamicProperty. +*/ +QDeclarativeDomDynamicProperty::~QDeclarativeDomDynamicProperty() +{ +} + +/*! + Assign \a other to this QDeclarativeDomDynamicProperty. +*/ +QDeclarativeDomDynamicProperty &QDeclarativeDomDynamicProperty::operator=(const QDeclarativeDomDynamicProperty &other) +{ + d = other.d; + return *this; +} + +bool QDeclarativeDomDynamicProperty::isValid() const +{ + return d && d->valid; +} + +/*! + Return the name of this dynamic property. + + \qml +Item { + property int count: 10; +} + \endqml + + As illustrated above, a dynamic property name can have a name and a + default value ("10"). +*/ +QByteArray QDeclarativeDomDynamicProperty::propertyName() const +{ + if (isValid()) + return d->property.name; + else + return QByteArray(); +} + +/*! + Returns the type of the dynamic property. Note that when the property is an + alias property, this will return -1. Use QDeclarativeDomProperty::isAlias() to check + if the property is an alias. +*/ +int QDeclarativeDomDynamicProperty::propertyType() const +{ + if (isValid()) { + switch (d->property.type) { + case QDeclarativeParser::Object::DynamicProperty::Bool: + return QMetaType::type("bool"); + + case QDeclarativeParser::Object::DynamicProperty::Color: + return QMetaType::type("QColor"); + + case QDeclarativeParser::Object::DynamicProperty::Time: + return QMetaType::type("QTime"); + + case QDeclarativeParser::Object::DynamicProperty::Date: + return QMetaType::type("QDate"); + + case QDeclarativeParser::Object::DynamicProperty::DateTime: + return QMetaType::type("QDateTime"); + + case QDeclarativeParser::Object::DynamicProperty::Int: + return QMetaType::type("int"); + + case QDeclarativeParser::Object::DynamicProperty::Real: + return sizeof(qreal) == sizeof(double) ? QMetaType::type("double") : QMetaType::type("float"); + + case QDeclarativeParser::Object::DynamicProperty::String: + return QMetaType::type("QString"); + + case QDeclarativeParser::Object::DynamicProperty::Url: + return QMetaType::type("QUrl"); + + case QDeclarativeParser::Object::DynamicProperty::Variant: + return QMetaType::type("QVariant"); + + default: + break; + } + } + + return -1; +} + +QByteArray QDeclarativeDomDynamicProperty::propertyTypeName() const +{ + if (isValid()) + return d->property.customType; + + return QByteArray(); +} + +/*! + Return true if this property is used as a default property in the QML + document. + + \code + +hello + \endcode + + The above two examples return the same DOM tree, except that the second has + the default property flag set on the text property. Observe that whether + or not a property has isDefaultProperty set is determined by how the + property is used, and not only by whether the property is the types default + property. +*/ +bool QDeclarativeDomDynamicProperty::isDefaultProperty() const +{ + if (isValid()) + return d->property.isDefaultProperty; + else + return false; +} + +/*! + Returns the default value as a QDeclarativeDomProperty. +*/ +QDeclarativeDomProperty QDeclarativeDomDynamicProperty::defaultValue() const +{ + QDeclarativeDomProperty rp; + + if (isValid() && d->property.defaultValue) { + rp.d->property = d->property.defaultValue; + rp.d->propertyName = propertyName(); + rp.d->property->addref(); + } + + return rp; +} + +/*! + Returns true if this dynamic property is an alias for another property, + false otherwise. +*/ +bool QDeclarativeDomDynamicProperty::isAlias() const +{ + if (isValid()) + return d->property.type == QDeclarativeParser::Object::DynamicProperty::Alias; + else + return false; +} + +/*! + Returns the position in the input data where the property ID startd, or 0 if + the property is invalid. +*/ +int QDeclarativeDomDynamicProperty::position() const +{ + if (isValid()) { + return d->property.location.range.offset; + } else + return -1; +} + +/*! + Returns the length in the input data from where the property ID started upto + the end of it, or 0 if the property is invalid. +*/ +int QDeclarativeDomDynamicProperty::length() const +{ + if (isValid()) + return d->property.location.range.length; + else + return -1; +} + +QDeclarativeDomObjectPrivate::QDeclarativeDomObjectPrivate() +: object(0) +{ +} + +QDeclarativeDomObjectPrivate::~QDeclarativeDomObjectPrivate() +{ + if (object) object->release(); +} + +QDeclarativeDomObjectPrivate::Properties +QDeclarativeDomObjectPrivate::properties() const +{ + Properties rv; + + for (QHash::ConstIterator iter = + object->properties.begin(); + iter != object->properties.end(); + ++iter) { + + rv << properties(*iter); + + } + return rv; +} + +QDeclarativeDomObjectPrivate::Properties +QDeclarativeDomObjectPrivate::properties(QDeclarativeParser::Property *property) const +{ + Properties rv; + + if (property->value) { + + for (QHash::ConstIterator iter = + property->value->properties.begin(); + iter != property->value->properties.end(); + ++iter) { + + rv << properties(*iter); + + } + + QByteArray name(property->name + '.'); + for (Properties::Iterator iter = rv.begin(); iter != rv.end(); ++iter) + iter->second.prepend(name); + + } else { + rv << qMakePair(property, property->name); + } + + return rv; +} + +/*! + \class QDeclarativeDomObject + \internal + \brief The QDeclarativeDomObject class represents an object instantiation. + + Each object instantiated in a QML file has a corresponding QDeclarativeDomObject + node in the QML DOM. + + In addition to the type information that determines the object to + instantiate, QDeclarativeDomObject's also have a set of associated QDeclarativeDomProperty's. + Each QDeclarativeDomProperty represents a QML property assignment on the instantiated + object. For example, + + \qml +QGraphicsWidget { + opacity: 0.5 + size: "100x100" +} + \endqml + + describes a single QDeclarativeDomObject - "QGraphicsWidget" - with two properties, + "opacity" and "size". Obviously QGraphicsWidget has many more properties than just + these two, but the QML DOM representation only contains those assigned + values (or bindings) in the QML file. +*/ + +/*! + Construct an invalid QDeclarativeDomObject. +*/ +QDeclarativeDomObject::QDeclarativeDomObject() +: d(new QDeclarativeDomObjectPrivate) +{ +} + +/*! + Create a copy of \a other QDeclarativeDomObject. +*/ +QDeclarativeDomObject::QDeclarativeDomObject(const QDeclarativeDomObject &other) +: d(other.d) +{ +} + +/*! + Destroy the QDeclarativeDomObject. +*/ +QDeclarativeDomObject::~QDeclarativeDomObject() +{ +} + +/*! + Assign \a other to this QDeclarativeDomObject. +*/ +QDeclarativeDomObject &QDeclarativeDomObject::operator=(const QDeclarativeDomObject &other) +{ + d = other.d; + return *this; +} + +/*! + Returns true if this is a valid QDeclarativeDomObject, false otherwise. +*/ +bool QDeclarativeDomObject::isValid() const +{ + return d->object != 0; +} + +/*! + Returns the fully-qualified type name of this object. + + For example, the type of this object would be "Qt/4.6/Rectangle". + \qml +Rectangle { } + \endqml +*/ +QByteArray QDeclarativeDomObject::objectType() const +{ + if (d->object) return d->object->typeName; + else return QByteArray(); +} + +/*! + Returns the type name as referenced in the qml file. + + For example, the type of this object would be "Rectangle". + \qml +Rectangle { } + \endqml +*/ +QByteArray QDeclarativeDomObject::objectClassName() const +{ + if (d->object) + return d->object->className; + else + return QByteArray(); +} + +int QDeclarativeDomObject::objectTypeMajorVersion() const +{ + if (d->object) + return d->object->majorVersion; + else + return -1; +} + +int QDeclarativeDomObject::objectTypeMinorVersion() const +{ + if (d->object) + return d->object->minorVersion; + else + return -1; +} + +/*! + Returns the QML id assigned to this object, or an empty QByteArray if no id + has been assigned. + + For example, the object id of this object would be "MyText". + \qml +Text { id: myText } + \endqml +*/ +QString QDeclarativeDomObject::objectId() const +{ + if (d->object) { + return d->object->id; + } else { + return QString(); + } +} + +/*! + Returns the list of assigned properties on this object. + + In the following example, "text" and "x" properties would be returned. + \qml +Text { + text: "Hello world!" + x: 100 +} + \endqml +*/ +QList QDeclarativeDomObject::properties() const +{ + QList rv; + + if (!d->object || isComponent()) + return rv; + + QDeclarativeDomObjectPrivate::Properties properties = d->properties(); + for (int ii = 0; ii < properties.count(); ++ii) { + + QDeclarativeDomProperty domProperty; + domProperty.d->property = properties.at(ii).first; + domProperty.d->property->addref(); + domProperty.d->propertyName = properties.at(ii).second; + rv << domProperty; + + } + + if (d->object->defaultProperty) { + QDeclarativeDomProperty domProperty; + domProperty.d->property = d->object->defaultProperty; + domProperty.d->property->addref(); + domProperty.d->propertyName = d->object->defaultProperty->name; + rv << domProperty; + } + + return rv; +} + +/*! + Returns the object's \a name property if a value has been assigned to + it, or an invalid QDeclarativeDomProperty otherwise. + + In the example below, \c {object.property("source")} would return a valid + QDeclarativeDomProperty, and \c {object.property("tile")} an invalid QDeclarativeDomProperty. + + \qml +Image { source: "sample.jpg" } + \endqml +*/ +QDeclarativeDomProperty QDeclarativeDomObject::property(const QByteArray &name) const +{ + QList props = properties(); + for (int ii = 0; ii < props.count(); ++ii) + if (props.at(ii).propertyName() == name) + return props.at(ii); + return QDeclarativeDomProperty(); +} + +QList QDeclarativeDomObject::dynamicProperties() const +{ + QList properties; + + for (int i = 0; i < d->object->dynamicProperties.size(); ++i) { + QDeclarativeDomDynamicProperty p; + p.d = new QDeclarativeDomDynamicPropertyPrivate; + p.d->property = d->object->dynamicProperties.at(i); + p.d->valid = true; + + if (p.d->property.defaultValue) + p.d->property.defaultValue->addref(); + + properties.append(p); + } + + return properties; +} + +QDeclarativeDomDynamicProperty QDeclarativeDomObject::dynamicProperty(const QByteArray &name) const +{ + QDeclarativeDomDynamicProperty p; + + if (!isValid()) + return p; + + for (int i = 0; i < d->object->dynamicProperties.size(); ++i) { + if (d->object->dynamicProperties.at(i).name == name) { + p.d = new QDeclarativeDomDynamicPropertyPrivate; + p.d->property = d->object->dynamicProperties.at(i); + if (p.d->property.defaultValue) p.d->property.defaultValue->addref(); + p.d->valid = true; + } + } + + return p; +} + +/*! + Returns true if this object is a custom type. Custom types are special + types that allow embeddeding non-QML data, such as SVG or HTML data, + directly into QML files. + + \note Currently this method will always return false, and is a placekeeper + for future functionality. + + \sa QDeclarativeDomObject::customTypeData() +*/ +bool QDeclarativeDomObject::isCustomType() const +{ + return false; +} + +/*! + If this object represents a custom type, returns the data associated with + the custom type, otherwise returns an empty QByteArray(). + QDeclarativeDomObject::isCustomType() can be used to check if this object represents + a custom type. +*/ +QByteArray QDeclarativeDomObject::customTypeData() const +{ + return QByteArray(); +} + +/*! + Returns true if this object is a sub-component object. Sub-component + objects can be converted into QDeclarativeDomComponent instances by calling + QDeclarativeDomObject::toComponent(). + + \sa QDeclarativeDomObject::toComponent() +*/ +bool QDeclarativeDomObject::isComponent() const +{ + return (d->object && (d->object->typeName == "Qt/Component" || d->object->typeName == "QtQuick/Component")); +} + +/*! + Returns a QDeclarativeDomComponent for this object if it is a sub-component, or + an invalid QDeclarativeDomComponent if not. QDeclarativeDomObject::isComponent() can be used + to check if this object represents a sub-component. + + \sa QDeclarativeDomObject::isComponent() +*/ +QDeclarativeDomComponent QDeclarativeDomObject::toComponent() const +{ + QDeclarativeDomComponent rv; + if (isComponent()) + rv.d = d; + return rv; +} + +/*! + Returns the position in the input data where the property assignment started +, or -1 if the property is invalid. +*/ +int QDeclarativeDomObject::position() const +{ + if (d && d->object) + return d->object->location.range.offset; + else + return -1; +} + +/*! + Returns the length in the input data from where the property assignment star +ted upto the end of it, or -1 if the property is invalid. +*/ +int QDeclarativeDomObject::length() const +{ + if (d && d->object) + return d->object->location.range.length; + else + return -1; +} + +// Returns the URL of the type, if it is an external type, or an empty URL if +// not +QUrl QDeclarativeDomObject::url() const +{ + if (d && d->object) + return d->object->url; + else + return QUrl(); +} + +QDeclarativeDomBasicValuePrivate::QDeclarativeDomBasicValuePrivate() +: value(0) +{ +} + +QDeclarativeDomBasicValuePrivate::~QDeclarativeDomBasicValuePrivate() +{ + if (value) value->release(); +} + +/*! + \class QDeclarativeDomValueLiteral + \internal + \brief The QDeclarativeDomValueLiteral class represents a literal value. + + A literal value is a simple value, written inline with the QML. In the + example below, the "x", "y" and "color" properties are being assigned + literal values. + + \qml +Rectangle { + x: 10 + y: 10 + color: "red" +} + \endqml +*/ + +/*! + Construct an empty QDeclarativeDomValueLiteral. +*/ +QDeclarativeDomValueLiteral::QDeclarativeDomValueLiteral(): + d(new QDeclarativeDomBasicValuePrivate) +{ +} + +/*! + Create a copy of \a other QDeclarativeDomValueLiteral. +*/ +QDeclarativeDomValueLiteral::QDeclarativeDomValueLiteral(const QDeclarativeDomValueLiteral &other) +: d(other.d) +{ +} + +/*! + Destroy the QDeclarativeDomValueLiteral. +*/ +QDeclarativeDomValueLiteral::~QDeclarativeDomValueLiteral() +{ +} + +/*! + Assign \a other to this QDeclarativeDomValueLiteral. +*/ +QDeclarativeDomValueLiteral &QDeclarativeDomValueLiteral::operator=(const QDeclarativeDomValueLiteral &other) +{ + d = other.d; + return *this; +} + +/*! + Return the literal value. + + In the example below, the literal value will be the string "10". + \qml +Rectangle { x: 10 } + \endqml +*/ +QString QDeclarativeDomValueLiteral::literal() const +{ + if (d->value) return d->value->primitive(); + else return QString(); +} + +/*! + \class QDeclarativeDomValueBinding + \internal + \brief The QDeclarativeDomValueBinding class represents a property binding. + + A property binding is an ECMAScript expression assigned to a property. In + the example below, the "x" property is being assigned a property binding. + + \qml +Rectangle { x: Other.x } + \endqml +*/ + +/*! + Construct an empty QDeclarativeDomValueBinding. +*/ +QDeclarativeDomValueBinding::QDeclarativeDomValueBinding(): + d(new QDeclarativeDomBasicValuePrivate) +{ +} + +/*! + Create a copy of \a other QDeclarativeDomValueBinding. +*/ +QDeclarativeDomValueBinding::QDeclarativeDomValueBinding(const QDeclarativeDomValueBinding &other) +: d(other.d) +{ +} + +/*! + Destroy the QDeclarativeDomValueBinding. +*/ +QDeclarativeDomValueBinding::~QDeclarativeDomValueBinding() +{ +} + +/*! + Assign \a other to this QDeclarativeDomValueBinding. +*/ +QDeclarativeDomValueBinding &QDeclarativeDomValueBinding::operator=(const QDeclarativeDomValueBinding &other) +{ + d = other.d; + return *this; +} + +/*! + Return the binding expression. + + In the example below, the string "Other.x" will be returned. + \qml +Rectangle { x: Other.x } + \endqml +*/ +QString QDeclarativeDomValueBinding::binding() const +{ + if (d->value) + return d->value->value.asScript(); + else + return QString(); +} + +/*! + \class QDeclarativeDomValueValueSource + \internal + \brief The QDeclarativeDomValueValueSource class represents a value source assignment value. + + In QML, value sources are special value generating types that may be + assigned to properties. Value sources inherit the QDeclarativePropertyValueSource + class. In the example below, the "x" property is being assigned the + NumberAnimation value source. + + \qml +Rectangle { + x: NumberAnimation { + from: 0 + to: 100 + loops: Animation.Infinite + } +} + \endqml +*/ + +/*! + Construct an empty QDeclarativeDomValueValueSource. +*/ +QDeclarativeDomValueValueSource::QDeclarativeDomValueValueSource(): + d(new QDeclarativeDomBasicValuePrivate) +{ +} + +/*! + Create a copy of \a other QDeclarativeDomValueValueSource. +*/ +QDeclarativeDomValueValueSource::QDeclarativeDomValueValueSource(const QDeclarativeDomValueValueSource &other) +: d(other.d) +{ +} + +/*! + Destroy the QDeclarativeDomValueValueSource. +*/ +QDeclarativeDomValueValueSource::~QDeclarativeDomValueValueSource() +{ +} + +/*! + Assign \a other to this QDeclarativeDomValueValueSource. +*/ +QDeclarativeDomValueValueSource &QDeclarativeDomValueValueSource::operator=(const QDeclarativeDomValueValueSource &other) +{ + d = other.d; + return *this; +} + +/*! + Return the value source object. + + In the example below, an object representing the NumberAnimation will be + returned. + \qml +Rectangle { + x: NumberAnimation { + from: 0 + to: 100 + loops: Animation.Infinite + } +} + \endqml +*/ +QDeclarativeDomObject QDeclarativeDomValueValueSource::object() const +{ + QDeclarativeDomObject rv; + if (d->value) { + rv.d->object = d->value->object; + rv.d->object->addref(); + } + return rv; +} + +/*! + \class QDeclarativeDomValueValueInterceptor + \internal + \brief The QDeclarativeDomValueValueInterceptor class represents a value interceptor assignment value. + + In QML, value interceptor are special write-intercepting types that may be + assigned to properties. Value interceptor inherit the QDeclarativePropertyValueInterceptor + class. In the example below, the "x" property is being assigned the + Behavior value interceptor. + + \qml +Rectangle { + Behavior on x { NumberAnimation { duration: 500 } } +} + \endqml +*/ + +/*! + Construct an empty QDeclarativeDomValueValueInterceptor. +*/ +QDeclarativeDomValueValueInterceptor::QDeclarativeDomValueValueInterceptor(): + d(new QDeclarativeDomBasicValuePrivate) +{ +} + +/*! + Create a copy of \a other QDeclarativeDomValueValueInterceptor. +*/ +QDeclarativeDomValueValueInterceptor::QDeclarativeDomValueValueInterceptor(const QDeclarativeDomValueValueInterceptor &other) +: d(other.d) +{ +} + +/*! + Destroy the QDeclarativeDomValueValueInterceptor. +*/ +QDeclarativeDomValueValueInterceptor::~QDeclarativeDomValueValueInterceptor() +{ +} + +/*! + Assign \a other to this QDeclarativeDomValueValueInterceptor. +*/ +QDeclarativeDomValueValueInterceptor &QDeclarativeDomValueValueInterceptor::operator=(const QDeclarativeDomValueValueInterceptor &other) +{ + d = other.d; + return *this; +} + +/*! + Return the value interceptor object. + + In the example below, an object representing the Behavior will be + returned. + \qml +Rectangle { + Behavior on x { NumberAnimation { duration: 500 } } +} + \endqml +*/ +QDeclarativeDomObject QDeclarativeDomValueValueInterceptor::object() const +{ + QDeclarativeDomObject rv; + if (d->value) { + rv.d->object = d->value->object; + rv.d->object->addref(); + } + return rv; +} + +QDeclarativeDomValuePrivate::QDeclarativeDomValuePrivate() +: property(0), value(0) +{ +} + +QDeclarativeDomValuePrivate::~QDeclarativeDomValuePrivate() +{ + if (property) property->release(); + if (value) value->release(); +} + +/*! + \class QDeclarativeDomValue + \internal + \brief The QDeclarativeDomValue class represents a generic Qml value. + + QDeclarativeDomValue's can be assigned to QML \l {QDeclarativeDomProperty}{properties}. In + QML, properties can be assigned various different values, including basic + literals, property bindings, property value sources, objects and lists of + values. The QDeclarativeDomValue class allows a programmer to determine the specific + value type being assigned and access more detailed information through a + corresponding value type class. + + For example, in the following example, + + \qml +Text { + text: "Hello World!" + y: Other.y +} + \endqml + + The text property is being assigned a literal, and the y property a property + binding. To output the values assigned to the text and y properties in the + above example from C++, + + \code + QDeclarativeDomDocument document; + QDeclarativeDomObject root = document.rootObject(); + + QDeclarativeDomProperty text = root.property("text"); + if (text.value().isLiteral()) { + QDeclarativeDomValueLiteral literal = text.value().toLiteral(); + qDebug() << literal.literal(); + } + + QDeclarativeDomProperty y = root.property("y"); + if (y.value().isBinding()) { + QDeclarativeDomValueBinding binding = y.value().toBinding(); + qDebug() << binding.binding(); + } + \endcode +*/ + +/*! + Construct an invalid QDeclarativeDomValue. +*/ +QDeclarativeDomValue::QDeclarativeDomValue() +: d(new QDeclarativeDomValuePrivate) +{ +} + +/*! + Create a copy of \a other QDeclarativeDomValue. +*/ +QDeclarativeDomValue::QDeclarativeDomValue(const QDeclarativeDomValue &other) +: d(other.d) +{ +} + +/*! + Destroy the QDeclarativeDomValue +*/ +QDeclarativeDomValue::~QDeclarativeDomValue() +{ +} + +/*! + Assign \a other to this QDeclarativeDomValue. +*/ +QDeclarativeDomValue &QDeclarativeDomValue::operator=(const QDeclarativeDomValue &other) +{ + d = other.d; + return *this; +} + +/*! + \enum QDeclarativeDomValue::Type + + The type of the QDeclarativeDomValue node. + + \value Invalid The QDeclarativeDomValue is invalid. + \value Literal The QDeclarativeDomValue is a literal value assignment. Use QDeclarativeDomValue::toLiteral() to access the type instance. + \value PropertyBinding The QDeclarativeDomValue is a property binding. Use QDeclarativeDomValue::toBinding() to access the type instance. + \value ValueSource The QDeclarativeDomValue is a property value source. Use QDeclarativeDomValue::toValueSource() to access the type instance. + \value ValueInterceptor The QDeclarativeDomValue is a property value interceptor. Use QDeclarativeDomValue::toValueInterceptor() to access the type instance. + \value Object The QDeclarativeDomValue is an object assignment. Use QDeclarativeDomValue::toObject() to access the type instnace. + \value List The QDeclarativeDomValue is a list of other values. Use QDeclarativeDomValue::toList() to access the type instance. +*/ + +/*! + Returns the type of this QDeclarativeDomValue. +*/ +QDeclarativeDomValue::Type QDeclarativeDomValue::type() const +{ + if (d->property) + if (QDeclarativeMetaType::isList(d->property->type) || + (d->property && (d->property->values.count() + d->property->onValues.count()) > 1)) + return List; + + QDeclarativeParser::Value *value = d->value; + if (!value && !d->property) + return Invalid; + + switch(value->type) { + case QDeclarativeParser::Value::Unknown: + return Invalid; + case QDeclarativeParser::Value::Literal: + return Literal; + case QDeclarativeParser::Value::PropertyBinding: + return PropertyBinding; + case QDeclarativeParser::Value::ValueSource: + return ValueSource; + case QDeclarativeParser::Value::ValueInterceptor: + return ValueInterceptor; + case QDeclarativeParser::Value::CreatedObject: + return Object; + case QDeclarativeParser::Value::SignalObject: + return Invalid; + case QDeclarativeParser::Value::SignalExpression: + return Literal; + case QDeclarativeParser::Value::Id: + return Literal; + } + return Invalid; +} + +/*! + Returns true if this is an invalid value, otherwise false. +*/ +bool QDeclarativeDomValue::isInvalid() const +{ + return type() == Invalid; +} + +/*! + Returns true if this is a literal value, otherwise false. +*/ +bool QDeclarativeDomValue::isLiteral() const +{ + return type() == Literal; +} + +/*! + Returns true if this is a property binding value, otherwise false. +*/ +bool QDeclarativeDomValue::isBinding() const +{ + return type() == PropertyBinding; +} + +/*! + Returns true if this is a value source value, otherwise false. +*/ +bool QDeclarativeDomValue::isValueSource() const +{ + return type() == ValueSource; +} + +/*! + Returns true if this is a value interceptor value, otherwise false. +*/ +bool QDeclarativeDomValue::isValueInterceptor() const +{ + return type() == ValueInterceptor; +} + +/*! + Returns true if this is an object value, otherwise false. +*/ +bool QDeclarativeDomValue::isObject() const +{ + return type() == Object; +} + +/*! + Returns true if this is a list value, otherwise false. +*/ +bool QDeclarativeDomValue::isList() const +{ + return type() == List; +} + +/*! + Returns a QDeclarativeDomValueLiteral if this value is a literal type, otherwise + returns an invalid QDeclarativeDomValueLiteral. + + \sa QDeclarativeDomValue::type() +*/ +QDeclarativeDomValueLiteral QDeclarativeDomValue::toLiteral() const +{ + QDeclarativeDomValueLiteral rv; + if (type() == Literal) { + rv.d->value = d->value; + rv.d->value->addref(); + } + return rv; +} + +/*! + Returns a QDeclarativeDomValueBinding if this value is a property binding type, + otherwise returns an invalid QDeclarativeDomValueBinding. + + \sa QDeclarativeDomValue::type() +*/ +QDeclarativeDomValueBinding QDeclarativeDomValue::toBinding() const +{ + QDeclarativeDomValueBinding rv; + if (type() == PropertyBinding) { + rv.d->value = d->value; + rv.d->value->addref(); + } + return rv; +} + +/*! + Returns a QDeclarativeDomValueValueSource if this value is a property value source + type, otherwise returns an invalid QDeclarativeDomValueValueSource. + + \sa QDeclarativeDomValue::type() +*/ +QDeclarativeDomValueValueSource QDeclarativeDomValue::toValueSource() const +{ + QDeclarativeDomValueValueSource rv; + if (type() == ValueSource) { + rv.d->value = d->value; + rv.d->value->addref(); + } + return rv; +} + +/*! + Returns a QDeclarativeDomValueValueInterceptor if this value is a property value interceptor + type, otherwise returns an invalid QDeclarativeDomValueValueInterceptor. + + \sa QDeclarativeDomValue::type() +*/ +QDeclarativeDomValueValueInterceptor QDeclarativeDomValue::toValueInterceptor() const +{ + QDeclarativeDomValueValueInterceptor rv; + if (type() == ValueInterceptor) { + rv.d->value = d->value; + rv.d->value->addref(); + } + return rv; +} + +/*! + Returns a QDeclarativeDomObject if this value is an object assignment type, otherwise + returns an invalid QDeclarativeDomObject. + + \sa QDeclarativeDomValue::type() +*/ +QDeclarativeDomObject QDeclarativeDomValue::toObject() const +{ + QDeclarativeDomObject rv; + if (type() == Object) { + rv.d->object = d->value->object; + rv.d->object->addref(); + } + return rv; +} + +/*! + Returns a QDeclarativeDomList if this value is a list type, otherwise returns an + invalid QDeclarativeDomList. + + \sa QDeclarativeDomValue::type() +*/ +QDeclarativeDomList QDeclarativeDomValue::toList() const +{ + QDeclarativeDomList rv; + if (type() == List) { + rv.d = d; + } + return rv; +} + +/*! + Returns the position in the input data where the property value startd, or -1 + if the value is invalid. +*/ +int QDeclarativeDomValue::position() const +{ + if (type() == Invalid) + return -1; + else + return d->value->location.range.offset; +} + +/*! + Returns the length in the input data from where the property value started u +pto the end of it, or -1 if the value is invalid. +*/ +int QDeclarativeDomValue::length() const +{ + if (type() == Invalid) + return -1; + else + return d->value->location.range.length; +} + +/*! + \class QDeclarativeDomList + \internal + \brief The QDeclarativeDomList class represents a list of values assigned to a QML property. + + Lists of values can be assigned to properties. For example, the following + example assigns multiple objects to Item's "children" property + \qml +Item { + children: [ + Text { }, + Rectangle { } + ] +} + \endqml + + Lists can also be implicitly created by assigning multiple + \l {QDeclarativeDomValueValueSource}{value sources} or constants to a property. + \qml +Item { + x: 10 + x: NumberAnimation { + running: false + from: 0 + to: 100 + } +} + \endqml +*/ + +/*! + Construct an empty QDeclarativeDomList. +*/ +QDeclarativeDomList::QDeclarativeDomList() +{ +} + +/*! + Create a copy of \a other QDeclarativeDomList. +*/ +QDeclarativeDomList::QDeclarativeDomList(const QDeclarativeDomList &other) +: d(other.d) +{ +} + +/*! + Destroy the QDeclarativeDomList. +*/ +QDeclarativeDomList::~QDeclarativeDomList() +{ +} + +/*! + Assign \a other to this QDeclarativeDomList. +*/ +QDeclarativeDomList &QDeclarativeDomList::operator=(const QDeclarativeDomList &other) +{ + d = other.d; + return *this; +} + +/*! + Returns the list of QDeclarativeDomValue's. +*/ +QList QDeclarativeDomList::values() const +{ + QList rv; + if (!d->property) + return rv; + + for (int ii = 0; ii < d->property->values.count(); ++ii) { + QDeclarativeDomValue v; + v.d->value = d->property->values.at(ii); + v.d->value->addref(); + rv << v; + } + + for (int ii = 0; ii < d->property->onValues.count(); ++ii) { + QDeclarativeDomValue v; + v.d->value = d->property->onValues.at(ii); + v.d->value->addref(); + rv << v; + } + + return rv; +} + +/*! + Returns the position in the input data where the list started, or -1 if + the property is invalid. +*/ +int QDeclarativeDomList::position() const +{ + if (d && d->property) { + return d->property->listValueRange.offset; + } else + return -1; +} + +/*! + Returns the length in the input data from where the list started upto + the end of it, or 0 if the property is invalid. +*/ +int QDeclarativeDomList::length() const +{ + if (d && d->property) + return d->property->listValueRange.length; + else + return -1; +} + +/*! + Returns a list of positions of the commas in the QML file. +*/ +QList QDeclarativeDomList:: commaPositions() const +{ + if (d && d->property) + return d->property->listCommaPositions; + else + return QList(); +} + +/*! + \class QDeclarativeDomComponent + \internal + \brief The QDeclarativeDomComponent class represents sub-component within a QML document. + + Sub-components are QDeclarativeComponents defined within a QML document. The + following example shows the definition of a sub-component with the id + "listDelegate". + + \qml +Item { + Component { + id: listDelegate + Text { + text: modelData.text + } + } +} + \endqml + + Like QDeclarativeDomDocument's, components contain a single root object. +*/ + +/*! + Construct an empty QDeclarativeDomComponent. +*/ +QDeclarativeDomComponent::QDeclarativeDomComponent() +{ +} + +/*! + Create a copy of \a other QDeclarativeDomComponent. +*/ +QDeclarativeDomComponent::QDeclarativeDomComponent(const QDeclarativeDomComponent &other) +: QDeclarativeDomObject(other) +{ +} + +/*! + Destroy the QDeclarativeDomComponent. +*/ +QDeclarativeDomComponent::~QDeclarativeDomComponent() +{ +} + +/*! + Assign \a other to this QDeclarativeDomComponent. +*/ +QDeclarativeDomComponent &QDeclarativeDomComponent::operator=(const QDeclarativeDomComponent &other) +{ + static_cast(*this) = other; + return *this; +} + +/*! + Returns the component's root object. + + In the example below, the root object is the "Text" object. + \qml +Item { + Component { + id: listDelegate + Text { + text: modelData.text + } + } +} + \endqml +*/ +QDeclarativeDomObject QDeclarativeDomComponent::componentRoot() const +{ + QDeclarativeDomObject rv; + if (d->object) { + QDeclarativeParser::Object *obj = 0; + if (d->object->defaultProperty && + d->object->defaultProperty->values.count() == 1 && + d->object->defaultProperty->values.at(0)->object) + obj = d->object->defaultProperty->values.at(0)->object; + + if (obj) { + rv.d->object = obj; + rv.d->object->addref(); + } + } + + return rv; +} + +QDeclarativeDomImportPrivate::QDeclarativeDomImportPrivate() +: type(File) +{ +} + +QDeclarativeDomImportPrivate::~QDeclarativeDomImportPrivate() +{ +} + +/*! + \class QDeclarativeDomImport + \internal + \brief The QDeclarativeDomImport class represents an import statement. +*/ + +/*! + Construct an empty QDeclarativeDomImport. +*/ +QDeclarativeDomImport::QDeclarativeDomImport() +: d(new QDeclarativeDomImportPrivate) +{ +} + +/*! + Create a copy of \a other QDeclarativeDomImport. +*/ +QDeclarativeDomImport::QDeclarativeDomImport(const QDeclarativeDomImport &other) +: d(other.d) +{ +} + +/*! + Destroy the QDeclarativeDomImport. +*/ +QDeclarativeDomImport::~QDeclarativeDomImport() +{ +} + +/*! + Assign \a other to this QDeclarativeDomImport. +*/ +QDeclarativeDomImport &QDeclarativeDomImport::operator=(const QDeclarativeDomImport &other) +{ + d = other.d; + return *this; +} + +/*! + Returns the type of the import. + */ +QDeclarativeDomImport::Type QDeclarativeDomImport::type() const +{ + return static_cast(d->type); +} + +/*! + Returns the URI of the import (e.g. 'subdir' or 'com.nokia.Qt') + */ +QString QDeclarativeDomImport::uri() const +{ + return d->uri; +} + +/*! + Returns the version specified by the import. An empty string if no version was specified. + */ +QString QDeclarativeDomImport::version() const +{ + return d->version; +} + +/*! + Returns the (optional) qualifier string (the token following the 'as' keyword) of the import. + */ +QString QDeclarativeDomImport::qualifier() const +{ + return d->qualifier; +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativedom_p.h b/src/declarative/qml/qdeclarativedom_p.h new file mode 100644 index 00000000..f8538f97 --- /dev/null +++ b/src/declarative/qml/qdeclarativedom_p.h @@ -0,0 +1,362 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEDOM_P_H +#define QDECLARATIVEDOM_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 "qdeclarativeerror.h" + +#include +#include + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QString; +class QByteArray; +class QDeclarativeDomObject; +class QDeclarativeDomList; +class QDeclarativeDomValue; +class QDeclarativeEngine; +class QDeclarativeDomComponent; +class QDeclarativeDomImport; +class QIODevice; + +class QDeclarativeDomDocumentPrivate; + +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeDomDocument +{ +public: + QDeclarativeDomDocument(); + QDeclarativeDomDocument(const QDeclarativeDomDocument &); + ~QDeclarativeDomDocument(); + QDeclarativeDomDocument &operator=(const QDeclarativeDomDocument &); + + QList imports() const; + + QList errors() const; + bool load(QDeclarativeEngine *, const QByteArray &, const QUrl & = QUrl()); + + QDeclarativeDomObject rootObject() const; + +private: + QSharedDataPointer d; +}; + +class QDeclarativeDomPropertyPrivate; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeDomProperty +{ +public: + QDeclarativeDomProperty(); + QDeclarativeDomProperty(const QDeclarativeDomProperty &); + ~QDeclarativeDomProperty(); + QDeclarativeDomProperty &operator=(const QDeclarativeDomProperty &); + + bool isValid() const; + + QByteArray propertyName() const; + QList propertyNameParts() const; + + bool isDefaultProperty() const; + + QDeclarativeDomValue value() const; + + int position() const; + int length() const; + +private: + friend class QDeclarativeDomObject; + friend class QDeclarativeDomDynamicProperty; + QSharedDataPointer d; +}; + +class QDeclarativeDomDynamicPropertyPrivate; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeDomDynamicProperty +{ +public: + QDeclarativeDomDynamicProperty(); + QDeclarativeDomDynamicProperty(const QDeclarativeDomDynamicProperty &); + ~QDeclarativeDomDynamicProperty(); + QDeclarativeDomDynamicProperty &operator=(const QDeclarativeDomDynamicProperty &); + + bool isValid() const; + + QByteArray propertyName() const; + int propertyType() const; + QByteArray propertyTypeName() const; + + bool isDefaultProperty() const; + QDeclarativeDomProperty defaultValue() const; + + bool isAlias() const; + + int position() const; + int length() const; + +private: + friend class QDeclarativeDomObject; + QSharedDataPointer d; +}; + +class QDeclarativeDomObjectPrivate; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeDomObject +{ +public: + QDeclarativeDomObject(); + QDeclarativeDomObject(const QDeclarativeDomObject &); + ~QDeclarativeDomObject(); + QDeclarativeDomObject &operator=(const QDeclarativeDomObject &); + + bool isValid() const; + + QByteArray objectType() const; + QByteArray objectClassName() const; + + int objectTypeMajorVersion() const; + int objectTypeMinorVersion() const; + + QString objectId() const; + + QList properties() const; + QDeclarativeDomProperty property(const QByteArray &) const; + + QList dynamicProperties() const; + QDeclarativeDomDynamicProperty dynamicProperty(const QByteArray &) const; + + bool isCustomType() const; + QByteArray customTypeData() const; + + bool isComponent() const; + QDeclarativeDomComponent toComponent() const; + + int position() const; + int length() const; + + QUrl url() const; +private: + friend class QDeclarativeDomDocument; + friend class QDeclarativeDomComponent; + friend class QDeclarativeDomValue; + friend class QDeclarativeDomValueValueSource; + friend class QDeclarativeDomValueValueInterceptor; + QSharedDataPointer d; +}; + +class QDeclarativeDomValuePrivate; +class QDeclarativeDomBasicValuePrivate; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeDomValueLiteral +{ +public: + QDeclarativeDomValueLiteral(); + QDeclarativeDomValueLiteral(const QDeclarativeDomValueLiteral &); + ~QDeclarativeDomValueLiteral(); + QDeclarativeDomValueLiteral &operator=(const QDeclarativeDomValueLiteral &); + + QString literal() const; + +private: + friend class QDeclarativeDomValue; + QSharedDataPointer d; +}; + +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeDomValueBinding +{ +public: + QDeclarativeDomValueBinding(); + QDeclarativeDomValueBinding(const QDeclarativeDomValueBinding &); + ~QDeclarativeDomValueBinding(); + QDeclarativeDomValueBinding &operator=(const QDeclarativeDomValueBinding &); + + QString binding() const; + +private: + friend class QDeclarativeDomValue; + QSharedDataPointer d; +}; + +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeDomValueValueSource +{ +public: + QDeclarativeDomValueValueSource(); + QDeclarativeDomValueValueSource(const QDeclarativeDomValueValueSource &); + ~QDeclarativeDomValueValueSource(); + QDeclarativeDomValueValueSource &operator=(const QDeclarativeDomValueValueSource &); + + QDeclarativeDomObject object() const; + +private: + friend class QDeclarativeDomValue; + QSharedDataPointer d; +}; + +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeDomValueValueInterceptor +{ +public: + QDeclarativeDomValueValueInterceptor(); + QDeclarativeDomValueValueInterceptor(const QDeclarativeDomValueValueInterceptor &); + ~QDeclarativeDomValueValueInterceptor(); + QDeclarativeDomValueValueInterceptor &operator=(const QDeclarativeDomValueValueInterceptor &); + + QDeclarativeDomObject object() const; + +private: + friend class QDeclarativeDomValue; + QSharedDataPointer d; +}; + + +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeDomComponent : public QDeclarativeDomObject +{ +public: + QDeclarativeDomComponent(); + QDeclarativeDomComponent(const QDeclarativeDomComponent &); + ~QDeclarativeDomComponent(); + QDeclarativeDomComponent &operator=(const QDeclarativeDomComponent &); + + QDeclarativeDomObject componentRoot() const; +}; + +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeDomValue +{ +public: + enum Type { + Invalid, + Literal, + PropertyBinding, + ValueSource, + ValueInterceptor, + Object, + List + }; + + QDeclarativeDomValue(); + QDeclarativeDomValue(const QDeclarativeDomValue &); + ~QDeclarativeDomValue(); + QDeclarativeDomValue &operator=(const QDeclarativeDomValue &); + + Type type() const; + + bool isInvalid() const; + bool isLiteral() const; + bool isBinding() const; + bool isValueSource() const; + bool isValueInterceptor() const; + bool isObject() const; + bool isList() const; + + QDeclarativeDomValueLiteral toLiteral() const; + QDeclarativeDomValueBinding toBinding() const; + QDeclarativeDomValueValueSource toValueSource() const; + QDeclarativeDomValueValueInterceptor toValueInterceptor() const; + QDeclarativeDomObject toObject() const; + QDeclarativeDomList toList() const; + + int position() const; + int length() const; + +private: + friend class QDeclarativeDomProperty; + friend class QDeclarativeDomList; + QSharedDataPointer d; +}; + +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeDomList +{ +public: + QDeclarativeDomList(); + QDeclarativeDomList(const QDeclarativeDomList &); + ~QDeclarativeDomList(); + QDeclarativeDomList &operator=(const QDeclarativeDomList &); + + QList values() const; + + int position() const; + int length() const; + + QList commaPositions() const; + +private: + friend class QDeclarativeDomValue; + QSharedDataPointer d; +}; + +class QDeclarativeDomImportPrivate; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeDomImport +{ +public: + enum Type { Library, File }; + + QDeclarativeDomImport(); + QDeclarativeDomImport(const QDeclarativeDomImport &); + ~QDeclarativeDomImport(); + QDeclarativeDomImport &operator=(const QDeclarativeDomImport &); + + Type type() const; + QString uri() const; + QString version() const; + QString qualifier() const; + +private: + friend class QDeclarativeDomDocument; + QSharedDataPointer d; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEDOM_P_H diff --git a/src/declarative/qml/qdeclarativedom_p_p.h b/src/declarative/qml/qdeclarativedom_p_p.h new file mode 100644 index 00000000..720d8289 --- /dev/null +++ b/src/declarative/qml/qdeclarativedom_p_p.h @@ -0,0 +1,157 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEDOM_P_P_H +#define QDECLARATIVEDOM_P_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 "private/qdeclarativeparser_p.h" + +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeDomDocumentPrivate : public QSharedData +{ +public: + QDeclarativeDomDocumentPrivate(); + QDeclarativeDomDocumentPrivate(const QDeclarativeDomDocumentPrivate &o) + : QSharedData(o) { qFatal("Not impl"); } + ~QDeclarativeDomDocumentPrivate(); + + QList errors; + QList imports; + QDeclarativeParser::Object *root; + QList automaticSemicolonOffsets; +}; + +class QDeclarativeDomObjectPrivate : public QSharedData +{ +public: + QDeclarativeDomObjectPrivate(); + QDeclarativeDomObjectPrivate(const QDeclarativeDomObjectPrivate &o) + : QSharedData(o) { qFatal("Not impl"); } + ~QDeclarativeDomObjectPrivate(); + + typedef QList > Properties; + Properties properties() const; + Properties properties(QDeclarativeParser::Property *) const; + + QDeclarativeParser::Object *object; +}; + +class QDeclarativeDomPropertyPrivate : public QSharedData +{ +public: + QDeclarativeDomPropertyPrivate(); + QDeclarativeDomPropertyPrivate(const QDeclarativeDomPropertyPrivate &o) + : QSharedData(o) { qFatal("Not impl"); } + ~QDeclarativeDomPropertyPrivate(); + + QByteArray propertyName; + QDeclarativeParser::Property *property; +}; + +class QDeclarativeDomDynamicPropertyPrivate : public QSharedData +{ +public: + QDeclarativeDomDynamicPropertyPrivate(); + QDeclarativeDomDynamicPropertyPrivate(const QDeclarativeDomDynamicPropertyPrivate &o) + : QSharedData(o) { qFatal("Not impl"); } + ~QDeclarativeDomDynamicPropertyPrivate(); + + bool valid; + QDeclarativeParser::Object::DynamicProperty property; +}; + +class QDeclarativeDomValuePrivate : public QSharedData +{ +public: + QDeclarativeDomValuePrivate(); + QDeclarativeDomValuePrivate(const QDeclarativeDomValuePrivate &o) + : QSharedData(o) { qFatal("Not impl"); } + ~QDeclarativeDomValuePrivate(); + + QDeclarativeParser::Property *property; + QDeclarativeParser::Value *value; +}; + +class QDeclarativeDomBasicValuePrivate : public QSharedData +{ +public: + QDeclarativeDomBasicValuePrivate(); + QDeclarativeDomBasicValuePrivate(const QDeclarativeDomBasicValuePrivate &o) + : QSharedData(o) { qFatal("Not impl"); } + ~QDeclarativeDomBasicValuePrivate(); + + QDeclarativeParser::Value *value; +}; + +class QDeclarativeDomImportPrivate : public QSharedData +{ +public: + QDeclarativeDomImportPrivate(); + QDeclarativeDomImportPrivate(const QDeclarativeDomImportPrivate &o) + : QSharedData(o) { qFatal("Not impl"); } + ~QDeclarativeDomImportPrivate(); + + enum Type { Library, File }; + + Type type; + QString uri; + QString version; + QString qualifier; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVEDOM_P_P_H + diff --git a/src/declarative/qml/qdeclarativeengine.cpp b/src/declarative/qml/qdeclarativeengine.cpp new file mode 100644 index 00000000..39a1733e --- /dev/null +++ b/src/declarative/qml/qdeclarativeengine.cpp @@ -0,0 +1,2523 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativeengine_p.h" +#include "qdeclarativeengine.h" + +#include "private/qdeclarativecontext_p.h" +#include "private/qdeclarativecompiler_p.h" +#include "private/qdeclarativeglobalscriptclass_p.h" +#include "qdeclarative.h" +#include "qdeclarativecontext.h" +#include "qdeclarativeexpression.h" +#include "qdeclarativecomponent.h" +#include "private/qdeclarativebinding_p_p.h" +#include "private/qdeclarativevme_p.h" +#include "private/qdeclarativeenginedebugservice_p.h" +#include "private/qdeclarativestringconverters_p.h" +#include "private/qdeclarativexmlhttprequest_p.h" +#include "private/qdeclarativesqldatabase_p.h" +#include "private/qdeclarativetypenamescriptclass_p.h" +#include "private/qdeclarativelistscriptclass_p.h" +#include "qdeclarativescriptstring.h" +#include "private/qdeclarativeglobal_p.h" +#include "private/qdeclarativeworkerscript_p.h" +#include "private/qdeclarativecomponent_p.h" +#include "qdeclarativenetworkaccessmanagerfactory.h" +#include "qdeclarativeimageprovider.h" +#include "private/qdeclarativedirparser_p.h" +#include "qdeclarativeextensioninterface.h" +#include "private/qdeclarativelist_p.h" +#include "private/qdeclarativetypenamecache_p.h" +#include "private/qdeclarativeinclude_p.h" +#include "private/qdeclarativenotifier_p.h" +#include "private/qdeclarativedebugtrace_p.h" +#include "private/qdeclarativeapplication_p.h" +#include "private/qjsdebugservice_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#ifdef Q_OS_WIN // for %APPDATA% +#include +#include +#include + +#define CSIDL_APPDATA 0x001a // \Application Data +#endif + +Q_DECLARE_METATYPE(QDeclarativeProperty) + +QT_BEGIN_NAMESPACE + +/*! + \qmlclass QtObject QObject + \ingroup qml-utility-elements + \since 4.7 + \brief The QtObject element is the most basic element in QML. + + The QtObject element is a non-visual element which contains only the + objectName property. + + It can be useful to create a QtObject if you need an extremely + lightweight element to enclose a set of custom properties: + + \snippet doc/src/snippets/declarative/qtobject.qml 0 + + It can also be useful for C++ integration, as it is just a plain + QObject. See the QObject documentation for further details. +*/ +/*! + \qmlproperty string QML:QtObject::objectName + This property holds the QObject::objectName for this specific object instance. + + This allows a C++ application to locate an item within a QML component + using the QObject::findChild() method. For example, the following C++ + application locates the child \l Rectangle item and dynamically changes its + \c color value: + + \qml + // MyRect.qml + + import QtQuick 1.0 + + Item { + width: 200; height: 200 + + Rectangle { + anchors.fill: parent + color: "red" + objectName: "myRect" + } + } + \endqml + + \code + // main.cpp + + QDeclarativeView view; + view.setSource(QUrl::fromLocalFile("MyRect.qml")); + view.show(); + + QDeclarativeItem *item = view.rootObject()->findChild("myRect"); + if (item) + item->setProperty("color", QColor(Qt::yellow)); + \endcode +*/ + +struct StaticQtMetaObject : public QObject +{ + static const QMetaObject *get() + { return &static_cast (0)->staticQtMetaObject; } +}; + +static bool qt_QmlQtModule_registered = false; +bool QDeclarativeEnginePrivate::qml_debugging_enabled = false; + +void QDeclarativeEnginePrivate::defineModule() +{ + qmlRegisterType("QtQuick",1,0,"Component"); + qmlRegisterType("QtQuick",1,0,"QtObject"); + qmlRegisterType("QtQuick",1,0,"WorkerScript"); + +#ifndef QT_NO_IMPORT_QT47_QML + qmlRegisterType("Qt",4,7,"Component"); + qmlRegisterType("Qt",4,7,"QtObject"); + qmlRegisterType("Qt",4,7,"WorkerScript"); +#endif + + qmlRegisterType(); +} + +/*! +\qmlclass QML:Qt QDeclarativeEnginePrivate + \ingroup qml-utility-elements +\brief The QML global Qt object provides useful enums and functions from Qt. + +\keyword QmlGlobalQtObject + +\brief The \c Qt object provides useful enums and functions from Qt, for use in all QML files. + +The \c Qt object is a global object with utility functions, properties and enums. + +It is not instantiable; to use it, call the members of the global \c Qt object directly. +For example: + +\qml +import QtQuick 1.0 + +Text { + color: Qt.rgba(1, 0, 0, 1) + text: Qt.md5("hello, world") +} +\endqml + + +\section1 Enums + +The Qt object contains the enums available in the \l {Qt Namespace}. For example, you can access +the \l Qt::LeftButton and \l Qt::RightButton enum values as \c Qt.LeftButton and \c Qt.RightButton. + + +\section1 Types +The Qt object also contains helper functions for creating objects of specific +data types. This is primarily useful when setting the properties of an item +when the property has one of the following types: + +\list +\o \c color - use \l{QML:Qt::rgba()}{Qt.rgba()}, \l{QML:Qt::hsla()}{Qt.hsla()}, \l{QML:Qt::darker()}{Qt.darker()}, \l{QML:Qt::lighter()}{Qt.lighter()} or \l{QML:Qt::tint()}{Qt.tint()} +\o \c rect - use \l{QML:Qt::rect()}{Qt.rect()} +\o \c point - use \l{QML:Qt::point()}{Qt.point()} +\o \c size - use \l{QML:Qt::size()}{Qt.size()} +\o \c vector3d - use \l{QML:Qt::vector3d()}{Qt.vector3d()} +\endlist + +There are also string based constructors for these types. See \l{qdeclarativebasictypes.html}{QML Basic Types} for more information. + +\section1 Date/Time Formatters + +The Qt object contains several functions for formatting QDateTime, QDate and QTime values. + +\list + \o \l{QML:Qt::formatDateTime}{string Qt.formatDateTime(datetime date, variant format)} + \o \l{QML:Qt::formatDate}{string Qt.formatDate(datetime date, variant format)} + \o \l{QML:Qt::formatTime}{string Qt.formatTime(datetime date, variant format)} +\endlist + +The format specification is described at \l{QML:Qt::formatDateTime}{Qt.formatDateTime}. + + +\section1 Dynamic Object Creation +The following functions on the global object allow you to dynamically create QML +items from files or strings. See \l{Dynamic Object Management in QML} for an overview +of their use. + +\list + \o \l{QML:Qt::createComponent()}{object Qt.createComponent(url)} + \o \l{QML:Qt::createQmlObject()}{object Qt.createQmlObject(string qml, object parent, string filepath)} +\endlist +*/ + + +/*! + \qmlproperty object QML:Qt::application + \since QtQuick 1.1 + + The \c application object provides access to global application state + properties shared by many QML components. + + Its properties are: + + \table + \row + \o \c application.active + \o + This read-only property indicates whether the application is the top-most and focused + application, and the user is able to interact with the application. The property + is false when the application is in the background, the device keylock or screen + saver is active, the screen backlight is turned off, or the global system dialog + is being displayed on top of the application. It can be used for stopping and + pausing animations, timers and active processing of data in order to save device + battery power and free device memory and processor load when the application is not + active. + + \row + \o \c application.layoutDirection + \o + This read-only property can be used to query the default layout direction of the + application. On system start-up, the default layout direction depends on the + application's language. The property has a value of \c Qt.RightToLeft in locales + where text and graphic elements are read from right to left, and \c Qt.LeftToRight + where the reading direction flows from left to right. You can bind to this + property to customize your application layouts to support both layout directions. + + Possible values are: + + \list + \o Qt.LeftToRight - Text and graphics elements should be positioned + from left to right. + \o Qt.RightToLeft - Text and graphics elements should be positioned + from right to left. + \endlist + \endtable + + The following example uses the \c application object to indicate + whether the application is currently active: + + \snippet doc/src/snippets/declarative/application.qml document + +*/ + + +/*! +\qmlmethod object Qt::include(string url, jsobject callback) + +Includes another JavaScript file. This method can only be used from within JavaScript files, +and not regular QML files. + +This imports all functions from \a url into the current script's namespace. + +Qt.include() returns an object that describes the status of the operation. The object has +a single property, \c {status}, that is set to one of the following values: + +\table +\header \o Symbol \o Value \o Description +\row \o result.OK \o 0 \o The include completed successfully. +\row \o result.LOADING \o 1 \o Data is being loaded from the network. +\row \o result.NETWORK_ERROR \o 2 \o A network error occurred while fetching the url. +\row \o result.EXCEPTION \o 3 \o A JavaScript exception occurred while executing the included code. +An additional \c exception property will be set in this case. +\endtable + +The \c status property will be updated as the operation progresses. + +If provided, \a callback is invoked when the operation completes. The callback is passed +the same object as is returned from the Qt.include() call. +*/ +// Qt.include() is implemented in qdeclarativeinclude.cpp + + +QDeclarativeEnginePrivate::QDeclarativeEnginePrivate(QDeclarativeEngine *e) +: captureProperties(false), rootContext(0), isDebugging(false), + outputWarningsToStdErr(true), contextClass(0), sharedContext(0), sharedScope(0), + objectClass(0), valueTypeClass(0), globalClass(0), cleanup(0), erroredBindings(0), + inProgressCreations(0), scriptEngine(this), workerScriptEngine(0), componentAttached(0), + inBeginCreate(false), networkAccessManager(0), networkAccessManagerFactory(0), + typeLoader(e), importDatabase(e), uniqueId(1) +{ + if (!qt_QmlQtModule_registered) { + qt_QmlQtModule_registered = true; + QDeclarativeItemModule::defineModule(); + QDeclarativeUtilModule::defineModule(); + QDeclarativeEnginePrivate::defineModule(); + QDeclarativeValueTypeFactory::registerValueTypes(); + } + globalClass = new QDeclarativeGlobalScriptClass(&scriptEngine); +} + +/*! + \qmlmethod url Qt::resolvedUrl(url url) + Returns \a url resolved relative to the URL of the caller. +*/ +QUrl QDeclarativeScriptEngine::resolvedUrl(QScriptContext *context, const QUrl& url) +{ + if (p) { + QDeclarativeContextData *ctxt = p->getContext(context); + if (ctxt) + return ctxt->resolvedUrl(url); + else + return p->getUrl(context).resolved(url); + } + return baseUrl.resolved(url); +} + +QDeclarativeScriptEngine::QDeclarativeScriptEngine(QDeclarativeEnginePrivate *priv) +: p(priv), sqlQueryClass(0), namedNodeMapClass(0), nodeListClass(0) +{ + // Note that all documentation for stuff put on the global object goes in + // doc/src/declarative/globalobject.qdoc + + bool mainthread = priv != 0; + + QScriptValue qtObject = + newQMetaObject(StaticQtMetaObject::get()); + globalObject().setProperty(QLatin1String("Qt"), qtObject); + +#ifndef QT_NO_DESKTOPSERVICES + offlineStoragePath = QDesktopServices::storageLocation(QDesktopServices::DataLocation).replace(QLatin1Char('/'), QDir::separator()) + + QDir::separator() + QLatin1String("QML") + + QDir::separator() + QLatin1String("OfflineStorage"); +#endif + +#ifndef QT_NO_XMLSTREAMREADER + qt_add_qmlxmlhttprequest(this); +#endif + qt_add_qmlsqldatabase(this); + // XXX A Multimedia "Qt.Sound" class also needs to be made available, + // XXX but we don't want a dependency in that cirection. + // XXX When the above a done some better way, that way should also be + // XXX used to add Qt.Sound class. + + //types + if (mainthread) + qtObject.setProperty(QLatin1String("include"), newFunction(QDeclarativeInclude::include, 2)); + else + qtObject.setProperty(QLatin1String("include"), newFunction(QDeclarativeInclude::worker_include, 2)); + + qtObject.setProperty(QLatin1String("isQtObject"), newFunction(QDeclarativeEnginePrivate::isQtObject, 1)); + qtObject.setProperty(QLatin1String("rgba"), newFunction(QDeclarativeEnginePrivate::rgba, 4)); + qtObject.setProperty(QLatin1String("hsla"), newFunction(QDeclarativeEnginePrivate::hsla, 4)); + qtObject.setProperty(QLatin1String("rect"), newFunction(QDeclarativeEnginePrivate::rect, 4)); + qtObject.setProperty(QLatin1String("point"), newFunction(QDeclarativeEnginePrivate::point, 2)); + qtObject.setProperty(QLatin1String("size"), newFunction(QDeclarativeEnginePrivate::size, 2)); + qtObject.setProperty(QLatin1String("vector3d"), newFunction(QDeclarativeEnginePrivate::vector3d, 3)); + + if (mainthread) { + //color helpers + qtObject.setProperty(QLatin1String("lighter"), newFunction(QDeclarativeEnginePrivate::lighter, 1)); + qtObject.setProperty(QLatin1String("darker"), newFunction(QDeclarativeEnginePrivate::darker, 1)); + qtObject.setProperty(QLatin1String("tint"), newFunction(QDeclarativeEnginePrivate::tint, 2)); + } + +#ifndef QT_NO_DATESTRING + //date/time formatting + qtObject.setProperty(QLatin1String("formatDate"),newFunction(QDeclarativeEnginePrivate::formatDate, 2)); + qtObject.setProperty(QLatin1String("formatTime"),newFunction(QDeclarativeEnginePrivate::formatTime, 2)); + qtObject.setProperty(QLatin1String("formatDateTime"),newFunction(QDeclarativeEnginePrivate::formatDateTime, 2)); +#endif + + //misc methods + qtObject.setProperty(QLatin1String("openUrlExternally"),newFunction(QDeclarativeEnginePrivate::desktopOpenUrl, 1)); + qtObject.setProperty(QLatin1String("fontFamilies"),newFunction(QDeclarativeEnginePrivate::fontFamilies, 0)); + qtObject.setProperty(QLatin1String("md5"),newFunction(QDeclarativeEnginePrivate::md5, 1)); + qtObject.setProperty(QLatin1String("btoa"),newFunction(QDeclarativeEnginePrivate::btoa, 1)); + qtObject.setProperty(QLatin1String("atob"),newFunction(QDeclarativeEnginePrivate::atob, 1)); + qtObject.setProperty(QLatin1String("quit"), newFunction(QDeclarativeEnginePrivate::quit, 0)); + qtObject.setProperty(QLatin1String("resolvedUrl"),newFunction(QDeclarativeScriptEngine::resolvedUrl, 1)); + + if (mainthread) { + qtObject.setProperty(QLatin1String("createQmlObject"), + newFunction(QDeclarativeEnginePrivate::createQmlObject, 1)); + qtObject.setProperty(QLatin1String("createComponent"), + newFunction(QDeclarativeEnginePrivate::createComponent, 1)); + } + + //firebug/webkit compat + QScriptValue consoleObject = newObject(); + consoleObject.setProperty(QLatin1String("log"),newFunction(QDeclarativeEnginePrivate::consoleLog, 1)); + consoleObject.setProperty(QLatin1String("debug"),newFunction(QDeclarativeEnginePrivate::consoleLog, 1)); + globalObject().setProperty(QLatin1String("console"), consoleObject); + + // translation functions need to be installed + // before the global script class is constructed (QTBUG-6437) + installTranslatorFunctions(); +} + +QDeclarativeScriptEngine::~QDeclarativeScriptEngine() +{ + delete sqlQueryClass; + delete nodeListClass; + delete namedNodeMapClass; +} + +QScriptValue QDeclarativeScriptEngine::resolvedUrl(QScriptContext *ctxt, QScriptEngine *engine) +{ + QString arg = ctxt->argument(0).toString(); + QUrl r = QDeclarativeScriptEngine::get(engine)->resolvedUrl(ctxt,QUrl(arg)); + return QScriptValue(r.toString()); +} + +QNetworkAccessManager *QDeclarativeScriptEngine::networkAccessManager() +{ + return p->getNetworkAccessManager(); +} + +QDeclarativeEnginePrivate::~QDeclarativeEnginePrivate() +{ + Q_ASSERT(inProgressCreations == 0); + Q_ASSERT(bindValues.isEmpty()); + Q_ASSERT(parserStatus.isEmpty()); + + while (cleanup) { + QDeclarativeCleanup *c = cleanup; + cleanup = c->next; + if (cleanup) cleanup->prev = &cleanup; + c->next = 0; + c->prev = 0; + c->clear(); + } + + delete rootContext; + rootContext = 0; + delete contextClass; + contextClass = 0; + delete objectClass; + objectClass = 0; + delete valueTypeClass; + valueTypeClass = 0; + delete typeNameClass; + typeNameClass = 0; + delete listClass; + listClass = 0; + delete globalClass; + globalClass = 0; + + for(QHash::ConstIterator iter = m_compositeTypes.constBegin(); iter != m_compositeTypes.constEnd(); ++iter) + (*iter)->release(); + for(QHash::Iterator iter = propertyCache.begin(); iter != propertyCache.end(); ++iter) + (*iter)->release(); + for(QHash, QDeclarativePropertyCache *>::Iterator iter = typePropertyCache.begin(); iter != typePropertyCache.end(); ++iter) + (*iter)->release(); + +} + +void QDeclarativeEnginePrivate::clear(SimpleList &bvs) +{ + bvs.clear(); +} + +void QDeclarativeEnginePrivate::clear(SimpleList &pss) +{ + for (int ii = 0; ii < pss.count; ++ii) { + QDeclarativeParserStatus *ps = pss.at(ii); + if(ps) + ps->d = 0; + } + pss.clear(); +} + +void QDeclarativePrivate::qdeclarativeelement_destructor(QObject *o) +{ + QObjectPrivate *p = QObjectPrivate::get(o); + if (p->declarativeData) { + QDeclarativeData *d = static_cast(p->declarativeData); + if (d->ownContext && d->context) { + d->context->destroy(); + d->context = 0; + } + } +} + +void QDeclarativeData::destroyed(QAbstractDeclarativeData *d, QObject *o) +{ + static_cast(d)->destroyed(o); +} + +void QDeclarativeData::parentChanged(QAbstractDeclarativeData *d, QObject *o, QObject *p) +{ + static_cast(d)->parentChanged(o, p); +} + +void QDeclarativeData::objectNameChanged(QAbstractDeclarativeData *d, QObject *o) +{ + static_cast(d)->objectNameChanged(o); +} + +void QDeclarativeEnginePrivate::init() +{ + Q_Q(QDeclarativeEngine); + qRegisterMetaType("QVariant"); + qRegisterMetaType("QDeclarativeScriptString"); + qRegisterMetaType("QScriptValue"); + qRegisterMetaType("QDeclarativeComponent::Status"); + + QDeclarativeData::init(); + + contextClass = new QDeclarativeContextScriptClass(q); + objectClass = new QDeclarativeObjectScriptClass(q); + valueTypeClass = new QDeclarativeValueTypeScriptClass(q); + typeNameClass = new QDeclarativeTypeNameScriptClass(q); + listClass = new QDeclarativeListScriptClass(q); + rootContext = new QDeclarativeContext(q,true); + + QScriptValue applicationObject = objectClass->newQObject(new QDeclarativeApplication(q)); + scriptEngine.globalObject().property(QLatin1String("Qt")).setProperty(QLatin1String("application"), applicationObject); + + if (QCoreApplication::instance()->thread() == q->thread() && + QDeclarativeEngineDebugService::isDebuggingEnabled()) { + isDebugging = true; + QDeclarativeEngineDebugService::instance()->addEngine(q); + QJSDebugService::instance()->addEngine(q); + } +} + +QDeclarativeWorkerScriptEngine *QDeclarativeEnginePrivate::getWorkerScriptEngine() +{ + Q_Q(QDeclarativeEngine); + if (!workerScriptEngine) + workerScriptEngine = new QDeclarativeWorkerScriptEngine(q); + return workerScriptEngine; +} + +/*! + \class QDeclarativeEngine + \since 4.7 + \brief The QDeclarativeEngine class provides an environment for instantiating QML components. + \mainclass + + Each QML component is instantiated in a QDeclarativeContext. + QDeclarativeContext's are essential for passing data to QML + components. In QML, contexts are arranged hierarchically and this + hierarchy is managed by the QDeclarativeEngine. + + Prior to creating any QML components, an application must have + created a QDeclarativeEngine to gain access to a QML context. The + following example shows how to create a simple Text item. + + \code + QDeclarativeEngine engine; + QDeclarativeComponent component(&engine); + component.setData("import QtQuick 1.0\nText { text: \"Hello world!\" }", QUrl()); + QDeclarativeItem *item = qobject_cast(component.create()); + + //add item to view, etc + ... + \endcode + + In this case, the Text item will be created in the engine's + \l {QDeclarativeEngine::rootContext()}{root context}. + + \sa QDeclarativeComponent QDeclarativeContext +*/ + +/*! + Create a new QDeclarativeEngine with the given \a parent. +*/ +QDeclarativeEngine::QDeclarativeEngine(QObject *parent) +: QObject(*new QDeclarativeEnginePrivate(this), parent) +{ + Q_D(QDeclarativeEngine); + d->init(); +} + +/*! + Destroys the QDeclarativeEngine. + + Any QDeclarativeContext's created on this engine will be + invalidated, but not destroyed (unless they are parented to the + QDeclarativeEngine object). +*/ +QDeclarativeEngine::~QDeclarativeEngine() +{ + Q_D(QDeclarativeEngine); + if (d->isDebugging) { + QDeclarativeEngineDebugService::instance()->remEngine(this); + QJSDebugService::instance()->removeEngine(this); + } +} + +/*! \fn void QDeclarativeEngine::quit() + This signal is emitted when the QML loaded by the engine would like to quit. + */ + +/*! \fn void QDeclarativeEngine::warnings(const QList &warnings) + This signal is emitted when \a warnings messages are generated by QML. + */ + +/*! + Clears the engine's internal component cache. + + Normally the QDeclarativeEngine caches components loaded from qml + files. This method clears this cache and forces the component to be + reloaded. + */ +void QDeclarativeEngine::clearComponentCache() +{ + Q_D(QDeclarativeEngine); + d->typeLoader.clearCache(); +} + +/*! + Returns the engine's root context. + + The root context is automatically created by the QDeclarativeEngine. + Data that should be available to all QML component instances + instantiated by the engine should be put in the root context. + + Additional data that should only be available to a subset of + component instances should be added to sub-contexts parented to the + root context. +*/ +QDeclarativeContext *QDeclarativeEngine::rootContext() const +{ + Q_D(const QDeclarativeEngine); + return d->rootContext; +} + +/*! + Sets the \a factory to use for creating QNetworkAccessManager(s). + + QNetworkAccessManager is used for all network access by QML. By + implementing a factory it is possible to create custom + QNetworkAccessManager with specialized caching, proxy and cookie + support. + + The factory must be set before executing the engine. +*/ +void QDeclarativeEngine::setNetworkAccessManagerFactory(QDeclarativeNetworkAccessManagerFactory *factory) +{ + Q_D(QDeclarativeEngine); + QMutexLocker locker(&d->mutex); + d->networkAccessManagerFactory = factory; +} + +/*! + Returns the current QDeclarativeNetworkAccessManagerFactory. + + \sa setNetworkAccessManagerFactory() +*/ +QDeclarativeNetworkAccessManagerFactory *QDeclarativeEngine::networkAccessManagerFactory() const +{ + Q_D(const QDeclarativeEngine); + return d->networkAccessManagerFactory; +} + +QNetworkAccessManager *QDeclarativeEnginePrivate::createNetworkAccessManager(QObject *parent) const +{ + QMutexLocker locker(&mutex); + QNetworkAccessManager *nam; + if (networkAccessManagerFactory) { + nam = networkAccessManagerFactory->create(parent); + } else { + nam = new QNetworkAccessManager(parent); + } + + return nam; +} + +QNetworkAccessManager *QDeclarativeEnginePrivate::getNetworkAccessManager() const +{ + Q_Q(const QDeclarativeEngine); + if (!networkAccessManager) + networkAccessManager = createNetworkAccessManager(const_cast(q)); + return networkAccessManager; +} + +/*! + Returns a common QNetworkAccessManager which can be used by any QML + element instantiated by this engine. + + If a QDeclarativeNetworkAccessManagerFactory has been set and a + QNetworkAccessManager has not yet been created, the + QDeclarativeNetworkAccessManagerFactory will be used to create the + QNetworkAccessManager; otherwise the returned QNetworkAccessManager + will have no proxy or cache set. + + \sa setNetworkAccessManagerFactory() +*/ +QNetworkAccessManager *QDeclarativeEngine::networkAccessManager() const +{ + Q_D(const QDeclarativeEngine); + return d->getNetworkAccessManager(); +} + +/*! + + Sets the \a provider to use for images requested via the \e + image: url scheme, with host \a providerId. The QDeclarativeEngine + takes ownership of \a provider. + + Image providers enable support for pixmap and threaded image + requests. See the QDeclarativeImageProvider documentation for details on + implementing and using image providers. + + All required image providers should be added to the engine before any + QML sources files are loaded. + + \sa removeImageProvider() +*/ +void QDeclarativeEngine::addImageProvider(const QString &providerId, QDeclarativeImageProvider *provider) +{ + Q_D(QDeclarativeEngine); + QMutexLocker locker(&d->mutex); + d->imageProviders.insert(providerId.toLower(), QSharedPointer(provider)); +} + +/*! + Returns the QDeclarativeImageProvider set for \a providerId. +*/ +QDeclarativeImageProvider *QDeclarativeEngine::imageProvider(const QString &providerId) const +{ + Q_D(const QDeclarativeEngine); + QMutexLocker locker(&d->mutex); + return d->imageProviders.value(providerId).data(); +} + +/*! + Removes the QDeclarativeImageProvider for \a providerId. + + Returns the provider if it was found; otherwise returns 0. + + \sa addImageProvider() +*/ +void QDeclarativeEngine::removeImageProvider(const QString &providerId) +{ + Q_D(QDeclarativeEngine); + QMutexLocker locker(&d->mutex); + d->imageProviders.take(providerId); +} + +QDeclarativeImageProvider::ImageType QDeclarativeEnginePrivate::getImageProviderType(const QUrl &url) +{ + QMutexLocker locker(&mutex); + QSharedPointer provider = imageProviders.value(url.host()); + locker.unlock(); + if (provider) + return provider->imageType(); + return static_cast(-1); +} + +QImage QDeclarativeEnginePrivate::getImageFromProvider(const QUrl &url, QSize *size, const QSize& req_size) +{ + QMutexLocker locker(&mutex); + QImage image; + QSharedPointer provider = imageProviders.value(url.host()); + locker.unlock(); + if (provider) { + QString imageId = url.toString(QUrl::RemoveScheme | QUrl::RemoveAuthority).mid(1); + image = provider->requestImage(imageId, size, req_size); + } + return image; +} + +QPixmap QDeclarativeEnginePrivate::getPixmapFromProvider(const QUrl &url, QSize *size, const QSize& req_size) +{ + QMutexLocker locker(&mutex); + QPixmap pixmap; + QSharedPointer provider = imageProviders.value(url.host()); + locker.unlock(); + if (provider) { + QString imageId = url.toString(QUrl::RemoveScheme | QUrl::RemoveAuthority).mid(1); + pixmap = provider->requestPixmap(imageId, size, req_size); + } + return pixmap; +} + +/*! + Return the base URL for this engine. The base URL is only used to + resolve components when a relative URL is passed to the + QDeclarativeComponent constructor. + + If a base URL has not been explicitly set, this method returns the + application's current working directory. + + \sa setBaseUrl() +*/ +QUrl QDeclarativeEngine::baseUrl() const +{ + Q_D(const QDeclarativeEngine); + if (d->baseUrl.isEmpty()) { + return QUrl::fromLocalFile(QDir::currentPath() + QDir::separator()); + } else { + return d->baseUrl; + } +} + +/*! + Set the base URL for this engine to \a url. + + \sa baseUrl() +*/ +void QDeclarativeEngine::setBaseUrl(const QUrl &url) +{ + Q_D(QDeclarativeEngine); + d->baseUrl = url; +} + +/*! + Returns true if warning messages will be output to stderr in addition + to being emitted by the warnings() signal, otherwise false. + + The default value is true. +*/ +bool QDeclarativeEngine::outputWarningsToStandardError() const +{ + Q_D(const QDeclarativeEngine); + return d->outputWarningsToStdErr; +} + +/*! + Set whether warning messages will be output to stderr to \a enabled. + + If \a enabled is true, any warning messages generated by QML will be + output to stderr and emitted by the warnings() signal. If \a enabled + is false, on the warnings() signal will be emitted. This allows + applications to handle warning output themselves. + + The default value is true. +*/ +void QDeclarativeEngine::setOutputWarningsToStandardError(bool enabled) +{ + Q_D(QDeclarativeEngine); + d->outputWarningsToStdErr = enabled; +} + +/*! + Returns the QDeclarativeContext for the \a object, or 0 if no + context has been set. + + When the QDeclarativeEngine instantiates a QObject, the context is + set automatically. + */ +QDeclarativeContext *QDeclarativeEngine::contextForObject(const QObject *object) +{ + if(!object) + return 0; + + QObjectPrivate *priv = QObjectPrivate::get(const_cast(object)); + + QDeclarativeData *data = + static_cast(priv->declarativeData); + + if (!data) + return 0; + else if (data->outerContext) + return data->outerContext->asQDeclarativeContext(); + else + return 0; +} + +/*! + Sets the QDeclarativeContext for the \a object to \a context. + If the \a object already has a context, a warning is + output, but the context is not changed. + + When the QDeclarativeEngine instantiates a QObject, the context is + set automatically. + */ +void QDeclarativeEngine::setContextForObject(QObject *object, QDeclarativeContext *context) +{ + if (!object || !context) + return; + + QDeclarativeData *data = QDeclarativeData::get(object, true); + if (data->context) { + qWarning("QDeclarativeEngine::setContextForObject(): Object already has a QDeclarativeContext"); + return; + } + + QDeclarativeContextData *contextData = QDeclarativeContextData::get(context); + contextData->addObject(object); +} + +/*! + \enum QDeclarativeEngine::ObjectOwnership + + Ownership controls whether or not QML automatically destroys the + QObject when the object is garbage collected by the JavaScript + engine. The two ownership options are: + + \value CppOwnership The object is owned by C++ code, and will + never be deleted by QML. The JavaScript destroy() method cannot be + used on objects with CppOwnership. This option is similar to + QScriptEngine::QtOwnership. + + \value JavaScriptOwnership The object is owned by JavaScript. + When the object is returned to QML as the return value of a method + call or property access, QML will delete the object if there are no + remaining JavaScript references to it and it has no + QObject::parent(). This option is similar to + QScriptEngine::ScriptOwnership. + + Generally an application doesn't need to set an object's ownership + explicitly. QML uses a heuristic to set the default object + ownership. By default, an object that is created by QML has + JavaScriptOwnership. The exception to this are the root objects + created by calling QDeclarativeCompnent::create() or + QDeclarativeComponent::beginCreate() which have CppOwnership by + default. The ownership of these root-level objects is considered to + have been transferred to the C++ caller. + + Objects not-created by QML have CppOwnership by default. The + exception to this is objects returned from a C++ method call. The + ownership of these objects is passed to JavaScript. + + Calling setObjectOwnership() overrides the default ownership + heuristic used by QML. +*/ + +/*! + Sets the \a ownership of \a object. +*/ +void QDeclarativeEngine::setObjectOwnership(QObject *object, ObjectOwnership ownership) +{ + if (!object) + return; + + QDeclarativeData *ddata = QDeclarativeData::get(object, true); + if (!ddata) + return; + + ddata->indestructible = (ownership == CppOwnership)?true:false; + ddata->explicitIndestructibleSet = true; +} + +/*! + Returns the ownership of \a object. +*/ +QDeclarativeEngine::ObjectOwnership QDeclarativeEngine::objectOwnership(QObject *object) +{ + if (!object) + return CppOwnership; + + QDeclarativeData *ddata = QDeclarativeData::get(object, false); + if (!ddata) + return CppOwnership; + else + return ddata->indestructible?CppOwnership:JavaScriptOwnership; +} + +Q_AUTOTEST_EXPORT void qmlExecuteDeferred(QObject *object) +{ + QDeclarativeData *data = QDeclarativeData::get(object); + + if (data && data->deferredComponent) { + if (QDeclarativeDebugService::isDebuggingEnabled()) { + QDeclarativeDebugTrace::startRange(QDeclarativeDebugTrace::Creating); + QDeclarativeType *type = QDeclarativeMetaType::qmlType(object->metaObject()); + QString typeName = type ? QLatin1String(type->qmlTypeName()) : QString::fromLatin1(object->metaObject()->className()); + QDeclarativeDebugTrace::rangeData(QDeclarativeDebugTrace::Creating, typeName); + if (data->outerContext) + QDeclarativeDebugTrace::rangeLocation(QDeclarativeDebugTrace::Creating, data->outerContext->url, data->lineNumber); + } + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(data->context->engine); + + QDeclarativeComponentPrivate::ConstructionState state; + QDeclarativeComponentPrivate::beginDeferred(ep, object, &state); + + data->deferredComponent->release(); + data->deferredComponent = 0; + + QDeclarativeComponentPrivate::complete(ep, &state); + QDeclarativeDebugTrace::endRange(QDeclarativeDebugTrace::Creating); + } +} + +QDeclarativeContext *qmlContext(const QObject *obj) +{ + return QDeclarativeEngine::contextForObject(obj); +} + +QDeclarativeEngine *qmlEngine(const QObject *obj) +{ + QDeclarativeContext *context = QDeclarativeEngine::contextForObject(obj); + return context?context->engine():0; +} + +QObject *qmlAttachedPropertiesObjectById(int id, const QObject *object, bool create) +{ + QDeclarativeData *data = QDeclarativeData::get(object); + if (!data) + return 0; // Attached properties are only on objects created by QML + + QObject *rv = data->hasExtendedData()?data->attachedProperties()->value(id):0; + if (rv || !create) + return rv; + + QDeclarativeAttachedPropertiesFunc pf = QDeclarativeMetaType::attachedPropertiesFuncById(id); + if (!pf) + return 0; + + rv = pf(const_cast(object)); + + if (rv) + data->attachedProperties()->insert(id, rv); + + return rv; +} + +QObject *qmlAttachedPropertiesObject(int *idCache, const QObject *object, + const QMetaObject *attachedMetaObject, bool create) +{ + if (*idCache == -1) + *idCache = QDeclarativeMetaType::attachedPropertiesFuncId(attachedMetaObject); + + if (*idCache == -1 || !object) + return 0; + + return qmlAttachedPropertiesObjectById(*idCache, object, create); +} + +QDeclarativeDebuggingEnabler::QDeclarativeDebuggingEnabler() +{ +#ifndef QDECLARATIVE_NO_DEBUG_PROTOCOL + if (!QDeclarativeEnginePrivate::qml_debugging_enabled) { + qDebug("Qml debugging is enabled. Only use this in a safe environment!"); + } + QDeclarativeEnginePrivate::qml_debugging_enabled = true; +#endif +} + + +class QDeclarativeDataExtended { +public: + QDeclarativeDataExtended(); + ~QDeclarativeDataExtended(); + + QHash attachedProperties; + QDeclarativeNotifier objectNameNotifier; +}; + +QDeclarativeDataExtended::QDeclarativeDataExtended() +{ +} + +QDeclarativeDataExtended::~QDeclarativeDataExtended() +{ +} + +QDeclarativeNotifier *QDeclarativeData::objectNameNotifier() const +{ + if (!extendedData) extendedData = new QDeclarativeDataExtended; + return &extendedData->objectNameNotifier; +} + +QHash *QDeclarativeData::attachedProperties() const +{ + if (!extendedData) extendedData = new QDeclarativeDataExtended; + return &extendedData->attachedProperties; +} + +void QDeclarativeData::destroyed(QObject *object) +{ + if (deferredComponent) + deferredComponent->release(); + + if (nextContextObject) + nextContextObject->prevContextObject = prevContextObject; + if (prevContextObject) + *prevContextObject = nextContextObject; + + QDeclarativeAbstractBinding *binding = bindings; + while (binding) { + QDeclarativeAbstractBinding *next = binding->m_nextBinding; + binding->m_prevBinding = 0; + binding->m_nextBinding = 0; + binding->destroy(); + binding = next; + } + + if (bindingBits) + free(bindingBits); + + if (propertyCache) + propertyCache->release(); + + if (ownContext && context) + context->destroy(); + + while (guards) { + QDeclarativeGuard *guard = static_cast *>(guards); + *guard = (QObject *)0; + guard->objectDestroyed(object); + } + + if (scriptValue) + delete scriptValue; + + if (extendedData) + delete extendedData; + + if (ownMemory) + delete this; +} + +void QDeclarativeData::parentChanged(QObject *, QObject *parent) +{ + if (!parent && scriptValue) { delete scriptValue; scriptValue = 0; } +} + +void QDeclarativeData::objectNameChanged(QObject *) +{ + if (extendedData) objectNameNotifier()->notify(); +} + +bool QDeclarativeData::hasBindingBit(int bit) const +{ + if (bindingBitsSize > bit) + return bindingBits[bit / 32] & (1 << (bit % 32)); + else + return false; +} + +void QDeclarativeData::clearBindingBit(int bit) +{ + if (bindingBitsSize > bit) + bindingBits[bit / 32] &= ~(1 << (bit % 32)); +} + +void QDeclarativeData::setBindingBit(QObject *obj, int bit) +{ + if (bindingBitsSize <= bit) { + int props = obj->metaObject()->propertyCount(); + Q_ASSERT(bit < props); + + int arraySize = (props + 31) / 32; + int oldArraySize = bindingBitsSize / 32; + + bindingBits = (quint32 *)q_check_ptr(realloc(bindingBits, + arraySize * sizeof(quint32))); + + memset(bindingBits + oldArraySize, + 0x00, + sizeof(quint32) * (arraySize - oldArraySize)); + + bindingBitsSize = arraySize * 32; + } + + bindingBits[bit / 32] |= (1 << (bit % 32)); +} + +/*! + Creates a QScriptValue allowing you to use \a object in QML script. + \a engine is the QDeclarativeEngine it is to be created in. + + The QScriptValue returned is a QtScript Object, not a QtScript QObject, due + to the special needs of QML requiring more functionality than a standard + QtScript QObject. +*/ +QScriptValue QDeclarativeEnginePrivate::qmlScriptObject(QObject* object, + QDeclarativeEngine* engine) +{ + QDeclarativeEnginePrivate *enginePriv = QDeclarativeEnginePrivate::get(engine); + return enginePriv->objectClass->newQObject(object); +} + +/*! + Returns the QDeclarativeContext for the executing QScript \a ctxt. +*/ +QDeclarativeContextData *QDeclarativeEnginePrivate::getContext(QScriptContext *ctxt) +{ + QScriptValue scopeNode = QScriptDeclarativeClass::scopeChainValue(ctxt, -3); + Q_ASSERT(scopeNode.isValid()); + Q_ASSERT(QScriptDeclarativeClass::scriptClass(scopeNode) == contextClass); + return contextClass->contextFromValue(scopeNode); +} + +/*! + Returns the QUrl associated with the script \a ctxt for the case that there is + no QDeclarativeContext. +*/ +QUrl QDeclarativeEnginePrivate::getUrl(QScriptContext *ctxt) +{ + QScriptValue scopeNode = QScriptDeclarativeClass::scopeChainValue(ctxt, -3); + Q_ASSERT(scopeNode.isValid()); + Q_ASSERT(QScriptDeclarativeClass::scriptClass(scopeNode) == contextClass); + return contextClass->urlFromValue(scopeNode); +} + +QString QDeclarativeEnginePrivate::urlToLocalFileOrQrc(const QUrl& url) +{ + if (url.scheme().compare(QLatin1String("qrc"), Qt::CaseInsensitive) == 0) { + if (url.authority().isEmpty()) + return QLatin1Char(':') + url.path(); + return QString(); + } + return url.toLocalFile(); +} + +/*! +\qmlmethod object Qt::createComponent(url) + +Returns a \l Component object created using the QML file at the specified \a url, +or \c null if an empty string was given. + +The returned component's \l Component::status property indicates whether the +component was successfully created. If the status is \c Component.Error, +see \l Component::errorString() for an error description. + +Call \l {Component::createObject()}{Component.createObject()} on the returned +component to create an object instance of the component. + +For example: + +\snippet doc/src/snippets/declarative/createComponent-simple.qml 0 + +See \l {Dynamic Object Management in QML} for more information on using this function. + +To create a QML object from an arbitrary string of QML (instead of a file), +use \l{QML:Qt::createQmlObject()}{Qt.createQmlObject()}. +*/ + +QScriptValue QDeclarativeEnginePrivate::createComponent(QScriptContext *ctxt, QScriptEngine *engine) +{ + QDeclarativeEnginePrivate *activeEnginePriv = + static_cast(engine)->p; + QDeclarativeEngine* activeEngine = activeEnginePriv->q_func(); + + if(ctxt->argumentCount() != 1) { + return ctxt->throwError(QLatin1String("Qt.createComponent(): Invalid arguments")); + } else { + + QString arg = ctxt->argument(0).toString(); + if (arg.isEmpty()) + return engine->nullValue(); + QUrl url = QDeclarativeScriptEngine::get(engine)->resolvedUrl(ctxt, QUrl(arg)); + QDeclarativeContextData* context = activeEnginePriv->getContext(ctxt); + QDeclarativeComponent *c = new QDeclarativeComponent(activeEngine, url, activeEngine); + QDeclarativeComponentPrivate::get(c)->creationContext = context; + QDeclarativeData::get(c, true)->setImplicitDestructible(); + return activeEnginePriv->objectClass->newQObject(c, qMetaTypeId()); + } +} + +/*! +\qmlmethod object Qt::createQmlObject(string qml, object parent, string filepath) + +Returns a new object created from the given \a string of QML which will have the specified \a parent, +or \c null if there was an error in creating the object. + +If \a filepath is specified, it will be used for error reporting for the created object. + +Example (where \c parentItem is the id of an existing QML item): + +\snippet doc/src/snippets/declarative/createQmlObject.qml 0 + +In the case of an error, a QtScript Error object is thrown. This object has an additional property, +\c qmlErrors, which is an array of the errors encountered. +Each object in this array has the members \c lineNumber, \c columnNumber, \c fileName and \c message. +For example, if the above snippet had misspelled color as 'colro' then the array would contain an object like the following: +{ "lineNumber" : 1, "columnNumber" : 32, "fileName" : "dynamicSnippet1", "message" : "Cannot assign to non-existent property \"colro\""}. + +Note that this function returns immediately, and therefore may not work if +the \a qml string loads new components (that is, external QML files that have not yet been loaded). +If this is the case, consider using \l{QML:Qt::createComponent()}{Qt.createComponent()} instead. + +See \l {Dynamic Object Management in QML} for more information on using this function. +*/ + +QScriptValue QDeclarativeEnginePrivate::createQmlObject(QScriptContext *ctxt, QScriptEngine *engine) +{ + QDeclarativeEnginePrivate *activeEnginePriv = + static_cast(engine)->p; + QDeclarativeEngine* activeEngine = activeEnginePriv->q_func(); + + if(ctxt->argumentCount() < 2 || ctxt->argumentCount() > 3) + return ctxt->throwError(QLatin1String("Qt.createQmlObject(): Invalid arguments")); + + QDeclarativeContextData* context = activeEnginePriv->getContext(ctxt); + Q_ASSERT(context); + + QString qml = ctxt->argument(0).toString(); + if (qml.isEmpty()) + return engine->nullValue(); + + QUrl url; + if(ctxt->argumentCount() > 2) + url = QUrl(ctxt->argument(2).toString()); + else + url = QUrl(QLatin1String("inline")); + + if (url.isValid() && url.isRelative()) + url = context->resolvedUrl(url); + + QObject *parentArg = activeEnginePriv->objectClass->toQObject(ctxt->argument(1)); + if(!parentArg) + return ctxt->throwError(QLatin1String("Qt.createQmlObject(): Missing parent object")); + + QDeclarativeComponent component(activeEngine); + component.setData(qml.toUtf8(), url); + + if(component.isError()) { + QList errors = component.errors(); + QString errstr = QLatin1String("Qt.createQmlObject() failed to create object: "); + QScriptValue arr = ctxt->engine()->newArray(errors.length()); + int i = 0; + foreach (const QDeclarativeError &error, errors){ + errstr += QLatin1String(" ") + error.toString() + QLatin1String("\n"); + QScriptValue qmlErrObject = ctxt->engine()->newObject(); + qmlErrObject.setProperty(QLatin1String("lineNumber"), QScriptValue(error.line())); + qmlErrObject.setProperty(QLatin1String("columnNumber"), QScriptValue(error.column())); + qmlErrObject.setProperty(QLatin1String("fileName"), QScriptValue(error.url().toString())); + qmlErrObject.setProperty(QLatin1String("message"), QScriptValue(error.description())); + arr.setProperty(i++, qmlErrObject); + } + QScriptValue err = ctxt->throwError(errstr); + err.setProperty(QLatin1String("qmlErrors"),arr); + return err; + } + + if (!component.isReady()) + return ctxt->throwError(QLatin1String("Qt.createQmlObject(): Component is not ready")); + + QObject *obj = component.beginCreate(context->asQDeclarativeContext()); + if(obj) + QDeclarativeData::get(obj, true)->setImplicitDestructible(); + component.completeCreate(); + + if(component.isError()) { + QList errors = component.errors(); + QString errstr = QLatin1String("Qt.createQmlObject() failed to create object: "); + QScriptValue arr = ctxt->engine()->newArray(errors.length()); + int i = 0; + foreach (const QDeclarativeError &error, errors){ + errstr += QLatin1String(" ") + error.toString() + QLatin1String("\n"); + QScriptValue qmlErrObject = ctxt->engine()->newObject(); + qmlErrObject.setProperty(QLatin1String("lineNumber"), QScriptValue(error.line())); + qmlErrObject.setProperty(QLatin1String("columnNumber"), QScriptValue(error.column())); + qmlErrObject.setProperty(QLatin1String("fileName"), QScriptValue(error.url().toString())); + qmlErrObject.setProperty(QLatin1String("message"), QScriptValue(error.description())); + arr.setProperty(i++, qmlErrObject); + } + QScriptValue err = ctxt->throwError(errstr); + err.setProperty(QLatin1String("qmlErrors"),arr); + return err; + } + + Q_ASSERT(obj); + + obj->setParent(parentArg); + + QList functions = QDeclarativeMetaType::parentFunctions(); + for (int ii = 0; ii < functions.count(); ++ii) { + if (QDeclarativePrivate::Parented == functions.at(ii)(obj, parentArg)) + break; + } + + QDeclarativeData::get(obj, true)->setImplicitDestructible(); + return activeEnginePriv->objectClass->newQObject(obj, QMetaType::QObjectStar); +} + +/*! +\qmlmethod bool Qt::isQtObject(object) +Returns true if \c object is a valid reference to a Qt or QML object, otherwise false. +*/ +QScriptValue QDeclarativeEnginePrivate::isQtObject(QScriptContext *ctxt, QScriptEngine *engine) +{ + if (ctxt->argumentCount() == 0) + return QScriptValue(engine, false); + + return QScriptValue(engine, 0 != ctxt->argument(0).toQObject()); +} + +/*! +\qmlmethod Qt::vector3d(real x, real y, real z) +Returns a Vector3D with the specified \c x, \c y and \c z. +*/ +QScriptValue QDeclarativeEnginePrivate::vector3d(QScriptContext *ctxt, QScriptEngine *engine) +{ + if(ctxt->argumentCount() != 3) + return ctxt->throwError(QLatin1String("Qt.vector(): Invalid arguments")); + qsreal x = ctxt->argument(0).toNumber(); + qsreal y = ctxt->argument(1).toNumber(); + qsreal z = ctxt->argument(2).toNumber(); + return QDeclarativeEnginePrivate::get(engine)->scriptValueFromVariant(QVariant::fromValue(QVector3D(x, y, z))); +} + +/*! +\qmlmethod string Qt::formatDate(datetime date, variant format) + +Returns a string representation of \c date, optionally formatted according +to \c format. + +The \a date parameter may be a JavaScript \c Date object, a \l{date}{date} +property, a QDate, or QDateTime value. The \a format parameter may be any of +the possible format values as described for +\l{QML:Qt::formatDateTime()}{Qt.formatDateTime()}. + +If \a format is not specified, \a date is formatted using +\l {Qt::DefaultLocaleShortDate}{Qt.DefaultLocaleShortDate}. +*/ +#ifndef QT_NO_DATESTRING +QScriptValue QDeclarativeEnginePrivate::formatDate(QScriptContext*ctxt, QScriptEngine*engine) +{ + int argCount = ctxt->argumentCount(); + if(argCount == 0 || argCount > 2) + return ctxt->throwError(QLatin1String("Qt.formatDate(): Invalid arguments")); + + QDate date = ctxt->argument(0).toDateTime().date(); + Qt::DateFormat enumFormat = Qt::DefaultLocaleShortDate; + if (argCount == 2) { + QScriptValue formatArg = ctxt->argument(1); + if (formatArg.isString()) { + QString format = formatArg.toString(); + return engine->newVariant(QVariant::fromValue(date.toString(format))); + } else if (formatArg.isNumber()) { + enumFormat = Qt::DateFormat(formatArg.toUInt32()); + } else { + return ctxt->throwError(QLatin1String("Qt.formatDate(): Invalid date format")); + } + } + return engine->newVariant(QVariant::fromValue(date.toString(enumFormat))); +} + +/*! +\qmlmethod string Qt::formatTime(datetime time, variant format) + +Returns a string representation of \c time, optionally formatted according to +\c format. + +The \a time parameter may be a JavaScript \c Date object, a QTime, or QDateTime +value. The \a format parameter may be any of the possible format values as +described for \l{QML:Qt::formatDateTime()}{Qt.formatDateTime()}. + +If \a format is not specified, \a time is formatted using +\l {Qt::DefaultLocaleShortDate}{Qt.DefaultLocaleShortDate}. +*/ +QScriptValue QDeclarativeEnginePrivate::formatTime(QScriptContext*ctxt, QScriptEngine*engine) +{ + int argCount = ctxt->argumentCount(); + if(argCount == 0 || argCount > 2) + return ctxt->throwError(QLatin1String("Qt.formatTime(): Invalid arguments")); + + QTime time; + QScriptValue sv = ctxt->argument(0); + if (sv.isDate()) + time = sv.toDateTime().time(); + else if (sv.toVariant().type() == QVariant::Time) + time = sv.toVariant().toTime(); + + Qt::DateFormat enumFormat = Qt::DefaultLocaleShortDate; + if (argCount == 2) { + QScriptValue formatArg = ctxt->argument(1); + if (formatArg.isString()) { + QString format = formatArg.toString(); + return engine->newVariant(QVariant::fromValue(time.toString(format))); + } else if (formatArg.isNumber()) { + enumFormat = Qt::DateFormat(formatArg.toUInt32()); + } else { + return ctxt->throwError(QLatin1String("Qt.formatTime(): Invalid time format")); + } + } + return engine->newVariant(QVariant::fromValue(time.toString(enumFormat))); +} + +/*! +\qmlmethod string Qt::formatDateTime(datetime dateTime, variant format) + +Returns a string representation of \c datetime, optionally formatted according to +\c format. + +The \a date parameter may be a JavaScript \c Date object, a \l{date}{date} +property, a QDate, QTime, or QDateTime value. + +If \a format is not provided, \a dateTime is formatted using +\l {Qt::DefaultLocaleShortDate}{Qt.DefaultLocaleShortDate}. Otherwise, +\a format should be either. + +\list +\o One of the Qt::DateFormat enumeration values, such as + \c Qt.DefaultLocaleShortDate or \c Qt.ISODate +\o A string that specifies the format of the returned string, as detailed below. +\endlist + +If \a format specifies a format string, it should use the following expressions +to specify the date: + + \table + \header \i Expression \i Output + \row \i d \i the day as number without a leading zero (1 to 31) + \row \i dd \i the day as number with a leading zero (01 to 31) + \row \i ddd + \i the abbreviated localized day name (e.g. 'Mon' to 'Sun'). + Uses QDate::shortDayName(). + \row \i dddd + \i the long localized day name (e.g. 'Monday' to 'Qt::Sunday'). + Uses QDate::longDayName(). + \row \i M \i the month as number without a leading zero (1-12) + \row \i MM \i the month as number with a leading zero (01-12) + \row \i MMM + \i the abbreviated localized month name (e.g. 'Jan' to 'Dec'). + Uses QDate::shortMonthName(). + \row \i MMMM + \i the long localized month name (e.g. 'January' to 'December'). + Uses QDate::longMonthName(). + \row \i yy \i the year as two digit number (00-99) + \row \i yyyy \i the year as four digit number + \endtable + +In addition the following expressions can be used to specify the time: + + \table + \header \i Expression \i Output + \row \i h + \i the hour without a leading zero (0 to 23 or 1 to 12 if AM/PM display) + \row \i hh + \i the hour with a leading zero (00 to 23 or 01 to 12 if AM/PM display) + \row \i m \i the minute without a leading zero (0 to 59) + \row \i mm \i the minute with a leading zero (00 to 59) + \row \i s \i the second without a leading zero (0 to 59) + \row \i ss \i the second with a leading zero (00 to 59) + \row \i z \i the milliseconds without leading zeroes (0 to 999) + \row \i zzz \i the milliseconds with leading zeroes (000 to 999) + \row \i AP + \i use AM/PM display. \e AP will be replaced by either "AM" or "PM". + \row \i ap + \i use am/pm display. \e ap will be replaced by either "am" or "pm". + \endtable + + All other input characters will be ignored. Any sequence of characters that + are enclosed in single quotes will be treated as text and not be used as an + expression. Two consecutive single quotes ("''") are replaced by a single quote + in the output. + +For example, if the following date/time value was specified: + + \code + // 21 May 2001 14:13:09 + var dateTime = new Date(2001, 5, 21, 14, 13, 09) + \endcode + +This \a dateTime value could be passed to \c Qt.formatDateTime(), +\l {QML:Qt::formatDate()}{Qt.formatDate()} or \l {QML:Qt::formatTime()}{Qt.formatTime()} +with the \a format values below to produce the following results: + + \table + \header \i Format \i Result + \row \i "dd.MM.yyyy" \i 21.05.2001 + \row \i "ddd MMMM d yy" \i Tue May 21 01 + \row \i "hh:mm:ss.zzz" \i 14:13:09.042 + \row \i "h:m:s ap" \i 2:13:9 pm + \endtable +*/ +QScriptValue QDeclarativeEnginePrivate::formatDateTime(QScriptContext*ctxt, QScriptEngine*engine) +{ + int argCount = ctxt->argumentCount(); + if(argCount == 0 || argCount > 2) + return ctxt->throwError(QLatin1String("Qt.formatDateTime(): Invalid arguments")); + + QDateTime date = ctxt->argument(0).toDateTime(); + Qt::DateFormat enumFormat = Qt::DefaultLocaleShortDate; + if (argCount == 2) { + QScriptValue formatArg = ctxt->argument(1); + if (formatArg.isString()) { + QString format = formatArg.toString(); + return engine->newVariant(QVariant::fromValue(date.toString(format))); + } else if (formatArg.isNumber()) { + enumFormat = Qt::DateFormat(formatArg.toUInt32()); + } else { + return ctxt->throwError(QLatin1String("Qt.formatDateTime(): Invalid datetime format")); + } + } + return engine->newVariant(QVariant::fromValue(date.toString(enumFormat))); +} +#endif // QT_NO_DATESTRING + +/*! +\qmlmethod color Qt::rgba(real red, real green, real blue, real alpha) + +Returns a color with the specified \c red, \c green, \c blue and \c alpha components. +All components should be in the range 0-1 inclusive. +*/ +QScriptValue QDeclarativeEnginePrivate::rgba(QScriptContext *ctxt, QScriptEngine *engine) +{ + int argCount = ctxt->argumentCount(); + if(argCount < 3 || argCount > 4) + return ctxt->throwError(QLatin1String("Qt.rgba(): Invalid arguments")); + qsreal r = ctxt->argument(0).toNumber(); + qsreal g = ctxt->argument(1).toNumber(); + qsreal b = ctxt->argument(2).toNumber(); + qsreal a = (argCount == 4) ? ctxt->argument(3).toNumber() : 1; + + if (r < 0.0) r=0.0; + if (r > 1.0) r=1.0; + if (g < 0.0) g=0.0; + if (g > 1.0) g=1.0; + if (b < 0.0) b=0.0; + if (b > 1.0) b=1.0; + if (a < 0.0) a=0.0; + if (a > 1.0) a=1.0; + + return engine->toScriptValue(QVariant::fromValue(QColor::fromRgbF(r, g, b, a))); +} + +/*! +\qmlmethod color Qt::hsla(real hue, real saturation, real lightness, real alpha) + +Returns a color with the specified \c hue, \c saturation, \c lightness and \c alpha components. +All components should be in the range 0-1 inclusive. +*/ +QScriptValue QDeclarativeEnginePrivate::hsla(QScriptContext *ctxt, QScriptEngine *engine) +{ + int argCount = ctxt->argumentCount(); + if(argCount < 3 || argCount > 4) + return ctxt->throwError(QLatin1String("Qt.hsla(): Invalid arguments")); + qsreal h = ctxt->argument(0).toNumber(); + qsreal s = ctxt->argument(1).toNumber(); + qsreal l = ctxt->argument(2).toNumber(); + qsreal a = (argCount == 4) ? ctxt->argument(3).toNumber() : 1; + + if (h < 0.0) h=0.0; + if (h > 1.0) h=1.0; + if (s < 0.0) s=0.0; + if (s > 1.0) s=1.0; + if (l < 0.0) l=0.0; + if (l > 1.0) l=1.0; + if (a < 0.0) a=0.0; + if (a > 1.0) a=1.0; + + return engine->toScriptValue(QVariant::fromValue(QColor::fromHslF(h, s, l, a))); +} + +/*! +\qmlmethod rect Qt::rect(int x, int y, int width, int height) + +Returns a \c rect with the top-left corner at \c x, \c y and the specified \c width and \c height. + +The returned object has \c x, \c y, \c width and \c height attributes with the given values. +*/ +QScriptValue QDeclarativeEnginePrivate::rect(QScriptContext *ctxt, QScriptEngine *engine) +{ + if(ctxt->argumentCount() != 4) + return ctxt->throwError(QLatin1String("Qt.rect(): Invalid arguments")); + + qsreal x = ctxt->argument(0).toNumber(); + qsreal y = ctxt->argument(1).toNumber(); + qsreal w = ctxt->argument(2).toNumber(); + qsreal h = ctxt->argument(3).toNumber(); + + return QDeclarativeEnginePrivate::get(engine)->scriptValueFromVariant(QVariant::fromValue(QRectF(x, y, w, h))); +} + +/*! +\qmlmethod point Qt::point(int x, int y) +Returns a Point with the specified \c x and \c y coordinates. +*/ +QScriptValue QDeclarativeEnginePrivate::point(QScriptContext *ctxt, QScriptEngine *engine) +{ + if(ctxt->argumentCount() != 2) + return ctxt->throwError(QLatin1String("Qt.point(): Invalid arguments")); + qsreal x = ctxt->argument(0).toNumber(); + qsreal y = ctxt->argument(1).toNumber(); + return QDeclarativeEnginePrivate::get(engine)->scriptValueFromVariant(QVariant::fromValue(QPointF(x, y))); +} + +/*! +\qmlmethod Qt::size(int width, int height) +Returns a Size with the specified \c width and \c height. +*/ +QScriptValue QDeclarativeEnginePrivate::size(QScriptContext *ctxt, QScriptEngine *engine) +{ + if(ctxt->argumentCount() != 2) + return ctxt->throwError(QLatin1String("Qt.size(): Invalid arguments")); + qsreal w = ctxt->argument(0).toNumber(); + qsreal h = ctxt->argument(1).toNumber(); + return QDeclarativeEnginePrivate::get(engine)->scriptValueFromVariant(QVariant::fromValue(QSizeF(w, h))); +} + +/*! +\qmlmethod color Qt::lighter(color baseColor, real factor) +Returns a color lighter than \c baseColor by the \c factor provided. + +If the factor is greater than 1.0, this functions returns a lighter color. +Setting factor to 1.5 returns a color that is 50% brighter. If the factor is less than 1.0, +the return color is darker, but we recommend using the Qt.darker() function for this purpose. +If the factor is 0 or negative, the return value is unspecified. + +The function converts the current RGB color to HSV, multiplies the value (V) component +by factor and converts the color back to RGB. + +If \c factor is not supplied, returns a color 50% lighter than \c baseColor (factor 1.5). +*/ +QScriptValue QDeclarativeEnginePrivate::lighter(QScriptContext *ctxt, QScriptEngine *engine) +{ + if(ctxt->argumentCount() != 1 && ctxt->argumentCount() != 2) + return ctxt->throwError(QLatin1String("Qt.lighter(): Invalid arguments")); + QVariant v = ctxt->argument(0).toVariant(); + QColor color; + if (v.userType() == QVariant::Color) + color = v.value(); + else if (v.userType() == QVariant::String) { + bool ok; + color = QDeclarativeStringConverters::colorFromString(v.toString(), &ok); + if (!ok) + return engine->nullValue(); + } else + return engine->nullValue(); + qsreal factor = 1.5; + if (ctxt->argumentCount() == 2) + factor = ctxt->argument(1).toNumber(); + color = color.lighter(int(qRound(factor*100.))); + return engine->toScriptValue(QVariant::fromValue(color)); +} + +/*! +\qmlmethod color Qt::darker(color baseColor, real factor) +Returns a color darker than \c baseColor by the \c factor provided. + +If the factor is greater than 1.0, this function returns a darker color. +Setting factor to 3.0 returns a color that has one-third the brightness. +If the factor is less than 1.0, the return color is lighter, but we recommend using +the Qt.lighter() function for this purpose. If the factor is 0 or negative, the return +value is unspecified. + +The function converts the current RGB color to HSV, divides the value (V) component +by factor and converts the color back to RGB. + +If \c factor is not supplied, returns a color 50% darker than \c baseColor (factor 2.0). +*/ +QScriptValue QDeclarativeEnginePrivate::darker(QScriptContext *ctxt, QScriptEngine *engine) +{ + if(ctxt->argumentCount() != 1 && ctxt->argumentCount() != 2) + return ctxt->throwError(QLatin1String("Qt.darker(): Invalid arguments")); + QVariant v = ctxt->argument(0).toVariant(); + QColor color; + if (v.userType() == QVariant::Color) + color = v.value(); + else if (v.userType() == QVariant::String) { + bool ok; + color = QDeclarativeStringConverters::colorFromString(v.toString(), &ok); + if (!ok) + return engine->nullValue(); + } else + return engine->nullValue(); + qsreal factor = 2.0; + if (ctxt->argumentCount() == 2) + factor = ctxt->argument(1).toNumber(); + color = color.darker(int(qRound(factor*100.))); + return engine->toScriptValue(QVariant::fromValue(color)); +} + +/*! +\qmlmethod bool Qt::openUrlExternally(url target) +Attempts to open the specified \c target url in an external application, based on the user's desktop preferences. Returns true if it succeeds, and false otherwise. +*/ +QScriptValue QDeclarativeEnginePrivate::desktopOpenUrl(QScriptContext *ctxt, QScriptEngine *e) +{ + if(ctxt->argumentCount() < 1) + return QScriptValue(e, false); + bool ret = false; +#ifndef QT_NO_DESKTOPSERVICES + ret = QDesktopServices::openUrl(QDeclarativeScriptEngine::get(e)->resolvedUrl(ctxt, QUrl(ctxt->argument(0).toString()))); +#endif + return QScriptValue(e, ret); +} + +/*! +\qmlmethod list Qt::fontFamilies() +Returns a list of the font families available to the application. +*/ + +QScriptValue QDeclarativeEnginePrivate::fontFamilies(QScriptContext *ctxt, QScriptEngine *e) +{ + if(ctxt->argumentCount() != 0) + return ctxt->throwError(QLatin1String("Qt.fontFamilies(): Invalid arguments")); + + QDeclarativeEnginePrivate *p = QDeclarativeEnginePrivate::get(e); + QFontDatabase database; + return p->scriptValueFromVariant(database.families()); +} + +/*! +\qmlmethod string Qt::md5(data) +Returns a hex string of the md5 hash of \c data. +*/ +QScriptValue QDeclarativeEnginePrivate::md5(QScriptContext *ctxt, QScriptEngine *) +{ + if (ctxt->argumentCount() != 1) + return ctxt->throwError(QLatin1String("Qt.md5(): Invalid arguments")); + + QByteArray data = ctxt->argument(0).toString().toUtf8(); + QByteArray result = QCryptographicHash::hash(data, QCryptographicHash::Md5); + + return QScriptValue(QLatin1String(result.toHex())); +} + +/*! +\qmlmethod string Qt::btoa(data) +Binary to ASCII - this function returns a base64 encoding of \c data. +*/ +QScriptValue QDeclarativeEnginePrivate::btoa(QScriptContext *ctxt, QScriptEngine *) +{ + if (ctxt->argumentCount() != 1) + return ctxt->throwError(QLatin1String("Qt.btoa(): Invalid arguments")); + + QByteArray data = ctxt->argument(0).toString().toUtf8(); + + return QScriptValue(QLatin1String(data.toBase64())); +} + +/*! +\qmlmethod string Qt::atob(data) +ASCII to binary - this function returns a base64 decoding of \c data. +*/ + +QScriptValue QDeclarativeEnginePrivate::atob(QScriptContext *ctxt, QScriptEngine *) +{ + if (ctxt->argumentCount() != 1) + return ctxt->throwError(QLatin1String("Qt.atob(): Invalid arguments")); + + QByteArray data = ctxt->argument(0).toString().toUtf8(); + + return QScriptValue(QLatin1String(QByteArray::fromBase64(data))); +} + +QScriptValue QDeclarativeEnginePrivate::consoleLog(QScriptContext *ctxt, QScriptEngine *e) +{ + if(ctxt->argumentCount() < 1) + return e->newVariant(QVariant(false)); + + QByteArray msg; + + for (int i=0; iargumentCount(); ++i) { + if (!msg.isEmpty()) msg += ' '; + msg += ctxt->argument(i).toString().toLocal8Bit(); + // does not support firebug "%[a-z]" formatting, since firebug really + // does just ignore the format letter, which makes it pointless. + } + + qDebug("%s",msg.constData()); + + return e->newVariant(QVariant(true)); +} + +void QDeclarativeEnginePrivate::sendQuit() +{ + Q_Q(QDeclarativeEngine); + emit q->quit(); + if (q->receivers(SIGNAL(quit())) == 0) { + qWarning("Signal QDeclarativeEngine::quit() emitted, but no receivers connected to handle it."); + } +} + +static void dumpwarning(const QDeclarativeError &error) +{ + qWarning().nospace() << qPrintable(error.toString()); +} + +static void dumpwarning(const QList &errors) +{ + for (int ii = 0; ii < errors.count(); ++ii) + dumpwarning(errors.at(ii)); +} + +void QDeclarativeEnginePrivate::warning(const QDeclarativeError &error) +{ + Q_Q(QDeclarativeEngine); + q->warnings(QList() << error); + if (outputWarningsToStdErr) + dumpwarning(error); +} + +void QDeclarativeEnginePrivate::warning(const QList &errors) +{ + Q_Q(QDeclarativeEngine); + q->warnings(errors); + if (outputWarningsToStdErr) + dumpwarning(errors); +} + +void QDeclarativeEnginePrivate::warning(QDeclarativeEngine *engine, const QDeclarativeError &error) +{ + if (engine) + QDeclarativeEnginePrivate::get(engine)->warning(error); + else + dumpwarning(error); +} + +void QDeclarativeEnginePrivate::warning(QDeclarativeEngine *engine, const QList &error) +{ + if (engine) + QDeclarativeEnginePrivate::get(engine)->warning(error); + else + dumpwarning(error); +} + +void QDeclarativeEnginePrivate::warning(QDeclarativeEnginePrivate *engine, const QDeclarativeError &error) +{ + if (engine) + engine->warning(error); + else + dumpwarning(error); +} + +void QDeclarativeEnginePrivate::warning(QDeclarativeEnginePrivate *engine, const QList &error) +{ + if (engine) + engine->warning(error); + else + dumpwarning(error); +} + +/*! +\qmlmethod Qt::quit() +This function causes the QDeclarativeEngine::quit() signal to be emitted. +Within the \l {QML Viewer}, this causes the launcher application to exit; +to quit a C++ application when this method is called, connect the +QDeclarativeEngine::quit() signal to the QCoreApplication::quit() slot. +*/ + +QScriptValue QDeclarativeEnginePrivate::quit(QScriptContext * /*ctxt*/, QScriptEngine *e) +{ + QDeclarativeEnginePrivate *qe = get (e); + qe->sendQuit(); + return QScriptValue(); +} + +/*! + \qmlmethod color Qt::tint(color baseColor, color tintColor) + This function allows tinting one color with another. + + The tint color should usually be mostly transparent, or you will not be + able to see the underlying color. The below example provides a slight red + tint by having the tint color be pure red which is only 1/16th opaque. + + \qml + Item { + Rectangle { + x: 0; width: 80; height: 80 + color: "lightsteelblue" + } + Rectangle { + x: 100; width: 80; height: 80 + color: Qt.tint("lightsteelblue", "#10FF0000") + } + } + \endqml + \image declarative-rect_tint.png + + Tint is most useful when a subtle change is intended to be conveyed due to some event; you can then use tinting to more effectively tune the visible color. +*/ +QScriptValue QDeclarativeEnginePrivate::tint(QScriptContext *ctxt, QScriptEngine *engine) +{ + if(ctxt->argumentCount() != 2) + return ctxt->throwError(QLatin1String("Qt.tint(): Invalid arguments")); + //get color + QVariant v = ctxt->argument(0).toVariant(); + QColor color; + if (v.userType() == QVariant::Color) + color = v.value(); + else if (v.userType() == QVariant::String) { + bool ok; + color = QDeclarativeStringConverters::colorFromString(v.toString(), &ok); + if (!ok) + return engine->nullValue(); + } else + return engine->nullValue(); + + //get tint color + v = ctxt->argument(1).toVariant(); + QColor tintColor; + if (v.userType() == QVariant::Color) + tintColor = v.value(); + else if (v.userType() == QVariant::String) { + bool ok; + tintColor = QDeclarativeStringConverters::colorFromString(v.toString(), &ok); + if (!ok) + return engine->nullValue(); + } else + return engine->nullValue(); + + //tint + QColor finalColor; + int a = tintColor.alpha(); + if (a == 0xFF) + finalColor = tintColor; + else if (a == 0x00) + finalColor = color; + else { + qreal a = tintColor.alphaF(); + qreal inv_a = 1.0 - a; + + finalColor.setRgbF(tintColor.redF() * a + color.redF() * inv_a, + tintColor.greenF() * a + color.greenF() * inv_a, + tintColor.blueF() * a + color.blueF() * inv_a, + a + inv_a * color.alphaF()); + } + + return engine->toScriptValue(QVariant::fromValue(finalColor)); +} + +QScriptValue QDeclarativeEnginePrivate::scriptValueFromVariant(const QVariant &val) +{ + if (val.userType() == qMetaTypeId()) { + QDeclarativeListReferencePrivate *p = + QDeclarativeListReferencePrivate::get((QDeclarativeListReference*)val.constData()); + if (p->object) { + return listClass->newList(p->property, p->propertyType); + } else { + return scriptEngine.nullValue(); + } + } else if (val.userType() == qMetaTypeId >()) { + const QList &list = *(QList*)val.constData(); + QScriptValue rv = scriptEngine.newArray(list.count()); + for (int ii = 0; ii < list.count(); ++ii) { + QObject *object = list.at(ii); + rv.setProperty(ii, objectClass->newQObject(object)); + } + return rv; + } else if (QDeclarativeValueType *vt = valueTypes[val.userType()]) { + return valueTypeClass->newObject(val, vt); + } + + bool objOk; + QObject *obj = QDeclarativeMetaType::toQObject(val, &objOk); + if (objOk) { + return objectClass->newQObject(obj); + } else { + return scriptEngine.toScriptValue(val); + } +} + +QVariant QDeclarativeEnginePrivate::scriptValueToVariant(const QScriptValue &val, int hint) +{ + QScriptDeclarativeClass *dc = QScriptDeclarativeClass::scriptClass(val); + if (dc == objectClass) + return QVariant::fromValue(objectClass->toQObject(val)); + else if (dc == valueTypeClass) + return valueTypeClass->toVariant(val); + else if (dc == contextClass) + return QVariant(); + + // Convert to a QList only if val is an array and we were explicitly hinted + if (hint == qMetaTypeId >() && val.isArray()) { + QList list; + int length = val.property(QLatin1String("length")).toInt32(); + for (int ii = 0; ii < length; ++ii) { + QScriptValue arrayItem = val.property(ii); + QObject *d = arrayItem.toQObject(); + list << d; + } + return QVariant::fromValue(list); + } + + return val.toVariant(); +} + +/*! + Adds \a path as a directory where the engine searches for + installed modules in a URL-based directory structure. + The \a path may be a local filesystem directory or a URL. + + The newly added \a path will be first in the importPathList(). + + \sa setImportPathList(), {QML Modules} +*/ +void QDeclarativeEngine::addImportPath(const QString& path) +{ + Q_D(QDeclarativeEngine); + d->importDatabase.addImportPath(path); +} + +/*! + Returns the list of directories where the engine searches for + installed modules in a URL-based directory structure. + + For example, if \c /opt/MyApp/lib/imports is in the path, then QML that + imports \c com.mycompany.Feature will cause the QDeclarativeEngine to look + in \c /opt/MyApp/lib/imports/com/mycompany/Feature/ for the components + provided by that module. A \c qmldir file is required for defining the + type version mapping and possibly declarative extensions plugins. + + By default, the list contains the directory of the application executable, + paths specified in the \c QML_IMPORT_PATH environment variable, + and the builtin \c ImportsPath from QLibraryInfo. + + \sa addImportPath() setImportPathList() +*/ +QStringList QDeclarativeEngine::importPathList() const +{ + Q_D(const QDeclarativeEngine); + return d->importDatabase.importPathList(); +} + +/*! + Sets \a paths as the list of directories where the engine searches for + installed modules in a URL-based directory structure. + + By default, the list contains the directory of the application executable, + paths specified in the \c QML_IMPORT_PATH environment variable, + and the builtin \c ImportsPath from QLibraryInfo. + + \sa importPathList() addImportPath() + */ +void QDeclarativeEngine::setImportPathList(const QStringList &paths) +{ + Q_D(QDeclarativeEngine); + d->importDatabase.setImportPathList(paths); +} + + +/*! + Adds \a path as a directory where the engine searches for + native plugins for imported modules (referenced in the \c qmldir file). + + By default, the list contains only \c ., i.e. the engine searches + in the directory of the \c qmldir file itself. + + The newly added \a path will be first in the pluginPathList(). + + \sa setPluginPathList() +*/ +void QDeclarativeEngine::addPluginPath(const QString& path) +{ + Q_D(QDeclarativeEngine); + d->importDatabase.addPluginPath(path); +} + + +/*! + Returns the list of directories where the engine searches for + native plugins for imported modules (referenced in the \c qmldir file). + + By default, the list contains only \c ., i.e. the engine searches + in the directory of the \c qmldir file itself. + + \sa addPluginPath() setPluginPathList() +*/ +QStringList QDeclarativeEngine::pluginPathList() const +{ + Q_D(const QDeclarativeEngine); + return d->importDatabase.pluginPathList(); +} + +/*! + Sets the list of directories where the engine searches for + native plugins for imported modules (referenced in the \c qmldir file) + to \a paths. + + By default, the list contains only \c ., i.e. the engine searches + in the directory of the \c qmldir file itself. + + \sa pluginPathList() addPluginPath() + */ +void QDeclarativeEngine::setPluginPathList(const QStringList &paths) +{ + Q_D(QDeclarativeEngine); + d->importDatabase.setPluginPathList(paths); +} + + +/*! + Imports the plugin named \a filePath with the \a uri provided. + Returns true if the plugin was successfully imported; otherwise returns false. + + On failure and if non-null, *\a errorString will be set to a message describing the failure. + + The plugin has to be a Qt plugin which implements the QDeclarativeExtensionPlugin interface. +*/ +bool QDeclarativeEngine::importPlugin(const QString &filePath, const QString &uri, QString *errorString) +{ + Q_D(QDeclarativeEngine); + return d->importDatabase.importPlugin(filePath, uri, errorString); +} + +/*! + \property QDeclarativeEngine::offlineStoragePath + \brief the directory for storing offline user data + + Returns the directory where SQL and other offline + storage is placed. + + QDeclarativeWebView and the SQL databases created with openDatabase() + are stored here. + + The default is QML/OfflineStorage in the platform-standard + user application data directory. + + Note that the path may not currently exist on the filesystem, so + callers wanting to \e create new files at this location should create + it first - see QDir::mkpath(). +*/ +void QDeclarativeEngine::setOfflineStoragePath(const QString& dir) +{ + Q_D(QDeclarativeEngine); + d->scriptEngine.offlineStoragePath = dir; +} + +QString QDeclarativeEngine::offlineStoragePath() const +{ + Q_D(const QDeclarativeEngine); + return d->scriptEngine.offlineStoragePath; +} + +static void voidptr_destructor(void *v) +{ + void **ptr = (void **)v; + delete ptr; +} + +static void *voidptr_constructor(const void *v) +{ + if (!v) { + return new void*; + } else { + return new void*(*(void **)v); + } +} + +QDeclarativePropertyCache *QDeclarativeEnginePrivate::createCache(const QMetaObject *mo) +{ + Q_Q(QDeclarativeEngine); + + if (!mo->superClass()) { + QDeclarativePropertyCache *rv = new QDeclarativePropertyCache(q, mo); + propertyCache.insert(mo, rv); + return rv; + } else { + QDeclarativePropertyCache *super = cache(mo->superClass()); + QDeclarativePropertyCache *rv = super->copy(); + rv->append(q, mo); + propertyCache.insert(mo, rv); + return rv; + } +} + +QDeclarativePropertyCache *QDeclarativeEnginePrivate::createCache(QDeclarativeType *type, int minorVersion, + QDeclarativeError &error) +{ + QList types; + + int maxMinorVersion = 0; + + const QMetaObject *metaObject = type->metaObject(); + while (metaObject) { + QDeclarativeType *t = QDeclarativeMetaType::qmlType(metaObject, type->module(), + type->majorVersion(), minorVersion); + if (t) { + maxMinorVersion = qMax(maxMinorVersion, t->minorVersion()); + types << t; + } else { + types << 0; + } + + metaObject = metaObject->superClass(); + } + + if (QDeclarativePropertyCache *c = typePropertyCache.value(qMakePair(type, maxMinorVersion))) { + c->addref(); + typePropertyCache.insert(qMakePair(type, minorVersion), c); + return c; + } + + QDeclarativePropertyCache *raw = cache(type->metaObject()); + + bool hasCopied = false; + + for (int ii = 0; ii < types.count(); ++ii) { + QDeclarativeType *currentType = types.at(ii); + if (!currentType) + continue; + + int rev = currentType->metaObjectRevision(); + int moIndex = types.count() - 1 - ii; + + if (raw->allowedRevisionCache[moIndex] != rev) { + if (!hasCopied) { + raw = raw->copy(); + hasCopied = true; + } + raw->allowedRevisionCache[moIndex] = rev; + } + } + + // Test revision compatibility - the basic rule is: + // * Anything that is excluded, cannot overload something that is not excluded * + + // Signals override: + // * other signals and methods of the same name. + // * properties named on + // * automatic Changed notify signals + + // Methods override: + // * other methods of the same name + + // Properties override: + // * other elements of the same name + + bool overloadError = false; + QString overloadName; + +#if 0 + for (QDeclarativePropertyCache::StringCache::ConstIterator iter = raw->stringCache.begin(); + !overloadError && iter != raw->stringCache.end(); + ++iter) { + + QDeclarativePropertyCache::Data *d = *iter; + if (raw->isAllowedInRevision(d)) + continue; // Not excluded - no problems + + // check that a regular "name" overload isn't happening + QDeclarativePropertyCache::Data *current = d; + while (!overloadError && current) { + current = d->overrideData(current); + if (current && raw->isAllowedInRevision(current)) + overloadError = true; + } + } +#endif + + if (overloadError) { + if (hasCopied) raw->release(); + + error.setDescription(QLatin1String("Type ") + QString::fromUtf8(type->qmlTypeName()) + QLatin1String(" ") + QString::number(type->majorVersion()) + QLatin1String(".") + QString::number(minorVersion) + QLatin1String(" contains an illegal property \"") + overloadName + QLatin1String("\". This is an error in the type's implementation.")); + return 0; + } + + if (!hasCopied) raw->addref(); + typePropertyCache.insert(qMakePair(type, minorVersion), raw); + + if (minorVersion != maxMinorVersion) { + raw->addref(); + typePropertyCache.insert(qMakePair(type, maxMinorVersion), raw); + } + + return raw; +} + +void QDeclarativeEnginePrivate::registerCompositeType(QDeclarativeCompiledData *data) +{ + QByteArray name = data->root->className(); + + QByteArray ptr = name + '*'; + QByteArray lst = "QDeclarativeListProperty<" + name + '>'; + + int ptr_type = QMetaType::registerType(ptr.constData(), voidptr_destructor, + voidptr_constructor); + int lst_type = QMetaType::registerType(lst.constData(), voidptr_destructor, + voidptr_constructor); + + m_qmlLists.insert(lst_type, ptr_type); + m_compositeTypes.insert(ptr_type, data); + data->addref(); +} + +bool QDeclarativeEnginePrivate::isList(int t) const +{ + return m_qmlLists.contains(t) || QDeclarativeMetaType::isList(t); +} + +int QDeclarativeEnginePrivate::listType(int t) const +{ + QHash::ConstIterator iter = m_qmlLists.find(t); + if (iter != m_qmlLists.end()) + return *iter; + else + return QDeclarativeMetaType::listType(t); +} + +bool QDeclarativeEnginePrivate::isQObject(int t) +{ + return m_compositeTypes.contains(t) || QDeclarativeMetaType::isQObject(t); +} + +QObject *QDeclarativeEnginePrivate::toQObject(const QVariant &v, bool *ok) const +{ + int t = v.userType(); + if (t == QMetaType::QObjectStar || m_compositeTypes.contains(t)) { + if (ok) *ok = true; + return *(QObject **)(v.constData()); + } else { + return QDeclarativeMetaType::toQObject(v, ok); + } +} + +QDeclarativeMetaType::TypeCategory QDeclarativeEnginePrivate::typeCategory(int t) const +{ + if (m_compositeTypes.contains(t)) + return QDeclarativeMetaType::Object; + else if (m_qmlLists.contains(t)) + return QDeclarativeMetaType::List; + else + return QDeclarativeMetaType::typeCategory(t); +} + +const QMetaObject *QDeclarativeEnginePrivate::rawMetaObjectForType(int t) const +{ + QHash::ConstIterator iter = m_compositeTypes.find(t); + if (iter != m_compositeTypes.end()) { + return (*iter)->root; + } else { + QDeclarativeType *type = QDeclarativeMetaType::qmlType(t); + return type?type->baseMetaObject():0; + } +} + +const QMetaObject *QDeclarativeEnginePrivate::metaObjectForType(int t) const +{ + QHash::ConstIterator iter = m_compositeTypes.find(t); + if (iter != m_compositeTypes.end()) { + return (*iter)->root; + } else { + QDeclarativeType *type = QDeclarativeMetaType::qmlType(t); + return type?type->metaObject():0; + } +} + +bool QDeclarative_isFileCaseCorrect(const QString &fileName) +{ +#if defined(Q_OS_MAC) || defined(Q_OS_WIN32) + QFileInfo info(fileName); + + QString absolute = info.absoluteFilePath(); + +#if defined(Q_OS_MAC) + QString canonical = info.canonicalFilePath(); +#elif defined(Q_OS_WIN32) + wchar_t buffer[1024]; + + DWORD rv = ::GetShortPathName((wchar_t*)absolute.utf16(), buffer, 1024); + if (rv == 0 || rv >= 1024) return true; + rv = ::GetLongPathName(buffer, buffer, 1024); + if (rv == 0 || rv >= 1024) return true; + + QString canonical((QChar *)buffer); +#endif + + int absoluteLength = absolute.length(); + int canonicalLength = canonical.length(); + + int length = qMin(absoluteLength, canonicalLength); + for (int ii = 0; ii < length; ++ii) { + const QChar &a = absolute.at(absoluteLength - 1 - ii); + const QChar &c = canonical.at(canonicalLength - 1 - ii); + + if (a.toLower() != c.toLower()) + return true; + if (a != c) + return false; + } +#else + Q_UNUSED(fileName) +#endif + return true; +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativeengine.h b/src/declarative/qml/qdeclarativeengine.h new file mode 100644 index 00000000..fd061159 --- /dev/null +++ b/src/declarative/qml/qdeclarativeengine.h @@ -0,0 +1,130 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEENGINE_H +#define QDECLARATIVEENGINE_H + +#include +#include +#include +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeComponent; +class QDeclarativeEnginePrivate; +class QDeclarativeImportsPrivate; +class QDeclarativeExpression; +class QDeclarativeContext; +class QDeclarativeType; +class QUrl; +class QScriptEngine; +class QScriptContext; +class QDeclarativeImageProvider; +class QNetworkAccessManager; +class QDeclarativeNetworkAccessManagerFactory; +class Q_DECLARATIVE_EXPORT QDeclarativeEngine : public QObject +{ + Q_PROPERTY(QString offlineStoragePath READ offlineStoragePath WRITE setOfflineStoragePath) + Q_OBJECT +public: + QDeclarativeEngine(QObject *p = 0); + virtual ~QDeclarativeEngine(); + + QDeclarativeContext *rootContext() const; + + void clearComponentCache(); + + QStringList importPathList() const; + void setImportPathList(const QStringList &paths); + void addImportPath(const QString& dir); + + QStringList pluginPathList() const; + void setPluginPathList(const QStringList &paths); + void addPluginPath(const QString& dir); + + bool importPlugin(const QString &filePath, const QString &uri, QString *errorString); + + void setNetworkAccessManagerFactory(QDeclarativeNetworkAccessManagerFactory *); + QDeclarativeNetworkAccessManagerFactory *networkAccessManagerFactory() const; + + QNetworkAccessManager *networkAccessManager() const; + + void addImageProvider(const QString &id, QDeclarativeImageProvider *); + QDeclarativeImageProvider *imageProvider(const QString &id) const; + void removeImageProvider(const QString &id); + + void setOfflineStoragePath(const QString& dir); + QString offlineStoragePath() const; + + QUrl baseUrl() const; + void setBaseUrl(const QUrl &); + + bool outputWarningsToStandardError() const; + void setOutputWarningsToStandardError(bool); + + static QDeclarativeContext *contextForObject(const QObject *); + static void setContextForObject(QObject *, QDeclarativeContext *); + + enum ObjectOwnership { CppOwnership, JavaScriptOwnership }; + static void setObjectOwnership(QObject *, ObjectOwnership); + static ObjectOwnership objectOwnership(QObject *); + +Q_SIGNALS: + void quit(); + void warnings(const QList &warnings); + +private: + Q_DISABLE_COPY(QDeclarativeEngine) + Q_DECLARE_PRIVATE(QDeclarativeEngine) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEENGINE_H diff --git a/src/declarative/qml/qdeclarativeengine_p.h b/src/declarative/qml/qdeclarativeengine_p.h new file mode 100644 index 00000000..e43f75bb --- /dev/null +++ b/src/declarative/qml/qdeclarativeengine_p.h @@ -0,0 +1,387 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEENGINE_P_H +#define QDECLARATIVEENGINE_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 "qdeclarativeengine.h" + +#include "private/qdeclarativetypeloader_p.h" +#include "private/qdeclarativeimport_p.h" +#include "private/qpodvector_p.h" +#include "qdeclarative.h" +#include "private/qdeclarativevaluetype_p.h" +#include "qdeclarativecontext.h" +#include "private/qdeclarativecontext_p.h" +#include "qdeclarativeexpression.h" +#include "qdeclarativeimageprovider.h" +#include "private/qdeclarativeproperty_p.h" +#include "private/qdeclarativepropertycache_p.h" +#include "private/qdeclarativeobjectscriptclass_p.h" +#include "private/qdeclarativecontextscriptclass_p.h" +#include "private/qdeclarativevaluetypescriptclass_p.h" +#include "private/qdeclarativemetatype_p.h" +#include "private/qdeclarativedirparser_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeContext; +class QDeclarativeEngine; +class QDeclarativeContextPrivate; +class QDeclarativeExpression; +class QDeclarativeContextScriptClass; +class QDeclarativeImportDatabase; +class QDeclarativeObjectScriptClass; +class QDeclarativeTypeNameScriptClass; +class QDeclarativeValueTypeScriptClass; +class QNetworkReply; +class QNetworkAccessManager; +class QDeclarativeNetworkAccessManagerFactory; +class QDeclarativeAbstractBinding; +class QScriptDeclarativeClass; +class QDeclarativeTypeNameScriptClass; +class QDeclarativeTypeNameCache; +class QDeclarativeComponentAttached; +class QDeclarativeListScriptClass; +class QDeclarativeCleanup; +class QDeclarativeDelayedError; +class QDeclarativeWorkerScriptEngine; +class QDeclarativeGlobalScriptClass; +class QDir; + +class QDeclarativeScriptEngine : public QScriptEngine +{ +public: + QDeclarativeScriptEngine(QDeclarativeEnginePrivate *priv); + virtual ~QDeclarativeScriptEngine(); + + QUrl resolvedUrl(QScriptContext *context, const QUrl& url); // resolved against p's context, or baseUrl if no p + static QScriptValue resolvedUrl(QScriptContext *ctxt, QScriptEngine *engine); + + static QDeclarativeScriptEngine *get(QScriptEngine* e) { return static_cast(e); } + + QDeclarativeEnginePrivate *p; + + // User by SQL API + QScriptClass *sqlQueryClass; + QString offlineStoragePath; + + // Used by DOM Core 3 API + QScriptClass *namedNodeMapClass; + QScriptClass *nodeListClass; + + QUrl baseUrl; + + virtual QNetworkAccessManager *networkAccessManager(); +}; + +class Q_AUTOTEST_EXPORT QDeclarativeEnginePrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeEngine) +public: + QDeclarativeEnginePrivate(QDeclarativeEngine *); + ~QDeclarativeEnginePrivate(); + + void init(); + + struct CapturedProperty { + CapturedProperty(QObject *o, int c, int n) + : object(o), coreIndex(c), notifier(0), notifyIndex(n) {} + CapturedProperty(QDeclarativeNotifier *n) + : object(0), coreIndex(-1), notifier(n), notifyIndex(-1) {} + + QObject *object; + int coreIndex; + QDeclarativeNotifier *notifier; + int notifyIndex; + }; + bool captureProperties; + QPODVector capturedProperties; + + QDeclarativeContext *rootContext; + bool isDebugging; + + bool outputWarningsToStdErr; + + QDeclarativeContextScriptClass *contextClass; + QDeclarativeContextData *sharedContext; + QObject *sharedScope; + QDeclarativeObjectScriptClass *objectClass; + QDeclarativeValueTypeScriptClass *valueTypeClass; + QDeclarativeTypeNameScriptClass *typeNameClass; + QDeclarativeListScriptClass *listClass; + // Global script class + QDeclarativeGlobalScriptClass *globalClass; + + // Registered cleanup handlers + QDeclarativeCleanup *cleanup; + + // Bindings that have had errors during startup + QDeclarativeDelayedError *erroredBindings; + int inProgressCreations; + + QDeclarativeScriptEngine scriptEngine; + + QDeclarativeWorkerScriptEngine *getWorkerScriptEngine(); + QDeclarativeWorkerScriptEngine *workerScriptEngine; + + QUrl baseUrl; + + template + struct SimpleList { + SimpleList() + : count(0), values(0) {} + SimpleList(int r) + : count(0), values(new T*[r]) {} + + int count; + T **values; + + void append(T *v) { + values[count++] = v; + } + + T *at(int idx) const { + return values[idx]; + } + + void clear() { + delete [] values; + } + }; + + static void clear(SimpleList &); + static void clear(SimpleList &); + + QList > bindValues; + QList > parserStatus; + QList,int> > finalizedParserStatus; + QDeclarativeComponentAttached *componentAttached; + + void registerFinalizedParserStatusObject(QObject *obj, int index) { + finalizedParserStatus.append(qMakePair(QDeclarativeGuard(obj), index)); + } + + bool inBeginCreate; + + QNetworkAccessManager *createNetworkAccessManager(QObject *parent) const; + QNetworkAccessManager *getNetworkAccessManager() const; + mutable QNetworkAccessManager *networkAccessManager; + mutable QDeclarativeNetworkAccessManagerFactory *networkAccessManagerFactory; + + QHash > imageProviders; + QDeclarativeImageProvider::ImageType getImageProviderType(const QUrl &url); + QImage getImageFromProvider(const QUrl &url, QSize *size, const QSize& req_size); + QPixmap getPixmapFromProvider(const QUrl &url, QSize *size, const QSize& req_size); + + mutable QMutex mutex; + + QDeclarativeTypeLoader typeLoader; + QDeclarativeImportDatabase importDatabase; + + QString offlineStoragePath; + + mutable quint32 uniqueId; + quint32 getUniqueId() const { + return uniqueId++; + } + + QDeclarativeValueTypeFactory valueTypes; + + QHash propertyCache; + QHash, QDeclarativePropertyCache *> typePropertyCache; + inline QDeclarativePropertyCache *cache(QObject *obj); + inline QDeclarativePropertyCache *cache(const QMetaObject *); + inline QDeclarativePropertyCache *cache(QDeclarativeType *, int, QDeclarativeError &error); + QDeclarativePropertyCache *createCache(const QMetaObject *); + QDeclarativePropertyCache *createCache(QDeclarativeType *, int, QDeclarativeError &error); + + void registerCompositeType(QDeclarativeCompiledData *); + + bool isQObject(int); + QObject *toQObject(const QVariant &, bool *ok = 0) const; + QDeclarativeMetaType::TypeCategory typeCategory(int) const; + bool isList(int) const; + int listType(int) const; + const QMetaObject *rawMetaObjectForType(int) const; + const QMetaObject *metaObjectForType(int) const; + QHash m_qmlLists; + QHash m_compositeTypes; + + QHash m_sharedScriptImports; + + QScriptValue scriptValueFromVariant(const QVariant &); + QVariant scriptValueToVariant(const QScriptValue &, int hint = QVariant::Invalid); + + void sendQuit(); + void warning(const QDeclarativeError &); + void warning(const QList &); + static void warning(QDeclarativeEngine *, const QDeclarativeError &); + static void warning(QDeclarativeEngine *, const QList &); + static void warning(QDeclarativeEnginePrivate *, const QDeclarativeError &); + static void warning(QDeclarativeEnginePrivate *, const QList &); + + static QScriptValue qmlScriptObject(QObject*, QDeclarativeEngine*); + + static QScriptValue createComponent(QScriptContext*, QScriptEngine*); + static QScriptValue createQmlObject(QScriptContext*, QScriptEngine*); + static QScriptValue isQtObject(QScriptContext*, QScriptEngine*); + static QScriptValue vector3d(QScriptContext*, QScriptEngine*); + static QScriptValue rgba(QScriptContext*, QScriptEngine*); + static QScriptValue hsla(QScriptContext*, QScriptEngine*); + static QScriptValue point(QScriptContext*, QScriptEngine*); + static QScriptValue size(QScriptContext*, QScriptEngine*); + static QScriptValue rect(QScriptContext*, QScriptEngine*); + + static QScriptValue lighter(QScriptContext*, QScriptEngine*); + static QScriptValue darker(QScriptContext*, QScriptEngine*); + static QScriptValue tint(QScriptContext*, QScriptEngine*); + + static QScriptValue desktopOpenUrl(QScriptContext*, QScriptEngine*); + static QScriptValue fontFamilies(QScriptContext*, QScriptEngine*); + static QScriptValue md5(QScriptContext*, QScriptEngine*); + static QScriptValue btoa(QScriptContext*, QScriptEngine*); + static QScriptValue atob(QScriptContext*, QScriptEngine*); + static QScriptValue consoleLog(QScriptContext*, QScriptEngine*); + static QScriptValue quit(QScriptContext*, QScriptEngine*); + +#ifndef QT_NO_DATESTRING + static QScriptValue formatDate(QScriptContext*, QScriptEngine*); + static QScriptValue formatTime(QScriptContext*, QScriptEngine*); + static QScriptValue formatDateTime(QScriptContext*, QScriptEngine*); +#endif + static QScriptEngine *getScriptEngine(QDeclarativeEngine *e) { return &e->d_func()->scriptEngine; } + static QDeclarativeEngine *getEngine(QScriptEngine *e) { return static_cast(e)->p->q_func(); } + static QDeclarativeEnginePrivate *get(QDeclarativeEngine *e) { return e->d_func(); } + static QDeclarativeEnginePrivate *get(QDeclarativeContext *c) { return (c && c->engine()) ? QDeclarativeEnginePrivate::get(c->engine()) : 0; } + static QDeclarativeEnginePrivate *get(QDeclarativeContextData *c) { return (c && c->engine) ? QDeclarativeEnginePrivate::get(c->engine) : 0; } + static QDeclarativeEnginePrivate *get(QScriptEngine *e) { return static_cast(e)->p; } + static QDeclarativeEngine *get(QDeclarativeEnginePrivate *p) { return p->q_func(); } + QDeclarativeContextData *getContext(QScriptContext *); + QUrl getUrl(QScriptContext *); + + static QString urlToLocalFileOrQrc(const QUrl& url); + + static void defineModule(); + + static bool qml_debugging_enabled; +}; + +/*! +Returns a QDeclarativePropertyCache for \a obj if one is available. + +If \a obj is null, being deleted or contains a dynamic meta object 0 +is returned. + +The returned cache is not referenced, so if it is to be stored, call addref(). +*/ +QDeclarativePropertyCache *QDeclarativeEnginePrivate::cache(QObject *obj) +{ + if (!obj || QObjectPrivate::get(obj)->metaObject || QObjectPrivate::get(obj)->wasDeleted) + return 0; + + const QMetaObject *mo = obj->metaObject(); + QDeclarativePropertyCache *rv = propertyCache.value(mo); + if (!rv) rv = createCache(mo); + return rv; +} + +/*! +Returns a QDeclarativePropertyCache for \a metaObject. + +As the cache is persisted for the life of the engine, \a metaObject must be +a static "compile time" meta-object, or a meta-object that is otherwise known to +exist for the lifetime of the QDeclarativeEngine. + +The returned cache is not referenced, so if it is to be stored, call addref(). +*/ +QDeclarativePropertyCache *QDeclarativeEnginePrivate::cache(const QMetaObject *metaObject) +{ + Q_ASSERT(metaObject); + + QDeclarativePropertyCache *rv = propertyCache.value(metaObject); + if (!rv) rv = createCache(metaObject); + return rv; +} + +/*! +Returns a QDeclarativePropertyCache for \a type with \a minorVersion. + +The returned cache is not referenced, so if it is to be stored, call addref(). +*/ +QDeclarativePropertyCache *QDeclarativeEnginePrivate::cache(QDeclarativeType *type, int minorVersion, QDeclarativeError &error) +{ + Q_ASSERT(type); + + if (minorVersion == -1 || !type->containsRevisionedAttributes()) + return cache(type->metaObject()); + + QDeclarativePropertyCache *rv = typePropertyCache.value(qMakePair(type, minorVersion)); + if (!rv) rv = createCache(type, minorVersion, error); + return rv; +} + +QT_END_NAMESPACE + +#endif // QDECLARATIVEENGINE_P_H diff --git a/src/declarative/qml/qdeclarativeerror.cpp b/src/declarative/qml/qdeclarativeerror.cpp new file mode 100644 index 00000000..a538c751 --- /dev/null +++ b/src/declarative/qml/qdeclarativeerror.cpp @@ -0,0 +1,285 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativeerror.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \class QDeclarativeError + \since 4.7 + \brief The QDeclarativeError class encapsulates a QML error. + + QDeclarativeError includes a textual description of the error, as well + as location information (the file, line, and column). The toString() + method creates a single-line, human-readable string containing all of + this information, for example: + \code + file:///home/user/test.qml:7:8: Invalid property assignment: double expected + \endcode + + You can use qDebug() or qWarning() to output errors to the console. This method + will attempt to open the file indicated by the error + and include additional contextual information. + \code + file:///home/user/test.qml:7:8: Invalid property assignment: double expected + y: "hello" + ^ + \endcode + + \sa QDeclarativeView::errors(), QDeclarativeComponent::errors() +*/ +class QDeclarativeErrorPrivate +{ +public: + QDeclarativeErrorPrivate(); + + QUrl url; + QString description; + int line; + int column; +}; + +QDeclarativeErrorPrivate::QDeclarativeErrorPrivate() +: line(-1), column(-1) +{ +} + +/*! + Creates an empty error object. +*/ +QDeclarativeError::QDeclarativeError() +: d(0) +{ +} + +/*! + Creates a copy of \a other. +*/ +QDeclarativeError::QDeclarativeError(const QDeclarativeError &other) +: d(0) +{ + *this = other; +} + +/*! + Assigns \a other to this error object. +*/ +QDeclarativeError &QDeclarativeError::operator=(const QDeclarativeError &other) +{ + if (!other.d) { + delete d; + d = 0; + } else { + if (!d) d = new QDeclarativeErrorPrivate; + d->url = other.d->url; + d->description = other.d->description; + d->line = other.d->line; + d->column = other.d->column; + } + return *this; +} + +/*! + \internal +*/ +QDeclarativeError::~QDeclarativeError() +{ + delete d; d = 0; +} + +/*! + Returns true if this error is valid, otherwise false. +*/ +bool QDeclarativeError::isValid() const +{ + return d != 0; +} + +/*! + Returns the url for the file that caused this error. +*/ +QUrl QDeclarativeError::url() const +{ + if (d) return d->url; + else return QUrl(); +} + +/*! + Sets the \a url for the file that caused this error. +*/ +void QDeclarativeError::setUrl(const QUrl &url) +{ + if (!d) d = new QDeclarativeErrorPrivate; + d->url = url; +} + +/*! + Returns the error description. +*/ +QString QDeclarativeError::description() const +{ + if (d) return d->description; + else return QString(); +} + +/*! + Sets the error \a description. +*/ +void QDeclarativeError::setDescription(const QString &description) +{ + if (!d) d = new QDeclarativeErrorPrivate; + d->description = description; +} + +/*! + Returns the error line number. +*/ +int QDeclarativeError::line() const +{ + if (d) return d->line; + else return -1; +} + +/*! + Sets the error \a line number. +*/ +void QDeclarativeError::setLine(int line) +{ + if (!d) d = new QDeclarativeErrorPrivate; + d->line = line; +} + +/*! + Returns the error column number. +*/ +int QDeclarativeError::column() const +{ + if (d) return d->column; + else return -1; +} + +/*! + Sets the error \a column number. +*/ +void QDeclarativeError::setColumn(int column) +{ + if (!d) d = new QDeclarativeErrorPrivate; + d->column = column; +} + +/*! + Returns the error as a human readable string. +*/ +QString QDeclarativeError::toString() const +{ + QString rv; + if (url().isEmpty()) { + rv = QLatin1String(""); + } else if (line() != -1) { + rv = url().toString() + QLatin1Char(':') + QString::number(line()); + if(column() != -1) + rv += QLatin1Char(':') + QString::number(column()); + } else { + rv = url().toString(); + } + + rv += QLatin1String(": ") + description(); + + return rv; +} + +/*! + \relates QDeclarativeError + \fn QDebug operator<<(QDebug debug, const QDeclarativeError &error) + + Outputs a human readable version of \a error to \a debug. +*/ + +QDebug operator<<(QDebug debug, const QDeclarativeError &error) +{ + debug << qPrintable(error.toString()); + + QUrl url = error.url(); + + if (error.line() > 0 && url.scheme() == QLatin1String("file")) { + QString file = url.toLocalFile(); + QFile f(file); + if (f.open(QIODevice::ReadOnly)) { + QByteArray data = f.readAll(); + QTextStream stream(data, QIODevice::ReadOnly); +#ifndef QT_NO_TEXTCODEC + stream.setCodec("UTF-8"); +#endif + const QString code = stream.readAll(); + const QStringList lines = code.split(QLatin1Char('\n')); + + if (lines.count() >= error.line()) { + const QString &line = lines.at(error.line() - 1); + debug << "\n " << qPrintable(line); + + if(error.column() > 0) { + int column = qMax(0, error.column() - 1); + column = qMin(column, line.length()); + + QByteArray ind; + ind.reserve(column); + for (int i = 0; i < column; ++i) { + const QChar ch = line.at(i); + if (ch.isSpace()) + ind.append(ch.unicode()); + else + ind.append(' '); + } + ind.append('^'); + debug << "\n " << ind.constData(); + } + } + } + } + return debug; +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativeerror.h b/src/declarative/qml/qdeclarativeerror.h new file mode 100644 index 00000000..0de1628b --- /dev/null +++ b/src/declarative/qml/qdeclarativeerror.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEERROR_H +#define QDECLARATIVEERROR_H + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDebug; +class QDeclarativeErrorPrivate; +class Q_DECLARATIVE_EXPORT QDeclarativeError +{ +public: + QDeclarativeError(); + QDeclarativeError(const QDeclarativeError &); + QDeclarativeError &operator=(const QDeclarativeError &); + ~QDeclarativeError(); + + bool isValid() const; + + QUrl url() const; + void setUrl(const QUrl &); + QString description() const; + void setDescription(const QString &); + int line() const; + void setLine(int); + int column() const; + void setColumn(int); + + QString toString() const; +private: + QDeclarativeErrorPrivate *d; +}; + +QDebug Q_DECLARATIVE_EXPORT operator<<(QDebug debug, const QDeclarativeError &error); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEERROR_H diff --git a/src/declarative/qml/qdeclarativeexpression.cpp b/src/declarative/qml/qdeclarativeexpression.cpp new file mode 100644 index 00000000..bffa1681 --- /dev/null +++ b/src/declarative/qml/qdeclarativeexpression.cpp @@ -0,0 +1,875 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativeexpression.h" +#include "private/qdeclarativeexpression_p.h" + +#include "private/qdeclarativeengine_p.h" +#include "private/qdeclarativecontext_p.h" +#include "private/qdeclarativerewrite_p.h" +#include "private/qdeclarativecompiler_p.h" +#include "private/qdeclarativeglobalscriptclass_p.h" + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +bool QDeclarativeDelayedError::addError(QDeclarativeEnginePrivate *e) +{ + if (!e) return false; + + if (e->inProgressCreations == 0) return false; // Not in construction + + if (prevError) return true; // Already in error chain + + prevError = &e->erroredBindings; + nextError = e->erroredBindings; + e->erroredBindings = this; + if (nextError) nextError->prevError = &nextError; + + return true; +} + +QDeclarativeQtScriptExpression::QDeclarativeQtScriptExpression() +: dataRef(0), expressionFunctionMode(ExplicitContext), scopeObject(0), trackChange(false), + guardList(0), guardListLength(0), guardObject(0), guardObjectNotifyIndex(-1), deleted(0) +{ +} + +QDeclarativeQtScriptExpression::~QDeclarativeQtScriptExpression() +{ + if (guardList) { delete [] guardList; guardList = 0; } + if (dataRef) dataRef->release(); + if (deleted) *deleted = true; +} + +QDeclarativeExpressionPrivate::QDeclarativeExpressionPrivate() +: expressionFunctionValid(true), line(-1) +{ +} + +QDeclarativeExpressionPrivate::~QDeclarativeExpressionPrivate() +{ +} + +void QDeclarativeExpressionPrivate::init(QDeclarativeContextData *ctxt, const QString &expr, + QObject *me) +{ + expression = expr; + + QDeclarativeAbstractExpression::setContext(ctxt); + scopeObject = me; + expressionFunctionValid = false; +} + +void QDeclarativeExpressionPrivate::init(QDeclarativeContextData *ctxt, const QScriptValue &func, + QObject *me) +{ + expression = func.toString(); + + QDeclarativeAbstractExpression::setContext(ctxt); + scopeObject = me; + + expressionFunction = func; + expressionFunctionMode = ExplicitContext; + expressionFunctionValid = true; +} + +void QDeclarativeExpressionPrivate::init(QDeclarativeContextData *ctxt, void *expr, + QDeclarativeRefCount *rc, + QObject *me, const QString &srcUrl, int lineNumber) +{ + url = srcUrl; + line = lineNumber; + + if (dataRef) dataRef->release(); + dataRef = rc; + if (dataRef) dataRef->addref(); + + quint32 *exprData = (quint32 *)expr; + QDeclarativeCompiledData *dd = (QDeclarativeCompiledData *)rc; + + expression = QString::fromRawData((QChar *)(exprData + 2), exprData[1]); + + int progIdx = *(exprData); + bool isSharedProgram = progIdx & 0x80000000; + progIdx &= 0x7FFFFFFF; + + QDeclarativeEngine *engine = ctxt->engine; + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); + QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine); + + if (isSharedProgram) { + + if (!dd->cachedClosures.at(progIdx)) { + QScriptContext *scriptContext = QScriptDeclarativeClass::pushCleanContext(scriptEngine); + scriptContext->pushScope(ep->contextClass->newSharedContext()); + scriptContext->pushScope(ep->globalClass->staticGlobalObject()); + dd->cachedClosures[progIdx] = new QScriptValue(scriptEngine->evaluate(expression, url, line)); + scriptEngine->popContext(); + } + + expressionFunction = *dd->cachedClosures.at(progIdx); + expressionFunctionMode = SharedContext; + expressionFunctionValid = true; + + } else { + + if (!dd->cachedPrograms.at(progIdx)) { + dd->cachedPrograms[progIdx] = new QScriptProgram(expression, url, line); + } + + expressionFunction = evalInObjectScope(ctxt, me, *dd->cachedPrograms.at(progIdx), + &expressionContext); + + expressionFunctionMode = ExplicitContext; + expressionFunctionValid = true; + } + + QDeclarativeAbstractExpression::setContext(ctxt); + scopeObject = me; +} + +QScriptValue QDeclarativeExpressionPrivate::evalInObjectScope(QDeclarativeContextData *context, QObject *object, + const QString &program, const QString &fileName, + int lineNumber, QScriptValue *contextObject) +{ + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(context->engine); + QScriptContext *scriptContext = QScriptDeclarativeClass::pushCleanContext(&ep->scriptEngine); + if (contextObject) { + *contextObject = ep->contextClass->newContext(context, object); + scriptContext->pushScope(*contextObject); + } else { + scriptContext->pushScope(ep->contextClass->newContext(context, object)); + } + scriptContext->pushScope(ep->globalClass->staticGlobalObject()); + QScriptValue rv = ep->scriptEngine.evaluate(program, fileName, lineNumber); + ep->scriptEngine.popContext(); + return rv; +} + +QScriptValue QDeclarativeExpressionPrivate::evalInObjectScope(QDeclarativeContextData *context, QObject *object, + const QScriptProgram &program, + QScriptValue *contextObject) +{ + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(context->engine); + QScriptContext *scriptContext = QScriptDeclarativeClass::pushCleanContext(&ep->scriptEngine); + if (contextObject) { + *contextObject = ep->contextClass->newContext(context, object); + scriptContext->pushScope(*contextObject); + } else { + scriptContext->pushScope(ep->contextClass->newContext(context, object)); + } + scriptContext->pushScope(ep->globalClass->staticGlobalObject()); + QScriptValue rv = ep->scriptEngine.evaluate(program); + ep->scriptEngine.popContext(); + return rv; +} + +/*! + \class QDeclarativeExpression + \since 4.7 + \brief The QDeclarativeExpression class evaluates JavaScript in a QML context. + + For example, given a file \c main.qml like this: + + \qml + import QtQuick 1.0 + + Item { + width: 200; height: 200 + } + \endqml + + The following code evaluates a JavaScript expression in the context of the + above QML: + + \code + QDeclarativeEngine *engine = new QDeclarativeEngine; + QDeclarativeComponent component(engine, QUrl::fromLocalFile("main.qml")); + + QObject *myObject = component.create(); + QDeclarativeExpression *expr = new QDeclarativeExpression(engine->rootContext(), myObject, "width * 2"); + int result = expr->evaluate().toInt(); // result = 400 + \endcode +*/ + +static int QDeclarativeExpression_notifyIdx = -1; + +/*! + Create an invalid QDeclarativeExpression. + + As the expression will not have an associated QDeclarativeContext, this will be a + null expression object and its value will always be an invalid QVariant. + */ +QDeclarativeExpression::QDeclarativeExpression() +: QObject(*new QDeclarativeExpressionPrivate, 0) +{ + Q_D(QDeclarativeExpression); + + if (QDeclarativeExpression_notifyIdx == -1) + QDeclarativeExpression_notifyIdx = QDeclarativeExpression::staticMetaObject.indexOfMethod("_q_notify()"); + d->setNotifyObject(this, QDeclarativeExpression_notifyIdx); +} + +/*! \internal */ +QDeclarativeExpression::QDeclarativeExpression(QDeclarativeContextData *ctxt, void *expr, + QDeclarativeRefCount *rc, QObject *me, + const QString &url, int lineNumber, + QDeclarativeExpressionPrivate &dd) +: QObject(dd, 0) +{ + Q_D(QDeclarativeExpression); + d->init(ctxt, expr, rc, me, url, lineNumber); + + if (QDeclarativeExpression_notifyIdx == -1) + QDeclarativeExpression_notifyIdx = QDeclarativeExpression::staticMetaObject.indexOfMethod("_q_notify()"); + d->setNotifyObject(this, QDeclarativeExpression_notifyIdx); +} + +/*! + Create a QDeclarativeExpression object that is a child of \a parent. + + The \a expression JavaScript will be executed in the \a ctxt QDeclarativeContext. + If specified, the \a scope object's properties will also be in scope during + the expression's execution. +*/ +QDeclarativeExpression::QDeclarativeExpression(QDeclarativeContext *ctxt, + QObject *scope, + const QString &expression, + QObject *parent) +: QObject(*new QDeclarativeExpressionPrivate, parent) +{ + Q_D(QDeclarativeExpression); + d->init(QDeclarativeContextData::get(ctxt), expression, scope); + + if (QDeclarativeExpression_notifyIdx == -1) + QDeclarativeExpression_notifyIdx = QDeclarativeExpression::staticMetaObject.indexOfMethod("_q_notify()"); + d->setNotifyObject(this, QDeclarativeExpression_notifyIdx); +} + +/*! + \internal +*/ +QDeclarativeExpression::QDeclarativeExpression(QDeclarativeContextData *ctxt, QObject *scope, + const QString &expression) +: QObject(*new QDeclarativeExpressionPrivate, 0) +{ + Q_D(QDeclarativeExpression); + d->init(ctxt, expression, scope); + + if (QDeclarativeExpression_notifyIdx == -1) + QDeclarativeExpression_notifyIdx = QDeclarativeExpression::staticMetaObject.indexOfMethod("_q_notify()"); + d->setNotifyObject(this, QDeclarativeExpression_notifyIdx); +} + +/*! \internal */ +QDeclarativeExpression::QDeclarativeExpression(QDeclarativeContextData *ctxt, QObject *scope, + const QString &expression, QDeclarativeExpressionPrivate &dd) +: QObject(dd, 0) +{ + Q_D(QDeclarativeExpression); + d->init(ctxt, expression, scope); + + if (QDeclarativeExpression_notifyIdx == -1) + QDeclarativeExpression_notifyIdx = QDeclarativeExpression::staticMetaObject.indexOfMethod("_q_notify()"); + d->setNotifyObject(this, QDeclarativeExpression_notifyIdx); +} + +/*! \internal */ +QDeclarativeExpression::QDeclarativeExpression(QDeclarativeContextData *ctxt, QObject *scope, const QScriptValue &func, + QDeclarativeExpressionPrivate &dd) +: QObject(dd, 0) +{ + Q_D(QDeclarativeExpression); + d->init(ctxt, func, scope); + + if (QDeclarativeExpression_notifyIdx == -1) + QDeclarativeExpression_notifyIdx = QDeclarativeExpression::staticMetaObject.indexOfMethod("_q_notify()"); + d->setNotifyObject(this, QDeclarativeExpression_notifyIdx); +} + +/*! + Destroy the QDeclarativeExpression instance. +*/ +QDeclarativeExpression::~QDeclarativeExpression() +{ +} + +/*! + Returns the QDeclarativeEngine this expression is associated with, or 0 if there + is no association or the QDeclarativeEngine has been destroyed. +*/ +QDeclarativeEngine *QDeclarativeExpression::engine() const +{ + Q_D(const QDeclarativeExpression); + return d->context()?d->context()->engine:0; +} + +/*! + Returns the QDeclarativeContext this expression is associated with, or 0 if there + is no association or the QDeclarativeContext has been destroyed. +*/ +QDeclarativeContext *QDeclarativeExpression::context() const +{ + Q_D(const QDeclarativeExpression); + QDeclarativeContextData *data = d->context(); + return data?data->asQDeclarativeContext():0; +} + +/*! + Returns the expression string. +*/ +QString QDeclarativeExpression::expression() const +{ + Q_D(const QDeclarativeExpression); + return d->expression; +} + +/*! + Set the expression to \a expression. +*/ +void QDeclarativeExpression::setExpression(const QString &expression) +{ + Q_D(QDeclarativeExpression); + + d->resetNotifyOnChange(); + d->expression = expression; + d->expressionFunctionValid = false; + d->expressionFunction = QScriptValue(); +} + +void QDeclarativeExpressionPrivate::exceptionToError(QScriptEngine *scriptEngine, + QDeclarativeError &error) +{ + if (scriptEngine->hasUncaughtException() && + scriptEngine->uncaughtException().isError()) { + + QString fileName; + int lineNumber = scriptEngine->uncaughtExceptionLineNumber(); + + QScriptValue exception = scriptEngine->uncaughtException(); + QLatin1String fileNameProp("fileName"); + + if (!exception.property(fileNameProp).toString().isEmpty()){ + fileName = exception.property(fileNameProp).toString(); + } else { + fileName = QLatin1String(""); + } + + error.setUrl(QUrl(fileName)); + error.setLine(lineNumber); + error.setColumn(-1); + error.setDescription(exception.toString()); + } else { + error = QDeclarativeError(); + } +} + +bool QDeclarativeQtScriptExpression::notifyOnValueChange() const +{ + return trackChange; +} + +void QDeclarativeQtScriptExpression::setNotifyOnValueChange(bool notify) +{ + trackChange = notify; + if (!notify && guardList) + clearGuards(); +} + +void QDeclarativeQtScriptExpression::resetNotifyOnChange() +{ + clearGuards(); +} + +void QDeclarativeQtScriptExpression::setNotifyObject(QObject *object, int notifyIndex) +{ + if (guardList) clearGuards(); + + if (!object || notifyIndex == -1) { + guardObject = 0; + notifyIndex = -1; + } else { + guardObject = object; + guardObjectNotifyIndex = notifyIndex; + + } +} + +void QDeclarativeQtScriptExpression::setEvaluateFlags(EvaluateFlags flags) +{ + evalFlags = flags; +} + +QDeclarativeQtScriptExpression::EvaluateFlags QDeclarativeQtScriptExpression::evaluateFlags() const +{ + return evalFlags; +} + +QScriptValue QDeclarativeQtScriptExpression::scriptValue(QObject *secondaryScope, bool *isUndefined) +{ + Q_ASSERT(context() && context()->engine); + Q_ASSERT(!trackChange || (guardObject && guardObjectNotifyIndex != -1)); + + if (!expressionFunction.isValid()) { + if (isUndefined) *isUndefined = true; + return QScriptValue(); + } + + DeleteWatcher watcher(this); + + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(context()->engine); + + bool lastCaptureProperties = ep->captureProperties; + QPODVector lastCapturedProperties; + ep->captureProperties = trackChange; + ep->capturedProperties.copyAndClear(lastCapturedProperties); + + QScriptValue value = eval(secondaryScope, isUndefined); + + if (!watcher.wasDeleted() && trackChange) { + if (ep->capturedProperties.count() == 0) { + + if (guardList) clearGuards(); + + } else { + + updateGuards(ep->capturedProperties); + + } + } + + lastCapturedProperties.copyAndClear(ep->capturedProperties); + ep->captureProperties = lastCaptureProperties; + + return value; +} + +QScriptValue QDeclarativeQtScriptExpression::eval(QObject *secondaryScope, bool *isUndefined) +{ + Q_ASSERT(context() && context()->engine); + + DeleteWatcher watcher(this); + + QDeclarativeEngine *engine = context()->engine; + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); + + QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine); + + QDeclarativeContextData *oldSharedContext = 0; + QObject *oldSharedScope = 0; + QObject *oldOverride = 0; + bool isShared = (expressionFunctionMode == SharedContext); + + if (isShared) { + oldSharedContext = ep->sharedContext; + oldSharedScope = ep->sharedScope; + ep->sharedContext = context(); + ep->sharedScope = scopeObject; + } else { + oldOverride = ep->contextClass->setOverrideObject(expressionContext, secondaryScope); + } + + QScriptValue thisObject; + if (evalFlags & RequiresThisObject) + thisObject = ep->objectClass->newQObject(scopeObject); + QScriptValue svalue = expressionFunction.call(thisObject); // This could cause this c++ object to be deleted + + if (isShared) { + ep->sharedContext = oldSharedContext; + ep->sharedScope = oldSharedScope; + } else if (!watcher.wasDeleted()) { + ep->contextClass->setOverrideObject(expressionContext, oldOverride); + } + + if (isUndefined) + *isUndefined = svalue.isUndefined() || scriptEngine->hasUncaughtException(); + + // Handle exception + if (scriptEngine->hasUncaughtException()) { + if (!watcher.wasDeleted()) + QDeclarativeExpressionPrivate::exceptionToError(scriptEngine, error); + + scriptEngine->clearExceptions(); + return QScriptValue(); + } else { + if (!watcher.wasDeleted()) + error = QDeclarativeError(); + + return svalue; + } +} + +void QDeclarativeQtScriptExpression::updateGuards(const QPODVector &properties) +{ + Q_ASSERT(guardObject); + Q_ASSERT(guardObjectNotifyIndex != -1); + + if (properties.count() != guardListLength) { + QDeclarativeNotifierEndpoint *newGuardList = new QDeclarativeNotifierEndpoint[properties.count()]; + + for (int ii = 0; ii < qMin(guardListLength, properties.count()); ++ii) + guardList[ii].copyAndClear(newGuardList[ii]); + + delete [] guardList; + guardList = newGuardList; + guardListLength = properties.count(); + } + + bool outputWarningHeader = false; + bool noChanges = true; + for (int ii = 0; ii < properties.count(); ++ii) { + QDeclarativeNotifierEndpoint &guard = guardList[ii]; + const QDeclarativeEnginePrivate::CapturedProperty &property = properties.at(ii); + + guard.target = guardObject; + guard.targetMethod = guardObjectNotifyIndex; + + if (property.notifier != 0) { + + if (!noChanges && guard.isConnected(property.notifier)) { + // Nothing to do + + } else { + noChanges = false; + + bool existing = false; + for (int jj = 0; !existing && jj < ii; ++jj) + if (guardList[jj].isConnected(property.notifier)) + existing = true; + + if (existing) { + // duplicate + guard.disconnect(); + } else { + guard.connect(property.notifier); + } + } + + + } else if (property.notifyIndex != -1) { + + if (!noChanges && guard.isConnected(property.object, property.notifyIndex)) { + // Nothing to do + + } else { + noChanges = false; + + bool existing = false; + for (int jj = 0; !existing && jj < ii; ++jj) + if (guardList[jj].isConnected(property.object, property.notifyIndex)) + existing = true; + + if (existing) { + // duplicate + guard.disconnect(); + } else { + guard.connect(property.object, property.notifyIndex); + } + } + + } else { + if (!outputWarningHeader) { + outputWarningHeader = true; + qWarning() << "QDeclarativeExpression: Expression" << expression + << "depends on non-NOTIFYable properties:"; + } + + const QMetaObject *metaObj = property.object->metaObject(); + QMetaProperty metaProp = metaObj->property(property.coreIndex); + + qWarning().nospace() << " " << metaObj->className() << "::" << metaProp.name(); + } + } +} + +QScriptValue QDeclarativeExpressionPrivate::scriptValue(QObject *secondaryScope, bool *isUndefined) +{ + if (!expressionFunctionValid) { + QDeclarativeEngine *engine = context()->engine; + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); + + QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine); + + QScriptContext *scriptContext = QScriptDeclarativeClass::pushCleanContext(scriptEngine); + expressionContext = ep->contextClass->newContext(context(), scopeObject); + scriptContext->pushScope(expressionContext); + scriptContext->pushScope(ep->globalClass->staticGlobalObject()); + + QDeclarativeRewrite::RewriteBinding rewriteBinding; + rewriteBinding.setName(name); + bool ok = true; + const QString code = rewriteBinding(expression, &ok); + if (ok) + expressionFunction = scriptEngine->evaluate(code, url, line); + + scriptEngine->popContext(); + expressionFunctionMode = ExplicitContext; + expressionFunctionValid = true; + } + + return QDeclarativeQtScriptExpression::scriptValue(secondaryScope, isUndefined); +} + +QVariant QDeclarativeExpressionPrivate::value(QObject *secondaryScope, bool *isUndefined) +{ + Q_Q(QDeclarativeExpression); + + if (!context() || !context()->isValid()) { + qWarning("QDeclarativeExpression: Attempted to evaluate an expression in an invalid context"); + return QVariant(); + } + + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(q->engine()); + + return ep->scriptValueToVariant(scriptValue(secondaryScope, isUndefined), qMetaTypeId >()); +} + +/*! + Evaulates the expression, returning the result of the evaluation, + or an invalid QVariant if the expression is invalid or has an error. + + \a valueIsUndefined is set to true if the expression resulted in an + undefined value. + + \sa hasError(), error() +*/ +QVariant QDeclarativeExpression::evaluate(bool *valueIsUndefined) +{ + Q_D(QDeclarativeExpression); + return d->value(0, valueIsUndefined); +} + +/*! +Returns true if the valueChanged() signal is emitted when the expression's evaluated +value changes. +*/ +bool QDeclarativeExpression::notifyOnValueChanged() const +{ + Q_D(const QDeclarativeExpression); + return d->notifyOnValueChange(); +} + +/*! + Sets whether the valueChanged() signal is emitted when the + expression's evaluated value changes. + + If \a notifyOnChange is true, the QDeclarativeExpression will + monitor properties involved in the expression's evaluation, and emit + QDeclarativeExpression::valueChanged() if they have changed. This + allows an application to ensure that any value associated with the + result of the expression remains up to date. + + If \a notifyOnChange is false (default), the QDeclarativeExpression + will not montitor properties involved in the expression's + evaluation, and QDeclarativeExpression::valueChanged() will never be + emitted. This is more efficient if an application wants a "one off" + evaluation of the expression. +*/ +void QDeclarativeExpression::setNotifyOnValueChanged(bool notifyOnChange) +{ + Q_D(QDeclarativeExpression); + d->setNotifyOnValueChange(notifyOnChange); +} + +/*! + Returns the source file URL for this expression. The source location must + have been previously set by calling setSourceLocation(). +*/ +QString QDeclarativeExpression::sourceFile() const +{ + Q_D(const QDeclarativeExpression); + return d->url; +} + +/*! + Returns the source file line number for this expression. The source location + must have been previously set by calling setSourceLocation(). +*/ +int QDeclarativeExpression::lineNumber() const +{ + Q_D(const QDeclarativeExpression); + return d->line; +} + +/*! + Set the location of this expression to \a line of \a url. This information + is used by the script engine. +*/ +void QDeclarativeExpression::setSourceLocation(const QString &url, int line) +{ + Q_D(QDeclarativeExpression); + d->url = url; + d->line = line; +} + +/*! + Returns the expression's scope object, if provided, otherwise 0. + + In addition to data provided by the expression's QDeclarativeContext, the scope + object's properties are also in scope during the expression's evaluation. +*/ +QObject *QDeclarativeExpression::scopeObject() const +{ + Q_D(const QDeclarativeExpression); + return d->scopeObject; +} + +/*! + Returns true if the last call to evaluate() resulted in an error, + otherwise false. + + \sa error(), clearError() +*/ +bool QDeclarativeExpression::hasError() const +{ + Q_D(const QDeclarativeExpression); + return d->error.isValid(); +} + +/*! + Clear any expression errors. Calls to hasError() following this will + return false. + + \sa hasError(), error() +*/ +void QDeclarativeExpression::clearError() +{ + Q_D(QDeclarativeExpression); + d->error = QDeclarativeError(); +} + +/*! + Return any error from the last call to evaluate(). If there was no error, + this returns an invalid QDeclarativeError instance. + + \sa hasError(), clearError() +*/ + +QDeclarativeError QDeclarativeExpression::error() const +{ + Q_D(const QDeclarativeExpression); + return d->error; +} + +/*! \internal */ +void QDeclarativeExpressionPrivate::_q_notify() +{ + emitValueChanged(); +} + +void QDeclarativeQtScriptExpression::clearGuards() +{ + delete [] guardList; + guardList = 0; + guardListLength = 0; +} + +/*! + \fn void QDeclarativeExpression::valueChanged() + + Emitted each time the expression value changes from the last time it was + evaluated. The expression must have been evaluated at least once (by + calling QDeclarativeExpression::evaluate()) before this signal will be emitted. +*/ + +void QDeclarativeExpressionPrivate::emitValueChanged() +{ + Q_Q(QDeclarativeExpression); + emit q->valueChanged(); +} + +QDeclarativeAbstractExpression::QDeclarativeAbstractExpression() +: m_context(0), m_prevExpression(0), m_nextExpression(0) +{ +} + +QDeclarativeAbstractExpression::~QDeclarativeAbstractExpression() +{ + if (m_prevExpression) { + *m_prevExpression = m_nextExpression; + if (m_nextExpression) + m_nextExpression->m_prevExpression = m_prevExpression; + } +} + +QDeclarativeContextData *QDeclarativeAbstractExpression::context() const +{ + return m_context; +} + +void QDeclarativeAbstractExpression::setContext(QDeclarativeContextData *context) +{ + if (m_prevExpression) { + *m_prevExpression = m_nextExpression; + if (m_nextExpression) + m_nextExpression->m_prevExpression = m_prevExpression; + m_prevExpression = 0; + m_nextExpression = 0; + } + + m_context = context; + + if (m_context) { + m_nextExpression = m_context->expressions; + if (m_nextExpression) + m_nextExpression->m_prevExpression = &m_nextExpression; + m_prevExpression = &context->expressions; + m_context->expressions = this; + } +} + +void QDeclarativeAbstractExpression::refresh() +{ +} + +bool QDeclarativeAbstractExpression::isValid() const +{ + return m_context != 0; +} + +QT_END_NAMESPACE + +#include diff --git a/src/declarative/qml/qdeclarativeexpression.h b/src/declarative/qml/qdeclarativeexpression.h new file mode 100644 index 00000000..732aa747 --- /dev/null +++ b/src/declarative/qml/qdeclarativeexpression.h @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEEXPRESSION_H +#define QDECLARATIVEEXPRESSION_H + +#include + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QString; +class QDeclarativeRefCount; +class QDeclarativeEngine; +class QDeclarativeContext; +class QDeclarativeExpressionPrivate; +class QDeclarativeContextData; +class QScriptValue; +class Q_DECLARATIVE_EXPORT QDeclarativeExpression : public QObject +{ + Q_OBJECT +public: + QDeclarativeExpression(); + QDeclarativeExpression(QDeclarativeContext *, QObject *, const QString &, QObject * = 0); + virtual ~QDeclarativeExpression(); + + QDeclarativeEngine *engine() const; + QDeclarativeContext *context() const; + + QString expression() const; + void setExpression(const QString &); + + bool notifyOnValueChanged() const; + void setNotifyOnValueChanged(bool); + + QString sourceFile() const; + int lineNumber() const; + void setSourceLocation(const QString &fileName, int line); + + QObject *scopeObject() const; + + bool hasError() const; + void clearError(); + QDeclarativeError error() const; + + QVariant evaluate(bool *valueIsUndefined = 0); + +Q_SIGNALS: + void valueChanged(); + +protected: + QDeclarativeExpression(QDeclarativeContextData *, QObject *, const QString &, + QDeclarativeExpressionPrivate &dd); + QDeclarativeExpression(QDeclarativeContextData *, QObject *, const QScriptValue &, + QDeclarativeExpressionPrivate &dd); + QDeclarativeExpression(QDeclarativeContextData *, void *, QDeclarativeRefCount *rc, + QObject *me, const QString &, int, QDeclarativeExpressionPrivate &dd); + +private: + QDeclarativeExpression(QDeclarativeContextData *, QObject *, const QString &); + + Q_DISABLE_COPY(QDeclarativeExpression) + Q_DECLARE_PRIVATE(QDeclarativeExpression) + Q_PRIVATE_SLOT(d_func(), void _q_notify()) + friend class QDeclarativeDebugger; + friend class QDeclarativeContext; + friend class QDeclarativeVME; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEEXPRESSION_H + diff --git a/src/declarative/qml/qdeclarativeexpression_p.h b/src/declarative/qml/qdeclarativeexpression_p.h new file mode 100644 index 00000000..bc47fb4b --- /dev/null +++ b/src/declarative/qml/qdeclarativeexpression_p.h @@ -0,0 +1,233 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEEXPRESSION_P_H +#define QDECLARATIVEEXPRESSION_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 "qdeclarativeexpression.h" + +#include "private/qdeclarativeengine_p.h" +#include "private/qdeclarativeguard_p.h" + +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeAbstractExpression +{ +public: + QDeclarativeAbstractExpression(); + virtual ~QDeclarativeAbstractExpression(); + + bool isValid() const; + + QDeclarativeContextData *context() const; + void setContext(QDeclarativeContextData *); + + virtual void refresh(); + +private: + friend class QDeclarativeContext; + friend class QDeclarativeContextData; + friend class QDeclarativeContextPrivate; + QDeclarativeContextData *m_context; + QDeclarativeAbstractExpression **m_prevExpression; + QDeclarativeAbstractExpression *m_nextExpression; +}; + +class QDeclarativeDelayedError +{ +public: + inline QDeclarativeDelayedError() : nextError(0), prevError(0) {} + inline ~QDeclarativeDelayedError() { removeError(); } + + QDeclarativeError error; + + bool addError(QDeclarativeEnginePrivate *); + + inline void removeError() { + if (!prevError) return; + if (nextError) nextError->prevError = prevError; + *prevError = nextError; + nextError = 0; + prevError = 0; + } + +private: + QDeclarativeDelayedError *nextError; + QDeclarativeDelayedError **prevError; +}; + +class QDeclarativeQtScriptExpression : public QDeclarativeAbstractExpression, + public QDeclarativeDelayedError +{ +public: + enum Mode { SharedContext, ExplicitContext }; + + enum EvaluateFlag { RequiresThisObject = 0x01 }; + Q_DECLARE_FLAGS(EvaluateFlags, EvaluateFlag) + + QDeclarativeQtScriptExpression(); + virtual ~QDeclarativeQtScriptExpression(); + + QDeclarativeRefCount *dataRef; + + QString expression; + + Mode expressionFunctionMode; + QScriptValue expressionFunction; + + QScriptValue expressionContext; // Only used in ExplicitContext + QObject *scopeObject; // Only used in SharedContext + + bool notifyOnValueChange() const; + void setNotifyOnValueChange(bool); + void resetNotifyOnChange(); + void setNotifyObject(QObject *, int ); + + void setEvaluateFlags(EvaluateFlags flags); + EvaluateFlags evaluateFlags() const; + + QScriptValue scriptValue(QObject *secondaryScope, bool *isUndefined); + + class DeleteWatcher { + public: + inline DeleteWatcher(QDeclarativeQtScriptExpression *data); + inline ~DeleteWatcher(); + inline bool wasDeleted() const; + private: + bool *m_wasDeleted; + bool m_wasDeletedStorage; + QDeclarativeQtScriptExpression *m_d; + }; + +private: + void clearGuards(); + QScriptValue eval(QObject *secondaryScope, bool *isUndefined); + void updateGuards(const QPODVector &properties); + + bool trackChange; + + QDeclarativeNotifierEndpoint *guardList; + int guardListLength; + + QObject *guardObject; + int guardObjectNotifyIndex; + bool *deleted; + + EvaluateFlags evalFlags; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QDeclarativeQtScriptExpression::EvaluateFlags) + + +class QDeclarativeExpression; +class QString; +class QDeclarativeExpressionPrivate : public QObjectPrivate, public QDeclarativeQtScriptExpression +{ + Q_DECLARE_PUBLIC(QDeclarativeExpression) +public: + QDeclarativeExpressionPrivate(); + ~QDeclarativeExpressionPrivate(); + + void init(QDeclarativeContextData *, const QString &, QObject *); + void init(QDeclarativeContextData *, const QScriptValue &, QObject *); + void init(QDeclarativeContextData *, void *, QDeclarativeRefCount *, QObject *, const QString &, int); + + QVariant value(QObject *secondaryScope = 0, bool *isUndefined = 0); + QScriptValue scriptValue(QObject *secondaryScope = 0, bool *isUndefined = 0); + + static QDeclarativeExpressionPrivate *get(QDeclarativeExpression *expr) { + return static_cast(QObjectPrivate::get(expr)); + } + static QDeclarativeExpression *get(QDeclarativeExpressionPrivate *expr) { + return expr->q_func(); + } + + void _q_notify(); + virtual void emitValueChanged(); + + static void exceptionToError(QScriptEngine *, QDeclarativeError &); + static QScriptValue evalInObjectScope(QDeclarativeContextData *, QObject *, const QString &, const QString &, + int, QScriptValue *); + static QScriptValue evalInObjectScope(QDeclarativeContextData *, QObject *, const QScriptProgram &, + QScriptValue *); + + bool expressionFunctionValid:1; + + QString url; // This is a QString for a reason. QUrls are slooooooow... + int line; + QByteArray name; //function name, hint for the debugger +}; + +QDeclarativeQtScriptExpression::DeleteWatcher::DeleteWatcher(QDeclarativeQtScriptExpression *data) +: m_wasDeletedStorage(false), m_d(data) +{ + if (!m_d->deleted) + m_d->deleted = &m_wasDeletedStorage; + m_wasDeleted = m_d->deleted; +} + +QDeclarativeQtScriptExpression::DeleteWatcher::~DeleteWatcher() +{ + if (false == *m_wasDeleted && m_wasDeleted == m_d->deleted) + m_d->deleted = 0; +} + +bool QDeclarativeQtScriptExpression::DeleteWatcher::wasDeleted() const +{ + return *m_wasDeleted; +} + +QT_END_NAMESPACE + +#endif // QDECLARATIVEEXPRESSION_P_H diff --git a/src/declarative/qml/qdeclarativeextensioninterface.h b/src/declarative/qml/qdeclarativeextensioninterface.h new file mode 100644 index 00000000..695d345f --- /dev/null +++ b/src/declarative/qml/qdeclarativeextensioninterface.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEEXTENSIONINTERFACE_H +#define QDECLARATIVEEXTENSIONINTERFACE_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeEngine; + +struct Q_DECLARATIVE_EXPORT QDeclarativeExtensionInterface +{ + virtual ~QDeclarativeExtensionInterface() {} + virtual void registerTypes(const char *uri) = 0; + virtual void initializeEngine(QDeclarativeEngine *engine, const char *uri) = 0; +}; + +Q_DECLARE_INTERFACE(QDeclarativeExtensionInterface, "com.trolltech.Qt.QDeclarativeExtensionInterface/1.0") + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEEXTENSIONINTERFACE_H diff --git a/src/declarative/qml/qdeclarativeextensionplugin.cpp b/src/declarative/qml/qdeclarativeextensionplugin.cpp new file mode 100644 index 00000000..c93e4db6 --- /dev/null +++ b/src/declarative/qml/qdeclarativeextensionplugin.cpp @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativeextensionplugin.h" + +QT_BEGIN_NAMESPACE + +/*! + \since 4.7 + \class QDeclarativeExtensionPlugin + \brief The QDeclarativeExtensionPlugin class provides an abstract base for custom QML extension plugins. + + \ingroup plugins + + QDeclarativeExtensionPlugin is a plugin interface that makes it possible to + create QML extensions that can be loaded dynamically into QML applications. + These extensions allow custom QML types to be made available to the QML engine. + + To write a QML extension plugin: + + \list + \o Subclass QDeclarativeExtensionPlugin, implement registerTypes() method + to register types using qmlRegisterType(), and export the class using the Q_EXPORT_PLUGIN2() macro + \o Write an appropriate project file for the plugin + \o Create a \l{Writing a qmldir file}{qmldir file} to describe the plugin + \endlist + + QML extension plugins can be used to provide either application-specific or + library-like plugins. Library plugins should limit themselves to registering types, + as any manipulation of the engine's root context may cause conflicts + or other issues in the library user's code. + + + \section1 An example + + Suppose there is a new \c TimeModel C++ class that should be made available + as a new QML element. It provides the current time through \c hour and \c minute + properties, like this: + + \snippet examples/declarative/cppextensions/plugins/plugin.cpp 0 + \dots + + To make this class available as a QML type, create a plugin that registers + this type with a specific \l {QML Modules}{module} using qmlRegisterType(). For this example the plugin + module will be named \c com.nokia.TimeExample (as defined in the project + file further below). + + \snippet examples/declarative/cppextensions/plugins/plugin.cpp plugin + \codeline + \snippet examples/declarative/cppextensions/plugins/plugin.cpp export + + This registers the \c TimeModel class with the 1.0 version of this + plugin library, as a QML type called \c Time. The Q_ASSERT statement + ensures the module is imported correctly by any QML components that use this plugin. + + The project file defines the project as a plugin library and specifies + it should be built into the \c com/nokia/TimeExample directory: + + \code + TEMPLATE = lib + CONFIG += qt plugin + QT += declarative + + DESTDIR = com/nokia/TimeExample + TARGET = qmlqtimeexampleplugin + ... + \endcode + + Finally, a \l{Writing a qmldir file}{qmldir file} is required in the \c com/nokia/TimeExample directory + that describes the plugin. This directory includes a \c Clock.qml file that + should be bundled with the plugin, so it needs to be specified in the \c qmldir + file: + + \quotefile examples/declarative/cppextensions/plugins/com/nokia/TimeExample/qmldir + + Once the project is built and installed, the new \c Time element can be + used by any QML component that imports the \c com.nokia.TimeExample module: + + \snippet examples/declarative/cppextensions/plugins/plugins.qml 0 + + The full source code is available in the \l {declarative/cppextensions/plugins}{plugins example}. + + The \l {Tutorial: Writing QML extensions with C++} also contains a chapter + on creating QML plugins. + + \sa QDeclarativeEngine::importPlugin(), {How to Create Qt Plugins} +*/ + +/*! + \fn void QDeclarativeExtensionPlugin::registerTypes(const char *uri) + + Registers the QML types in the given \a uri. Subclasses should implement + this to call qmlRegisterType() for all types which are provided by the extension + plugin. + + The \a uri is an identifier for the plugin generated by the QML engine + based on the name and path of the extension's plugin library. +*/ + +/*! + Constructs a QML extension plugin with the given \a parent. + + Note that this constructor is invoked automatically by the + Q_EXPORT_PLUGIN2() macro, so there is no need for calling it + explicitly. +*/ +QDeclarativeExtensionPlugin::QDeclarativeExtensionPlugin(QObject *parent) + : QObject(parent) +{ +} + +/*! + \internal + */ +QDeclarativeExtensionPlugin::~QDeclarativeExtensionPlugin() +{ +} + +/*! + \fn void QDeclarativeExtensionPlugin::initializeEngine(QDeclarativeEngine *engine, const char *uri) + + Initializes the extension from the \a uri using the \a engine. Here an application + plugin might, for example, expose some data or objects to QML, + as context properties on the engine's root context. +*/ + +void QDeclarativeExtensionPlugin::initializeEngine(QDeclarativeEngine *engine, const char *uri) +{ + Q_UNUSED(engine); + Q_UNUSED(uri); +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativeextensionplugin.h b/src/declarative/qml/qdeclarativeextensionplugin.h new file mode 100644 index 00000000..7ae49870 --- /dev/null +++ b/src/declarative/qml/qdeclarativeextensionplugin.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEEXTENSIONPLUGIN_H +#define QDECLARATIVEEXTENSIONPLUGIN_H + +#include + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeEngine; + +class Q_DECLARATIVE_EXPORT QDeclarativeExtensionPlugin : public QObject, public QDeclarativeExtensionInterface +{ + Q_OBJECT + Q_INTERFACES(QDeclarativeExtensionInterface) +public: + explicit QDeclarativeExtensionPlugin(QObject *parent = 0); + ~QDeclarativeExtensionPlugin(); + + virtual void registerTypes(const char *uri) = 0; + virtual void initializeEngine(QDeclarativeEngine *engine, const char *uri); + +private: + Q_DISABLE_COPY(QDeclarativeExtensionPlugin) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEEXTENSIONPLUGIN_H diff --git a/src/declarative/qml/qdeclarativefastproperties.cpp b/src/declarative/qml/qdeclarativefastproperties.cpp new file mode 100644 index 00000000..2c4d6223 --- /dev/null +++ b/src/declarative/qml/qdeclarativefastproperties.cpp @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativefastproperties_p.h" + +#include + +QT_BEGIN_NAMESPACE + +// Adding entries to the QDeclarativeFastProperties class allows the QML +// binding optimizer to bypass Qt's meta system and read and, more +// importantly, subscribe to properties directly. Any property that is +// primarily read from bindings is a candidate for inclusion as a fast +// property. + +static void QObject_objectName(QObject *object, void *output, QDeclarativeNotifierEndpoint *endpoint) +{ + if (endpoint) + endpoint->connect(QDeclarativeData::get(object, true)->objectNameNotifier()); + *((QString *)output) = object->objectName(); +} + +QDeclarativeFastProperties::QDeclarativeFastProperties() +{ + add(&QDeclarativeItem::staticMetaObject, QDeclarativeItem::staticMetaObject.indexOfProperty("parent"), + QDeclarativeItemPrivate::parentProperty); + add(&QObject::staticMetaObject, QObject::staticMetaObject.indexOfProperty("objectName"), + QObject_objectName); +} + +int QDeclarativeFastProperties::accessorIndexForProperty(const QMetaObject *metaObject, int propertyIndex) +{ + Q_ASSERT(metaObject); + Q_ASSERT(propertyIndex >= 0); + + // Find the "real" metaObject + while (metaObject->propertyOffset() > propertyIndex) + metaObject = metaObject->superClass(); + + QHash, int>::Iterator iter = + m_index.find(qMakePair(metaObject, propertyIndex)); + if (iter != m_index.end()) + return *iter; + else + return -1; +} + +void QDeclarativeFastProperties::add(const QMetaObject *metaObject, int propertyIndex, Accessor accessor) +{ + Q_ASSERT(metaObject); + Q_ASSERT(propertyIndex >= 0); + + // Find the "real" metaObject + while (metaObject->propertyOffset() > propertyIndex) + metaObject = metaObject->superClass(); + + QPair data = qMakePair(metaObject, propertyIndex); + int accessorIndex = m_accessors.count(); + m_accessors.append(accessor); + m_index.insert(data, accessorIndex); +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativefastproperties_p.h b/src/declarative/qml/qdeclarativefastproperties_p.h new file mode 100644 index 00000000..e81f5642 --- /dev/null +++ b/src/declarative/qml/qdeclarativefastproperties_p.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEFASTPROPERTIES_P_H +#define QDECLARATIVEFASTPROPERTIES_P_H + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QObject; +class QDeclarativeNotifierEndpoint; +class QDeclarativeFastProperties +{ +public: + typedef void (*Accessor)(QObject *object, void *output, QDeclarativeNotifierEndpoint *endpoint); + + QDeclarativeFastProperties(); + + Accessor accessor(int index) const { return m_accessors.at(index); } + int accessorIndexForProperty(const QMetaObject *, int); + +private: + void add(const QMetaObject *, int, Accessor); + + QHash, int> m_index; + QVector m_accessors; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEFASTPROPERTIES_P_H diff --git a/src/declarative/qml/qdeclarativeglobal_p.h b/src/declarative/qml/qdeclarativeglobal_p.h new file mode 100644 index 00000000..f46f44e5 --- /dev/null +++ b/src/declarative/qml/qdeclarativeglobal_p.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEGLOBAL_H +#define QDECLARATIVEGLOBAL_H + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +#define DEFINE_BOOL_CONFIG_OPTION(name, var) \ + static bool name() \ + { \ + static enum { Yes, No, Unknown } status = Unknown; \ + if (status == Unknown) { \ + QByteArray v = qgetenv(#var); \ + bool value = !v.isEmpty() && v != "0" && v != "false"; \ + if (value) status = Yes; \ + else status = No; \ + } \ + return status == Yes; \ + } + +#ifdef Q_OS_SYMBIAN +#define Q_DECLARATIVE_PRIVATE_EXPORT Q_AUTOTEST_EXPORT +#else +#define Q_DECLARATIVE_PRIVATE_EXPORT Q_DECLARATIVE_EXPORT +#endif + +struct QDeclarativeGraphics_DerivedObject : public QObject +{ + void setParent_noEvent(QObject *parent) { + bool sce = d_ptr->sendChildEvents; + d_ptr->sendChildEvents = false; + setParent(parent); + d_ptr->sendChildEvents = sce; + } +}; + +/*! + Returns true if the case of \a fileName is equivalent to the file case of + \a fileName on disk, and false otherwise. + + This is used to ensure that the behavior of QML on a case-insensitive file + system is the same as on a case-sensitive file system. This function + performs a "best effort" attempt to determine the real case of the file. + It may have false positives (say the case is correct when it isn't), but it + should never have a false negative (say the case is incorrect when it is + correct). +*/ +bool QDeclarative_isFileCaseCorrect(const QString &fileName); + +/*! + Makes the \a object a child of \a parent. Note that when using this method, + neither \a parent nor the object's previous parent (if it had one) will + receive ChildRemoved or ChildAdded events. +*/ +inline void QDeclarative_setParent_noEvent(QObject *object, QObject *parent) +{ + static_cast(object)->setParent_noEvent(parent); +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEGLOBAL_H diff --git a/src/declarative/qml/qdeclarativeglobalscriptclass.cpp b/src/declarative/qml/qdeclarativeglobalscriptclass.cpp new file mode 100644 index 00000000..724c36c3 --- /dev/null +++ b/src/declarative/qml/qdeclarativeglobalscriptclass.cpp @@ -0,0 +1,147 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativeglobalscriptclass_p.h" + +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +/* + Used to prevent any writes to the global object. +*/ +QDeclarativeGlobalScriptClass::QDeclarativeGlobalScriptClass(QScriptEngine *engine) +: QScriptClass(engine) +{ + QString eval = QLatin1String("eval"); + QString version = QLatin1String("version"); + + QScriptValue originalGlobalObject = engine->globalObject(); + + QScriptValue newGlobalObject = engine->newObject(); + + { + QScriptValueIterator iter(originalGlobalObject); + QVector names; + QVector values; + QVector flags; + while (iter.hasNext()) { + iter.next(); + + QString name = iter.name(); + + if (name == version) + continue; + + if (name != eval) { + names.append(name); + values.append(iter.value()); + flags.append(iter.flags() | QScriptValue::Undeletable); + } + newGlobalObject.setProperty(iter.scriptName(), iter.value()); + + m_illegalNames.insert(name); + } + m_staticGlobalObject = QScriptDeclarativeClass::newStaticScopeObject( + engine, names.size(), names.constData(), values.constData(), flags.constData()); + } + + newGlobalObject.setScriptClass(this); + engine->setGlobalObject(newGlobalObject); +} + +QScriptClass::QueryFlags +QDeclarativeGlobalScriptClass::queryProperty(const QScriptValue &object, + const QScriptString &name, + QueryFlags flags, uint *id) +{ + Q_UNUSED(object); + Q_UNUSED(name); + Q_UNUSED(flags); + Q_UNUSED(id); + return HandlesWriteAccess; +} + +void QDeclarativeGlobalScriptClass::setProperty(QScriptValue &object, + const QScriptString &name, + uint id, const QScriptValue &value) +{ + Q_UNUSED(object); + Q_UNUSED(id); + Q_UNUSED(value); + QString error = QLatin1String("Invalid write to global property \"") + + name.toString() + QLatin1Char('\"'); + engine()->currentContext()->throwError(error); +} + +/* This method is for the use of tst_qdeclarativeecmascript::callQtInvokables() only */ +void QDeclarativeGlobalScriptClass::explicitSetProperty(const QStringList &names, const QList &values) +{ + Q_ASSERT(names.count() == values.count()); + QScriptValue globalObject = engine()->globalObject(); + + QScriptValue v = engine()->newObject(); + + QScriptValueIterator iter(v); + while (iter.hasNext()) { + iter.next(); + v.setProperty(iter.scriptName(), iter.value()); + } + + for (int ii = 0; ii < names.count(); ++ii) { + const QString &name = names.at(ii); + const QScriptValue &value = values.at(ii); + v.setProperty(name, value); + } + + v.setScriptClass(this); + + engine()->setGlobalObject(v); +} + +QT_END_NAMESPACE + diff --git a/src/declarative/qml/qdeclarativeglobalscriptclass_p.h b/src/declarative/qml/qdeclarativeglobalscriptclass_p.h new file mode 100644 index 00000000..2e751e18 --- /dev/null +++ b/src/declarative/qml/qdeclarativeglobalscriptclass_p.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEGLOBALSCRIPTCLASS_P_H +#define QDECLARATIVEGLOBALSCRIPTCLASS_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 +#include + +QT_BEGIN_NAMESPACE + +class Q_AUTOTEST_EXPORT QDeclarativeGlobalScriptClass : public QScriptClass +{ +public: + QDeclarativeGlobalScriptClass(QScriptEngine *); + + virtual QueryFlags queryProperty(const QScriptValue &object, + const QScriptString &name, + QueryFlags flags, uint *id); + + virtual void setProperty(QScriptValue &object, const QScriptString &name, + uint id, const QScriptValue &value); + + void explicitSetProperty(const QStringList &, const QList &); + + const QScriptValue &staticGlobalObject() const { return m_staticGlobalObject; } + + const QSet &illegalNames() const { return m_illegalNames; } + +private: + QSet m_illegalNames; + QScriptValue m_staticGlobalObject; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVEGLOBALSCRIPTCLASS_P_H diff --git a/src/declarative/qml/qdeclarativeguard_p.h b/src/declarative/qml/qdeclarativeguard_p.h new file mode 100644 index 00000000..0685ed2f --- /dev/null +++ b/src/declarative/qml/qdeclarativeguard_p.h @@ -0,0 +1,214 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEGUARD_P_H +#define QDECLARATIVEGUARD_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeGuardImpl +{ +public: + inline QDeclarativeGuardImpl(); + inline QDeclarativeGuardImpl(QObject *); + inline QDeclarativeGuardImpl(const QDeclarativeGuardImpl &); + inline ~QDeclarativeGuardImpl(); + + QObject *o; + QDeclarativeGuardImpl *next; + QDeclarativeGuardImpl **prev; + + inline void addGuard(); + inline void remGuard(); +}; + +class QObject; +template +class QDeclarativeGuard : private QDeclarativeGuardImpl +{ + friend class QDeclarativeData; +public: + inline QDeclarativeGuard(); + inline QDeclarativeGuard(T *); + inline QDeclarativeGuard(const QDeclarativeGuard &); + inline virtual ~QDeclarativeGuard(); + + inline QDeclarativeGuard &operator=(const QDeclarativeGuard &o); + inline QDeclarativeGuard &operator=(T *); + + inline T *object() const; + inline void setObject(T *g); + + inline bool isNull() const + { return !o; } + + inline T* operator->() const + { return static_cast(const_cast(o)); } + inline T& operator*() const + { return *static_cast(const_cast(o)); } + inline operator T*() const + { return static_cast(const_cast(o)); } + inline T* data() const + { return static_cast(const_cast(o)); } + +protected: + virtual void objectDestroyed(T *) {} +}; + +QDeclarativeGuardImpl::QDeclarativeGuardImpl() +: o(0), next(0), prev(0) +{ +} + +QDeclarativeGuardImpl::QDeclarativeGuardImpl(QObject *g) +: o(g), next(0), prev(0) +{ + if (o) addGuard(); +} + +QDeclarativeGuardImpl::QDeclarativeGuardImpl(const QDeclarativeGuardImpl &g) +: o(g.o), next(0), prev(0) +{ + if (o) addGuard(); +} + +QDeclarativeGuardImpl::~QDeclarativeGuardImpl() +{ + if (prev) remGuard(); + o = 0; +} + +void QDeclarativeGuardImpl::addGuard() +{ + Q_ASSERT(!prev); + + if (QObjectPrivate::get(o)->wasDeleted) + return; + + QDeclarativeData *data = QDeclarativeData::get(o, true); + next = data->guards; + if (next) next->prev = &next; + data->guards = this; + prev = &data->guards; +} + +void QDeclarativeGuardImpl::remGuard() +{ + Q_ASSERT(prev); + + if (next) next->prev = prev; + *prev = next; + next = 0; + prev = 0; +} + +template +QDeclarativeGuard::QDeclarativeGuard() +{ +} + +template +QDeclarativeGuard::QDeclarativeGuard(T *g) +: QDeclarativeGuardImpl(g) +{ +} + +template +QDeclarativeGuard::QDeclarativeGuard(const QDeclarativeGuard &g) +: QDeclarativeGuardImpl(g) +{ +} + +template +QDeclarativeGuard::~QDeclarativeGuard() +{ +} + +template +QDeclarativeGuard &QDeclarativeGuard::operator=(const QDeclarativeGuard &g) +{ + setObject(g.object()); + return *this; +} + +template +QDeclarativeGuard &QDeclarativeGuard::operator=(T *g) +{ + setObject(g); + return *this; +} + +template +T *QDeclarativeGuard::object() const +{ + return static_cast(o); +}; + +template +void QDeclarativeGuard::setObject(T *g) +{ + if (g != o) { + if (prev) remGuard(); + o = g; + if (o) addGuard(); + } +} + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QDeclarativeGuard) + +#endif // QDECLARATIVEGUARD_P_H diff --git a/src/declarative/qml/qdeclarativeimageprovider.cpp b/src/declarative/qml/qdeclarativeimageprovider.cpp new file mode 100644 index 00000000..8eb15d18 --- /dev/null +++ b/src/declarative/qml/qdeclarativeimageprovider.cpp @@ -0,0 +1,259 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativeimageprovider.h" + +QT_BEGIN_NAMESPACE + +class QDeclarativeImageProviderPrivate +{ +public: + QDeclarativeImageProvider::ImageType type; +}; + +/*! + \class QDeclarativeImageProvider + \since 4.7 + \brief The QDeclarativeImageProvider class provides an interface for supporting pixmaps and threaded image requests in QML. + + QDeclarativeImageProvider is used to provide advanced image loading features + in QML applications. It allows images in QML to be: + + \list + \o Loaded using QPixmaps rather than actual image files + \o Loaded asynchronously in a separate thread, if imageType() is \l{QDeclarativeImageProvider::ImageType}{ImageType::Image} + \endlist + + To specify that an image should be loaded by an image provider, use the + \bold {"image:"} scheme for the URL source of the image, followed by the + identifiers of the image provider and the requested image. For example: + + \qml + Image { source: "image://myimageprovider/image.png" } + \endqml + + This specifies that the image should be loaded by the image provider named + "myimageprovider", and the image to be loaded is named "image.png". The QML engine + invokes the appropriate image provider according to the providers that have + been registered through QDeclarativeEngine::addImageProvider(). + + Note that the identifiers are case-insensitive, but the rest of the URL will be passed on with + preserved case. For example, the below snippet would still specify that the image is loaded by the + image provider named "myimageprovider", but it would request a different image than the above snippet + ("Image.png" instead of "image.png"). + \qml + Image { source: "image://MyImageProvider/Image.png" } + \endqml + + If you want the rest of the URL to be case insensitive, you will have to take care + of that yourself inside your image provider. + + \section2 An example + + Here are two images. Their \c source values indicate they should be loaded by + an image provider named "colors", and the images to be loaded are "yellow" + and "red", respectively: + + \snippet examples/declarative/cppextensions/imageprovider/imageprovider-example.qml 0 + + When these images are loaded by QML, it looks for a matching image provider + and calls its requestImage() or requestPixmap() method (depending on its + imageType()) to load the image. The method is called with the \c id + parameter set to "yellow" for the first image, and "red" for the second. + + Here is an image provider implementation that can load the images + requested by the above QML. This implementation dynamically + generates QPixmap images that are filled with the requested color: + + \snippet examples/declarative/cppextensions/imageprovider/imageprovider.cpp 0 + \codeline + \snippet examples/declarative/cppextensions/imageprovider/imageprovider.cpp 1 + + To make this provider accessible to QML, it is registered with the QML engine + with a "colors" identifier: + + \code + int main(int argc, char *argv[]) + { + ... + + QDeclarativeEngine engine; + engine->addImageProvider(QLatin1String("colors"), new ColorPixmapProvider); + + ... + } + \endcode + + Now the images can be successfully loaded in QML: + + \image imageprovider.png + + A complete example is available in Qt's + \l {declarative/cppextensions/imageprovider}{examples/declarative/cppextensions/imageprovider} + directory. Note the example registers the provider via a \l{QDeclarativeExtensionPlugin}{plugin} + instead of registering it in the application \c main() function as shown above. + + + \section2 Asynchronous image loading + + Image providers that support QImage loading automatically include support + for asychronous loading of images. To enable asynchronous loading for an + image source, set the \c asynchronous property to \c true for the relevant + \l Image, \l BorderImage or \l AnimatedImage object. When this is enabled, + the image request to the provider is run in a low priority thread, + allowing image loading to be executed in the background, and reducing the + performance impact on the user interface. + + Asynchronous loading is not supported for image providers that provide + QPixmap rather than QImage values, as pixmaps can only be created in the + main thread. In this case, if \l {Image::}{asynchronous} is set to + \c true, the value is ignored and the image is loaded + synchronously. + + + \section2 Image caching + + Images returned by a QDeclarativeImageProvider are automatically cached, + similar to any image loaded by the QML engine. When an image with a + "image://" prefix is loaded from cache, requestImage() and requestPixmap() + will not be called for the relevant image provider. If an image should always + be fetched from the image provider, and should not be cached at all, set the + \c cache property to \c false for the relevant \l Image, \l BorderImage or + \l AnimatedImage object. + + \sa QDeclarativeEngine::addImageProvider() +*/ + +/*! + \enum QDeclarativeImageProvider::ImageType + + Defines the type of image supported by this image provider. + + \value Image The Image Provider provides QImage images. The + requestImage() method will be called for all image requests. + \value Pixmap The Image Provider provides QPixmap images. The + requestPixmap() method will be called for all image requests. +*/ + +/*! + Creates an image provider that will provide images of the given \a type. +*/ +QDeclarativeImageProvider::QDeclarativeImageProvider(ImageType type) + : d(new QDeclarativeImageProviderPrivate) +{ + d->type = type; +} + +/*! + Destroys the QDeclarativeImageProvider + + \note The destructor of your derived class need to be thread safe. +*/ +QDeclarativeImageProvider::~QDeclarativeImageProvider() +{ + delete d; +} + +/*! + Returns the image type supported by this provider. +*/ +QDeclarativeImageProvider::ImageType QDeclarativeImageProvider::imageType() const +{ + return d->type; +} + +/*! + Implement this method to return the image with \a id. The default + implementation returns an empty image. + + The \a id is the requested image source, with the "image:" scheme and + provider identifier removed. For example, if the image \l{Image::}{source} + was "image://myprovider/icons/home", the given \a id would be "icons/home". + + The \a requestedSize corresponds to the \l {Image::sourceSize} requested by + an Image element. If \a requestedSize is a valid size, the image + returned should be of that size. + + In all cases, \a size must be set to the original size of the image. This + is used to set the \l {Item::}{width} and \l {Item::}{height} of the + relevant \l Image if these values have not been set explicitly. + + \note this method may be called by multiple threads, so ensure the + implementation of this method is reentrant. +*/ +QImage QDeclarativeImageProvider::requestImage(const QString &id, QSize *size, const QSize& requestedSize) +{ + Q_UNUSED(id); + Q_UNUSED(size); + Q_UNUSED(requestedSize); + if (d->type == Image) + qWarning("ImageProvider supports Image type but has not implemented requestImage()"); + return QImage(); +} + +/*! + Implement this method to return the pixmap with \a id. The default + implementation returns an empty pixmap. + + The \a id is the requested image source, with the "image:" scheme and + provider identifier removed. For example, if the image \l{Image::}{source} + was "image://myprovider/icons/home", the given \a id would be "icons/home". + + The \a requestedSize corresponds to the \l {Image::sourceSize} requested by + an Image element. If \a requestedSize is a valid size, the image + returned should be of that size. + + In all cases, \a size must be set to the original size of the image. This + is used to set the \l {Item::}{width} and \l {Item::}{height} of the + relevant \l Image if these values have not been set explicitly. +*/ +QPixmap QDeclarativeImageProvider::requestPixmap(const QString &id, QSize *size, const QSize& requestedSize) +{ + Q_UNUSED(id); + Q_UNUSED(size); + Q_UNUSED(requestedSize); + if (d->type == Pixmap) + qWarning("ImageProvider supports Pixmap type but has not implemented requestPixmap()"); + return QPixmap(); +} + +QT_END_NAMESPACE + diff --git a/src/declarative/qml/qdeclarativeimageprovider.h b/src/declarative/qml/qdeclarativeimageprovider.h new file mode 100644 index 00000000..c1bfba0e --- /dev/null +++ b/src/declarative/qml/qdeclarativeimageprovider.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEIMAGEPROVIDER_H +#define QDECLARATIVEIMAGEPROVIDER_H + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeImageProviderPrivate; + +class Q_DECLARATIVE_EXPORT QDeclarativeImageProvider +{ +public: + enum ImageType { + Image, + Pixmap + }; + + QDeclarativeImageProvider(ImageType type); + virtual ~QDeclarativeImageProvider(); + + ImageType imageType() const; + + virtual QImage requestImage(const QString &id, QSize *size, const QSize& requestedSize); + virtual QPixmap requestPixmap(const QString &id, QSize *size, const QSize& requestedSize); + +private: + QDeclarativeImageProviderPrivate *d; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEIMAGEPROVIDER diff --git a/src/declarative/qml/qdeclarativeimport.cpp b/src/declarative/qml/qdeclarativeimport.cpp new file mode 100644 index 00000000..a3592f93 --- /dev/null +++ b/src/declarative/qml/qdeclarativeimport.cpp @@ -0,0 +1,1083 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativeimport_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef Q_OS_SYMBIAN +#include "private/qcore_symbian_p.h" +#endif + +QT_BEGIN_NAMESPACE + +DEFINE_BOOL_CONFIG_OPTION(qmlImportTrace, QML_IMPORT_TRACE) +DEFINE_BOOL_CONFIG_OPTION(qmlCheckTypes, QML_CHECK_TYPES) + +static bool greaterThan(const QString &s1, const QString &s2) +{ + return s1 > s2; +} + +typedef QMap StringStringMap; +Q_GLOBAL_STATIC(StringStringMap, qmlEnginePluginsWithRegisteredTypes); // stores the uri + +class QDeclarativeImportedNamespace +{ +public: + QStringList uris; + QStringList urls; + QList majversions; + QList minversions; + QList isLibrary; + QList qmlDirComponents; + + + bool find_helper(QDeclarativeTypeLoader *typeLoader, int i, const QByteArray& type, int *vmajor, int *vminor, + QDeclarativeType** type_return, QUrl* url_return, + QUrl *base = 0, bool *typeRecursionDetected = 0); + bool find(QDeclarativeTypeLoader *typeLoader, const QByteArray& type, int *vmajor, int *vminor, QDeclarativeType** type_return, + QUrl* url_return, QUrl *base = 0, QString *errorString = 0); +}; + +class QDeclarativeImportsPrivate { +public: + QDeclarativeImportsPrivate(QDeclarativeTypeLoader *loader); + ~QDeclarativeImportsPrivate(); + + bool importExtension(const QString &absoluteFilePath, const QString &uri, + QDeclarativeImportDatabase *database, QDeclarativeDirComponents* components, + QString *errorString); + + QString resolvedUri(const QString &dir_arg, QDeclarativeImportDatabase *database); + bool add(const QDeclarativeDirComponents &qmldircomponentsnetwork, + const QString& uri_arg, const QString& prefix, + int vmaj, int vmin, QDeclarativeScriptParser::Import::Type importType, + QDeclarativeImportDatabase *database, QString *errorString); + bool find(const QByteArray& type, int *vmajor, int *vminor, + QDeclarativeType** type_return, QUrl* url_return, QString *errorString); + + QDeclarativeImportedNamespace *findNamespace(const QString& type); + + QUrl base; + int ref; + + QSet qmlDirFilesForWhichPluginsHaveBeenLoaded; + QDeclarativeImportedNamespace unqualifiedset; + QHash set; + QDeclarativeTypeLoader *typeLoader; +}; + +/*! +\class QDeclarativeImports +\brief The QDeclarativeImports class encapsulates one QML document's import statements. +\internal +*/ +QDeclarativeImports::QDeclarativeImports(const QDeclarativeImports ©) +: d(copy.d) +{ + ++d->ref; +} + +QDeclarativeImports & +QDeclarativeImports::operator =(const QDeclarativeImports ©) +{ + ++copy.d->ref; + if (--d->ref == 0) + delete d; + d = copy.d; + return *this; +} + +QDeclarativeImports::QDeclarativeImports() + : d(new QDeclarativeImportsPrivate(0)){ +} + +QDeclarativeImports::QDeclarativeImports(QDeclarativeTypeLoader *typeLoader) + : d(new QDeclarativeImportsPrivate(typeLoader)){ +} + +QDeclarativeImports::~QDeclarativeImports() +{ + if (--d->ref == 0) + delete d; +} + +/*! + Sets the base URL to be used for all relative file imports added. +*/ +void QDeclarativeImports::setBaseUrl(const QUrl& url) +{ + d->base = url; +} + +/*! + Returns the base URL to be used for all relative file imports added. +*/ +QUrl QDeclarativeImports::baseUrl() const +{ + return d->base; +} + +static QDeclarativeTypeNameCache * +cacheForNamespace(QDeclarativeEngine *engine, const QDeclarativeImportedNamespace &set, + QDeclarativeTypeNameCache *cache) +{ + if (!cache) + cache = new QDeclarativeTypeNameCache(engine); + + QList types = QDeclarativeMetaType::qmlTypes(); + + for (int ii = 0; ii < set.uris.count(); ++ii) { + QByteArray base = set.uris.at(ii).toUtf8() + '/'; + int major = set.majversions.at(ii); + int minor = set.minversions.at(ii); + + foreach (QDeclarativeType *type, types) { + if (type->qmlTypeName().startsWith(base) && + type->qmlTypeName().lastIndexOf('/') == (base.length() - 1) && + type->availableInVersion(major,minor)) + { + QString name = QString::fromUtf8(type->qmlTypeName().mid(base.length())); + + cache->add(name, type); + } + } + } + + return cache; +} + +void QDeclarativeImports::populateCache(QDeclarativeTypeNameCache *cache, QDeclarativeEngine *engine) const +{ + const QDeclarativeImportedNamespace &set = d->unqualifiedset; + + for (QHash::ConstIterator iter = d->set.begin(); + iter != d->set.end(); ++iter) { + + QDeclarativeTypeNameCache::Data *d = cache->data(iter.key()); + if (d) { + if (!d->typeNamespace) + cacheForNamespace(engine, *(*iter), d->typeNamespace); + } else { + QDeclarativeTypeNameCache *nc = cacheForNamespace(engine, *(*iter), 0); + cache->add(iter.key(), nc); + nc->release(); + } + } + + cacheForNamespace(engine, set, cache); +} + +/*! + \internal + + The given (namespace qualified) \a type is resolved to either + \list + \o a QDeclarativeImportedNamespace stored at \a ns_return, + \o a QDeclarativeType stored at \a type_return, or + \o a component located at \a url_return. + \endlist + + If any return pointer is 0, the corresponding search is not done. + + \sa addImport() +*/ +bool QDeclarativeImports::resolveType(const QByteArray& type, + QDeclarativeType** type_return, QUrl* url_return, int *vmaj, int *vmin, + QDeclarativeImportedNamespace** ns_return, QString *errorString) const +{ + QDeclarativeImportedNamespace* ns = d->findNamespace(QString::fromUtf8(type)); + if (ns) { + if (ns_return) + *ns_return = ns; + return true; + } + if (type_return || url_return) { + if (d->find(type,vmaj,vmin,type_return,url_return, errorString)) { + if (qmlImportTrace()) { + if (type_return && *type_return && url_return && !url_return->isEmpty()) + qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::resolveType: " + << type << " => " << (*type_return)->typeName() << " " << *url_return; + if (type_return && *type_return) + qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::resolveType: " + << type << " => " << (*type_return)->typeName(); + if (url_return && !url_return->isEmpty()) + qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::resolveType: " + << type << " => " << *url_return; + } + return true; + } + } + return false; +} + +/*! + \internal + + Searching \e only in the namespace \a ns (previously returned in a call to + resolveType(), \a type is found and returned to either + a QDeclarativeType stored at \a type_return, or + a component located at \a url_return. + + If either return pointer is 0, the corresponding search is not done. +*/ +bool QDeclarativeImports::resolveType(QDeclarativeImportedNamespace* ns, const QByteArray& type, + QDeclarativeType** type_return, QUrl* url_return, + int *vmaj, int *vmin) const +{ + Q_ASSERT(d->typeLoader); + return ns->find(d->typeLoader,type,vmaj,vmin,type_return,url_return); +} + +bool QDeclarativeImportedNamespace::find_helper(QDeclarativeTypeLoader *typeLoader, int i, const QByteArray& type, int *vmajor, int *vminor, + QDeclarativeType** type_return, QUrl* url_return, + QUrl *base, bool *typeRecursionDetected) +{ + int vmaj = majversions.at(i); + int vmin = minversions.at(i); + + QByteArray qt = uris.at(i).toUtf8(); + qt += '/'; + qt += type; + + QDeclarativeType *t = QDeclarativeMetaType::qmlType(qt,vmaj,vmin); + if (t) { + if (vmajor) *vmajor = vmaj; + if (vminor) *vminor = vmin; + if (type_return) + *type_return = t; + return true; + } + + QDeclarativeDirComponents qmldircomponents = qmlDirComponents.at(i); + + bool typeWasDeclaredInQmldir = false; + if (!qmldircomponents.isEmpty()) { + const QString typeName = QString::fromUtf8(type); + foreach (const QDeclarativeDirParser::Component &c, qmldircomponents) { + if (c.typeName == typeName) { + typeWasDeclaredInQmldir = true; + + // importing version -1 means import ALL versions + if ((vmaj == -1) || (c.majorVersion < vmaj || (c.majorVersion == vmaj && vmin >= c.minorVersion))) { + QUrl url = QUrl(urls.at(i) + QLatin1Char('/') + QString::fromUtf8(type) + QLatin1String(".qml")); + QUrl candidate = url.resolved(QUrl(c.fileName)); + if (c.internal && base) { + if (base->resolved(QUrl(c.fileName)) != candidate) + continue; // failed attempt to access an internal type + } + if (base && *base == candidate) { + if (typeRecursionDetected) + *typeRecursionDetected = true; + continue; // no recursion + } + if (url_return) + *url_return = candidate; + return true; + } + } + } + } + + if (!typeWasDeclaredInQmldir && !isLibrary.at(i)) { + // XXX search non-files too! (eg. zip files, see QT-524) + QUrl url = QUrl(urls.at(i) + QLatin1Char('/') + QString::fromUtf8(type) + QLatin1String(".qml")); + QString file = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url); + if (!typeLoader->absoluteFilePath(file).isEmpty()) { + if (base && *base == url) { // no recursion + if (typeRecursionDetected) + *typeRecursionDetected = true; + } else { + if (url_return) + *url_return = url; + return true; + } + } + } + return false; +} + +QDeclarativeImportsPrivate::QDeclarativeImportsPrivate(QDeclarativeTypeLoader *loader) + : ref(1), typeLoader(loader){ +} + +QDeclarativeImportsPrivate::~QDeclarativeImportsPrivate() +{ + foreach (QDeclarativeImportedNamespace* s, set.values()) + delete s; +} + +bool QDeclarativeImportsPrivate::importExtension(const QString &absoluteFilePath, const QString &uri, + QDeclarativeImportDatabase *database, + QDeclarativeDirComponents* components, QString *errorString) +{ + Q_ASSERT(typeLoader); + const QDeclarativeDirParser *qmldirParser = typeLoader->qmlDirParser(absoluteFilePath); + if (qmldirParser->hasError()) { + if (errorString) { + const QList qmldirErrors = qmldirParser->errors(uri); + for (int i = 0; i < qmldirErrors.size(); ++i) + *errorString += qmldirErrors.at(i).description(); + } + return false; + } + + if (! qmlDirFilesForWhichPluginsHaveBeenLoaded.contains(absoluteFilePath)) { + qmlDirFilesForWhichPluginsHaveBeenLoaded.insert(absoluteFilePath); + + QDir dir = QFileInfo(absoluteFilePath).dir(); + foreach (const QDeclarativeDirParser::Plugin &plugin, qmldirParser->plugins()) { + + QString resolvedFilePath = database->resolvePlugin(dir, plugin.path, plugin.name); +#if defined(QT_LIBINFIX) && defined(Q_OS_SYMBIAN) + if (resolvedFilePath.isEmpty()) { + // In case of libinfixed build, attempt to load libinfixed version, too. + QString infixedPluginName = plugin.name + QLatin1String(QT_LIBINFIX); + resolvedFilePath = database->resolvePlugin(dir, plugin.path, infixedPluginName); + } +#endif + if (!resolvedFilePath.isEmpty()) { + if (!database->importPlugin(resolvedFilePath, uri, errorString)) { + if (errorString) + *errorString = QDeclarativeImportDatabase::tr("plugin cannot be loaded for module \"%1\": %2").arg(uri).arg(*errorString); + return false; + } + } else { + if (errorString) + *errorString = QDeclarativeImportDatabase::tr("module \"%1\" plugin \"%2\" not found").arg(uri).arg(plugin.name); + return false; + } + } + } + + if (components) + *components = qmldirParser->components(); + + return true; +} + +QString QDeclarativeImportsPrivate::resolvedUri(const QString &dir_arg, QDeclarativeImportDatabase *database) +{ + QString dir = dir_arg; + if (dir.endsWith(QLatin1Char('/')) || dir.endsWith(QLatin1Char('\\'))) + dir.chop(1); + + QStringList paths = database->fileImportPath; + qSort(paths.begin(), paths.end(), greaterThan); // Ensure subdirs preceed their parents. + + QString stableRelativePath = dir; + foreach(const QString &path, paths) { + if (dir.startsWith(path)) { + stableRelativePath = dir.mid(path.length()+1); + break; + } + } + + stableRelativePath.replace(QLatin1Char('\\'), QLatin1Char('/')); + + // remove optional versioning in dot notation from uri + int lastSlash = stableRelativePath.lastIndexOf(QLatin1Char('/')); + if (lastSlash >= 0) { + int versionDot = stableRelativePath.indexOf(QLatin1Char('.'), lastSlash); + if (versionDot >= 0) + stableRelativePath = stableRelativePath.left(versionDot); + } + + stableRelativePath.replace(QLatin1Char('/'), QLatin1Char('.')); + return stableRelativePath; +} + +bool QDeclarativeImportsPrivate::add(const QDeclarativeDirComponents &qmldircomponentsnetwork, + const QString& uri_arg, const QString& prefix, int vmaj, int vmin, + QDeclarativeScriptParser::Import::Type importType, + QDeclarativeImportDatabase *database, QString *errorString) +{ + Q_ASSERT(typeLoader); + QDeclarativeDirComponents qmldircomponents = qmldircomponentsnetwork; + QString uri = uri_arg; + QDeclarativeImportedNamespace *s; + if (prefix.isEmpty()) { + s = &unqualifiedset; + } else { + s = set.value(prefix); + if (!s) + set.insert(prefix,(s=new QDeclarativeImportedNamespace)); + } + + QString url = uri; + bool versionFound = false; + if (importType == QDeclarativeScriptParser::Import::Library) { + url.replace(QLatin1Char('.'), QLatin1Char('/')); + bool found = false; + QString dir; + QString qmldir; + + // step 1: search for extension with fully encoded version number + if (vmaj >= 0 && vmin >= 0) { + foreach (const QString &p, database->fileImportPath) { + dir = p+QLatin1Char('/')+url; + qmldir = dir+QString(QLatin1String(".%1.%2")).arg(vmaj).arg(vmin)+QLatin1String("/qmldir"); + + QString absoluteFilePath = typeLoader->absoluteFilePath(qmldir); + if (!absoluteFilePath.isEmpty()) { + found = true; + + QString absolutePath = absoluteFilePath.left(absoluteFilePath.lastIndexOf(QLatin1Char('/'))); + url = QUrl::fromLocalFile(absolutePath).toString(); + uri = resolvedUri(dir, database); + if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errorString)) + return false; + break; + } + } + } + // step 2: search for extension with encoded version major + if (vmaj >= 0 && vmin >= 0) { + foreach (const QString &p, database->fileImportPath) { + dir = p+QLatin1Char('/')+url; + qmldir = dir+QString(QLatin1String(".%1")).arg(vmaj)+QLatin1String("/qmldir"); + + QString absoluteFilePath = typeLoader->absoluteFilePath(qmldir); + if (!absoluteFilePath.isEmpty()) { + found = true; + + QString absolutePath = absoluteFilePath.left(absoluteFilePath.lastIndexOf(QLatin1Char('/'))); + url = QUrl::fromLocalFile(absolutePath).toString(); + uri = resolvedUri(dir, database); + if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errorString)) + return false; + break; + } + } + } + if (!found) { + // step 3: search for extension without version number + + foreach (const QString &p, database->fileImportPath) { + dir = p+QLatin1Char('/')+url; + qmldir = dir+QLatin1String("/qmldir"); + + QString absoluteFilePath = typeLoader->absoluteFilePath(qmldir); + if (!absoluteFilePath.isEmpty()) { + found = true; + + QString absolutePath = absoluteFilePath.left(absoluteFilePath.lastIndexOf(QLatin1Char('/'))); + url = QUrl::fromLocalFile(absolutePath).toString(); + uri = resolvedUri(dir, database); + if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errorString)) + return false; + break; + } + } + } + + if (QDeclarativeMetaType::isModule(uri.toUtf8(), vmaj, vmin)) + versionFound = true; + + if (!versionFound && qmldircomponents.isEmpty()) { + if (errorString) { + bool anyversion = QDeclarativeMetaType::isModule(uri.toUtf8(), -1, -1); + if (anyversion) + *errorString = QDeclarativeImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin); + else + *errorString = QDeclarativeImportDatabase::tr("module \"%1\" is not installed").arg(uri_arg); + } + return false; + } + } else { + + if (importType == QDeclarativeScriptParser::Import::File && qmldircomponents.isEmpty()) { + QUrl importUrl = base.resolved(QUrl(uri + QLatin1String("/qmldir"))); + QString localFileOrQrc = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(importUrl); + if (!localFileOrQrc.isEmpty()) { + QString dir = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(base.resolved(QUrl(uri))); + QFileInfo dirinfo(dir); + if (dir.isEmpty() || !dirinfo.exists() || !dirinfo.isDir()) { + if (errorString) + *errorString = QDeclarativeImportDatabase::tr("\"%1\": no such directory").arg(uri_arg); + return false; // local import dirs must exist + } + uri = resolvedUri(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(base.resolved(QUrl(uri))), database); + if (uri.endsWith(QLatin1Char('/'))) + uri.chop(1); + if (!typeLoader->absoluteFilePath(localFileOrQrc).isEmpty()) { + if (!importExtension(localFileOrQrc,uri,database,&qmldircomponents,errorString)) + return false; + } + } else { + if (prefix.isEmpty()) { + // directory must at least exist for valid import + QString localFileOrQrc = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(base.resolved(QUrl(uri))); + QFileInfo dirinfo(localFileOrQrc); + if (localFileOrQrc.isEmpty() || !dirinfo.exists() || !dirinfo.isDir()) { + if (errorString) { + if (localFileOrQrc.isEmpty()) + *errorString = QDeclarativeImportDatabase::tr("import \"%1\" has no qmldir and no namespace").arg(uri); + else + *errorString = QDeclarativeImportDatabase::tr("\"%1\": no such directory").arg(uri); + } + return false; + } + } + } + } + + url = base.resolved(QUrl(url)).toString(); + if (url.endsWith(QLatin1Char('/'))) + url.chop(1); + } + + if (!versionFound && vmaj > -1 && vmin > -1 && !qmldircomponents.isEmpty()) { + QList::ConstIterator it = qmldircomponents.begin(); + int lowest_maj = INT_MAX; + int lowest_min = INT_MAX; + int highest_maj = INT_MIN; + int highest_min = INT_MIN; + for (; it != qmldircomponents.end(); ++it) { + if (it->majorVersion > highest_maj || (it->majorVersion == highest_maj && it->minorVersion > highest_min)) { + highest_maj = it->majorVersion; + highest_min = it->minorVersion; + } + if (it->majorVersion < lowest_maj || (it->majorVersion == lowest_maj && it->minorVersion < lowest_min)) { + lowest_maj = it->majorVersion; + lowest_min = it->minorVersion; + } + } + if (lowest_maj > vmaj || (lowest_maj == vmaj && lowest_min > vmin) + || highest_maj < vmaj || (highest_maj == vmaj && highest_min < vmin)) + { + *errorString = QDeclarativeImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin); + return false; + } + } + + s->uris.prepend(uri); + s->urls.prepend(url); + s->majversions.prepend(vmaj); + s->minversions.prepend(vmin); + s->isLibrary.prepend(importType == QDeclarativeScriptParser::Import::Library); + s->qmlDirComponents.prepend(qmldircomponents); + return true; +} + +bool QDeclarativeImportsPrivate::find(const QByteArray& type, int *vmajor, int *vminor, QDeclarativeType** type_return, + QUrl* url_return, QString *errorString) +{ + Q_ASSERT(typeLoader); + QDeclarativeImportedNamespace *s = 0; + int slash = type.indexOf('/'); + if (slash >= 0) { + QString namespaceName = QString::fromUtf8(type.left(slash)); + s = set.value(namespaceName); + if (!s) { + if (errorString) + *errorString = QDeclarativeImportDatabase::tr("- %1 is not a namespace").arg(namespaceName); + return false; + } + int nslash = type.indexOf('/',slash+1); + if (nslash > 0) { + if (errorString) + *errorString = QDeclarativeImportDatabase::tr("- nested namespaces not allowed"); + return false; + } + } else { + s = &unqualifiedset; + } + QByteArray unqualifiedtype = slash < 0 ? type : type.mid(slash+1); // common-case opt (QString::mid works fine, but slower) + if (s) { + if (s->find(typeLoader, unqualifiedtype,vmajor,vminor,type_return,url_return, &base, errorString)) + return true; + if (s->urls.count() == 1 && !s->isLibrary[0] && url_return && s != &unqualifiedset) { + // qualified, and only 1 url + *url_return = QUrl(s->urls[0]+QLatin1Char('/')).resolved(QUrl(QString::fromUtf8(unqualifiedtype) + QLatin1String(".qml"))); + return true; + } + } + + return false; +} + +QDeclarativeImportedNamespace *QDeclarativeImportsPrivate::findNamespace(const QString& type) +{ + return set.value(type); +} + +bool QDeclarativeImportedNamespace::find(QDeclarativeTypeLoader *typeLoader, const QByteArray& type, int *vmajor, int *vminor, QDeclarativeType** type_return, + QUrl* url_return, QUrl *base, QString *errorString) +{ + bool typeRecursionDetected = false; + for (int i=0; itoString(); + int slash = b.lastIndexOf(QLatin1Char('/')); + if (slash >= 0) { + b = b.left(slash+1); + QString l = b.left(slash); + if (u1.startsWith(b)) + u1 = u1.mid(b.count()); + else if (u1 == l) + u1 = QDeclarativeImportDatabase::tr("local directory"); + if (u2.startsWith(b)) + u2 = u2.mid(b.count()); + else if (u2 == l) + u2 = QDeclarativeImportDatabase::tr("local directory"); + } + } + + if (u1 != u2) + *errorString + = QDeclarativeImportDatabase::tr("is ambiguous. Found in %1 and in %2") + .arg(u1).arg(u2); + else + *errorString + = QDeclarativeImportDatabase::tr("is ambiguous. Found in %1 in version %2.%3 and %4.%5") + .arg(u1) + .arg(majversions.at(i)).arg(minversions.at(i)) + .arg(majversions.at(j)).arg(minversions.at(j)); + } + return false; + } + } + } + return true; + } + } + if (errorString) { + if (typeRecursionDetected) + *errorString = QDeclarativeImportDatabase::tr("is instantiated recursively"); + else + *errorString = QDeclarativeImportDatabase::tr("is not a type"); + } + return false; +} + +/*! +\class QDeclarativeImportDatabase +\brief The QDeclarativeImportDatabase class manages the QML imports for a QDeclarativeEngine. +\internal +*/ +QDeclarativeImportDatabase::QDeclarativeImportDatabase(QDeclarativeEngine *e) +: engine(e) +{ + filePluginPath << QLatin1String("."); + + // Search order is applicationDirPath(), $QML_IMPORT_PATH, QLibraryInfo::ImportsPath + +#ifndef QT_NO_SETTINGS + QString installImportsPath = QLibraryInfo::location(QLibraryInfo::ImportsPath); + +#if defined(Q_OS_SYMBIAN) + // Append imports path for all available drives in Symbian + if (installImportsPath.at(1) != QChar(QLatin1Char(':'))) { + QString tempPath = installImportsPath; + if (tempPath.at(tempPath.length() - 1) != QDir::separator()) { + tempPath += QDir::separator(); + } + RFs& fs = qt_s60GetRFs(); + TPtrC tempPathPtr(reinterpret_cast (tempPath.constData())); + // Symbian searches should start from Y:. Fix start drive otherwise TFindFile starts from the session drive + _LIT(KStartDir, "Y:"); + TFileName dirPath(KStartDir); + dirPath.Append(tempPathPtr); + TFindFile finder(fs); + TInt err = finder.FindByDir(tempPathPtr, dirPath); + while (err == KErrNone) { + QString foundDir(reinterpret_cast(finder.File().Ptr()), + finder.File().Length()); + foundDir = QDir(foundDir).canonicalPath(); + addImportPath(foundDir); + err = finder.Find(); + } + // TFindFile found the directories in the order we want, but addImportPath reverses it. + // Reverse the order again to get it right. + QAlgorithmsPrivate::qReverse(fileImportPath.begin(), fileImportPath.end()); + } else { + addImportPath(installImportsPath); + } +#else + addImportPath(installImportsPath); +#endif + +#endif // QT_NO_SETTINGS + + // env import paths + QByteArray envImportPath = qgetenv("QML_IMPORT_PATH"); + if (!envImportPath.isEmpty()) { +#if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN) + QLatin1Char pathSep(';'); +#else + QLatin1Char pathSep(':'); +#endif + QStringList paths = QString::fromLatin1(envImportPath).split(pathSep, QString::SkipEmptyParts); + for (int ii = paths.count() - 1; ii >= 0; --ii) + addImportPath(paths.at(ii)); + } + + addImportPath(QCoreApplication::applicationDirPath()); +} + +QDeclarativeImportDatabase::~QDeclarativeImportDatabase() +{ +} + +/*! + \internal + + Adds information to \a imports such that subsequent calls to resolveType() + will resolve types qualified by \a prefix by considering types found at the given \a uri. + + The uri is either a directory (if importType is FileImport), or a URI resolved using paths + added via addImportPath() (if importType is LibraryImport). + + The \a prefix may be empty, in which case the import location is considered for + unqualified types. + + The base URL must already have been set with Import::setBaseUrl(). +*/ +bool QDeclarativeImports::addImport(QDeclarativeImportDatabase *importDb, + const QString& uri, const QString& prefix, int vmaj, int vmin, + QDeclarativeScriptParser::Import::Type importType, + const QDeclarativeDirComponents &qmldircomponentsnetwork, + QString *errorString) +{ + if (qmlImportTrace()) + qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::addImport: " + << uri << " " << vmaj << '.' << vmin << " " + << (importType==QDeclarativeScriptParser::Import::Library? "Library" : "File") + << " as " << prefix; + + return d->add(qmldircomponentsnetwork, uri, prefix, vmaj, vmin, importType, importDb, errorString); +} + +/*! + \internal + + Returns the result of the merge of \a baseName with \a path, \a suffixes, and \a prefix. + The \a prefix must contain the dot. + + \a qmldirPath is the location of the qmldir file. + */ +QString QDeclarativeImportDatabase::resolvePlugin(const QDir &qmldirPath, const QString &qmldirPluginPath, + const QString &baseName, const QStringList &suffixes, + const QString &prefix) +{ + QStringList searchPaths = filePluginPath; + bool qmldirPluginPathIsRelative = QDir::isRelativePath(qmldirPluginPath); + if (!qmldirPluginPathIsRelative) + searchPaths.prepend(qmldirPluginPath); + + foreach (const QString &pluginPath, searchPaths) { + + QString resolvedPath; + + if (pluginPath == QLatin1String(".")) { + if (qmldirPluginPathIsRelative) + resolvedPath = qmldirPath.absoluteFilePath(qmldirPluginPath); + else + resolvedPath = qmldirPath.absolutePath(); + } else { + resolvedPath = pluginPath; + } + + // hack for resources, should probably go away + if (resolvedPath.startsWith(QLatin1Char(':'))) + resolvedPath = QCoreApplication::applicationDirPath(); + + QDir dir(resolvedPath); + foreach (const QString &suffix, suffixes) { + QString pluginFileName = prefix; + + pluginFileName += baseName; + pluginFileName += suffix; + + QFileInfo fileInfo(dir, pluginFileName); + + if (fileInfo.exists()) + return fileInfo.absoluteFilePath(); + } + } + + if (qmlImportTrace()) + qDebug() << "QDeclarativeImportDatabase::resolvePlugin: Could not resolve plugin" << baseName + << "in" << qmldirPath.absolutePath(); + + return QString(); +} + +/*! + \internal + + Returns the result of the merge of \a baseName with \a dir and the platform suffix. + + \table + \header \i Platform \i Valid suffixes + \row \i Windows \i \c .dll + \row \i Unix/Linux \i \c .so + \row \i AIX \i \c .a + \row \i HP-UX \i \c .sl, \c .so (HP-UXi) + \row \i Mac OS X \i \c .dylib, \c .bundle, \c .so + \row \i Symbian \i \c .dll + \endtable + + Version number on unix are ignored. +*/ +QString QDeclarativeImportDatabase::resolvePlugin(const QDir &qmldirPath, const QString &qmldirPluginPath, + const QString &baseName) +{ +#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) + return resolvePlugin(qmldirPath, qmldirPluginPath, baseName, + QStringList() +# ifdef QT_DEBUG + << QLatin1String("d.dll") // try a qmake-style debug build first +# endif + << QLatin1String(".dll")); +#elif defined(Q_OS_SYMBIAN) + return resolvePlugin(qmldirPath, qmldirPluginPath, baseName, + QStringList() + << QLatin1String(".dll") + << QLatin1String(".qtplugin")); +#else + +# if defined(Q_OS_DARWIN) + + return resolvePlugin(qmldirPath, qmldirPluginPath, baseName, + QStringList() +# ifdef QT_DEBUG + << QLatin1String("_debug.dylib") // try a qmake-style debug build first + << QLatin1String(".dylib") +# else + << QLatin1String(".dylib") + << QLatin1String("_debug.dylib") // try a qmake-style debug build after +# endif + << QLatin1String(".so") + << QLatin1String(".bundle"), + QLatin1String("lib")); +# else // Generic Unix + QStringList validSuffixList; + +# if defined(Q_OS_HPUX) +/* + See "HP-UX Linker and Libraries User's Guide", section "Link-time Differences between PA-RISC and IPF": + "In PA-RISC (PA-32 and PA-64) shared libraries are suffixed with .sl. In IPF (32-bit and 64-bit), + the shared libraries are suffixed with .so. For compatibility, the IPF linker also supports the .sl suffix." + */ + validSuffixList << QLatin1String(".sl"); +# if defined __ia64 + validSuffixList << QLatin1String(".so"); +# endif +# elif defined(Q_OS_AIX) + validSuffixList << QLatin1String(".a") << QLatin1String(".so"); +# elif defined(Q_OS_UNIX) + validSuffixList << QLatin1String(".so"); +# endif + + // Examples of valid library names: + // libfoo.so + + return resolvePlugin(qmldirPath, qmldirPluginPath, baseName, validSuffixList, QLatin1String("lib")); +# endif + +#endif +} + +/*! + \internal +*/ +QStringList QDeclarativeImportDatabase::pluginPathList() const +{ + return filePluginPath; +} + +/*! + \internal +*/ +void QDeclarativeImportDatabase::setPluginPathList(const QStringList &paths) +{ + filePluginPath = paths; +} + +/*! + \internal +*/ +void QDeclarativeImportDatabase::addPluginPath(const QString& path) +{ + if (qmlImportTrace()) + qDebug().nospace() << "QDeclarativeImportDatabase::addPluginPath: " << path; + + QUrl url = QUrl(path); + if (url.isRelative() || url.scheme() == QLatin1String("file") + || (url.scheme().length() == 1 && QFile::exists(path)) ) { // windows path + QDir dir = QDir(path); + filePluginPath.prepend(dir.canonicalPath()); + } else { + filePluginPath.prepend(path); + } +} + +/*! + \internal +*/ +void QDeclarativeImportDatabase::addImportPath(const QString& path) +{ + if (qmlImportTrace()) + qDebug().nospace() << "QDeclarativeImportDatabase::addImportPath: " << path; + + if (path.isEmpty()) + return; + + QUrl url = QUrl(path); + QString cPath; + + if (url.isRelative() || url.scheme() == QLatin1String("file") + || (url.scheme().length() == 1 && QFile::exists(path)) ) { // windows path + QDir dir = QDir(path); + cPath = dir.canonicalPath(); + } else { + cPath = path; + cPath.replace(QLatin1Char('\\'), QLatin1Char('/')); + } + + if (!cPath.isEmpty() + && !fileImportPath.contains(cPath)) + fileImportPath.prepend(cPath); +} + +/*! + \internal +*/ +QStringList QDeclarativeImportDatabase::importPathList() const +{ + return fileImportPath; +} + +/*! + \internal +*/ +void QDeclarativeImportDatabase::setImportPathList(const QStringList &paths) +{ + fileImportPath = paths; +} + +/*! + \internal +*/ +bool QDeclarativeImportDatabase::importPlugin(const QString &filePath, const QString &uri, QString *errorString) +{ + if (qmlImportTrace()) + qDebug().nospace() << "QDeclarativeImportDatabase::importPlugin: " << uri << " from " << filePath; + +#ifndef QT_NO_LIBRARY + QFileInfo fileInfo(filePath); + const QString absoluteFilePath = fileInfo.absoluteFilePath(); + + bool engineInitialized = initializedPlugins.contains(absoluteFilePath); + bool typesRegistered = qmlEnginePluginsWithRegisteredTypes()->contains(absoluteFilePath); + + if (typesRegistered) { + Q_ASSERT_X(qmlEnginePluginsWithRegisteredTypes()->value(absoluteFilePath) == uri, + "QDeclarativeImportDatabase::importExtension", + "Internal error: Plugin imported previously with different uri"); + } + + if (!engineInitialized || !typesRegistered) { + if (!QDeclarative_isFileCaseCorrect(absoluteFilePath)) { + if (errorString) + *errorString = tr("File name case mismatch for \"%1\"").arg(absoluteFilePath); + return false; + } + QPluginLoader loader(absoluteFilePath); + + if (!loader.load()) { + if (errorString) + *errorString = loader.errorString(); + return false; + } + + if (QDeclarativeExtensionInterface *iface = qobject_cast(loader.instance())) { + + const QByteArray bytes = uri.toUtf8(); + const char *moduleId = bytes.constData(); + if (!typesRegistered) { + + // ### this code should probably be protected with a mutex. + qmlEnginePluginsWithRegisteredTypes()->insert(absoluteFilePath, uri); + iface->registerTypes(moduleId); + } + if (!engineInitialized) { + // things on the engine (eg. adding new global objects) have to be done for every engine. + + // protect against double initialization + initializedPlugins.insert(absoluteFilePath); + iface->initializeEngine(engine, moduleId); + } + } else { + if (errorString) + *errorString = loader.errorString(); + return false; + } + } + + return true; +#else + return false; +#endif +} + + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativeimport_p.h b/src/declarative/qml/qdeclarativeimport_p.h new file mode 100644 index 00000000..33061d6a --- /dev/null +++ b/src/declarative/qml/qdeclarativeimport_p.h @@ -0,0 +1,144 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEIMPORT_P_H +#define QDECLARATIVEIMPORT_P_H + +#include +#include +#include +#include +#include +#include + +// +// 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_BEGIN_NAMESPACE + +class QDeclarativeTypeNameCache; +class QDeclarativeEngine; +class QDir; +class QDeclarativeImportedNamespace; +class QDeclarativeImportsPrivate; +class QDeclarativeImportDatabase; +class QDeclarativeTypeLoader; + +class QDeclarativeImports +{ +public: + QDeclarativeImports(); + QDeclarativeImports(QDeclarativeTypeLoader *); + QDeclarativeImports(const QDeclarativeImports &); + ~QDeclarativeImports(); + QDeclarativeImports &operator=(const QDeclarativeImports &); + + void setBaseUrl(const QUrl &url); + QUrl baseUrl() const; + + bool resolveType(const QByteArray& type, + QDeclarativeType** type_return, QUrl* url_return, + int *version_major, int *version_minor, + QDeclarativeImportedNamespace** ns_return, + QString *errorString = 0) const; + bool resolveType(QDeclarativeImportedNamespace*, + const QByteArray& type, + QDeclarativeType** type_return, QUrl* url_return, + int *version_major, int *version_minor) const; + + bool addImport(QDeclarativeImportDatabase *, + const QString& uri, const QString& prefix, int vmaj, int vmin, + QDeclarativeScriptParser::Import::Type importType, + const QDeclarativeDirComponents &qmldircomponentsnetwork, + QString *errorString); + + void populateCache(QDeclarativeTypeNameCache *cache, QDeclarativeEngine *) const; + +private: + friend class QDeclarativeImportDatabase; + QDeclarativeImportsPrivate *d; +}; + +class QDeclarativeImportDatabase +{ + Q_DECLARE_TR_FUNCTIONS(QDeclarativeImportDatabase) +public: + QDeclarativeImportDatabase(QDeclarativeEngine *); + ~QDeclarativeImportDatabase(); + + bool importPlugin(const QString &filePath, const QString &uri, QString *errorString); + + QStringList importPathList() const; + void setImportPathList(const QStringList &paths); + void addImportPath(const QString& dir); + + QStringList pluginPathList() const; + void setPluginPathList(const QStringList &paths); + void addPluginPath(const QString& path); + +private: + friend class QDeclarativeImportsPrivate; + QString resolvePlugin(const QDir &qmldirPath, const QString &qmldirPluginPath, + const QString &baseName, const QStringList &suffixes, + const QString &prefix = QString()); + QString resolvePlugin(const QDir &qmldirPath, const QString &qmldirPluginPath, + const QString &baseName); + + + QStringList filePluginPath; + QStringList fileImportPath; + + QSet initializedPlugins; + QDeclarativeEngine *engine; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVEIMPORT_P_H + diff --git a/src/declarative/qml/qdeclarativeinclude.cpp b/src/declarative/qml/qdeclarativeinclude.cpp new file mode 100644 index 00000000..d02143bc --- /dev/null +++ b/src/declarative/qml/qdeclarativeinclude.cpp @@ -0,0 +1,310 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativeinclude_p.h" + +#include +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +QDeclarativeInclude::QDeclarativeInclude(const QUrl &url, + QDeclarativeEngine *engine, + QScriptContext *ctxt) +: QObject(engine), m_engine(engine), m_network(0), m_reply(0), m_url(url), m_redirectCount(0) +{ + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); + m_context = ep->contextClass->contextFromValue(QScriptDeclarativeClass::scopeChainValue(ctxt, -3)); + + m_scope[0] = QScriptDeclarativeClass::scopeChainValue(ctxt, -4); + m_scope[1] = QScriptDeclarativeClass::scopeChainValue(ctxt, -5); + + m_scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine); + m_network = QDeclarativeScriptEngine::get(m_scriptEngine)->networkAccessManager(); + + m_result = resultValue(m_scriptEngine); + + QNetworkRequest request; + request.setUrl(url); + + m_reply = m_network->get(request); + QObject::connect(m_reply, SIGNAL(finished()), this, SLOT(finished())); +} + +QDeclarativeInclude::~QDeclarativeInclude() +{ + delete m_reply; +} + +QScriptValue QDeclarativeInclude::resultValue(QScriptEngine *engine, Status status) +{ + QScriptValue result = engine->newObject(); + result.setProperty(QLatin1String("OK"), QScriptValue(engine, Ok)); + result.setProperty(QLatin1String("LOADING"), QScriptValue(engine, Loading)); + result.setProperty(QLatin1String("NETWORK_ERROR"), QScriptValue(engine, NetworkError)); + result.setProperty(QLatin1String("EXCEPTION"), QScriptValue(engine, Exception)); + + result.setProperty(QLatin1String("status"), QScriptValue(engine, status)); + return result; +} + +QScriptValue QDeclarativeInclude::result() const +{ + return m_result; +} + +void QDeclarativeInclude::setCallback(const QScriptValue &c) +{ + m_callback = c; +} + +QScriptValue QDeclarativeInclude::callback() const +{ + return m_callback; +} + +#define INCLUDE_MAXIMUM_REDIRECT_RECURSION 15 +void QDeclarativeInclude::finished() +{ + m_redirectCount++; + + if (m_redirectCount < INCLUDE_MAXIMUM_REDIRECT_RECURSION) { + QVariant redirect = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute); + if (redirect.isValid()) { + m_url = m_url.resolved(redirect.toUrl()); + delete m_reply; + + QNetworkRequest request; + request.setUrl(m_url); + + m_reply = m_network->get(request); + QObject::connect(m_reply, SIGNAL(finished()), this, SLOT(finished())); + return; + } + } + + if (m_reply->error() == QNetworkReply::NoError) { + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(m_engine); + + QByteArray data = m_reply->readAll(); + + QString code = QString::fromUtf8(data); + + QString urlString = m_url.toString(); + QScriptContext *scriptContext = QScriptDeclarativeClass::pushCleanContext(m_scriptEngine); + scriptContext->pushScope(ep->contextClass->newUrlContext(m_context, 0, urlString)); + scriptContext->pushScope(m_scope[0]); + + scriptContext->pushScope(m_scope[1]); + scriptContext->setActivationObject(m_scope[1]); + QDeclarativeScriptParser::extractPragmas(code); + + m_scriptEngine->evaluate(code, urlString, 1); + + m_scriptEngine->popContext(); + + if (m_scriptEngine->hasUncaughtException()) { + m_result.setProperty(QLatin1String("status"), QScriptValue(m_scriptEngine, Exception)); + m_result.setProperty(QLatin1String("exception"), m_scriptEngine->uncaughtException()); + m_scriptEngine->clearExceptions(); + } else { + m_result.setProperty(QLatin1String("status"), QScriptValue(m_scriptEngine, Ok)); + } + } else { + m_result.setProperty(QLatin1String("status"), QScriptValue(m_scriptEngine, NetworkError)); + } + + callback(m_scriptEngine, m_callback, m_result); + + disconnect(); + deleteLater(); +} + +void QDeclarativeInclude::callback(QScriptEngine *engine, QScriptValue &callback, QScriptValue &status) +{ + if (callback.isValid()) { + QScriptValue args = engine->newArray(1); + args.setProperty(0, status); + callback.call(QScriptValue(), args); + } +} + +/* + Documented in qdeclarativeengine.cpp +*/ +QScriptValue QDeclarativeInclude::include(QScriptContext *ctxt, QScriptEngine *engine) +{ + if (ctxt->argumentCount() == 0) + return engine->undefinedValue(); + + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); + + QUrl contextUrl = ep->contextClass->urlFromValue(QScriptDeclarativeClass::scopeChainValue(ctxt, -3)); + if (contextUrl.isEmpty()) + return ctxt->throwError(QLatin1String("Qt.include(): Can only be called from JavaScript files")); + + QString urlString = ctxt->argument(0).toString(); + QUrl url(urlString); + if (url.isRelative()) { + url = QUrl(contextUrl).resolved(url); + urlString = url.toString(); + } + + QString localFile = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url); + + QScriptValue func = ctxt->argument(1); + if (!func.isFunction()) + func = QScriptValue(); + + QScriptValue result; + if (localFile.isEmpty()) { + QDeclarativeInclude *i = + new QDeclarativeInclude(url, QDeclarativeEnginePrivate::getEngine(engine), ctxt); + + if (func.isValid()) + i->setCallback(func); + + result = i->result(); + } else { + + QFile f(localFile); + if (f.open(QIODevice::ReadOnly)) { + QByteArray data = f.readAll(); + QString code = QString::fromUtf8(data); + + QDeclarativeContextData *context = + ep->contextClass->contextFromValue(QScriptDeclarativeClass::scopeChainValue(ctxt, -3)); + + QScriptContext *scriptContext = QScriptDeclarativeClass::pushCleanContext(engine); + scriptContext->pushScope(ep->contextClass->newUrlContext(context, 0, urlString)); + scriptContext->pushScope(ep->globalClass->staticGlobalObject()); + QScriptValue scope = QScriptDeclarativeClass::scopeChainValue(ctxt, -5); + scriptContext->pushScope(scope); + scriptContext->setActivationObject(scope); + QDeclarativeScriptParser::extractPragmas(code); + + engine->evaluate(code, urlString, 1); + + engine->popContext(); + + if (engine->hasUncaughtException()) { + result = resultValue(engine, Exception); + result.setProperty(QLatin1String("exception"), engine->uncaughtException()); + engine->clearExceptions(); + } else { + result = resultValue(engine, Ok); + } + callback(engine, func, result); + } else { + result = resultValue(engine, NetworkError); + callback(engine, func, result); + } + } + + return result; +} + +QScriptValue QDeclarativeInclude::worker_include(QScriptContext *ctxt, QScriptEngine *engine) +{ + if (ctxt->argumentCount() == 0) + return engine->undefinedValue(); + + QString urlString = ctxt->argument(0).toString(); + QUrl url(ctxt->argument(0).toString()); + if (url.isRelative()) { + QString contextUrl = QScriptDeclarativeClass::scopeChainValue(ctxt, -3).data().toString(); + Q_ASSERT(!contextUrl.isEmpty()); + + url = QUrl(contextUrl).resolved(url); + urlString = url.toString(); + } + + QString localFile = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url); + + QScriptValue func = ctxt->argument(1); + if (!func.isFunction()) + func = QScriptValue(); + + QScriptValue result; + if (!localFile.isEmpty()) { + + QFile f(localFile); + if (f.open(QIODevice::ReadOnly)) { + QByteArray data = f.readAll(); + QString code = QString::fromUtf8(data); + + QScriptContext *scriptContext = QScriptDeclarativeClass::pushCleanContext(engine); + QScriptValue urlContext = engine->newObject(); + urlContext.setData(QScriptValue(engine, urlString)); + scriptContext->pushScope(urlContext); + + QScriptValue scope = QScriptDeclarativeClass::scopeChainValue(ctxt, -4); + scriptContext->pushScope(scope); + scriptContext->setActivationObject(scope); + QDeclarativeScriptParser::extractPragmas(code); + + engine->evaluate(code, urlString, 1); + + engine->popContext(); + + if (engine->hasUncaughtException()) { + result = resultValue(engine, Exception); + result.setProperty(QLatin1String("exception"), engine->uncaughtException()); + engine->clearExceptions(); + } else { + result = resultValue(engine, Ok); + } + callback(engine, func, result); + } else { + result = resultValue(engine, NetworkError); + callback(engine, func, result); + } + } + + return result; +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativeinclude_p.h b/src/declarative/qml/qdeclarativeinclude_p.h new file mode 100644 index 00000000..d92a9dda --- /dev/null +++ b/src/declarative/qml/qdeclarativeinclude_p.h @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEINCLUDE_P_H +#define QDECLARATIVEINCLUDE_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 +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeEngine; +class QScriptContext; +class QScriptEngine; +class QNetworkAccessManager; +class QNetworkReply; +class QDeclarativeInclude : public QObject +{ + Q_OBJECT +public: + enum Status { + Ok = 0, + Loading = 1, + NetworkError = 2, + Exception = 3 + }; + + QDeclarativeInclude(const QUrl &, QDeclarativeEngine *, QScriptContext *ctxt); + ~QDeclarativeInclude(); + + void setCallback(const QScriptValue &); + QScriptValue callback() const; + + QScriptValue result() const; + + static QScriptValue resultValue(QScriptEngine *, Status status = Loading); + static void callback(QScriptEngine *, QScriptValue &callback, QScriptValue &status); + + static QScriptValue include(QScriptContext *ctxt, QScriptEngine *engine); + static QScriptValue worker_include(QScriptContext *ctxt, QScriptEngine *engine); + +public slots: + void finished(); + +private: + QDeclarativeEngine *m_engine; + QScriptEngine *m_scriptEngine; + QNetworkAccessManager *m_network; + QDeclarativeGuard m_reply; + + QUrl m_url; + int m_redirectCount; + QScriptValue m_callback; + QScriptValue m_result; + QDeclarativeGuardedContextData m_context; + QScriptValue m_scope[2]; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVEINCLUDE_P_H + diff --git a/src/declarative/qml/qdeclarativeinfo.cpp b/src/declarative/qml/qdeclarativeinfo.cpp new file mode 100644 index 00000000..52268b79 --- /dev/null +++ b/src/declarative/qml/qdeclarativeinfo.cpp @@ -0,0 +1,179 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativeinfo.h" + +#include "private/qdeclarativedata_p.h" +#include "qdeclarativecontext.h" +#include "private/qdeclarativecontext_p.h" +#include "private/qdeclarativemetatype_p.h" +#include "private/qdeclarativeengine_p.h" + +#include + +QT_BEGIN_NAMESPACE + +/*! + \fn QDeclarativeInfo qmlInfo(const QObject *object) + \relates QDeclarativeEngine + + Prints warning messages that include the file and line number for the + specified QML \a object. + + When QML types display warning messages, it improves traceability + if they include the QML file and line number on which the + particular instance was instantiated. + + To include the file and line number, an object must be passed. If + the file and line number is not available for that instance + (either it was not instantiated by the QML engine or location + information is disabled), "unknown location" will be used instead. + + For example, + + \code + qmlInfo(object) << tr("component property is a write-once property"); + \endcode + + prints + + \code + QML MyCustomType (unknown location): component property is a write-once property + \endcode +*/ + +class QDeclarativeInfoPrivate +{ +public: + QDeclarativeInfoPrivate() : ref (1), object(0) {} + + int ref; + const QObject *object; + QString buffer; + QList errors; +}; + +QDeclarativeInfo::QDeclarativeInfo(QDeclarativeInfoPrivate *p) +: QDebug(&p->buffer), d(p) +{ + nospace(); +} + +QDeclarativeInfo::QDeclarativeInfo(const QDeclarativeInfo &other) +: QDebug(other), d(other.d) +{ + d->ref++; +} + +QDeclarativeInfo::~QDeclarativeInfo() +{ + if (0 == --d->ref) { + QList errors = d->errors; + + QDeclarativeEngine *engine = 0; + + if (!d->buffer.isEmpty()) { + QDeclarativeError error; + + QObject *object = const_cast(d->object); + + if (object) { + engine = qmlEngine(d->object); + QString typeName; + QDeclarativeType *type = QDeclarativeMetaType::qmlType(object->metaObject()); + if (type) { + typeName = QLatin1String(type->qmlTypeName()); + int lastSlash = typeName.lastIndexOf(QLatin1Char('/')); + if (lastSlash != -1) + typeName = typeName.mid(lastSlash+1); + } else { + typeName = QString::fromUtf8(object->metaObject()->className()); + int marker = typeName.indexOf(QLatin1String("_QMLTYPE_")); + if (marker != -1) + typeName = typeName.left(marker); + } + + d->buffer.prepend(QLatin1String("QML ") + typeName + QLatin1String(": ")); + + QDeclarativeData *ddata = QDeclarativeData::get(object, false); + if (ddata && ddata->outerContext && !ddata->outerContext->url.isEmpty()) { + error.setUrl(ddata->outerContext->url); + error.setLine(ddata->lineNumber); + error.setColumn(ddata->columnNumber); + } + } + + error.setDescription(d->buffer); + + errors.prepend(error); + } + + QDeclarativeEnginePrivate::warning(engine, errors); + + delete d; + } +} + +QDeclarativeInfo qmlInfo(const QObject *me) +{ + QDeclarativeInfoPrivate *d = new QDeclarativeInfoPrivate; + d->object = me; + return QDeclarativeInfo(d); +} + +QDeclarativeInfo qmlInfo(const QObject *me, const QDeclarativeError &error) +{ + QDeclarativeInfoPrivate *d = new QDeclarativeInfoPrivate; + d->object = me; + d->errors << error; + return QDeclarativeInfo(d); +} + +QDeclarativeInfo qmlInfo(const QObject *me, const QList &errors) +{ + QDeclarativeInfoPrivate *d = new QDeclarativeInfoPrivate; + d->object = me; + d->errors = errors; + return QDeclarativeInfo(d); +} + + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativeinfo.h b/src/declarative/qml/qdeclarativeinfo.h new file mode 100644 index 00000000..e97d8412 --- /dev/null +++ b/src/declarative/qml/qdeclarativeinfo.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEINFO_H +#define QDECLARATIVEINFO_H + +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeInfoPrivate; +class Q_DECLARATIVE_EXPORT QDeclarativeInfo : public QDebug +{ +public: + QDeclarativeInfo(const QDeclarativeInfo &); + ~QDeclarativeInfo(); + + inline QDeclarativeInfo &operator<<(QChar t) { QDebug::operator<<(t); return *this; } + inline QDeclarativeInfo &operator<<(QBool t) { QDebug::operator<<(t); return *this; } + inline QDeclarativeInfo &operator<<(bool t) { QDebug::operator<<(t); return *this; } + inline QDeclarativeInfo &operator<<(char t) { QDebug::operator<<(t); return *this; } + inline QDeclarativeInfo &operator<<(signed short t) { QDebug::operator<<(t); return *this; } + inline QDeclarativeInfo &operator<<(unsigned short t) { QDebug::operator<<(t); return *this; } + inline QDeclarativeInfo &operator<<(signed int t) { QDebug::operator<<(t); return *this; } + inline QDeclarativeInfo &operator<<(unsigned int t) { QDebug::operator<<(t); return *this; } + inline QDeclarativeInfo &operator<<(signed long t) { QDebug::operator<<(t); return *this; } + inline QDeclarativeInfo &operator<<(unsigned long t) { QDebug::operator<<(t); return *this; } + inline QDeclarativeInfo &operator<<(qint64 t) { QDebug::operator<<(t); return *this; } + inline QDeclarativeInfo &operator<<(quint64 t) { QDebug::operator<<(t); return *this; } + inline QDeclarativeInfo &operator<<(float t) { QDebug::operator<<(t); return *this; } + inline QDeclarativeInfo &operator<<(double t) { QDebug::operator<<(t); return *this; } + inline QDeclarativeInfo &operator<<(const char* t) { QDebug::operator<<(t); return *this; } + inline QDeclarativeInfo &operator<<(const QString & t) { QDebug::operator<<(t.toLocal8Bit().constData()); return *this; } + inline QDeclarativeInfo &operator<<(const QStringRef & t) { return operator<<(t.toString()); } + inline QDeclarativeInfo &operator<<(const QLatin1String &t) { QDebug::operator<<(t.latin1()); return *this; } + inline QDeclarativeInfo &operator<<(const QByteArray & t) { QDebug::operator<<(t); return *this; } + inline QDeclarativeInfo &operator<<(const void * t) { QDebug::operator<<(t); return *this; } + inline QDeclarativeInfo &operator<<(QTextStreamFunction f) { QDebug::operator<<(f); return *this; } + inline QDeclarativeInfo &operator<<(QTextStreamManipulator m) { QDebug::operator<<(m); return *this; } +#ifndef QT_NO_DEBUG_STREAM + inline QDeclarativeInfo &operator<<(const QUrl &t) { static_cast(*this) << t; return *this; } +#endif + +private: + friend Q_DECLARATIVE_EXPORT QDeclarativeInfo qmlInfo(const QObject *me); + friend Q_DECLARATIVE_EXPORT QDeclarativeInfo qmlInfo(const QObject *me, const QDeclarativeError &error); + friend Q_DECLARATIVE_EXPORT QDeclarativeInfo qmlInfo(const QObject *me, const QList &errors); + + QDeclarativeInfo(QDeclarativeInfoPrivate *); + QDeclarativeInfoPrivate *d; +}; + +Q_DECLARATIVE_EXPORT QDeclarativeInfo qmlInfo(const QObject *me); +Q_DECLARATIVE_EXPORT QDeclarativeInfo qmlInfo(const QObject *me, const QDeclarativeError &error); +Q_DECLARATIVE_EXPORT QDeclarativeInfo qmlInfo(const QObject *me, const QList &errors); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEINFO_H diff --git a/src/declarative/qml/qdeclarativeinstruction.cpp b/src/declarative/qml/qdeclarativeinstruction.cpp new file mode 100644 index 00000000..aaa87981 --- /dev/null +++ b/src/declarative/qml/qdeclarativeinstruction.cpp @@ -0,0 +1,230 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativeinstruction_p.h" + +#include "private/qdeclarativecompiler_p.h" + +#include + +QT_BEGIN_NAMESPACE + +void QDeclarativeCompiledData::dump(QDeclarativeInstruction *instr, int idx) +{ +#ifdef QT_NO_DEBUG_STREAM + Q_UNUSED(instr) + Q_UNUSED(idx) +#else + QByteArray lineNumber = QByteArray::number(instr->line); + if (instr->line == (unsigned short)-1) + lineNumber = "NA"; + const char *line = lineNumber.constData(); + + switch(instr->type) { + case QDeclarativeInstruction::Init: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "INIT\t\t\t" << instr->init.bindingsSize << "\t" << instr->init.parserStatusSize << "\t" << instr->init.contextCache << "\t" << instr->init.compiledBinding; + break; + case QDeclarativeInstruction::CreateObject: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "CREATE\t\t\t" << instr->create.type << "\t" << instr->create.bindingBits << "\t\t" << types.at(instr->create.type).className; + break; + case QDeclarativeInstruction::CreateSimpleObject: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "CREATE_SIMPLE\t\t" << instr->createSimple.typeSize; + break; + case QDeclarativeInstruction::SetId: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "SETID\t\t\t" << instr->setId.value << "\t\t\t" << primitives.at(instr->setId.value); + break; + case QDeclarativeInstruction::SetDefault: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "SET_DEFAULT"; + break; + case QDeclarativeInstruction::CreateComponent: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "CREATE_COMPONENT\t" << instr->createComponent.count; + break; + case QDeclarativeInstruction::StoreMetaObject: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_META\t\t" << instr->storeMeta.data; + break; + + case QDeclarativeInstruction::StoreFloat: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_FLOAT\t\t" << instr->storeFloat.propertyIndex << "\t" << instr->storeFloat.value; + break; + case QDeclarativeInstruction::StoreDouble: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_DOUBLE\t\t" << instr->storeDouble.propertyIndex << "\t" << instr->storeDouble.value; + break; + case QDeclarativeInstruction::StoreInteger: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_INTEGER\t\t" << instr->storeInteger.propertyIndex << "\t" << instr->storeInteger.value; + break; + case QDeclarativeInstruction::StoreBool: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_BOOL\t\t" << instr->storeBool.propertyIndex << "\t" << instr->storeBool.value; + break; + case QDeclarativeInstruction::StoreString: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_STRING\t\t" << instr->storeString.propertyIndex << "\t" << instr->storeString.value << "\t\t" << primitives.at(instr->storeString.value); + break; + case QDeclarativeInstruction::StoreUrl: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_URL\t\t" << instr->storeUrl.propertyIndex << "\t" << instr->storeUrl.value << "\t\t" << urls.at(instr->storeUrl.value); + break; + case QDeclarativeInstruction::StoreColor: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_COLOR\t\t" << instr->storeColor.propertyIndex << "\t\t\t" << QString::number(instr->storeColor.value, 16); + break; + case QDeclarativeInstruction::StoreDate: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_DATE\t\t" << instr->storeDate.propertyIndex << "\t" << instr->storeDate.value; + break; + case QDeclarativeInstruction::StoreTime: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_TIME\t\t" << instr->storeTime.propertyIndex << "\t" << instr->storeTime.valueIndex; + break; + case QDeclarativeInstruction::StoreDateTime: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_DATETIME\t\t" << instr->storeDateTime.propertyIndex << "\t" << instr->storeDateTime.valueIndex; + break; + case QDeclarativeInstruction::StorePoint: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_POINT\t\t" << instr->storeRealPair.propertyIndex << "\t" << instr->storeRealPair.valueIndex; + break; + case QDeclarativeInstruction::StorePointF: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_POINTF\t\t" << instr->storeRealPair.propertyIndex << "\t" << instr->storeRealPair.valueIndex; + break; + case QDeclarativeInstruction::StoreSize: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_SIZE\t\t" << instr->storeRealPair.propertyIndex << "\t" << instr->storeRealPair.valueIndex; + break; + case QDeclarativeInstruction::StoreSizeF: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_SIZEF\t\t" << instr->storeRealPair.propertyIndex << "\t" << instr->storeRealPair.valueIndex; + break; + case QDeclarativeInstruction::StoreRect: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_RECT\t\t" << instr->storeRect.propertyIndex << "\t" << instr->storeRect.valueIndex; + break; + case QDeclarativeInstruction::StoreRectF: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_RECTF\t\t" << instr->storeRect.propertyIndex << "\t" << instr->storeRect.valueIndex; + break; + case QDeclarativeInstruction::StoreVector3D: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_VECTOR3D\t\t" << instr->storeVector3D.propertyIndex << "\t" << instr->storeVector3D.valueIndex; + break; + case QDeclarativeInstruction::StoreVariant: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_VARIANT\t\t" << instr->storeString.propertyIndex << "\t" << instr->storeString.value << "\t\t" << primitives.at(instr->storeString.value); + break; + case QDeclarativeInstruction::StoreVariantInteger: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_VARIANT_INTEGER\t\t" << instr->storeInteger.propertyIndex << "\t" << instr->storeInteger.value; + break; + case QDeclarativeInstruction::StoreVariantDouble: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_VARIANT_DOUBLE\t\t" << instr->storeDouble.propertyIndex << "\t" << instr->storeDouble.value; + break; + case QDeclarativeInstruction::StoreVariantBool: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_VARIANT_BOOL\t\t" << instr->storeBool.propertyIndex << "\t" << instr->storeBool.value; + break; + case QDeclarativeInstruction::StoreObject: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_OBJECT\t\t" << instr->storeObject.propertyIndex; + break; + case QDeclarativeInstruction::StoreVariantObject: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_VARIANT_OBJECT\t" << instr->storeObject.propertyIndex; + break; + case QDeclarativeInstruction::StoreInterface: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_INTERFACE\t\t" << instr->storeObject.propertyIndex; + break; + + case QDeclarativeInstruction::StoreSignal: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_SIGNAL\t\t" << instr->storeSignal.signalIndex << "\t" << instr->storeSignal.value << "\t\t" << primitives.at(instr->storeSignal.value); + break; + case QDeclarativeInstruction::StoreImportedScript: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_IMPORTED_SCRIPT\t" << instr->storeScript.value; + break; + case QDeclarativeInstruction::StoreScriptString: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_SCRIPT_STRING\t" << instr->storeScriptString.propertyIndex << "\t" << instr->storeScriptString.value << "\t" << instr->storeScriptString.scope; + break; + + case QDeclarativeInstruction::AssignSignalObject: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "ASSIGN_SIGNAL_OBJECT\t" << instr->assignSignalObject.signal << "\t\t\t" << datas.at(instr->assignSignalObject.signal); + break; + case QDeclarativeInstruction::AssignCustomType: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "ASSIGN_CUSTOMTYPE\t" << instr->assignCustomType.propertyIndex << "\t" << instr->assignCustomType.valueIndex; + break; + + case QDeclarativeInstruction::StoreBinding: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_BINDING\t" << instr->assignBinding.property << "\t" << instr->assignBinding.value << "\t" << instr->assignBinding.context; + break; + case QDeclarativeInstruction::StoreBindingOnAlias: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_BINDING_ALIAS\t" << instr->assignBinding.property << "\t" << instr->assignBinding.value << "\t" << instr->assignBinding.context; + break; + case QDeclarativeInstruction::StoreCompiledBinding: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_COMPILED_BINDING\t" << instr->assignBinding.property << "\t" << instr->assignBinding.value << "\t" << instr->assignBinding.context; + break; + case QDeclarativeInstruction::StoreValueSource: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_VALUE_SOURCE\t" << instr->assignValueSource.property << "\t" << instr->assignValueSource.castValue; + break; + case QDeclarativeInstruction::StoreValueInterceptor: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_VALUE_INTERCEPTOR\t" << instr->assignValueInterceptor.property << "\t" << instr->assignValueInterceptor.castValue; + break; + + case QDeclarativeInstruction::BeginObject: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "BEGIN\t\t\t" << instr->begin.castValue; + break; + case QDeclarativeInstruction::StoreObjectQList: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_OBJECT_QLIST"; + break; + case QDeclarativeInstruction::AssignObjectList: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "ASSIGN_OBJECT_LIST"; + break; + case QDeclarativeInstruction::FetchAttached: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "FETCH_ATTACHED\t\t" << instr->fetchAttached.id; + break; + case QDeclarativeInstruction::FetchQList: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "FETCH_QLIST\t\t" << instr->fetch.property; + break; + case QDeclarativeInstruction::FetchObject: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "FETCH\t\t\t" << instr->fetch.property; + break; + case QDeclarativeInstruction::FetchValueType: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "FETCH_VALUE\t\t" << instr->fetchValue.property << "\t" << instr->fetchValue.type << "\t" << instr->fetchValue.bindingSkipList; + break; + case QDeclarativeInstruction::PopFetchedObject: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "POP"; + break; + case QDeclarativeInstruction::PopQList: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "POP_QLIST"; + break; + case QDeclarativeInstruction::PopValueType: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "POP_VALUE\t\t" << instr->fetchValue.property << "\t" << instr->fetchValue.type; + break; + case QDeclarativeInstruction::Defer: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "DEFER" << "\t\t\t" << instr->defer.deferCount; + break; + default: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "XXX UNKNOWN INSTRUCTION" << "\t" << instr->type; + break; + } +#endif // QT_NO_DEBUG_STREAM +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativeinstruction_p.h b/src/declarative/qml/qdeclarativeinstruction_p.h new file mode 100644 index 00000000..34de1116 --- /dev/null +++ b/src/declarative/qml/qdeclarativeinstruction_p.h @@ -0,0 +1,359 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEINSTRUCTION_P_H +#define QDECLARATIVEINSTRUCTION_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 + +QT_BEGIN_NAMESPACE + +class QDeclarativeCompiledData; +class Q_AUTOTEST_EXPORT QDeclarativeInstruction +{ +public: + enum Type { + // + // Object Creation + // + // CreateObject - Create a new object instance and push it on the + // object stack + // SetId - Set the id of the object on the top of the object stack + // SetDefault - Sets the instance on the top of the object stack to + // be the context's default object. + // StoreMetaObject - Assign the dynamic metaobject to object on the + // top of the stack. + Init, /* init */ + CreateObject, /* create */ + CreateSimpleObject, /* createSimple */ + SetId, /* setId */ + SetDefault, + CreateComponent, /* createComponent */ + StoreMetaObject, /* storeMeta */ + + // + // Precomputed single assignment + // + // StoreFloat - Store a float in a core property + // StoreDouble - Store a double in a core property + // StoreInteger - Store a int or uint in a core property + // StoreBool - Store a bool in a core property + // StoreString - Store a QString in a core property + // StoreUrl - Store a QUrl in a core property + // StoreColor - Store a QColor in a core property + // StoreDate - Store a QDate in a core property + // StoreTime - Store a QTime in a core property + // StoreDateTime - Store a QDateTime in a core property + // StoreVariant - Store a QVariant in a core property + // StoreObject - Pop the object on the top of the object stack and + // store it in a core property + StoreFloat, /* storeFloat */ + StoreDouble, /* storeDouble */ + StoreInteger, /* storeInteger */ + StoreBool, /* storeBool */ + StoreString, /* storeString */ + StoreUrl, /* storeUrl */ + StoreColor, /* storeColor */ + StoreDate, /* storeDate */ + StoreTime, /* storeTime */ + StoreDateTime, /* storeDateTime */ + StorePoint, /* storeRealPair */ + StorePointF, /* storeRealPair */ + StoreSize, /* storeRealPair */ + StoreSizeF, /* storeRealPair */ + StoreRect, /* storeRect */ + StoreRectF, /* storeRect */ + StoreVector3D, /* storeVector3D */ + StoreVariant, /* storeString */ + StoreVariantInteger, /* storeInteger */ + StoreVariantDouble, /* storeDouble */ + StoreVariantBool, /* storeBool */ + StoreObject, /* storeObject */ + StoreVariantObject, /* storeObject */ + StoreInterface, /* storeObject */ + + StoreSignal, /* storeSignal */ + StoreImportedScript, /* storeScript */ + StoreScriptString, /* storeScriptString */ + + // + // Unresolved single assignment + // + AssignSignalObject, /* assignSignalObject */ + AssignCustomType, /* assignCustomType */ + + StoreBinding, /* assignBinding */ + StoreBindingOnAlias, /* assignBinding */ + StoreCompiledBinding, /* assignBinding */ + StoreValueSource, /* assignValueSource */ + StoreValueInterceptor, /* assignValueInterceptor */ + + BeginObject, /* begin */ + + StoreObjectQList, /* NA */ + AssignObjectList, /* NA */ + + FetchAttached, /* fetchAttached */ + FetchQList, /* fetch */ + FetchObject, /* fetch */ + FetchValueType, /* fetchValue */ + + // + // Stack manipulation + // + // PopFetchedObject - Remove an object from the object stack + // PopQList - Remove a list from the list stack + PopFetchedObject, + PopQList, + PopValueType, /* fetchValue */ + + // + // Deferred creation + // + Defer /* defer */ + }; + QDeclarativeInstruction() + : line(0) {} + + Type type; + unsigned short line; + + struct InitInstruction { + int bindingsSize; + int parserStatusSize; + int contextCache; + int compiledBinding; + }; + struct CreateInstruction { + int type; + int data; + int bindingBits; + ushort column; + }; + struct CreateSimpleInstruction { + void (*create)(void *); + int typeSize; + int type; + ushort column; + }; + struct StoreMetaInstruction { + int data; + int aliasData; + int propertyCache; + }; + struct SetIdInstruction { + int value; + int index; + }; + struct AssignValueSourceInstruction { + int property; + int owner; + int castValue; + }; + struct AssignValueInterceptorInstruction { + int property; + int owner; + int castValue; + }; + struct AssignBindingInstruction { + unsigned int property; + int value; + short context; + short owner; + }; + struct FetchInstruction { + int property; + }; + struct FetchValueInstruction { + int property; + int type; + quint32 bindingSkipList; + }; + struct FetchQmlListInstruction { + int property; + int type; + }; + struct BeginInstruction { + int castValue; + }; + struct StoreFloatInstruction { + int propertyIndex; + float value; + }; + struct StoreDoubleInstruction { + int propertyIndex; + double value; + }; + struct StoreIntegerInstruction { + int propertyIndex; + int value; + }; + struct StoreBoolInstruction { + int propertyIndex; + bool value; + }; + struct StoreStringInstruction { + int propertyIndex; + int value; + }; + struct StoreScriptStringInstruction { + int propertyIndex; + int value; + int scope; + }; + struct StoreScriptInstruction { + int value; + }; + struct StoreUrlInstruction { + int propertyIndex; + int value; + }; + struct StoreColorInstruction { + int propertyIndex; + unsigned int value; + }; + struct StoreDateInstruction { + int propertyIndex; + int value; + }; + struct StoreTimeInstruction { + int propertyIndex; + int valueIndex; + }; + struct StoreDateTimeInstruction { + int propertyIndex; + int valueIndex; + }; + struct StoreRealPairInstruction { + int propertyIndex; + int valueIndex; + }; + struct StoreRectInstruction { + int propertyIndex; + int valueIndex; + }; + struct StoreVector3DInstruction { + int propertyIndex; + int valueIndex; + }; + struct StoreObjectInstruction { + int propertyIndex; + }; + struct AssignCustomTypeInstruction { + int propertyIndex; + int valueIndex; + }; + struct StoreSignalInstruction { + int signalIndex; + int value; + short context; + int name; + }; + struct AssignSignalObjectInstruction { + int signal; + }; + struct CreateComponentInstruction { + int count; + ushort column; + int endLine; + int metaObject; + }; + struct FetchAttachedInstruction { + int id; + }; + struct DeferInstruction { + int deferCount; + }; + + union { + InitInstruction init; + CreateInstruction create; + CreateSimpleInstruction createSimple; + StoreMetaInstruction storeMeta; + SetIdInstruction setId; + AssignValueSourceInstruction assignValueSource; + AssignValueInterceptorInstruction assignValueInterceptor; + AssignBindingInstruction assignBinding; + FetchInstruction fetch; + FetchValueInstruction fetchValue; + FetchQmlListInstruction fetchQmlList; + BeginInstruction begin; + StoreFloatInstruction storeFloat; + StoreDoubleInstruction storeDouble; + StoreIntegerInstruction storeInteger; + StoreBoolInstruction storeBool; + StoreStringInstruction storeString; + StoreScriptStringInstruction storeScriptString; + StoreScriptInstruction storeScript; + StoreUrlInstruction storeUrl; + StoreColorInstruction storeColor; + StoreDateInstruction storeDate; + StoreTimeInstruction storeTime; + StoreDateTimeInstruction storeDateTime; + StoreRealPairInstruction storeRealPair; + StoreRectInstruction storeRect; + StoreVector3DInstruction storeVector3D; + StoreObjectInstruction storeObject; + AssignCustomTypeInstruction assignCustomType; + StoreSignalInstruction storeSignal; + AssignSignalObjectInstruction assignSignalObject; + CreateComponentInstruction createComponent; + FetchAttachedInstruction fetchAttached; + DeferInstruction defer; + }; + + void dump(QDeclarativeCompiledData *); +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVEINSTRUCTION_P_H diff --git a/src/declarative/qml/qdeclarativeintegercache.cpp b/src/declarative/qml/qdeclarativeintegercache.cpp new file mode 100644 index 00000000..feb39fef --- /dev/null +++ b/src/declarative/qml/qdeclarativeintegercache.cpp @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativeintegercache_p.h" + +#include "private/qdeclarativeengine_p.h" +#include "private/qdeclarativemetatype_p.h" + +QT_BEGIN_NAMESPACE + +QDeclarativeIntegerCache::QDeclarativeIntegerCache(QDeclarativeEngine *e) +: QDeclarativeCleanup(e), engine(e) +{ +} + +QDeclarativeIntegerCache::~QDeclarativeIntegerCache() +{ + clear(); +} + +void QDeclarativeIntegerCache::clear() +{ + qDeleteAll(stringCache); + stringCache.clear(); + identifierCache.clear(); + engine = 0; +} + +QString QDeclarativeIntegerCache::findId(int value) const +{ + for (StringCache::ConstIterator iter = stringCache.begin(); + iter != stringCache.end(); ++iter) { + if (iter.value() && iter.value()->value == value) + return iter.key(); + } + return QString(); +} + +void QDeclarativeIntegerCache::add(const QString &id, int value) +{ + Q_ASSERT(!stringCache.contains(id)); + + QDeclarativeEnginePrivate *enginePriv = QDeclarativeEnginePrivate::get(engine); + + // ### use contextClass + Data *d = new Data(enginePriv->objectClass->createPersistentIdentifier(id), value); + + stringCache.insert(id, d); + identifierCache.insert(d->identifier, d); +} + +int QDeclarativeIntegerCache::value(const QString &id) +{ + Data *d = stringCache.value(id); + return d?d->value:-1; +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativeintegercache_p.h b/src/declarative/qml/qdeclarativeintegercache_p.h new file mode 100644 index 00000000..148f44e2 --- /dev/null +++ b/src/declarative/qml/qdeclarativeintegercache_p.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEINTEGERCACHE_P_H +#define QDECLARATIVEINTEGERCACHE_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 "private/qdeclarativerefcount_p.h" +#include "private/qdeclarativecleanup_p.h" + +#include + +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeType; +class QDeclarativeEngine; +class QDeclarativeIntegerCache : public QDeclarativeRefCount, public QDeclarativeCleanup +{ +public: + QDeclarativeIntegerCache(QDeclarativeEngine *); + virtual ~QDeclarativeIntegerCache(); + + inline int count() const; + void add(const QString &, int); + int value(const QString &); + QString findId(int value) const; + inline int value(const QScriptDeclarativeClass::Identifier &id) const; + +protected: + virtual void clear(); + +private: + struct Data : public QScriptDeclarativeClass::PersistentIdentifier { + Data(const QScriptDeclarativeClass::PersistentIdentifier &i, int v) + : QScriptDeclarativeClass::PersistentIdentifier(i), value(v) {} + + int value; + }; + + typedef QHash StringCache; + typedef QHash IdentifierCache; + + StringCache stringCache; + IdentifierCache identifierCache; + QDeclarativeEngine *engine; +}; + +int QDeclarativeIntegerCache::value(const QScriptDeclarativeClass::Identifier &id) const +{ + Data *d = identifierCache.value(id); + return d?d->value:-1; +} + +int QDeclarativeIntegerCache::count() const +{ + return stringCache.count(); +} + +QT_END_NAMESPACE + +#endif // QDECLARATIVEINTEGERCACHE_P_H + diff --git a/src/declarative/qml/qdeclarativelist.cpp b/src/declarative/qml/qdeclarativelist.cpp new file mode 100644 index 00000000..a9124c7d --- /dev/null +++ b/src/declarative/qml/qdeclarativelist.cpp @@ -0,0 +1,417 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativelist.h" +#include "private/qdeclarativelist_p.h" +#include "private/qdeclarativeengine_p.h" +#include "private/qdeclarativeproperty_p.h" + +QT_BEGIN_NAMESPACE + +QDeclarativeListReferencePrivate::QDeclarativeListReferencePrivate() +: propertyType(-1), refCount(1) +{ +} + +QDeclarativeListReference QDeclarativeListReferencePrivate::init(const QDeclarativeListProperty &prop, int propType, QDeclarativeEngine *engine) +{ + QDeclarativeListReference rv; + + if (!prop.object) return rv; + + QDeclarativeEnginePrivate *p = engine?QDeclarativeEnginePrivate::get(engine):0; + + int listType = p?p->listType(propType):QDeclarativeMetaType::listType(propType); + if (listType == -1) return rv; + + rv.d = new QDeclarativeListReferencePrivate; + rv.d->object = prop.object; + rv.d->elementType = p?p->rawMetaObjectForType(listType):QDeclarativeMetaType::qmlType(listType)->baseMetaObject(); + rv.d->property = prop; + rv.d->propertyType = propType; + + return rv; +} + +void QDeclarativeListReferencePrivate::addref() +{ + Q_ASSERT(refCount > 0); + ++refCount; +} + +void QDeclarativeListReferencePrivate::release() +{ + Q_ASSERT(refCount > 0); + --refCount; + if (!refCount) + delete this; +} + +/*! +\class QDeclarativeListReference +\since 4.7 +\module QtDeclarative +\brief The QDeclarativeListReference class allows the manipulation of QDeclarativeListProperty properties. + +QDeclarativeListReference allows C++ programs to read from, and assign values to a QML list property in a +simple and type safe way. A QDeclarativeListReference can be created by passing an object and property +name or through a QDeclarativeProperty instance. These two are equivalant: + +\code +QDeclarativeListReference ref1(object, "children"); + +QDeclarativeProperty ref2(object, "children"); +QDeclarativeListReference ref2 = qvariant_cast(ref2.read()); +\endcode + +Not all QML list properties support all operations. A set of methods, canAppend(), canAt(), canClear() and +canCount() allow programs to query whether an operation is supported on a given property. + +QML list properties are typesafe. Only QObject's that derive from the correct base class can be assigned to +the list. The listElementType() method can be used to query the QMetaObject of the QObject type supported. +Attempting to add objects of the incorrect type to a list property will fail. + +Like with normal lists, when accessing a list element by index, it is the callers responsibility to ensure +that it does not request an out of range element using the count() method before calling at(). +*/ + +/*! +Constructs an invalid instance. +*/ +QDeclarativeListReference::QDeclarativeListReference() +: d(0) +{ +} + +/*! +Constructs a QDeclarativeListReference for \a object's \a property. If \a property is not a list +property, an invalid QDeclarativeListReference is created. If \a object is destroyed after +the reference is constructed, it will automatically become invalid. That is, it is safe to hold +QDeclarativeListReference instances even after \a object is deleted. + +Passing \a engine is required to access some QML created list properties. If in doubt, and an engine +is available, pass it. +*/ +QDeclarativeListReference::QDeclarativeListReference(QObject *object, const char *property, QDeclarativeEngine *engine) +: d(0) +{ + if (!object || !property) return; + + QDeclarativePropertyCache::Data local; + QDeclarativePropertyCache::Data *data = + QDeclarativePropertyCache::property(engine, object, QLatin1String(property), local); + + if (!data || !(data->flags & QDeclarativePropertyCache::Data::IsQList)) return; + + QDeclarativeEnginePrivate *p = engine?QDeclarativeEnginePrivate::get(engine):0; + + int listType = p?p->listType(data->propType):QDeclarativeMetaType::listType(data->propType); + if (listType == -1) return; + + d = new QDeclarativeListReferencePrivate; + d->object = object; + d->elementType = p?p->rawMetaObjectForType(listType):QDeclarativeMetaType::qmlType(listType)->baseMetaObject(); + d->propertyType = data->propType; + + void *args[] = { &d->property, 0 }; + QMetaObject::metacall(object, QMetaObject::ReadProperty, data->coreIndex, args); +} + +/*! \internal */ +QDeclarativeListReference::QDeclarativeListReference(const QDeclarativeListReference &o) +: d(o.d) +{ + if (d) d->addref(); +} + +/*! \internal */ +QDeclarativeListReference &QDeclarativeListReference::operator=(const QDeclarativeListReference &o) +{ + if (o.d) o.d->addref(); + if (d) d->release(); + d = o.d; + return *this; +} + +/*! \internal */ +QDeclarativeListReference::~QDeclarativeListReference() +{ + if (d) d->release(); +} + +/*! +Returns true if the instance refers to a valid list property, otherwise false. +*/ +bool QDeclarativeListReference::isValid() const +{ + return d && d->object; +} + +/*! +Returns the list property's object. Returns 0 if the reference is invalid. +*/ +QObject *QDeclarativeListReference::object() const +{ + if (isValid()) return d->object; + else return 0; +} + +/*! +Returns the QMetaObject for the elements stored in the list property. Returns 0 if the reference +is invalid. + +The QMetaObject can be used ahead of time to determine whether a given instance can be added +to a list. +*/ +const QMetaObject *QDeclarativeListReference::listElementType() const +{ + if (isValid()) return d->elementType; + else return 0; +} + +/*! +Returns true if the list property can be appended to, otherwise false. Returns false if the +reference is invalid. + +\sa append() +*/ +bool QDeclarativeListReference::canAppend() const +{ + return (isValid() && d->property.append); +} + +/*! +Returns true if the list property can queried by index, otherwise false. Returns false if the +reference is invalid. + +\sa at() +*/ +bool QDeclarativeListReference::canAt() const +{ + return (isValid() && d->property.at); +} + +/*! +Returns true if the list property can be cleared, otherwise false. Returns false if the +reference is invalid. + +\sa clear() +*/ +bool QDeclarativeListReference::canClear() const +{ + return (isValid() && d->property.clear); +} + +/*! +Returns true if the list property can be queried for its element count, otherwise false. +Returns false if the reference is invalid. + +\sa count() +*/ +bool QDeclarativeListReference::canCount() const +{ + return (isValid() && d->property.count); +} + +/*! +Appends \a object to the list. Returns true if the operation succeeded, otherwise false. + +\sa canAppend() +*/ +bool QDeclarativeListReference::append(QObject *object) const +{ + if (!canAppend()) return false; + + if (object && !QDeclarativePropertyPrivate::canConvert(object->metaObject(), d->elementType)) + return false; + + d->property.append(&d->property, object); + + return true; +} + +/*! +Returns the list element at \a index, or 0 if the operation failed. + +\sa canAt() +*/ +QObject *QDeclarativeListReference::at(int index) const +{ + if (!canAt()) return 0; + + return d->property.at(&d->property, index); +} + +/*! +Clears the list. Returns true if the operation succeeded, otherwise false. + +\sa canClear() +*/ +bool QDeclarativeListReference::clear() const +{ + if (!canClear()) return false; + + d->property.clear(&d->property); + + return true; +} + +/*! +Returns the number of objects in the list, or 0 if the operation failed. +*/ +int QDeclarativeListReference::count() const +{ + if (!canCount()) return 0; + + return d->property.count(&d->property); +} + +/*! +\class QDeclarativeListProperty +\since 4.7 +\brief The QDeclarativeListProperty class allows applications to expose list-like +properties to QML. + +QML has many list properties, where more than one object value can be assigned. +The use of a list property from QML looks like this: + +\code +FruitBasket { + fruit: [ + Apple {}, + Orange{}, + Banana{} + ] +} +\endcode + +The QDeclarativeListProperty encapsulates a group of function pointers that represet the +set of actions QML can perform on the list - adding items, retrieving items and +clearing the list. In the future, additional operations may be supported. All +list properties must implement the append operation, but the rest are optional. + +To provide a list property, a C++ class must implement the operation callbacks, +and then return an appropriate QDeclarativeListProperty value from the property getter. +List properties should have no setter. In the example above, the Q_PROPERTY() +declarative will look like this: + +\code +Q_PROPERTY(QDeclarativeListProperty fruit READ fruit); +\endcode + +QML list properties are typesafe - in this case \c {Fruit} is a QObject type that +\c {Apple}, \c {Orange} and \c {Banana} all derive from. + +\note QDeclarativeListProperty can only be used for lists of QObject-derived object pointers. + +\sa {Object and List Property Types} + +*/ + +/*! +\fn QDeclarativeListProperty::QDeclarativeListProperty() +\internal +*/ + +/*! +\fn QDeclarativeListProperty::QDeclarativeListProperty(QObject *object, QList &list) + +Convenience constructor for making a QDeclarativeListProperty value from an existing +QList \a list. The \a list reference must remain valid for as long as \a object +exists. \a object must be provided. + +Generally this constructor should not be used in production code, as a +writable QList violates QML's memory management rules. However, this constructor +can very useful while prototyping. +*/ + +/*! +\fn QDeclarativeListProperty::QDeclarativeListProperty(QObject *object, void *data, AppendFunction append, + CountFunction count = 0, AtFunction at = 0, + ClearFunction clear = 0) + +Construct a QDeclarativeListProperty from a set of operation functions. An opaque \a data handle +may be passed which can be accessed from within the operation functions. The list property +remains valid while \a object exists. + +The \a append operation is compulsory and must be provided, while the \a count, \a at and +\a clear methods are optional. +*/ + +/*! +\typedef QDeclarativeListProperty::AppendFunction + +Synonym for \c {void (*)(QDeclarativeListProperty *property, T *value)}. + +Append the \a value to the list \a property. +*/ + +/*! +\typedef QDeclarativeListProperty::CountFunction + +Synonym for \c {int (*)(QDeclarativeListProperty *property)}. + +Return the number of elements in the list \a property. +*/ + +/*! +\fn bool QDeclarativeListProperty::operator==(const QDeclarativeListProperty &other) const + +Returns true if this QDeclarativeListProperty is equal to \a other, otherwise false. +*/ + +/*! +\typedef QDeclarativeListProperty::AtFunction + +Synonym for \c {T *(*)(QDeclarativeListProperty *property, int index)}. + +Return the element at position \a index in the list \a property. +*/ + +/*! +\typedef QDeclarativeListProperty::ClearFunction + +Synonym for \c {void (*)(QDeclarativeListProperty *property)}. + +Clear the list \a property. +*/ + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativelist.h b/src/declarative/qml/qdeclarativelist.h new file mode 100644 index 00000000..5b2d5b4e --- /dev/null +++ b/src/declarative/qml/qdeclarativelist.h @@ -0,0 +1,152 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVELIST_H +#define QDECLARATIVELIST_H + +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QObject; +struct QMetaObject; + +#ifndef QDECLARATIVELISTPROPERTY +#define QDECLARATIVELISTPROPERTY +template +class QDeclarativeListProperty { +public: + typedef void (*AppendFunction)(QDeclarativeListProperty *, T*); + typedef int (*CountFunction)(QDeclarativeListProperty *); + typedef T *(*AtFunction)(QDeclarativeListProperty *, int); + typedef void (*ClearFunction)(QDeclarativeListProperty *); + + QDeclarativeListProperty() + : object(0), data(0), append(0), count(0), at(0), clear(0), dummy1(0), dummy2(0) {} + QDeclarativeListProperty(QObject *o, QList &list) + : object(o), data(&list), append(qlist_append), count(qlist_count), at(qlist_at), + clear(qlist_clear), dummy1(0), dummy2(0) {} + QDeclarativeListProperty(QObject *o, void *d, AppendFunction a, CountFunction c = 0, AtFunction t = 0, + ClearFunction r = 0) + : object(o), data(d), append(a), count(c), at(t), clear(r), dummy1(0), dummy2(0) {} + + bool operator==(const QDeclarativeListProperty &o) const { + return object == o.object && + data == o.data && + append == o.append && + count == o.count && + at == o.at && + clear == o.clear; + } + + QObject *object; + void *data; + + AppendFunction append; + + CountFunction count; + AtFunction at; + + ClearFunction clear; + + void *dummy1; + void *dummy2; + +private: + static void qlist_append(QDeclarativeListProperty *p, T *v) { + reinterpret_cast *>(p->data)->append(v); + } + static int qlist_count(QDeclarativeListProperty *p) { + return reinterpret_cast *>(p->data)->count(); + } + static T *qlist_at(QDeclarativeListProperty *p, int idx) { + return reinterpret_cast *>(p->data)->at(idx); + } + static void qlist_clear(QDeclarativeListProperty *p) { + return reinterpret_cast *>(p->data)->clear(); + } +}; +#endif + +class QDeclarativeEngine; +class QDeclarativeListReferencePrivate; +class Q_DECLARATIVE_EXPORT QDeclarativeListReference +{ +public: + QDeclarativeListReference(); + QDeclarativeListReference(QObject *, const char *property, QDeclarativeEngine * = 0); + QDeclarativeListReference(const QDeclarativeListReference &); + QDeclarativeListReference &operator=(const QDeclarativeListReference &); + ~QDeclarativeListReference(); + + bool isValid() const; + + QObject *object() const; + const QMetaObject *listElementType() const; + + bool canAppend() const; + bool canAt() const; + bool canClear() const; + bool canCount() const; + + bool append(QObject *) const; + QObject *at(int) const; + bool clear() const; + int count() const; + +private: + friend class QDeclarativeListReferencePrivate; + QDeclarativeListReferencePrivate* d; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QDeclarativeListReference) + +QT_END_HEADER + +#endif // QDECLARATIVELIST_H diff --git a/src/declarative/qml/qdeclarativelist_p.h b/src/declarative/qml/qdeclarativelist_p.h new file mode 100644 index 00000000..76ac83f0 --- /dev/null +++ b/src/declarative/qml/qdeclarativelist_p.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVELIST_P_H +#define QDECLARATIVELIST_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 "qdeclarativelist.h" +#include "private/qdeclarativeguard_p.h" + +QT_BEGIN_NAMESPACE + +class QDeclarativeListReferencePrivate +{ +public: + QDeclarativeListReferencePrivate(); + + static QDeclarativeListReference init(const QDeclarativeListProperty &, int, QDeclarativeEngine *); + + QDeclarativeGuard object; + const QMetaObject *elementType; + QDeclarativeListProperty property; + int propertyType; + + void addref(); + void release(); + int refCount; + + static inline QDeclarativeListReferencePrivate *get(QDeclarativeListReference *ref) { + return ref->d; + } +}; + + +QT_END_NAMESPACE + +#endif // QDECLARATIVELIST_P_H diff --git a/src/declarative/qml/qdeclarativelistscriptclass.cpp b/src/declarative/qml/qdeclarativelistscriptclass.cpp new file mode 100644 index 00000000..4bebd7af --- /dev/null +++ b/src/declarative/qml/qdeclarativelistscriptclass.cpp @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativelistscriptclass_p.h" + +#include "private/qdeclarativeengine_p.h" +#include "private/qdeclarativeguard_p.h" +#include "private/qdeclarativelist_p.h" + +QT_BEGIN_NAMESPACE + +struct ListData : public QScriptDeclarativeClass::Object { + QDeclarativeGuard object; + QDeclarativeListProperty property; + int propertyType; +}; + +QDeclarativeListScriptClass::QDeclarativeListScriptClass(QDeclarativeEngine *e) +: QScriptDeclarativeClass(QDeclarativeEnginePrivate::getScriptEngine(e)), engine(e) +{ + QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine); + Q_UNUSED(scriptEngine); + + m_lengthId = createPersistentIdentifier(QLatin1String("length")); +} + +QDeclarativeListScriptClass::~QDeclarativeListScriptClass() +{ +} + +QScriptValue QDeclarativeListScriptClass::newList(QObject *object, int propId, int propType) +{ + QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine); + + if (!object || propId == -1) + return scriptEngine->nullValue(); + + ListData *data = new ListData; + data->object = object; + data->propertyType = propType; + void *args[] = { &data->property, 0 }; + QMetaObject::metacall(object, QMetaObject::ReadProperty, propId, args); + + return newObject(scriptEngine, this, data); +} + +QScriptValue QDeclarativeListScriptClass::newList(const QDeclarativeListProperty &prop, int propType) +{ + QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine); + + ListData *data = new ListData; + data->object = prop.object; + data->property = prop; + data->propertyType = propType; + + return newObject(scriptEngine, this, data); +} + +QScriptClass::QueryFlags +QDeclarativeListScriptClass::queryProperty(Object *object, const Identifier &name, + QScriptClass::QueryFlags flags) +{ + Q_UNUSED(object); + Q_UNUSED(flags); + if (name == m_lengthId.identifier) + return QScriptClass::HandlesReadAccess; + + bool ok = false; + quint32 idx = toArrayIndex(name, &ok); + + if (ok) { + lastIndex = idx; + return QScriptClass::HandlesReadAccess; + } else { + return 0; + } +} + +QDeclarativeListScriptClass::Value QDeclarativeListScriptClass::property(Object *obj, const Identifier &name) +{ + QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine); + QDeclarativeEnginePrivate *enginePriv = QDeclarativeEnginePrivate::get(engine); + + ListData *data = (ListData *)obj; + if (!data->object) + return Value(); + + quint32 count = data->property.count?data->property.count(&data->property):0; + + if (name == m_lengthId.identifier) + return Value(scriptEngine, count); + else if (lastIndex < count && data->property.at) + return Value(scriptEngine, enginePriv->objectClass->newQObject(data->property.at(&data->property, lastIndex))); + else + return Value(); +} + +QVariant QDeclarativeListScriptClass::toVariant(Object *obj, bool *ok) +{ + ListData *data = (ListData *)obj; + + if (!data->object) { + if (ok) *ok = false; + return QVariant(); + } + + return QVariant::fromValue(QDeclarativeListReferencePrivate::init(data->property, data->propertyType, engine)); +} + +QT_END_NAMESPACE + diff --git a/src/declarative/qml/qdeclarativelistscriptclass_p.h b/src/declarative/qml/qdeclarativelistscriptclass_p.h new file mode 100644 index 00000000..b545014f --- /dev/null +++ b/src/declarative/qml/qdeclarativelistscriptclass_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVELISTSCRIPTCLASS_P_H +#define QDECLARATIVELISTSCRIPTCLASS_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 +#include "qdeclarativelist.h" + +QT_BEGIN_NAMESPACE + +class QDeclarativeEngine; +class QDeclarativeListScriptClass : public QScriptDeclarativeClass +{ +public: + QDeclarativeListScriptClass(QDeclarativeEngine *); + ~QDeclarativeListScriptClass(); + + QScriptValue newList(QObject *, int, int); + QScriptValue newList(const QDeclarativeListProperty &, int); + +protected: + virtual QScriptClass::QueryFlags queryProperty(Object *, const Identifier &, + QScriptClass::QueryFlags flags); + virtual Value property(Object *, const Identifier &); + virtual QVariant toVariant(Object *, bool *ok); + +private: + PersistentIdentifier m_lengthId; + QDeclarativeEngine *engine; + + quint32 lastIndex; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVELISTSCRIPTCLASS_P_H + diff --git a/src/declarative/qml/qdeclarativemetatype.cpp b/src/declarative/qml/qdeclarativemetatype.cpp new file mode 100644 index 00000000..d46eb78b --- /dev/null +++ b/src/declarative/qml/qdeclarativemetatype.cpp @@ -0,0 +1,1536 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include "private/qdeclarativemetatype_p.h" + +#include "private/qdeclarativeproxymetaobject_p.h" +#include "private/qdeclarativecustomparser_p.h" +#include "private/qdeclarativeguard_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef QT_BOOTSTRAPPED +# ifndef QT_NO_GEOM_VARIANT +# define QT_NO_GEOM_VARIANT +# endif +#else +# include +# include +# include +#endif + +#ifndef QT_NO_GEOM_VARIANT +# include +# include +# include +# include +# include +#endif +#define NS(x) QT_PREPEND_NAMESPACE(x) + +QT_BEGIN_NAMESPACE + +struct QDeclarativeMetaTypeData +{ + ~QDeclarativeMetaTypeData(); + QList types; + typedef QHash Ids; + Ids idToType; + typedef QHash Names; + Names nameToType; + typedef QHash MetaObjects; + MetaObjects metaObjectToType; + typedef QHash StringConverters; + StringConverters stringConverters; + + struct ModuleInfo { + ModuleInfo(int major, int minor) + : vmajor_min(major), vminor_min(minor), vmajor_max(major), vminor_max(minor) {} + ModuleInfo(int major_min, int minor_min, int major_max, int minor_max) + : vmajor_min(major_min), vminor_min(minor_min), vmajor_max(major_max), vminor_max(minor_max) {} + int vmajor_min, vminor_min; + int vmajor_max, vminor_max; + }; + typedef QHash ModuleInfoHash; + ModuleInfoHash modules; + + QBitArray objects; + QBitArray interfaces; + QBitArray lists; + + QList parentFunctions; +}; +Q_GLOBAL_STATIC(QDeclarativeMetaTypeData, metaTypeData) +Q_GLOBAL_STATIC(QReadWriteLock, metaTypeDataLock) + +QDeclarativeMetaTypeData::~QDeclarativeMetaTypeData() +{ + for (int i = 0; i < types.count(); ++i) + delete types.at(i); +} + +class QDeclarativeTypePrivate +{ +public: + QDeclarativeTypePrivate(); + + void init() const; + + bool m_isInterface : 1; + const char *m_iid; + QByteArray m_module; + QByteArray m_name; + int m_version_maj; + int m_version_min; + int m_typeId; int m_listId; + int m_revision; + mutable bool m_containsRevisionedAttributes; + mutable QDeclarativeType *m_superType; + + int m_allocationSize; + void (*m_newFunc)(void *); + QString m_noCreationReason; + + const QMetaObject *m_baseMetaObject; + QDeclarativeAttachedPropertiesFunc m_attachedPropertiesFunc; + const QMetaObject *m_attachedPropertiesType; + int m_attachedPropertiesId; + int m_parserStatusCast; + int m_propertyValueSourceCast; + int m_propertyValueInterceptorCast; + QObject *(*m_extFunc)(QObject *); + const QMetaObject *m_extMetaObject; + int m_index; + QDeclarativeCustomParser *m_customParser; + mutable volatile bool m_isSetup:1; + mutable bool m_haveSuperType : 1; + mutable QList m_metaObjects; + + static QHash m_attachedPropertyIds; +}; + +QHash QDeclarativeTypePrivate::m_attachedPropertyIds; + +QDeclarativeTypePrivate::QDeclarativeTypePrivate() +: m_isInterface(false), m_iid(0), m_typeId(0), m_listId(0), m_revision(0), m_containsRevisionedAttributes(false), + m_superType(0), m_allocationSize(0), m_newFunc(0), m_baseMetaObject(0), m_attachedPropertiesFunc(0), + m_attachedPropertiesType(0), m_parserStatusCast(-1), m_propertyValueSourceCast(-1), + m_propertyValueInterceptorCast(-1), m_extFunc(0), m_extMetaObject(0), m_index(-1), m_customParser(0), + m_isSetup(false), m_haveSuperType(false) +{ +} + + +QDeclarativeType::QDeclarativeType(int index, const QDeclarativePrivate::RegisterInterface &interface) +: d(new QDeclarativeTypePrivate) +{ + d->m_isInterface = true; + d->m_iid = interface.iid; + d->m_typeId = interface.typeId; + d->m_listId = interface.listId; + d->m_newFunc = 0; + d->m_index = index; + d->m_isSetup = true; + d->m_version_maj = 0; + d->m_version_min = 0; +} + +QDeclarativeType::QDeclarativeType(int index, const QDeclarativePrivate::RegisterType &type) +: d(new QDeclarativeTypePrivate) +{ + QByteArray name = type.uri; + if (type.uri) name += '/'; + name += type.elementName; + + d->m_module = type.uri; + d->m_name = name; + d->m_version_maj = type.versionMajor; + d->m_version_min = type.versionMinor; + if (type.version >= 1) // revisions added in version 1 + d->m_revision = type.revision; + d->m_typeId = type.typeId; + d->m_listId = type.listId; + d->m_allocationSize = type.objectSize; + d->m_newFunc = type.create; + d->m_noCreationReason = type.noCreationReason; + d->m_baseMetaObject = type.metaObject; + d->m_attachedPropertiesFunc = type.attachedPropertiesFunction; + d->m_attachedPropertiesType = type.attachedPropertiesMetaObject; + if (d->m_attachedPropertiesType) { + QHash::Iterator iter = d->m_attachedPropertyIds.find(d->m_baseMetaObject); + if (iter == d->m_attachedPropertyIds.end()) + iter = d->m_attachedPropertyIds.insert(d->m_baseMetaObject, index); + d->m_attachedPropertiesId = *iter; + } else { + d->m_attachedPropertiesId = -1; + } + d->m_parserStatusCast = type.parserStatusCast; + d->m_propertyValueSourceCast = type.valueSourceCast; + d->m_propertyValueInterceptorCast = type.valueInterceptorCast; + d->m_extFunc = type.extensionObjectCreate; + d->m_index = index; + d->m_customParser = type.customParser; + + if (type.extensionMetaObject) + d->m_extMetaObject = type.extensionMetaObject; +} + +QDeclarativeType::~QDeclarativeType() +{ + delete d->m_customParser; + delete d; +} + +QByteArray QDeclarativeType::module() const +{ + return d->m_module; +} + +int QDeclarativeType::majorVersion() const +{ + return d->m_version_maj; +} + +int QDeclarativeType::minorVersion() const +{ + return d->m_version_min; +} + +bool QDeclarativeType::availableInVersion(int vmajor, int vminor) const +{ + return vmajor > d->m_version_maj || (vmajor == d->m_version_maj && vminor >= d->m_version_min); +} + +bool QDeclarativeType::availableInVersion(const QByteArray &module, int vmajor, int vminor) const +{ + return module == d->m_module && (vmajor > d->m_version_maj || (vmajor == d->m_version_maj && vminor >= d->m_version_min)); +} + +// returns the nearest _registered_ super class +QDeclarativeType *QDeclarativeType::superType() const +{ + if (!d->m_haveSuperType) { + const QMetaObject *mo = d->m_baseMetaObject->superClass(); + while (mo && !d->m_superType) { + d->m_superType = QDeclarativeMetaType::qmlType(mo, d->m_module, d->m_version_maj, d->m_version_min); + mo = mo->superClass(); + } + d->m_haveSuperType = true; + } + + return d->m_superType; +} + +static void clone(QMetaObjectBuilder &builder, const QMetaObject *mo, + const QMetaObject *ignoreStart, const QMetaObject *ignoreEnd) +{ + // Clone Q_CLASSINFO + for (int ii = mo->classInfoOffset(); ii < mo->classInfoCount(); ++ii) { + QMetaClassInfo info = mo->classInfo(ii); + + int otherIndex = ignoreEnd->indexOfClassInfo(info.name()); + if (otherIndex >= ignoreStart->classInfoOffset() + ignoreStart->classInfoCount()) { + // Skip + } else { + builder.addClassInfo(info.name(), info.value()); + } + } + + // Clone Q_PROPERTY + for (int ii = mo->propertyOffset(); ii < mo->propertyCount(); ++ii) { + QMetaProperty property = mo->property(ii); + + int otherIndex = ignoreEnd->indexOfProperty(property.name()); + if (otherIndex >= ignoreStart->propertyOffset() + ignoreStart->propertyCount()) { + builder.addProperty(QByteArray("__qml_ignore__") + property.name(), QByteArray("void")); + // Skip + } else { + builder.addProperty(property); + } + } + + // Clone Q_METHODS + for (int ii = mo->methodOffset(); ii < mo->methodCount(); ++ii) { + QMetaMethod method = mo->method(ii); + + // More complex - need to search name + QByteArray name = method.signature(); + int parenIdx = name.indexOf('('); + if (parenIdx != -1) name = name.left(parenIdx); + + + bool found = false; + + for (int ii = ignoreStart->methodOffset() + ignoreStart->methodCount(); + !found && ii < ignoreEnd->methodOffset() + ignoreEnd->methodCount(); + ++ii) { + + QMetaMethod other = ignoreEnd->method(ii); + QByteArray othername = other.signature(); + int parenIdx = othername.indexOf('('); + if (parenIdx != -1) othername = othername.left(parenIdx); + + found = name == othername; + } + + QMetaMethodBuilder m = builder.addMethod(method); + if (found) // SKIP + m.setAccess(QMetaMethod::Private); + } + + // Clone Q_ENUMS + for (int ii = mo->enumeratorOffset(); ii < mo->enumeratorCount(); ++ii) { + QMetaEnum enumerator = mo->enumerator(ii); + + int otherIndex = ignoreEnd->indexOfEnumerator(enumerator.name()); + if (otherIndex >= ignoreStart->enumeratorOffset() + ignoreStart->enumeratorCount()) { + // Skip + } else { + builder.addEnumerator(enumerator); + } + } +} + +void QDeclarativeTypePrivate::init() const +{ + if (m_isSetup) return; + + QWriteLocker lock(metaTypeDataLock()); + if (m_isSetup) + return; + + // Setup extended meta object + // XXX - very inefficient + const QMetaObject *mo = m_baseMetaObject; + if (m_extFunc) { + QMetaObject *mmo = new QMetaObject; + *mmo = *m_extMetaObject; + mmo->d.superdata = mo; + QDeclarativeProxyMetaObject::ProxyData data = { mmo, m_extFunc, 0, 0 }; + m_metaObjects << data; + } + + mo = mo->d.superdata; + while(mo) { + QDeclarativeType *t = metaTypeData()->metaObjectToType.value(mo); + if (t) { + if (t->d->m_extFunc) { + QMetaObjectBuilder builder; + clone(builder, t->d->m_extMetaObject, t->d->m_baseMetaObject, m_baseMetaObject); + QMetaObject *mmo = builder.toMetaObject(); + mmo->d.superdata = m_baseMetaObject; + if (!m_metaObjects.isEmpty()) + m_metaObjects.last().metaObject->d.superdata = mmo; + QDeclarativeProxyMetaObject::ProxyData data = { mmo, t->d->m_extFunc, 0, 0 }; + m_metaObjects << data; + } + } + mo = mo->d.superdata; + } + + for (int ii = 0; ii < m_metaObjects.count(); ++ii) { + m_metaObjects[ii].propertyOffset = + m_metaObjects.at(ii).metaObject->propertyOffset(); + m_metaObjects[ii].methodOffset = + m_metaObjects.at(ii).metaObject->methodOffset(); + } + + // Check for revisioned details + { + const QMetaObject *mo = 0; + if (m_metaObjects.isEmpty()) + mo = m_baseMetaObject; + else + mo = m_metaObjects.first().metaObject; + + for (int ii = 0; !m_containsRevisionedAttributes && ii < mo->propertyCount(); ++ii) { + if (mo->property(ii).revision() != 0) + m_containsRevisionedAttributes = true; + } + + for (int ii = 0; !m_containsRevisionedAttributes && ii < mo->methodCount(); ++ii) { + if (mo->method(ii).revision() != 0) + m_containsRevisionedAttributes = true; + } + } + + m_isSetup = true; + lock.unlock(); +} + +QByteArray QDeclarativeType::typeName() const +{ + if (d->m_baseMetaObject) + return d->m_baseMetaObject->className(); + else + return QByteArray(); +} + +QByteArray QDeclarativeType::qmlTypeName() const +{ + return d->m_name; +} + +QObject *QDeclarativeType::create() const +{ + d->init(); + + QObject *rv = (QObject *)operator new(d->m_allocationSize); + d->m_newFunc(rv); + + if (rv && !d->m_metaObjects.isEmpty()) + (void *)new QDeclarativeProxyMetaObject(rv, &d->m_metaObjects); + + return rv; +} + +void QDeclarativeType::create(QObject **out, void **memory, size_t additionalMemory) const +{ + d->init(); + + QObject *rv = (QObject *)operator new(d->m_allocationSize + additionalMemory); + d->m_newFunc(rv); + + if (rv && !d->m_metaObjects.isEmpty()) + (void *)new QDeclarativeProxyMetaObject(rv, &d->m_metaObjects); + + *out = rv; + *memory = ((char *)rv) + d->m_allocationSize; +} + +QDeclarativeCustomParser *QDeclarativeType::customParser() const +{ + return d->m_customParser; +} + +QDeclarativeType::CreateFunc QDeclarativeType::createFunction() const +{ + return d->m_newFunc; +} + +QString QDeclarativeType::noCreationReason() const +{ + return d->m_noCreationReason; +} + +int QDeclarativeType::createSize() const +{ + return d->m_allocationSize; +} + +bool QDeclarativeType::isCreatable() const +{ + return d->m_newFunc != 0; +} + +bool QDeclarativeType::isExtendedType() const +{ + d->init(); + + return !d->m_metaObjects.isEmpty(); +} + +bool QDeclarativeType::isInterface() const +{ + return d->m_isInterface; +} + +int QDeclarativeType::typeId() const +{ + return d->m_typeId; +} + +int QDeclarativeType::qListTypeId() const +{ + return d->m_listId; +} + +const QMetaObject *QDeclarativeType::metaObject() const +{ + d->init(); + + if (d->m_metaObjects.isEmpty()) + return d->m_baseMetaObject; + else + return d->m_metaObjects.first().metaObject; + +} + +const QMetaObject *QDeclarativeType::baseMetaObject() const +{ + return d->m_baseMetaObject; +} + +bool QDeclarativeType::containsRevisionedAttributes() const +{ + d->init(); + + return d->m_containsRevisionedAttributes; +} + +int QDeclarativeType::metaObjectRevision() const +{ + return d->m_revision; +} + +QDeclarativeAttachedPropertiesFunc QDeclarativeType::attachedPropertiesFunction() const +{ + return d->m_attachedPropertiesFunc; +} + +const QMetaObject *QDeclarativeType::attachedPropertiesType() const +{ + return d->m_attachedPropertiesType; +} + +/* +This is the id passed to qmlAttachedPropertiesById(). This is different from the index +for the case that a single class is registered under two or more names (eg. Item in +Qt 4.7 and QtQuick 1.0). +*/ +int QDeclarativeType::attachedPropertiesId() const +{ + return d->m_attachedPropertiesId; +} + +int QDeclarativeType::parserStatusCast() const +{ + return d->m_parserStatusCast; +} + +int QDeclarativeType::propertyValueSourceCast() const +{ + return d->m_propertyValueSourceCast; +} + +int QDeclarativeType::propertyValueInterceptorCast() const +{ + return d->m_propertyValueInterceptorCast; +} + +const char *QDeclarativeType::interfaceIId() const +{ + return d->m_iid; +} + +int QDeclarativeType::index() const +{ + return d->m_index; +} + +int registerAutoParentFunction(QDeclarativePrivate::RegisterAutoParent &autoparent) +{ + QWriteLocker lock(metaTypeDataLock()); + QDeclarativeMetaTypeData *data = metaTypeData(); + + data->parentFunctions.append(autoparent.function); + + return data->parentFunctions.count() - 1; +} + +int registerInterface(const QDeclarativePrivate::RegisterInterface &interface) +{ + if (interface.version > 0) + qFatal("qmlRegisterType(): Cannot mix incompatible QML versions."); + + QWriteLocker lock(metaTypeDataLock()); + QDeclarativeMetaTypeData *data = metaTypeData(); + + int index = data->types.count(); + + QDeclarativeType *type = new QDeclarativeType(index, interface); + + data->types.append(type); + data->idToType.insert(type->typeId(), type); + data->idToType.insert(type->qListTypeId(), type); + // XXX No insertMulti, so no multi-version interfaces? + if (!type->qmlTypeName().isEmpty()) + data->nameToType.insert(type->qmlTypeName(), type); + + if (data->interfaces.size() <= interface.typeId) + data->interfaces.resize(interface.typeId + 16); + if (data->lists.size() <= interface.listId) + data->lists.resize(interface.listId + 16); + data->interfaces.setBit(interface.typeId, true); + data->lists.setBit(interface.listId, true); + + return index; +} + +int registerType(const QDeclarativePrivate::RegisterType &type) +{ + if (type.elementName) { + for (int ii = 0; type.elementName[ii]; ++ii) { + if (!isalnum(type.elementName[ii])) { + qWarning("qmlRegisterType(): Invalid QML element name \"%s\"", type.elementName); + return -1; + } + } + } + + QWriteLocker lock(metaTypeDataLock()); + QDeclarativeMetaTypeData *data = metaTypeData(); + int index = data->types.count(); + + QDeclarativeType *dtype = new QDeclarativeType(index, type); + + data->types.append(dtype); + data->idToType.insert(dtype->typeId(), dtype); + if (dtype->qListTypeId()) data->idToType.insert(dtype->qListTypeId(), dtype); + + if (!dtype->qmlTypeName().isEmpty()) + data->nameToType.insertMulti(dtype->qmlTypeName(), dtype); + + data->metaObjectToType.insertMulti(dtype->baseMetaObject(), dtype); + + if (data->objects.size() <= type.typeId) + data->objects.resize(type.typeId + 16); + if (data->lists.size() <= type.listId) + data->lists.resize(type.listId + 16); + data->objects.setBit(type.typeId, true); + if (type.listId) data->lists.setBit(type.listId, true); + + if (type.uri) { + QByteArray mod(type.uri); + QDeclarativeMetaTypeData::ModuleInfoHash::Iterator it = data->modules.find(mod); + if (it == data->modules.end()) { + // New module + data->modules.insert(mod, QDeclarativeMetaTypeData::ModuleInfo(type.versionMajor,type.versionMinor)); + } else if ((*it).vmajor_max < type.versionMajor || ((*it).vmajor_max == type.versionMajor && (*it).vminor_max < type.versionMinor)) { + // Newer module + data->modules.insert(mod, QDeclarativeMetaTypeData::ModuleInfo((*it).vmajor_min, (*it).vminor_min, type.versionMajor, type.versionMinor)); + } else if ((*it).vmajor_min > type.versionMajor || ((*it).vmajor_min == type.versionMajor && (*it).vminor_min > type.versionMinor)) { + // Older module + data->modules.insert(mod, QDeclarativeMetaTypeData::ModuleInfo(type.versionMajor, type.versionMinor, (*it).vmajor_min, (*it).vminor_min)); + } + } + + return index; +} + +/* +This method is "over generalized" to allow us to (potentially) register more types of things in +the future without adding exported symbols. +*/ +int QDeclarativePrivate::qmlregister(RegistrationType type, void *data) +{ + if (type == TypeRegistration) { + return registerType(*reinterpret_cast(data)); + } else if (type == InterfaceRegistration) { + return registerInterface(*reinterpret_cast(data)); + } else if (type == AutoParentRegistration) { + return registerAutoParentFunction(*reinterpret_cast(data)); + } + return -1; +} + +/* + Have any types been registered for \a module with at least versionMajor.versionMinor, and types + for \a module with at most versionMajor.versionMinor. + + So if only 4.7 and 4.9 have been registered, 4.7,4.8, and 4.9 are valid, but not 4.6 nor 4.10. + + Passing -1 for both \a versionMajor \a versionMinor will return true if any version is installed. +*/ +bool QDeclarativeMetaType::isModule(const QByteArray &module, int versionMajor, int versionMinor) +{ + QDeclarativeMetaTypeData *data = metaTypeData(); + QDeclarativeMetaTypeData::ModuleInfoHash::Iterator it = data->modules.find(module); + return it != data->modules.end() + && ((versionMajor<0 && versionMinor<0) || + (((*it).vmajor_max > versionMajor || + ((*it).vmajor_max == versionMajor && (*it).vminor_max >= versionMinor)) + && ((*it).vmajor_min < versionMajor || + ((*it).vmajor_min == versionMajor && (*it).vminor_min <= versionMinor)))); +} + +QList QDeclarativeMetaType::parentFunctions() +{ + QReadLocker lock(metaTypeDataLock()); + QDeclarativeMetaTypeData *data = metaTypeData(); + return data->parentFunctions; +} + +QObject *QDeclarativeMetaType::toQObject(const QVariant &v, bool *ok) +{ + if (!isQObject(v.userType())) { + if (ok) *ok = false; + return 0; + } + + if (ok) *ok = true; + + return *(QObject **)v.constData(); +} + +bool QDeclarativeMetaType::isQObject(int userType) +{ + if (userType == QMetaType::QObjectStar) + return true; + + QReadLocker lock(metaTypeDataLock()); + QDeclarativeMetaTypeData *data = metaTypeData(); + return userType >= 0 && userType < data->objects.size() && data->objects.testBit(userType); +} + +/* + Returns the item type for a list of type \a id. + */ +int QDeclarativeMetaType::listType(int id) +{ + QReadLocker lock(metaTypeDataLock()); + QDeclarativeMetaTypeData *data = metaTypeData(); + QDeclarativeType *type = data->idToType.value(id); + if (type && type->qListTypeId() == id) + return type->typeId(); + else + return 0; +} + +int QDeclarativeMetaType::attachedPropertiesFuncId(const QMetaObject *mo) +{ + QReadLocker lock(metaTypeDataLock()); + QDeclarativeMetaTypeData *data = metaTypeData(); + + QDeclarativeType *type = data->metaObjectToType.value(mo); + if (type && type->attachedPropertiesFunction()) + return type->attachedPropertiesId(); + else + return -1; +} + +QDeclarativeAttachedPropertiesFunc QDeclarativeMetaType::attachedPropertiesFuncById(int id) +{ + if (id < 0) + return 0; + QReadLocker lock(metaTypeDataLock()); + QDeclarativeMetaTypeData *data = metaTypeData(); + return data->types.at(id)->attachedPropertiesFunction(); +} + +QMetaProperty QDeclarativeMetaType::defaultProperty(const QMetaObject *metaObject) +{ + int idx = metaObject->indexOfClassInfo("DefaultProperty"); + if (-1 == idx) + return QMetaProperty(); + + QMetaClassInfo info = metaObject->classInfo(idx); + if (!info.value()) + return QMetaProperty(); + + idx = metaObject->indexOfProperty(info.value()); + if (-1 == idx) + return QMetaProperty(); + + return metaObject->property(idx); +} + +QMetaProperty QDeclarativeMetaType::defaultProperty(QObject *obj) +{ + if (!obj) + return QMetaProperty(); + + const QMetaObject *metaObject = obj->metaObject(); + return defaultProperty(metaObject); +} + +QMetaMethod QDeclarativeMetaType::defaultMethod(const QMetaObject *metaObject) +{ + int idx = metaObject->indexOfClassInfo("DefaultMethod"); + if (-1 == idx) + return QMetaMethod(); + + QMetaClassInfo info = metaObject->classInfo(idx); + if (!info.value()) + return QMetaMethod(); + + idx = metaObject->indexOfMethod(info.value()); + if (-1 == idx) + return QMetaMethod(); + + return metaObject->method(idx); +} + +QMetaMethod QDeclarativeMetaType::defaultMethod(QObject *obj) +{ + if (!obj) + return QMetaMethod(); + + const QMetaObject *metaObject = obj->metaObject(); + return defaultMethod(metaObject); +} + +QDeclarativeMetaType::TypeCategory QDeclarativeMetaType::typeCategory(int userType) +{ + if (userType < 0) + return Unknown; + if (userType == QMetaType::QObjectStar) + return Object; + + QReadLocker lock(metaTypeDataLock()); + QDeclarativeMetaTypeData *data = metaTypeData(); + if (userType < data->objects.size() && data->objects.testBit(userType)) + return Object; + else if (userType < data->lists.size() && data->lists.testBit(userType)) + return List; + else + return Unknown; +} + +bool QDeclarativeMetaType::isInterface(int userType) +{ + QReadLocker lock(metaTypeDataLock()); + QDeclarativeMetaTypeData *data = metaTypeData(); + return userType >= 0 && userType < data->interfaces.size() && data->interfaces.testBit(userType); +} + +const char *QDeclarativeMetaType::interfaceIId(int userType) +{ + QReadLocker lock(metaTypeDataLock()); + QDeclarativeMetaTypeData *data = metaTypeData(); + QDeclarativeType *type = data->idToType.value(userType); + lock.unlock(); + if (type && type->isInterface() && type->typeId() == userType) + return type->interfaceIId(); + else + return 0; +} + +bool QDeclarativeMetaType::isList(int userType) +{ + QReadLocker lock(metaTypeDataLock()); + QDeclarativeMetaTypeData *data = metaTypeData(); + return userType >= 0 && userType < data->lists.size() && data->lists.testBit(userType); +} + +/*! + A custom string convertor allows you to specify a function pointer that + returns a variant of \a type. For example, if you have written your own icon + class that you want to support as an object property assignable in QML: + + \code + int type = qRegisterMetaType("SuperIcon"); + QML::addCustomStringConvertor(type, &SuperIcon::pixmapFromString); + \endcode + + The function pointer must be of the form: + \code + QVariant (*StringConverter)(const QString &); + \endcode + */ +void QDeclarativeMetaType::registerCustomStringConverter(int type, StringConverter converter) +{ + QWriteLocker lock(metaTypeDataLock()); + + QDeclarativeMetaTypeData *data = metaTypeData(); + if (data->stringConverters.contains(type)) + return; + data->stringConverters.insert(type, converter); +} + +/*! + Return the custom string converter for \a type, previously installed through + registerCustomStringConverter() + */ +QDeclarativeMetaType::StringConverter QDeclarativeMetaType::customStringConverter(int type) +{ + QReadLocker lock(metaTypeDataLock()); + + QDeclarativeMetaTypeData *data = metaTypeData(); + return data->stringConverters.value(type); +} + +/*! + Returns the type (if any) of URI-qualified named \a name in version specified + by \a version_major and \a version_minor. +*/ +QDeclarativeType *QDeclarativeMetaType::qmlType(const QByteArray &name, int version_major, int version_minor) +{ + QReadLocker lock(metaTypeDataLock()); + QDeclarativeMetaTypeData *data = metaTypeData(); + + QList types = data->nameToType.values(name); + foreach (QDeclarativeType *t, types) { + // XXX version_major<0 just a kludge for QDeclarativePropertyPrivate::initProperty + if (version_major<0 || t->availableInVersion(version_major,version_minor)) + return t; + } + return 0; +} + +/*! + Returns the type (if any) that corresponds to the \a metaObject. Returns null if no + type is registered. +*/ +QDeclarativeType *QDeclarativeMetaType::qmlType(const QMetaObject *metaObject) +{ + QReadLocker lock(metaTypeDataLock()); + QDeclarativeMetaTypeData *data = metaTypeData(); + + return data->metaObjectToType.value(metaObject); +} + +/*! + Returns the type (if any) that corresponds to the \a metaObject in version specified + by \a version_major and \a version_minor in module specified by \a uri. Returns null if no + type is registered. +*/ +QDeclarativeType *QDeclarativeMetaType::qmlType(const QMetaObject *metaObject, const QByteArray &module, int version_major, int version_minor) +{ + QReadLocker lock(metaTypeDataLock()); + QDeclarativeMetaTypeData *data = metaTypeData(); + + QDeclarativeMetaTypeData::MetaObjects::const_iterator it = data->metaObjectToType.find(metaObject); + while (it != data->metaObjectToType.end() && it.key() == metaObject) { + QDeclarativeType *t = *it; + if (version_major < 0 || t->availableInVersion(module, version_major,version_minor)) + return t; + ++it; + } + + return 0; +} + +/*! + Returns the type (if any) that corresponds to the QVariant::Type \a userType. + Returns null if no type is registered. +*/ +QDeclarativeType *QDeclarativeMetaType::qmlType(int userType) +{ + QReadLocker lock(metaTypeDataLock()); + QDeclarativeMetaTypeData *data = metaTypeData(); + + QDeclarativeType *type = data->idToType.value(userType); + if (type && type->typeId() == userType) + return type; + else + return 0; +} + +/*! + Returns the list of registered QML type names. +*/ +QList QDeclarativeMetaType::qmlTypeNames() +{ + QReadLocker lock(metaTypeDataLock()); + QDeclarativeMetaTypeData *data = metaTypeData(); + + return data->nameToType.keys(); +} + +/*! + Returns the list of registered QML types. +*/ +QList QDeclarativeMetaType::qmlTypes() +{ + QReadLocker lock(metaTypeDataLock()); + QDeclarativeMetaTypeData *data = metaTypeData(); + + return data->nameToType.values(); +} + +QT_END_NAMESPACE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#include +#include +#include +#include +#include +#include +#include +#include +#include + +Q_DECLARE_METATYPE(QScriptValue); + +QT_BEGIN_NAMESPACE + +bool QDeclarativeMetaType::canCopy(int type) +{ + switch(type) { + case QMetaType::VoidStar: + case QMetaType::QObjectStar: + case QMetaType::QWidgetStar: + case QMetaType::Long: + case QMetaType::Int: + case QMetaType::Short: + case QMetaType::Char: + case QMetaType::ULong: + case QMetaType::UInt: + case QMetaType::LongLong: + case QMetaType::ULongLong: + case QMetaType::UShort: + case QMetaType::UChar: + case QMetaType::Bool: + case QMetaType::Float: + case QMetaType::Double: + case QMetaType::QChar: + case QMetaType::QVariantMap: + case QMetaType::QVariantHash: + case QMetaType::QVariantList: + case QMetaType::QByteArray: + case QMetaType::QString: + case QMetaType::QStringList: + case QMetaType::QBitArray: + case QMetaType::QDate: + case QMetaType::QTime: + case QMetaType::QDateTime: + case QMetaType::QUrl: + case QMetaType::QLocale: + case QMetaType::QRect: + case QMetaType::QRectF: + case QMetaType::QSize: + case QMetaType::QSizeF: + case QMetaType::QLine: + case QMetaType::QLineF: + case QMetaType::QPoint: + case QMetaType::QPointF: + case QMetaType::QVector3D: +#ifndef QT_NO_REGEXP + case QMetaType::QRegExp: +#endif + case QMetaType::Void: +#ifdef QT3_SUPPORT + case QMetaType::QColorGroup: +#endif + case QMetaType::QFont: + case QMetaType::QPixmap: + case QMetaType::QBrush: + case QMetaType::QColor: + case QMetaType::QPalette: + case QMetaType::QIcon: + case QMetaType::QImage: + case QMetaType::QPolygon: + case QMetaType::QRegion: + case QMetaType::QBitmap: +#ifndef QT_NO_CURSOR + case QMetaType::QCursor: +#endif + case QMetaType::QSizePolicy: + case QMetaType::QKeySequence: + case QMetaType::QPen: + case QMetaType::QTextLength: + case QMetaType::QTextFormat: + case QMetaType::QMatrix: + case QMetaType::QTransform: + case QMetaType::QMatrix4x4: + case QMetaType::QVector2D: + case QMetaType::QVector4D: + case QMetaType::QQuaternion: + return true; + + default: + if (type == qMetaTypeId() || + type == qMetaTypeId() || + typeCategory(type) != Unknown) { + return true; + } + break; + } + + return false; +} + +/*! + Copies \a copy into \a data, assuming they both are of type \a type. If + \a copy is zero, a default type is copied. Returns true if the copy was + successful and false if not. + + \note This should move into QMetaType once complete + +*/ +bool QDeclarativeMetaType::copy(int type, void *data, const void *copy) +{ + if (copy) { + switch(type) { + case QMetaType::VoidStar: + case QMetaType::QObjectStar: + case QMetaType::QWidgetStar: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::Long: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::Int: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::Short: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::Char: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::ULong: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::UInt: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::LongLong: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::ULongLong: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::UShort: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::UChar: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::Bool: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::Float: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::Double: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::QChar: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::QVariantMap: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::QVariantHash: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::QVariantList: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::QByteArray: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::QString: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::QStringList: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::QBitArray: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::QDate: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::QTime: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::QDateTime: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::QUrl: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::QLocale: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::QRect: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::QRectF: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::QSize: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::QSizeF: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::QLine: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::QLineF: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::QPoint: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::QPointF: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::QVector3D: + *static_cast(data) = *static_cast(copy); + return true; +#ifndef QT_NO_REGEXP + case QMetaType::QRegExp: + *static_cast(data) = *static_cast(copy); + return true; +#endif + case QMetaType::Void: + return true; + + +#ifdef QT3_SUPPORT + case QMetaType::QColorGroup: + *static_cast(data) = *static_cast(copy); + return true; +#endif + + case QMetaType::QFont: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::QPixmap: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::QBrush: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::QColor: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::QPalette: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::QIcon: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::QImage: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::QPolygon: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::QRegion: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::QBitmap: + *static_cast(data) = *static_cast(copy); + return true; +#ifndef QT_NO_CURSOR + case QMetaType::QCursor: + *static_cast(data) = *static_cast(copy); + return true; +#endif + case QMetaType::QSizePolicy: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::QKeySequence: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::QPen: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::QTextLength: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::QTextFormat: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::QMatrix: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::QTransform: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::QMatrix4x4: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::QVector2D: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::QVector4D: + *static_cast(data) = *static_cast(copy); + return true; + case QMetaType::QQuaternion: + *static_cast(data) = *static_cast(copy); + return true; + + default: + if (type == qMetaTypeId()) { + *static_cast(data) = *static_cast(copy); + return true; + } else if (type == qMetaTypeId()) { + *static_cast(data) = *static_cast(copy); + return true; + } else if (typeCategory(type) != Unknown) { + *static_cast(data) = *static_cast(copy); + return true; + } + break; + } + } else { + switch(type) { + case QMetaType::VoidStar: + case QMetaType::QObjectStar: + case QMetaType::QWidgetStar: + *static_cast(data) = 0; + return true; + case QMetaType::Long: + *static_cast(data) = long(0); + return true; + case QMetaType::Int: + *static_cast(data) = int(0); + return true; + case QMetaType::Short: + *static_cast(data) = short(0); + return true; + case QMetaType::Char: + *static_cast(data) = char(0); + return true; + case QMetaType::ULong: + *static_cast(data) = ulong(0); + return true; + case QMetaType::UInt: + *static_cast(data) = uint(0); + return true; + case QMetaType::LongLong: + *static_cast(data) = qlonglong(0); + return true; + case QMetaType::ULongLong: + *static_cast(data) = qulonglong(0); + return true; + case QMetaType::UShort: + *static_cast(data) = ushort(0); + return true; + case QMetaType::UChar: + *static_cast(data) = uchar(0); + return true; + case QMetaType::Bool: + *static_cast(data) = bool(false); + return true; + case QMetaType::Float: + *static_cast(data) = float(0); + return true; + case QMetaType::Double: + *static_cast(data) = double(0); + return true; + case QMetaType::QChar: + *static_cast(data) = NS(QChar)(); + return true; + case QMetaType::QVariantMap: + *static_cast(data) = NS(QVariantMap)(); + return true; + case QMetaType::QVariantHash: + *static_cast(data) = NS(QVariantHash)(); + return true; + case QMetaType::QVariantList: + *static_cast(data) = NS(QVariantList)(); + return true; + case QMetaType::QByteArray: + *static_cast(data) = NS(QByteArray)(); + return true; + case QMetaType::QString: + *static_cast(data) = NS(QString)(); + return true; + case QMetaType::QStringList: + *static_cast(data) = NS(QStringList)(); + return true; + case QMetaType::QBitArray: + *static_cast(data) = NS(QBitArray)(); + return true; + case QMetaType::QDate: + *static_cast(data) = NS(QDate)(); + return true; + case QMetaType::QTime: + *static_cast(data) = NS(QTime)(); + return true; + case QMetaType::QDateTime: + *static_cast(data) = NS(QDateTime)(); + return true; + case QMetaType::QUrl: + *static_cast(data) = NS(QUrl)(); + return true; + case QMetaType::QLocale: + *static_cast(data) = NS(QLocale)(); + return true; + case QMetaType::QRect: + *static_cast(data) = NS(QRect)(); + return true; + case QMetaType::QRectF: + *static_cast(data) = NS(QRectF)(); + return true; + case QMetaType::QSize: + *static_cast(data) = NS(QSize)(); + return true; + case QMetaType::QSizeF: + *static_cast(data) = NS(QSizeF)(); + return true; + case QMetaType::QLine: + *static_cast(data) = NS(QLine)(); + return true; + case QMetaType::QLineF: + *static_cast(data) = NS(QLineF)(); + return true; + case QMetaType::QPoint: + *static_cast(data) = NS(QPoint)(); + return true; + case QMetaType::QPointF: + *static_cast(data) = NS(QPointF)(); + return true; + case QMetaType::QVector3D: + *static_cast(data) = NS(QVector3D)(); + return true; +#ifndef QT_NO_REGEXP + case QMetaType::QRegExp: + *static_cast(data) = NS(QRegExp)(); + return true; +#endif + case QMetaType::Void: + return true; + +#ifdef QT3_SUPPORT + case QMetaType::QColorGroup: + *static_cast(data) = NS(QColorGroup)(); + return true; +#endif + + case QMetaType::QFont: + *static_cast(data) = NS(QFont)(); + return true; + case QMetaType::QPixmap: + *static_cast(data) = NS(QPixmap)(); + return true; + case QMetaType::QBrush: + *static_cast(data) = NS(QBrush)(); + return true; + case QMetaType::QColor: + *static_cast(data) = NS(QColor)(); + return true; + case QMetaType::QPalette: + *static_cast(data) = NS(QPalette)(); + return true; + case QMetaType::QIcon: + *static_cast(data) = NS(QIcon)(); + return true; + case QMetaType::QImage: + *static_cast(data) = NS(QImage)(); + return true; + case QMetaType::QPolygon: + *static_cast(data) = NS(QPolygon)(); + return true; + case QMetaType::QRegion: + *static_cast(data) = NS(QRegion)(); + return true; + case QMetaType::QBitmap: + *static_cast(data) = NS(QBitmap)(); + return true; +#ifndef QT_NO_CURSOR + case QMetaType::QCursor: + *static_cast(data) = NS(QCursor)(); + return true; +#endif + case QMetaType::QSizePolicy: + *static_cast(data) = NS(QSizePolicy)(); + return true; + case QMetaType::QKeySequence: + *static_cast(data) = NS(QKeySequence)(); + return true; + case QMetaType::QPen: + *static_cast(data) = NS(QPen)(); + return true; + case QMetaType::QTextLength: + *static_cast(data) = NS(QTextLength)(); + return true; + case QMetaType::QTextFormat: + *static_cast(data) = NS(QTextFormat)(); + return true; + case QMetaType::QMatrix: + *static_cast(data) = NS(QMatrix)(); + return true; + case QMetaType::QTransform: + *static_cast(data) = NS(QTransform)(); + return true; + case QMetaType::QMatrix4x4: + *static_cast(data) = NS(QMatrix4x4)(); + return true; + case QMetaType::QVector2D: + *static_cast(data) = NS(QVector2D)(); + return true; + case QMetaType::QVector4D: + *static_cast(data) = NS(QVector4D)(); + return true; + case QMetaType::QQuaternion: + *static_cast(data) = NS(QQuaternion)(); + return true; + default: + if (type == qMetaTypeId()) { + *static_cast(data) = NS(QVariant)(); + return true; + } else if (type == qMetaTypeId()) { + *static_cast(data) = NS(QScriptValue)(); + return true; + } else if (typeCategory(type) != Unknown) { + *static_cast(data) = 0; + return true; + } + break; + } + } + + return false; +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativemetatype_p.h b/src/declarative/qml/qdeclarativemetatype_p.h new file mode 100644 index 00000000..8af72737 --- /dev/null +++ b/src/declarative/qml/qdeclarativemetatype_p.h @@ -0,0 +1,174 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEMETATYPE_P_H +#define QDECLARATIVEMETATYPE_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 "qdeclarative.h" + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeType; +class QDeclarativeCustomParser; +class QDeclarativeTypePrivate; + +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeMetaType +{ +public: + static bool canCopy(int type); + static bool copy(int type, void *data, const void *copy = 0); + + static QList qmlTypeNames(); + static QList qmlTypes(); + + static QDeclarativeType *qmlType(const QByteArray &, int, int); + static QDeclarativeType *qmlType(const QMetaObject *); + static QDeclarativeType *qmlType(const QMetaObject *metaObject, const QByteArray &module, int version_major, int version_minor); + static QDeclarativeType *qmlType(int); + + static QMetaProperty defaultProperty(const QMetaObject *); + static QMetaProperty defaultProperty(QObject *); + static QMetaMethod defaultMethod(const QMetaObject *); + static QMetaMethod defaultMethod(QObject *); + + static bool isQObject(int); + static QObject *toQObject(const QVariant &, bool *ok = 0); + + static int listType(int); + static int attachedPropertiesFuncId(const QMetaObject *); + static QDeclarativeAttachedPropertiesFunc attachedPropertiesFuncById(int); + + enum TypeCategory { Unknown, Object, List }; + static TypeCategory typeCategory(int); + + static bool isInterface(int); + static const char *interfaceIId(int); + static bool isList(int); + + typedef QVariant (*StringConverter)(const QString &); + static void registerCustomStringConverter(int, StringConverter); + static StringConverter customStringConverter(int); + + static bool isModule(const QByteArray &module, int versionMajor, int versionMinor); + + static QList parentFunctions(); +}; + +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeType +{ +public: + QByteArray typeName() const; + QByteArray qmlTypeName() const; + + QByteArray module() const; + int majorVersion() const; + int minorVersion() const; + + bool availableInVersion(int vmajor, int vminor) const; + bool availableInVersion(const QByteArray &module, int vmajor, int vminor) const; + + QObject *create() const; + void create(QObject **, void **, size_t) const; + + typedef void (*CreateFunc)(void *); + CreateFunc createFunction() const; + int createSize() const; + + QDeclarativeCustomParser *customParser() const; + + bool isCreatable() const; + bool isExtendedType() const; + QString noCreationReason() const; + + bool isInterface() const; + int typeId() const; + int qListTypeId() const; + + const QMetaObject *metaObject() const; + const QMetaObject *baseMetaObject() const; + int metaObjectRevision() const; + bool containsRevisionedAttributes() const; + + QDeclarativeAttachedPropertiesFunc attachedPropertiesFunction() const; + const QMetaObject *attachedPropertiesType() const; + int attachedPropertiesId() const; + + int parserStatusCast() const; + QVariant fromObject(QObject *) const; + const char *interfaceIId() const; + int propertyValueSourceCast() const; + int propertyValueInterceptorCast() const; + + int index() const; + +private: + QDeclarativeType *superType() const; + friend class QDeclarativeTypePrivate; + friend struct QDeclarativeMetaTypeData; + friend int registerType(const QDeclarativePrivate::RegisterType &); + friend int registerInterface(const QDeclarativePrivate::RegisterInterface &); + QDeclarativeType(int, const QDeclarativePrivate::RegisterInterface &); + QDeclarativeType(int, const QDeclarativePrivate::RegisterType &); + ~QDeclarativeType(); + + QDeclarativeTypePrivate *d; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVEMETATYPE_P_H + diff --git a/src/declarative/qml/qdeclarativenetworkaccessmanagerfactory.cpp b/src/declarative/qml/qdeclarativenetworkaccessmanagerfactory.cpp new file mode 100644 index 00000000..fd08badd --- /dev/null +++ b/src/declarative/qml/qdeclarativenetworkaccessmanagerfactory.cpp @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativenetworkaccessmanagerfactory.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QDeclarativeNetworkAccessManagerFactory + \since 4.7 + \brief The QDeclarativeNetworkAccessManagerFactory class creates QNetworkAccessManager instances for a QML engine. + + A QML engine uses QNetworkAccessManager for all network access. + By implementing a factory, it is possible to provide the QML engine + with custom QNetworkAccessManager instances with specialized caching, + proxy and cookies support. + + To implement a factory, subclass QDeclarativeNetworkAccessManagerFactory and + implement the virtual create() method, then assign it to the relevant QML + engine using QDeclarativeEngine::setNetworkAccessManagerFactory(). + + Note the QML engine may create QNetworkAccessManager instances + from multiple threads. Because of this, the implementation of the create() + method must be \l{Reentrancy and Thread-Safety}{reentrant}. In addition, + the developer should be careful if the signals of the object to be + returned from create() are connected to the slots of an object that may + be created in a different thread: + + \list + \o The QML engine internally handles all requests, and cleans up any + QNetworkReply objects it creates. Receiving the + QNetworkAccessManager::finished() signal in another thread may not + provide the receiver with a valid reply object if it has already + been deleted. + \o Authentication details provided to QNetworkAccessManager::authenticationRequired() + must be provided immediately, so this signal cannot be connected as a + Qt::QueuedConnection (or as the default Qt::AutoConnection from another + thread). + \endlist + + For more information about signals and threads, see + \l {Threads and QObjects} and \l {Signals and Slots Across Threads}. + + \sa {declarative/cppextensions/networkaccessmanagerfactory}{NetworkAccessManagerFactory example} +*/ + +/*! + Destroys the factory. The default implementation does nothing. + */ +QDeclarativeNetworkAccessManagerFactory::~QDeclarativeNetworkAccessManagerFactory() +{ +} + +/*! + \fn QNetworkAccessManager *QDeclarativeNetworkAccessManagerFactory::create(QObject *parent) + + Creates and returns a network access manager with the specified \a parent. + This method must return a new QNetworkAccessManager instance each time + it is called. + + Note: this method may be called by multiple threads, so ensure the + implementation of this method is reentrant. +*/ + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativenetworkaccessmanagerfactory.h b/src/declarative/qml/qdeclarativenetworkaccessmanagerfactory.h new file mode 100644 index 00000000..c55f8e30 --- /dev/null +++ b/src/declarative/qml/qdeclarativenetworkaccessmanagerfactory.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVENETWORKACCESSMANAGERFACTORY_H +#define QDECLARATIVENETWORKACCESSMANAGERFACTORY_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QNetworkAccessManager; +class Q_DECLARATIVE_EXPORT QDeclarativeNetworkAccessManagerFactory +{ +public: + virtual ~QDeclarativeNetworkAccessManagerFactory(); + virtual QNetworkAccessManager *create(QObject *parent) = 0; + +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVENETWORKACCESSMANAGERFACTORY_H diff --git a/src/declarative/qml/qdeclarativenotifier.cpp b/src/declarative/qml/qdeclarativenotifier.cpp new file mode 100644 index 00000000..64731dee --- /dev/null +++ b/src/declarative/qml/qdeclarativenotifier.cpp @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativenotifier_p.h" +#include "private/qdeclarativeproperty_p.h" + +QT_BEGIN_NAMESPACE + +void QDeclarativeNotifier::emitNotify(QDeclarativeNotifierEndpoint *endpoint) +{ + QDeclarativeNotifierEndpoint::Notifier *n = endpoint->asNotifier(); + + QDeclarativeNotifierEndpoint **oldDisconnected = n->disconnected; + n->disconnected = &endpoint; + + if (n->next) + emitNotify(n->next); + + if (endpoint) { + void *args[] = { 0 }; + + QMetaObject::metacall(endpoint->target, QMetaObject::InvokeMetaMethod, + endpoint->targetMethod, args); + + if (endpoint) + endpoint->asNotifier()->disconnected = oldDisconnected; + } + + if (oldDisconnected) *oldDisconnected = endpoint; +} + +void QDeclarativeNotifierEndpoint::connect(QObject *source, int sourceSignal) +{ + Signal *s = toSignal(); + + if (s->source == source && s->sourceSignal == sourceSignal) + return; + + disconnect(); + + QDeclarativePropertyPrivate::connect(source, sourceSignal, target, targetMethod); + + s->source = source; + s->sourceSignal = sourceSignal; +} + +void QDeclarativeNotifierEndpoint::copyAndClear(QDeclarativeNotifierEndpoint &other) +{ + other.disconnect(); + + other.target = target; + other.targetMethod = targetMethod; + + if (!isConnected()) + return; + + if (SignalType == type) { + Signal *other_s = other.toSignal(); + Signal *s = asSignal(); + + other_s->source = s->source; + other_s->sourceSignal = s->sourceSignal; + s->source = 0; + } else if(NotifierType == type) { + Notifier *other_n = other.toNotifier(); + Notifier *n = asNotifier(); + + other_n->notifier = n->notifier; + other_n->disconnected = n->disconnected; + if (other_n->disconnected) *other_n->disconnected = &other; + + if (n->next) { + other_n->next = n->next; + n->next->asNotifier()->prev = &other_n->next; + } + other_n->prev = n->prev; + *other_n->prev = &other; + + n->prev = 0; + n->next = 0; + n->disconnected = 0; + n->notifier = 0; + } +} + + +QT_END_NAMESPACE + diff --git a/src/declarative/qml/qdeclarativenotifier_p.h b/src/declarative/qml/qdeclarativenotifier_p.h new file mode 100644 index 00000000..433022f8 --- /dev/null +++ b/src/declarative/qml/qdeclarativenotifier_p.h @@ -0,0 +1,268 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVENOTIFIER_P_H +#define QDECLARATIVENOTIFIER_P_H + +#include "private/qdeclarativeguard_p.h" + +QT_BEGIN_NAMESPACE + +class QDeclarativeNotifierEndpoint; +class QDeclarativeNotifier +{ +public: + inline QDeclarativeNotifier(); + inline ~QDeclarativeNotifier(); + inline void notify(); + +private: + friend class QDeclarativeNotifierEndpoint; + + static void emitNotify(QDeclarativeNotifierEndpoint *); + QDeclarativeNotifierEndpoint *endpoints; +}; + +class QDeclarativeNotifierEndpoint +{ +public: + inline QDeclarativeNotifierEndpoint(); + inline QDeclarativeNotifierEndpoint(QObject *t, int m); + inline ~QDeclarativeNotifierEndpoint(); + + QObject *target; + int targetMethod; + + inline bool isConnected(); + inline bool isConnected(QObject *source, int sourceSignal); + inline bool isConnected(QDeclarativeNotifier *); + + void connect(QObject *source, int sourceSignal); + inline void connect(QDeclarativeNotifier *); + inline void disconnect(); + + void copyAndClear(QDeclarativeNotifierEndpoint &other); + +private: + friend class QDeclarativeNotifier; + + struct Signal { + QDeclarativeGuard source; + int sourceSignal; + }; + + struct Notifier { + QDeclarativeNotifier *notifier; + QDeclarativeNotifierEndpoint **disconnected; + + QDeclarativeNotifierEndpoint *next; + QDeclarativeNotifierEndpoint **prev; + }; + + enum { InvalidType, SignalType, NotifierType } type; + union { + struct { + Signal *signal; + union { + char signalData[sizeof(Signal)]; + qint64 q_for_alignment_1; + double q_for_alignment_2; + }; + } signal; + Notifier notifier; + }; + + inline Notifier *toNotifier(); + inline Notifier *asNotifier(); + inline Signal *toSignal(); + inline Signal *asSignal(); +}; + +QDeclarativeNotifier::QDeclarativeNotifier() +: endpoints(0) +{ +} + +QDeclarativeNotifier::~QDeclarativeNotifier() +{ + QDeclarativeNotifierEndpoint *endpoint = endpoints; + while (endpoint) { + QDeclarativeNotifierEndpoint::Notifier *n = endpoint->asNotifier(); + endpoint = n->next; + + n->next = 0; + n->prev = 0; + n->notifier = 0; + if (n->disconnected) *n->disconnected = 0; + n->disconnected = 0; + } + endpoints = 0; +} + +void QDeclarativeNotifier::notify() +{ + if (endpoints) emitNotify(endpoints); +} + +QDeclarativeNotifierEndpoint::QDeclarativeNotifierEndpoint() +: target(0), targetMethod(0), type(InvalidType) +{ +} + +QDeclarativeNotifierEndpoint::QDeclarativeNotifierEndpoint(QObject *t, int m) +: target(t), targetMethod(m), type(InvalidType) +{ +} + +QDeclarativeNotifierEndpoint::~QDeclarativeNotifierEndpoint() +{ + disconnect(); + if (SignalType == type) { + Signal *s = asSignal(); + s->~Signal(); + } +} + +bool QDeclarativeNotifierEndpoint::isConnected() +{ + if (SignalType == type) { + return asSignal()->source; + } else if (NotifierType == type) { + return asNotifier()->notifier; + } else { + return false; + } +} + +bool QDeclarativeNotifierEndpoint::isConnected(QObject *source, int sourceSignal) +{ + return SignalType == type && asSignal()->source == source && asSignal()->sourceSignal == sourceSignal; +} + +bool QDeclarativeNotifierEndpoint::isConnected(QDeclarativeNotifier *notifier) +{ + return NotifierType == type && asNotifier()->notifier == notifier; +} + +void QDeclarativeNotifierEndpoint::connect(QDeclarativeNotifier *notifier) +{ + Notifier *n = toNotifier(); + + if (n->notifier == notifier) + return; + + disconnect(); + + n->next = notifier->endpoints; + if (n->next) { n->next->asNotifier()->prev = &n->next; } + notifier->endpoints = this; + n->prev = ¬ifier->endpoints; + n->notifier = notifier; +} + +void QDeclarativeNotifierEndpoint::disconnect() +{ + if (type == SignalType) { + Signal *s = asSignal(); + if (s->source) { + QMetaObject::disconnectOne(s->source, s->sourceSignal, target, targetMethod); + s->source = 0; + } + } else if (type == NotifierType) { + Notifier *n = asNotifier(); + + if (n->next) n->next->asNotifier()->prev = n->prev; + if (n->prev) *n->prev = n->next; + if (n->disconnected) *n->disconnected = 0; + n->next = 0; + n->prev = 0; + n->disconnected = 0; + n->notifier = 0; + } +} + +QDeclarativeNotifierEndpoint::Notifier *QDeclarativeNotifierEndpoint::toNotifier() +{ + if (NotifierType == type) + return asNotifier(); + + if (SignalType == type) { + disconnect(); + Signal *s = asSignal(); + s->~Signal(); + } + + type = NotifierType; + Notifier *n = asNotifier(); + n->next = 0; + n->prev = 0; + n->disconnected = 0; + n->notifier = 0; + return n; +} + +QDeclarativeNotifierEndpoint::Notifier *QDeclarativeNotifierEndpoint::asNotifier() +{ + Q_ASSERT(type == NotifierType); + return ¬ifier; +} + +QDeclarativeNotifierEndpoint::Signal *QDeclarativeNotifierEndpoint::toSignal() +{ + if (SignalType == type) + return asSignal(); + + disconnect(); + signal.signal = new (&signal.signalData) Signal; + type = SignalType; + return signal.signal; +} + +QDeclarativeNotifierEndpoint::Signal *QDeclarativeNotifierEndpoint::asSignal() +{ + Q_ASSERT(type == SignalType); + return signal.signal; +} + +QT_END_NAMESPACE + +#endif // QDECLARATIVENOTIFIER_P_H + diff --git a/src/declarative/qml/qdeclarativeobjectscriptclass.cpp b/src/declarative/qml/qdeclarativeobjectscriptclass.cpp new file mode 100644 index 00000000..e3d01c39 --- /dev/null +++ b/src/declarative/qml/qdeclarativeobjectscriptclass.cpp @@ -0,0 +1,1242 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativeobjectscriptclass_p.h" + +#include "private/qdeclarativeengine_p.h" +#include "private/qdeclarativecontext_p.h" +#include "private/qdeclarativedata_p.h" +#include "private/qdeclarativetypenamescriptclass_p.h" +#include "private/qdeclarativelistscriptclass_p.h" +#include "private/qdeclarativebinding_p.h" +#include "private/qdeclarativeguard_p.h" +#include "private/qdeclarativevmemetaobject_p.h" + +#include +#include +#include + +Q_DECLARE_METATYPE(QScriptValue) + +#if defined(__GNUC__) +# if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 +// The code in this file does not violate strict aliasing, but GCC thinks it does +// so turn off the warnings for us to have a clean build +# pragma GCC diagnostic ignored "-Wstrict-aliasing" +# endif +#endif + +QT_BEGIN_NAMESPACE + +struct ObjectData : public QScriptDeclarativeClass::Object { + ObjectData(QObject *o, int t) : object(o), type(t) { + if (o) { + QDeclarativeData *ddata = QDeclarativeData::get(object, true); + if (ddata) ddata->objectDataRefCount++; + } + } + + virtual ~ObjectData() { + if (object && !object->parent()) { + QDeclarativeData *ddata = QDeclarativeData::get(object, false); + if (ddata && !ddata->indestructible && 0 == --ddata->objectDataRefCount) + object->deleteLater(); + } + } + + QDeclarativeGuard object; + int type; +}; + +/* + The QDeclarativeObjectScriptClass handles property access for QObjects + via QtScript. It is also used to provide a more useful API in + QtScript for QML. + */ +QDeclarativeObjectScriptClass::QDeclarativeObjectScriptClass(QDeclarativeEngine *bindEngine) +: QScriptDeclarativeClass(QDeclarativeEnginePrivate::getScriptEngine(bindEngine)), + methods(bindEngine), lastData(0), engine(bindEngine) +{ + QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine); + + m_destroy = scriptEngine->newFunction(destroy); + m_destroyId = createPersistentIdentifier(QLatin1String("destroy")); + m_toString = scriptEngine->newFunction(tostring); + m_toStringId = createPersistentIdentifier(QLatin1String("toString")); +} + +QDeclarativeObjectScriptClass::~QDeclarativeObjectScriptClass() +{ +} + +QScriptValue QDeclarativeObjectScriptClass::newQObject(QObject *object, int type) +{ + QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine); + + if (!object) + return scriptEngine->nullValue(); +// return newObject(scriptEngine, this, new ObjectData(object, type)); + + if (QObjectPrivate::get(object)->wasDeleted) + return scriptEngine->undefinedValue(); + + QDeclarativeData *ddata = QDeclarativeData::get(object, true); + + if (!ddata) { + return scriptEngine->undefinedValue(); + } else if (!ddata->indestructible && !object->parent()) { + return newObject(scriptEngine, this, new ObjectData(object, type)); + } else if (!ddata->scriptValue) { + ddata->scriptValue = new QScriptValue(newObject(scriptEngine, this, new ObjectData(object, type))); + return *ddata->scriptValue; + } else if (ddata->scriptValue->engine() == QDeclarativeEnginePrivate::getScriptEngine(engine)) { + return *ddata->scriptValue; + } else { + return newObject(scriptEngine, this, new ObjectData(object, type)); + } +} + +QObject *QDeclarativeObjectScriptClass::toQObject(const QScriptValue &value) const +{ + return value.toQObject(); +} + +int QDeclarativeObjectScriptClass::objectType(const QScriptValue &value) const +{ + if (scriptClass(value) != this) + return QVariant::Invalid; + + Object *o = object(value); + return ((ObjectData*)(o))->type; +} + +QScriptClass::QueryFlags +QDeclarativeObjectScriptClass::queryProperty(Object *object, const Identifier &name, + QScriptClass::QueryFlags flags) +{ + return queryProperty(toQObject(object), name, flags, 0); +} + +QScriptClass::QueryFlags +QDeclarativeObjectScriptClass::queryProperty(QObject *obj, const Identifier &name, + QScriptClass::QueryFlags flags, QDeclarativeContextData *evalContext, + QueryHints hints) +{ + Q_UNUSED(flags); + lastData = 0; + lastTNData = 0; + + if (name == m_destroyId.identifier || + name == m_toStringId.identifier) + return QScriptClass::HandlesReadAccess; + + if (!obj) + return 0; + + QDeclarativeEnginePrivate *enginePrivate = QDeclarativeEnginePrivate::get(engine); + lastData = QDeclarativePropertyCache::property(engine, obj, name, local); + if ((hints & ImplicitObject) && lastData && lastData->revision != 0) { + + QDeclarativeData *ddata = QDeclarativeData::get(obj); + if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(lastData)) + return 0; + } + + if (lastData) + return QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess; + + if (!(hints & SkipAttachedProperties)) { + if (!evalContext && context()) { + // Global object, QScriptContext activation object, QDeclarativeContext object + QScriptValue scopeNode = scopeChainValue(context(), -3); + if (scopeNode.isValid()) { + Q_ASSERT(scriptClass(scopeNode) == enginePrivate->contextClass); + + evalContext = enginePrivate->contextClass->contextFromValue(scopeNode); + } + } + + if (evalContext && evalContext->imports) { + QDeclarativeTypeNameCache::Data *data = evalContext->imports->data(name); + if (data) { + lastTNData = data; + return QScriptClass::HandlesReadAccess; + } + } + } + + if (!(hints & ImplicitObject)) { + local.coreIndex = -1; + lastData = &local; + return QScriptClass::HandlesWriteAccess; + } + + return 0; +} + +QDeclarativeObjectScriptClass::Value +QDeclarativeObjectScriptClass::property(Object *object, const Identifier &name) +{ + return property(toQObject(object), name); +} + +QDeclarativeObjectScriptClass::Value +QDeclarativeObjectScriptClass::property(QObject *obj, const Identifier &name) +{ + QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine); + + if (name == m_destroyId.identifier) + return Value(scriptEngine, m_destroy); + else if (name == m_toStringId.identifier) + return Value(scriptEngine, m_toString); + + if (lastData && !lastData->isValid()) + return Value(); + + Q_ASSERT(obj); + + QDeclarativeEnginePrivate *enginePriv = QDeclarativeEnginePrivate::get(engine); + + if (lastTNData) { + + if (lastTNData->type) + return Value(scriptEngine, enginePriv->typeNameClass->newObject(obj, lastTNData->type)); + else + return Value(scriptEngine, enginePriv->typeNameClass->newObject(obj, lastTNData->typeNamespace)); + + } else if (lastData->flags & QDeclarativePropertyCache::Data::IsFunction) { + if (lastData->flags & QDeclarativePropertyCache::Data::IsVMEFunction) { + return Value(scriptEngine, ((QDeclarativeVMEMetaObject *)(obj->metaObject()))->vmeMethod(lastData->coreIndex)); + } else { + // Uncomment to use QtScript method call logic + // QScriptValue sobj = scriptEngine->newQObject(obj); + // return Value(scriptEngine, sobj.property(toString(name))); + return Value(scriptEngine, methods.newMethod(obj, lastData)); + } + } else { + if (enginePriv->captureProperties && !(lastData->flags & QDeclarativePropertyCache::Data::IsConstant)) { + if (lastData->coreIndex == 0) { + enginePriv->capturedProperties << + QDeclarativeEnginePrivate::CapturedProperty(QDeclarativeData::get(obj, true)->objectNameNotifier()); + } else { + enginePriv->capturedProperties << + QDeclarativeEnginePrivate::CapturedProperty(obj, lastData->coreIndex, lastData->notifyIndex); + } + } + + if (QDeclarativeValueTypeFactory::isValueType((uint)lastData->propType)) { + QDeclarativeValueType *valueType = enginePriv->valueTypes[lastData->propType]; + if (valueType) + return Value(scriptEngine, enginePriv->valueTypeClass->newObject(obj, lastData->coreIndex, valueType)); + } + + if (lastData->flags & QDeclarativePropertyCache::Data::IsQList) { + return Value(scriptEngine, enginePriv->listClass->newList(obj, lastData->coreIndex, lastData->propType)); + } else if (lastData->flags & QDeclarativePropertyCache::Data::IsQObjectDerived) { + QObject *rv = 0; + void *args[] = { &rv, 0 }; + QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args); + return Value(scriptEngine, newQObject(rv, lastData->propType)); + } else if (lastData->flags & QDeclarativePropertyCache::Data::IsQScriptValue) { + QScriptValue rv = scriptEngine->nullValue(); + void *args[] = { &rv, 0 }; + QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args); + return Value(scriptEngine, rv); + } else if (lastData->propType == QMetaType::QReal) { + qreal rv = 0; + void *args[] = { &rv, 0 }; + QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args); + return Value(scriptEngine, rv); + } else if (lastData->propType == QMetaType::Int || lastData->flags & QDeclarativePropertyCache::Data::IsEnumType) { + int rv = 0; + void *args[] = { &rv, 0 }; + QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args); + return Value(scriptEngine, rv); + } else if (lastData->propType == QMetaType::Bool) { + bool rv = false; + void *args[] = { &rv, 0 }; + QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args); + return Value(scriptEngine, rv); + } else if (lastData->propType == QMetaType::QString) { + QString rv; + void *args[] = { &rv, 0 }; + QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args); + return Value(scriptEngine, rv); + } else if (lastData->propType == QMetaType::UInt) { + uint rv = 0; + void *args[] = { &rv, 0 }; + QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args); + return Value(scriptEngine, rv); + } else if (lastData->propType == QMetaType::Float) { + float rv = 0; + void *args[] = { &rv, 0 }; + QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args); + return Value(scriptEngine, rv); + } else if (lastData->propType == QMetaType::Double) { + double rv = 0; + void *args[] = { &rv, 0 }; + QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args); + return Value(scriptEngine, rv); + } else { + QVariant var = obj->metaObject()->property(lastData->coreIndex).read(obj); + return Value(scriptEngine, enginePriv->scriptValueFromVariant(var)); + } + } +} + +void QDeclarativeObjectScriptClass::setProperty(Object *object, + const Identifier &name, + const QScriptValue &value) +{ + return setProperty(toQObject(object), name, value, context()); +} + +void QDeclarativeObjectScriptClass::setProperty(QObject *obj, + const Identifier &name, + const QScriptValue &value, + QScriptContext *context, + QDeclarativeContextData *evalContext) +{ + Q_UNUSED(name); + + Q_ASSERT(obj); + Q_ASSERT(lastData); + Q_ASSERT(context); + + if (!lastData->isValid()) { + QString error = QLatin1String("Cannot assign to non-existent property \"") + + toString(name) + QLatin1Char('\"'); + context->throwError(error); + return; + } + + if (!(lastData->flags & QDeclarativePropertyCache::Data::IsWritable) && + !(lastData->flags & QDeclarativePropertyCache::Data::IsQList)) { + QString error = QLatin1String("Cannot assign to read-only property \"") + + toString(name) + QLatin1Char('\"'); + context->throwError(error); + return; + } + + QDeclarativeEnginePrivate *enginePriv = QDeclarativeEnginePrivate::get(engine); + + if (!evalContext) { + // Global object, QScriptContext activation object, QDeclarativeContext object + QScriptValue scopeNode = scopeChainValue(context, -3); + if (scopeNode.isValid()) { + Q_ASSERT(scriptClass(scopeNode) == enginePriv->contextClass); + + evalContext = enginePriv->contextClass->contextFromValue(scopeNode); + } + } + + QDeclarativeBinding *newBinding = 0; + if (value.isFunction() && !value.isRegExp()) { + QScriptContextInfo ctxtInfo(context); + QDeclarativePropertyCache::ValueTypeData valueTypeData; + + newBinding = new QDeclarativeBinding(value, obj, evalContext); + newBinding->setSourceLocation(ctxtInfo.fileName(), ctxtInfo.functionStartLineNumber()); + newBinding->setTarget(QDeclarativePropertyPrivate::restore(*lastData, valueTypeData, obj, evalContext)); + if (newBinding->expression().contains(QLatin1String("this"))) + newBinding->setEvaluateFlags(newBinding->evaluateFlags() | QDeclarativeBinding::RequiresThisObject); + } + + QDeclarativeAbstractBinding *delBinding = + QDeclarativePropertyPrivate::setBinding(obj, lastData->coreIndex, -1, newBinding); + if (delBinding) + delBinding->destroy(); + + if (value.isNull() && lastData->flags & QDeclarativePropertyCache::Data::IsQObjectDerived) { + QObject *o = 0; + int status = -1; + int flags = 0; + void *argv[] = { &o, 0, &status, &flags }; + QMetaObject::metacall(obj, QMetaObject::WriteProperty, lastData->coreIndex, argv); + } else if (value.isUndefined() && lastData->flags & QDeclarativePropertyCache::Data::IsResettable) { + void *a[] = { 0 }; + QMetaObject::metacall(obj, QMetaObject::ResetProperty, lastData->coreIndex, a); + } else if (value.isUndefined() && lastData->propType == qMetaTypeId()) { + QDeclarativePropertyPrivate::write(obj, *lastData, QVariant(), evalContext); + } else if (value.isUndefined()) { + QString error = QLatin1String("Cannot assign [undefined] to ") + + QLatin1String(QMetaType::typeName(lastData->propType)); + context->throwError(error); + } else if (value.isFunction() && !value.isRegExp()) { + // this is handled by the binding creation above + } else { + //### expand optimization for other known types + if (lastData->propType == QMetaType::Int && value.isNumber()) { + int rawValue = qRound(value.toNumber()); + int status = -1; + int flags = 0; + void *a[] = { (void *)&rawValue, 0, &status, &flags }; + QMetaObject::metacall(obj, QMetaObject::WriteProperty, + lastData->coreIndex, a); + return; + } else if (lastData->propType == QMetaType::QReal && value.isNumber()) { + qreal rawValue = qreal(value.toNumber()); + int status = -1; + int flags = 0; + void *a[] = { (void *)&rawValue, 0, &status, &flags }; + QMetaObject::metacall(obj, QMetaObject::WriteProperty, + lastData->coreIndex, a); + return; + } else if (lastData->propType == QMetaType::QString && value.isString()) { + const QString &rawValue = value.toString(); + int status = -1; + int flags = 0; + void *a[] = { (void *)&rawValue, 0, &status, &flags }; + QMetaObject::metacall(obj, QMetaObject::WriteProperty, + lastData->coreIndex, a); + return; + } + + QVariant v; + if (lastData->flags & QDeclarativePropertyCache::Data::IsQList) + v = enginePriv->scriptValueToVariant(value, qMetaTypeId >()); + else + v = enginePriv->scriptValueToVariant(value, lastData->propType); + + if (!QDeclarativePropertyPrivate::write(obj, *lastData, v, evalContext)) { + const char *valueType = 0; + if (v.userType() == QVariant::Invalid) valueType = "null"; + else valueType = QMetaType::typeName(v.userType()); + + QString error = QLatin1String("Cannot assign ") + + QLatin1String(valueType) + + QLatin1String(" to ") + + QLatin1String(QMetaType::typeName(lastData->propType)); + context->throwError(error); + } + } +} + +bool QDeclarativeObjectScriptClass::isQObject() const +{ + return true; +} + +QObject *QDeclarativeObjectScriptClass::toQObject(Object *object, bool *ok) +{ + if (ok) *ok = true; + + ObjectData *data = (ObjectData*)object; + return data->object.data(); +} + +QScriptValue QDeclarativeObjectScriptClass::tostring(QScriptContext *context, QScriptEngine *) +{ + QObject* obj = context->thisObject().toQObject(); + + QString ret; + if(obj){ + QString objectName = obj->objectName(); + + ret += QString::fromUtf8(obj->metaObject()->className()); + ret += QLatin1String("(0x"); + ret += QString::number((quintptr)obj,16); + + if (!objectName.isEmpty()) { + ret += QLatin1String(", \""); + ret += objectName; + ret += QLatin1Char('\"'); + } + + ret += QLatin1Char(')'); + }else{ + ret += QLatin1String("null"); + } + return QScriptValue(ret); +} + +QScriptValue QDeclarativeObjectScriptClass::destroy(QScriptContext *context, QScriptEngine *engine) +{ + QDeclarativeEnginePrivate *p = QDeclarativeEnginePrivate::get(engine); + QScriptValue that = context->thisObject(); + + if (scriptClass(that) != p->objectClass) + return engine->undefinedValue(); + + ObjectData *data = (ObjectData *)p->objectClass->object(that); + if (!data->object) + return engine->undefinedValue(); + + QDeclarativeData *ddata = QDeclarativeData::get(data->object, false); + if (!ddata || ddata->indestructible) + return engine->currentContext()->throwError(QLatin1String("Invalid attempt to destroy() an indestructible object")); + + QObject *obj = data->object; + int delay = 0; + if (context->argumentCount() > 0) + delay = context->argument(0).toInt32(); + if (delay > 0) + QTimer::singleShot(delay, obj, SLOT(deleteLater())); + else + obj->deleteLater(); + + return engine->undefinedValue(); +} + +QStringList QDeclarativeObjectScriptClass::propertyNames(Object *object) +{ + QObject *obj = toQObject(object); + if (!obj) + return QStringList(); + + QDeclarativeEnginePrivate *enginePrivate = QDeclarativeEnginePrivate::get(engine); + + QDeclarativePropertyCache *cache = 0; + QDeclarativeData *ddata = QDeclarativeData::get(obj); + if (ddata) + cache = ddata->propertyCache; + if (!cache) { + cache = enginePrivate->cache(obj); + if (cache) { + if (ddata) { cache->addref(); ddata->propertyCache = cache; } + } else { + // Not cachable - fall back to QMetaObject (eg. dynamic meta object) + // XXX QDeclarativeOpenMetaObject has a cache, so this is suboptimal. + // XXX This is a workaround for QTBUG-9420. + const QMetaObject *mo = obj->metaObject(); + QStringList r; + int pc = mo->propertyCount(); + int po = mo->propertyOffset(); + for (int i=po; iproperty(i).name()); + return r; + } + } + return cache->propertyNames(); +} + +bool QDeclarativeObjectScriptClass::compare(Object *o1, Object *o2) +{ + ObjectData *d1 = (ObjectData *)o1; + ObjectData *d2 = (ObjectData *)o2; + + return d1 == d2 || d1->object == d2->object; +} + +struct MethodData : public QScriptDeclarativeClass::Object { + MethodData(QObject *o, const QDeclarativePropertyCache::Data &d) : object(o), data(d) {} + + QDeclarativeGuard object; + QDeclarativePropertyCache::Data data; +}; + +QDeclarativeObjectMethodScriptClass::QDeclarativeObjectMethodScriptClass(QDeclarativeEngine *bindEngine) +: QScriptDeclarativeClass(QDeclarativeEnginePrivate::getScriptEngine(bindEngine)), + engine(bindEngine) +{ + qRegisterMetaType >("QList"); + + setSupportsCall(true); + + QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine); + + m_connect = scriptEngine->newFunction(connect); + m_connectId = createPersistentIdentifier(QLatin1String("connect")); + m_disconnect = scriptEngine->newFunction(disconnect); + m_disconnectId = createPersistentIdentifier(QLatin1String("disconnect")); +} + +QDeclarativeObjectMethodScriptClass::~QDeclarativeObjectMethodScriptClass() +{ +} + +QScriptValue QDeclarativeObjectMethodScriptClass::newMethod(QObject *object, const QDeclarativePropertyCache::Data *method) +{ + QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine); + + return newObject(scriptEngine, this, new MethodData(object, *method)); +} + +QScriptValue QDeclarativeObjectMethodScriptClass::connect(QScriptContext *context, QScriptEngine *engine) +{ + QDeclarativeEnginePrivate *p = QDeclarativeEnginePrivate::get(engine); + + QScriptValue that = context->thisObject(); + if (&p->objectClass->methods != scriptClass(that)) + return engine->undefinedValue(); + + MethodData *data = (MethodData *)object(that); + + if (!data->object || context->argumentCount() == 0) + return engine->undefinedValue(); + + QByteArray signal("2"); + signal.append(data->object->metaObject()->method(data->data.coreIndex).signature()); + + if (context->argumentCount() == 1) { + qScriptConnect(data->object, signal.constData(), QScriptValue(), context->argument(0)); + } else { + qScriptConnect(data->object, signal.constData(), context->argument(0), context->argument(1)); + } + + return engine->undefinedValue(); +} + +QScriptValue QDeclarativeObjectMethodScriptClass::disconnect(QScriptContext *context, QScriptEngine *engine) +{ + QDeclarativeEnginePrivate *p = QDeclarativeEnginePrivate::get(engine); + + QScriptValue that = context->thisObject(); + if (&p->objectClass->methods != scriptClass(that)) + return engine->undefinedValue(); + + MethodData *data = (MethodData *)object(that); + + if (!data->object || context->argumentCount() == 0) + return engine->undefinedValue(); + + QByteArray signal("2"); + signal.append(data->object->metaObject()->method(data->data.coreIndex).signature()); + + if (context->argumentCount() == 1) { + qScriptDisconnect(data->object, signal.constData(), QScriptValue(), context->argument(0)); + } else { + qScriptDisconnect(data->object, signal.constData(), context->argument(0), context->argument(1)); + } + + return engine->undefinedValue(); +} + +QScriptClass::QueryFlags +QDeclarativeObjectMethodScriptClass::queryProperty(Object *, const Identifier &name, + QScriptClass::QueryFlags flags) +{ + Q_UNUSED(flags); + if (name == m_connectId.identifier || name == m_disconnectId.identifier) + return QScriptClass::HandlesReadAccess; + else + return 0; + +} + +QDeclarativeObjectMethodScriptClass::Value +QDeclarativeObjectMethodScriptClass::property(Object *, const Identifier &name) +{ + QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine); + + if (name == m_connectId.identifier) + return Value(scriptEngine, m_connect); + else if (name == m_disconnectId.identifier) + return Value(scriptEngine, m_disconnect); + else + return Value(); +} + +namespace { +struct MetaCallArgument { + inline MetaCallArgument(); + inline ~MetaCallArgument(); + inline void *dataPtr(); + + inline void initAsType(int type, QDeclarativeEngine *); + void fromScriptValue(int type, QDeclarativeEngine *, const QScriptValue &); + inline QScriptDeclarativeClass::Value toValue(QDeclarativeEngine *); + +private: + MetaCallArgument(const MetaCallArgument &); + + inline void cleanup(); + + union { + float floatValue; + double doubleValue; + quint32 intValue; + bool boolValue; + QObject *qobjectPtr; + + char allocData[sizeof(QVariant)]; + }; + + // Pointers to allocData + union { + QString *qstringPtr; + QVariant *qvariantPtr; + QList *qlistPtr; + QScriptValue *qscriptValuePtr; + }; + + int type; +}; +} + +MetaCallArgument::MetaCallArgument() +: type(QVariant::Invalid) +{ +} + +MetaCallArgument::~MetaCallArgument() +{ + cleanup(); +} + +void MetaCallArgument::cleanup() +{ + if (type == QMetaType::QString) { + qstringPtr->~QString(); + } else if (type == -1 || type == QMetaType::QVariant) { + qvariantPtr->~QVariant(); + } else if (type == qMetaTypeId()) { + qscriptValuePtr->~QScriptValue(); + } else if (type == qMetaTypeId >()) { + qlistPtr->~QList(); + } +} + +void *MetaCallArgument::dataPtr() +{ + if (type == -1) + return qvariantPtr->data(); + else + return (void *)&allocData; +} + +void MetaCallArgument::initAsType(int callType, QDeclarativeEngine *e) +{ + if (type != 0) { cleanup(); type = 0; } + if (callType == 0) return; + + QScriptEngine *engine = QDeclarativeEnginePrivate::getScriptEngine(e); + + if (callType == qMetaTypeId()) { + qscriptValuePtr = new (&allocData) QScriptValue(engine->undefinedValue()); + type = callType; + } else if (callType == QMetaType::Int || + callType == QMetaType::UInt || + callType == QMetaType::Bool || + callType == QMetaType::Double || + callType == QMetaType::Float) { + type = callType; + } else if (callType == QMetaType::QObjectStar) { + qobjectPtr = 0; + type = callType; + } else if (callType == QMetaType::QString) { + qstringPtr = new (&allocData) QString(); + type = callType; + } else if (callType == qMetaTypeId()) { + type = callType; + qvariantPtr = new (&allocData) QVariant(); + } else if (callType == qMetaTypeId >()) { + type = callType; + qlistPtr = new (&allocData) QList(); + } else { + type = -1; + qvariantPtr = new (&allocData) QVariant(callType, (void *)0); + } +} + +void MetaCallArgument::fromScriptValue(int callType, QDeclarativeEngine *engine, const QScriptValue &value) +{ + if (type != 0) { cleanup(); type = 0; } + + if (callType == qMetaTypeId()) { + qscriptValuePtr = new (&allocData) QScriptValue(value); + type = qMetaTypeId(); + } else if (callType == QMetaType::Int) { + intValue = quint32(value.toInt32()); + type = callType; + } else if (callType == QMetaType::UInt) { + intValue = quint32(value.toUInt32()); + type = callType; + } else if (callType == QMetaType::Bool) { + boolValue = value.toBool(); + type = callType; + } else if (callType == QMetaType::Double) { + doubleValue = double(value.toNumber()); + type = callType; + } else if (callType == QMetaType::Float) { + floatValue = float(value.toNumber()); + type = callType; + } else if (callType == QMetaType::QString) { + if (value.isNull() || value.isUndefined()) + qstringPtr = new (&allocData) QString(); + else + qstringPtr = new (&allocData) QString(value.toString()); + type = callType; + } else if (callType == QMetaType::QObjectStar) { + qobjectPtr = value.toQObject(); + type = callType; + } else if (callType == qMetaTypeId()) { + QVariant other = QDeclarativeEnginePrivate::get(engine)->scriptValueToVariant(value); + qvariantPtr = new (&allocData) QVariant(other); + type = callType; + } else if (callType == qMetaTypeId >()) { + qlistPtr = new (&allocData) QList(); + if (value.isArray()) { + int length = value.property(QLatin1String("length")).toInt32(); + for (int ii = 0; ii < length; ++ii) { + QScriptValue arrayItem = value.property(ii); + QObject *d = arrayItem.toQObject(); + qlistPtr->append(d); + } + } else if (QObject *d = value.toQObject()) { + qlistPtr->append(d); + } + type = callType; + } else { + qvariantPtr = new (&allocData) QVariant(); + type = -1; + + QDeclarativeEnginePrivate *priv = QDeclarativeEnginePrivate::get(engine); + QVariant v = priv->scriptValueToVariant(value); + if (v.userType() == callType) { + *qvariantPtr = v; + } else if (v.canConvert((QVariant::Type)callType)) { + *qvariantPtr = v; + qvariantPtr->convert((QVariant::Type)callType); + } else if (const QMetaObject *mo = priv->rawMetaObjectForType(callType)) { + QObject *obj = priv->toQObject(v); + + if (obj) { + const QMetaObject *objMo = obj->metaObject(); + while (objMo && objMo != mo) objMo = objMo->superClass(); + if (!objMo) obj = 0; + } + + *qvariantPtr = QVariant(callType, &obj); + } else { + *qvariantPtr = QVariant(callType, (void *)0); + } + } +} + +QScriptDeclarativeClass::Value MetaCallArgument::toValue(QDeclarativeEngine *e) +{ + QScriptEngine *engine = QDeclarativeEnginePrivate::getScriptEngine(e); + + if (type == qMetaTypeId()) { + return QScriptDeclarativeClass::Value(engine, *qscriptValuePtr); + } else if (type == QMetaType::Int) { + return QScriptDeclarativeClass::Value(engine, int(intValue)); + } else if (type == QMetaType::UInt) { + return QScriptDeclarativeClass::Value(engine, uint(intValue)); + } else if (type == QMetaType::Bool) { + return QScriptDeclarativeClass::Value(engine, boolValue); + } else if (type == QMetaType::Double) { + return QScriptDeclarativeClass::Value(engine, doubleValue); + } else if (type == QMetaType::Float) { + return QScriptDeclarativeClass::Value(engine, floatValue); + } else if (type == QMetaType::QString) { + return QScriptDeclarativeClass::Value(engine, *qstringPtr); + } else if (type == QMetaType::QObjectStar) { + if (qobjectPtr) + QDeclarativeData::get(qobjectPtr, true)->setImplicitDestructible(); + QDeclarativeEnginePrivate *priv = QDeclarativeEnginePrivate::get(e); + return QScriptDeclarativeClass::Value(engine, priv->objectClass->newQObject(qobjectPtr)); + } else if (type == qMetaTypeId >()) { + QList &list = *qlistPtr; + QScriptValue rv = engine->newArray(list.count()); + QDeclarativeEnginePrivate *priv = QDeclarativeEnginePrivate::get(e); + for (int ii = 0; ii < list.count(); ++ii) { + QObject *object = list.at(ii); + QDeclarativeData::get(object, true)->setImplicitDestructible(); + rv.setProperty(ii, priv->objectClass->newQObject(object)); + } + return QScriptDeclarativeClass::Value(engine, rv); + } else if (type == -1 || type == qMetaTypeId()) { + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(e); + QScriptValue rv = ep->scriptValueFromVariant(*qvariantPtr); + if (rv.isQObject()) { + QObject *object = rv.toQObject(); + if (object) + QDeclarativeData::get(object, true)->setImplicitDestructible(); + } + return QScriptDeclarativeClass::Value(engine, rv); + } else { + return QScriptDeclarativeClass::Value(); + } +} + +int QDeclarativeObjectMethodScriptClass::enumType(const QMetaObject *meta, const QString &strname) +{ + QByteArray str = strname.toUtf8(); + QByteArray scope; + QByteArray name; + int scopeIdx = str.lastIndexOf("::"); + if (scopeIdx != -1) { + scope = str.left(scopeIdx); + name = str.mid(scopeIdx + 2); + } else { + name = str; + } + for (int i = meta->enumeratorCount() - 1; i >= 0; --i) { + QMetaEnum m = meta->enumerator(i); + if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope))) + return QVariant::Int; + } + return QVariant::Invalid; +} + +QDeclarativeObjectMethodScriptClass::Value QDeclarativeObjectMethodScriptClass::call(Object *o, QScriptContext *ctxt) +{ + MethodData *method = static_cast(o); + + if (method->data.relatedIndex == -1) + return callPrecise(method->object, method->data, ctxt); + else + return callOverloaded(method, ctxt); +} + +QDeclarativeObjectMethodScriptClass::Value +QDeclarativeObjectMethodScriptClass::callPrecise(QObject *object, const QDeclarativePropertyCache::Data &data, + QScriptContext *ctxt) +{ + if (data.flags & QDeclarativePropertyCache::Data::HasArguments) { + + QMetaMethod m = object->metaObject()->method(data.coreIndex); + QList argTypeNames = m.parameterTypes(); + QVarLengthArray argTypes(argTypeNames.count()); + + // ### Cache + for (int ii = 0; ii < argTypeNames.count(); ++ii) { + argTypes[ii] = QMetaType::type(argTypeNames.at(ii)); + if (argTypes[ii] == QVariant::Invalid) + argTypes[ii] = enumType(object->metaObject(), QString::fromLatin1(argTypeNames.at(ii))); + if (argTypes[ii] == QVariant::Invalid) + return Value(ctxt, ctxt->throwError(QString::fromLatin1("Unknown method parameter type: %1").arg(QLatin1String(argTypeNames.at(ii))))); + } + + if (argTypes.count() > ctxt->argumentCount()) + return Value(ctxt, ctxt->throwError(QLatin1String("Insufficient arguments"))); + + return callMethod(object, data.coreIndex, data.propType, argTypes.count(), argTypes.data(), ctxt); + + } else { + + return callMethod(object, data.coreIndex, data.propType, 0, 0, ctxt); + + } +} + +QDeclarativeObjectMethodScriptClass::Value +QDeclarativeObjectMethodScriptClass::callMethod(QObject *object, int index, + int returnType, int argCount, int *argTypes, + QScriptContext *ctxt) +{ + if (argCount > 0) { + + QVarLengthArray args(argCount + 1); + args[0].initAsType(returnType, engine); + + for (int ii = 0; ii < argCount; ++ii) + args[ii + 1].fromScriptValue(argTypes[ii], engine, ctxt->argument(ii)); + + QVarLengthArray argData(args.count()); + for (int ii = 0; ii < args.count(); ++ii) + argData[ii] = args[ii].dataPtr(); + + QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, argData.data()); + + return args[0].toValue(engine); + + } else if (returnType != 0) { + + MetaCallArgument arg; + arg.initAsType(returnType, engine); + + void *args[] = { arg.dataPtr() }; + + QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args); + + return arg.toValue(engine); + + } else { + + void *args[] = { 0 }; + QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args); + return Value(); + + } +} + +/*! +Resolve the overloaded method to call. The algorithm works conceptually like this: + 1. Resolve the set of overloads it is *possible* to call. + Impossible overloads include those that have too many parameters or have parameters + of unknown type. + 2. Filter the set of overloads to only contain those with the closest number of + parameters. + For example, if we are called with 3 parameters and there are 2 overloads that + take 2 parameters and one that takes 3, eliminate the 2 parameter overloads. + 3. Find the best remaining overload based on its match score. + If two or more overloads have the same match score, call the last one. The match + score is constructed by adding the matchScore() result for each of the parameters. +*/ +QDeclarativeObjectMethodScriptClass::Value +QDeclarativeObjectMethodScriptClass::callOverloaded(MethodData *method, QScriptContext *ctxt) +{ + int argumentCount = ctxt->argumentCount(); + + QDeclarativePropertyCache::Data *best = 0; + int bestParameterScore = INT_MAX; + int bestMatchScore = INT_MAX; + + QDeclarativePropertyCache::Data dummy; + QDeclarativePropertyCache::Data *attempt = &method->data; + + do { + QList methodArgTypeNames; + + if (attempt->flags & QDeclarativePropertyCache::Data::HasArguments) + methodArgTypeNames = method->object->metaObject()->method(attempt->coreIndex).parameterTypes(); + + int methodArgumentCount = methodArgTypeNames.count(); + + if (methodArgumentCount > argumentCount) + continue; // We don't have sufficient arguments to call this method + + int methodParameterScore = argumentCount - methodArgumentCount; + if (methodParameterScore > bestParameterScore) + continue; // We already have a better option + + int methodMatchScore = 0; + QVarLengthArray methodArgTypes(methodArgumentCount); + + bool unknownArgument = false; + for (int ii = 0; ii < methodArgumentCount; ++ii) { + methodArgTypes[ii] = QMetaType::type(methodArgTypeNames.at(ii)); + if (methodArgTypes[ii] == QVariant::Invalid) + methodArgTypes[ii] = enumType(method->object->metaObject(), + QString::fromLatin1(methodArgTypeNames.at(ii))); + if (methodArgTypes[ii] == QVariant::Invalid) { + unknownArgument = true; + break; + } + methodMatchScore += matchScore(ctxt->argument(ii), methodArgTypes[ii], methodArgTypeNames.at(ii)); + } + if (unknownArgument) + continue; // We don't understand all the parameters + + if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) { + best = attempt; + bestParameterScore = methodParameterScore; + bestMatchScore = methodMatchScore; + } + + if (bestParameterScore == 0 && bestMatchScore == 0) + break; // We can't get better than that + + } while((attempt = relatedMethod(method->object, attempt, dummy)) != 0); + + if (best) { + return callPrecise(method->object, *best, ctxt); + } else { + QString error = QLatin1String("Unable to determine callable overload. Candidates are:"); + QDeclarativePropertyCache::Data *candidate = &method->data; + while (candidate) { + error += QLatin1String("\n ") + QString::fromUtf8(method->object->metaObject()->method(candidate->coreIndex).signature()); + candidate = relatedMethod(method->object, candidate, dummy); + } + return Value(ctxt, ctxt->throwError(error)); + } +} + +/*! + Returns the match score for converting \a actual to be of type \a conversionType. A + zero score means "perfect match" whereas a higher score is worse. + + The conversion table is copied out of the QtScript callQtMethod() function. +*/ +int QDeclarativeObjectMethodScriptClass::matchScore(const QScriptValue &actual, int conversionType, + const QByteArray &conversionTypeName) +{ + if (actual.isNumber()) { + switch (conversionType) { + case QMetaType::Double: + return 0; + case QMetaType::Float: + return 1; + case QMetaType::LongLong: + case QMetaType::ULongLong: + return 2; + case QMetaType::Long: + case QMetaType::ULong: + return 3; + case QMetaType::Int: + case QMetaType::UInt: + return 4; + case QMetaType::Short: + case QMetaType::UShort: + return 5; + break; + case QMetaType::Char: + case QMetaType::UChar: + return 6; + default: + return 10; + } + } else if (actual.isString()) { + switch (conversionType) { + case QMetaType::QString: + return 0; + default: + return 10; + } + } else if (actual.isBoolean()) { + switch (conversionType) { + case QMetaType::Bool: + return 0; + default: + return 10; + } + } else if (actual.isDate()) { + switch (conversionType) { + case QMetaType::QDateTime: + return 0; + case QMetaType::QDate: + return 1; + case QMetaType::QTime: + return 2; + default: + return 10; + } + } else if (actual.isRegExp()) { + switch (conversionType) { + case QMetaType::QRegExp: + return 0; + default: + return 10; + } + } else if (actual.isVariant()) { + if (conversionType == qMetaTypeId()) + return 0; + else if (actual.toVariant().userType() == conversionType) + return 0; + else + return 10; + } else if (actual.isArray()) { + switch (conversionType) { + case QMetaType::QStringList: + case QMetaType::QVariantList: + return 5; + default: + return 10; + } + } else if (actual.isQObject()) { + switch (conversionType) { + case QMetaType::QObjectStar: + return 0; + default: + return 10; + } + } else if (actual.isNull()) { + switch (conversionType) { + case QMetaType::VoidStar: + case QMetaType::QObjectStar: + return 0; + default: + if (!conversionTypeName.endsWith('*')) + return 10; + else + return 0; + } + } else { + return 10; + } +} + +static inline int QMetaObject_methods(const QMetaObject *metaObject) +{ + struct Private + { + int revision; + int className; + int classInfoCount, classInfoData; + int methodCount, methodData; + }; + + return reinterpret_cast(metaObject->d.data)->methodCount; +} + +static QByteArray QMetaMethod_name(const QMetaMethod &m) +{ + QByteArray sig = m.signature(); + int paren = sig.indexOf('('); + if (paren == -1) + return sig; + else + return sig.left(paren); +} + +/*! +Returns the next related method, if one, or 0. +*/ +QDeclarativePropertyCache::Data * +QDeclarativeObjectMethodScriptClass::relatedMethod(QObject *object, QDeclarativePropertyCache::Data *current, + QDeclarativePropertyCache::Data &dummy) +{ + QDeclarativePropertyCache *cache = QDeclarativeData::get(object)->propertyCache; + if (current->relatedIndex == -1) + return 0; + + if (cache) { + return cache->method(current->relatedIndex); + } else { + const QMetaObject *mo = object->metaObject(); + int methodOffset = mo->methodCount() - QMetaObject_methods(mo); + + while (methodOffset > current->relatedIndex) { + mo = mo->superClass(); + methodOffset -= QMetaObject_methods(mo); + } + + QMetaMethod method = mo->method(current->relatedIndex); + dummy.load(method); + + // Look for overloaded methods + QByteArray methodName = QMetaMethod_name(method); + for (int ii = current->relatedIndex - 1; ii >= methodOffset; --ii) { + if (methodName == QMetaMethod_name(mo->method(ii))) { + dummy.relatedIndex = ii; + return &dummy; + } + } + + return &dummy; + } +} + +QT_END_NAMESPACE + diff --git a/src/declarative/qml/qdeclarativeobjectscriptclass_p.h b/src/declarative/qml/qdeclarativeobjectscriptclass_p.h new file mode 100644 index 00000000..6910960f --- /dev/null +++ b/src/declarative/qml/qdeclarativeobjectscriptclass_p.h @@ -0,0 +1,166 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEOBJECTSCRIPTCLASS_P_H +#define QDECLARATIVEOBJECTSCRIPTCLASS_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 "private/qdeclarativepropertycache_p.h" +#include "private/qdeclarativetypenamecache_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeEngine; +class QScriptContext; +class QScriptEngine; +class QDeclarativeContextData; +class MethodData; + +class Q_AUTOTEST_EXPORT QDeclarativeObjectMethodScriptClass : public QScriptDeclarativeClass +{ +public: + QDeclarativeObjectMethodScriptClass(QDeclarativeEngine *); + ~QDeclarativeObjectMethodScriptClass(); + + QScriptValue newMethod(QObject *, const QDeclarativePropertyCache::Data *); + +protected: + virtual Value call(Object *, QScriptContext *); + virtual QScriptClass::QueryFlags queryProperty(Object *, const Identifier &, QScriptClass::QueryFlags flags); + virtual Value property(Object *, const Identifier &); + +private: + int enumType(const QMetaObject *, const QString &); + + Value callPrecise(QObject *, const QDeclarativePropertyCache::Data &, QScriptContext *); + Value callOverloaded(MethodData *, QScriptContext *); + Value callMethod(QObject *, int index, int returnType, int argCount, int *argTypes, QScriptContext *ctxt); + + int matchScore(const QScriptValue &, int, const QByteArray &); + QDeclarativePropertyCache::Data *relatedMethod(QObject *, QDeclarativePropertyCache::Data *current, + QDeclarativePropertyCache::Data &dummy); + + PersistentIdentifier m_connectId; + PersistentIdentifier m_disconnectId; + QScriptValue m_connect; + QScriptValue m_disconnect; + + static QScriptValue connect(QScriptContext *context, QScriptEngine *engine); + static QScriptValue disconnect(QScriptContext *context, QScriptEngine *engine); + + QDeclarativeEngine *engine; +}; + +class Q_AUTOTEST_EXPORT QDeclarativeObjectScriptClass : public QScriptDeclarativeClass +{ +public: + QDeclarativeObjectScriptClass(QDeclarativeEngine *); + ~QDeclarativeObjectScriptClass(); + + QScriptValue newQObject(QObject *, int type = QMetaType::QObjectStar); + + QObject *toQObject(const QScriptValue &) const; + int objectType(const QScriptValue &) const; + + enum QueryHint { + ImplicitObject = 0x01, + SkipAttachedProperties = 0x02 + }; + Q_DECLARE_FLAGS(QueryHints, QueryHint) + + QScriptClass::QueryFlags queryProperty(QObject *, const Identifier &, + QScriptClass::QueryFlags flags, + QDeclarativeContextData *evalContext, + QueryHints hints = 0); + + Value property(QObject *, const Identifier &); + + void setProperty(QObject *, const Identifier &name, const QScriptValue &, + QScriptContext *context, QDeclarativeContextData *evalContext = 0); + virtual QStringList propertyNames(Object *); + virtual bool compare(Object *, Object *); + +protected: + virtual QScriptClass::QueryFlags queryProperty(Object *, const Identifier &, + QScriptClass::QueryFlags flags); + + virtual Value property(Object *, const Identifier &); + virtual void setProperty(Object *, const Identifier &name, const QScriptValue &); + virtual bool isQObject() const; + virtual QObject *toQObject(Object *, bool *ok = 0); + +private: + friend class QDeclarativeObjectMethodScriptClass; + QDeclarativeObjectMethodScriptClass methods; + + QDeclarativeTypeNameCache::Data *lastTNData; + QDeclarativePropertyCache::Data *lastData; + QDeclarativePropertyCache::Data local; + + PersistentIdentifier m_destroyId; + PersistentIdentifier m_toStringId; + QScriptValue m_destroy; + QScriptValue m_toString; + + static QScriptValue tostring(QScriptContext *context, QScriptEngine *engine); + static QScriptValue destroy(QScriptContext *context, QScriptEngine *engine); + + QDeclarativeEngine *engine; +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(QDeclarativeObjectScriptClass::QueryHints); + +QT_END_NAMESPACE + +#endif // QDECLARATIVEOBJECTSCRIPTCLASS_P_H + diff --git a/src/declarative/qml/qdeclarativeparser.cpp b/src/declarative/qml/qdeclarativeparser.cpp new file mode 100644 index 00000000..0bb52564 --- /dev/null +++ b/src/declarative/qml/qdeclarativeparser.cpp @@ -0,0 +1,437 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativeparser_p.h" + +#include "qdeclarativepropertyvaluesource.h" +#include "private/qdeclarativevme_p.h" +#include "qdeclarative.h" +#include "private/qdeclarativecomponent_p.h" +#include "qdeclarativecomponent.h" +#include "private/qmetaobjectbuilder_p.h" +#include "private/qdeclarativevmemetaobject_p.h" +#include "private/qdeclarativecompiler_p.h" +#include "parser/qdeclarativejsast_p.h" +#include "parser/qdeclarativejsengine_p.h" + +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +using namespace QDeclarativeJS; +using namespace QDeclarativeParser; + +QDeclarativeParser::Object::Object() +: type(-1), majorVersion(-1), minorVersion(-1), idIndex(-1), metatype(0), synthCache(0), defaultProperty(0), parserStatusCast(-1) +{ +} + +QDeclarativeParser::Object::~Object() +{ + if (defaultProperty) defaultProperty->release(); + if (synthCache) synthCache->release(); + foreach(Property *prop, properties) + prop->release(); + foreach(Property *prop, valueProperties) + prop->release(); + foreach(Property *prop, signalProperties) + prop->release(); + foreach(Property *prop, attachedProperties) + prop->release(); + foreach(Property *prop, groupedProperties) + prop->release(); + foreach(Property *prop, valueTypeProperties) + prop->release(); + typedef QPair PropPair; + foreach(const PropPair &prop, scriptStringProperties) + prop.first->release(); + foreach(const DynamicProperty &prop, dynamicProperties) + if (prop.defaultValue) prop.defaultValue->release(); +} + +void Object::setBindingBit(int b) +{ + while (bindingBitmask.size() < 4 * (1 + b / 32)) + bindingBitmask.append(char(0)); + + quint32 *bits = (quint32 *)bindingBitmask.data(); + bits[b / 32] |= (1 << (b % 32)); +} + +const QMetaObject *Object::metaObject() const +{ + if (!metadata.isEmpty() && metatype) + return &extObject; + else + return metatype; +} + +QDeclarativeParser::Property *Object::getDefaultProperty() +{ + if (!defaultProperty) { + defaultProperty = new Property; + defaultProperty->parent = this; + } + return defaultProperty; +} + +void QDeclarativeParser::Object::addValueProperty(Property *p) +{ + p->addref(); + valueProperties << p; +} + +void QDeclarativeParser::Object::addSignalProperty(Property *p) +{ + p->addref(); + signalProperties << p; +} + +void QDeclarativeParser::Object::addAttachedProperty(Property *p) +{ + p->addref(); + attachedProperties << p; +} + +void QDeclarativeParser::Object::addGroupedProperty(Property *p) +{ + p->addref(); + groupedProperties << p; +} + +void QDeclarativeParser::Object::addValueTypeProperty(Property *p) +{ + p->addref(); + valueTypeProperties << p; +} + +void QDeclarativeParser::Object::addScriptStringProperty(Property *p, int stack) +{ + p->addref(); + scriptStringProperties << qMakePair(p, stack); +} + + +Property *QDeclarativeParser::Object::getProperty(const QByteArray &name, bool create) +{ + if (!properties.contains(name)) { + if (create) { + Property *property = new Property(name); + property->parent = this; + properties.insert(name, property); + } else { + return 0; + } + } + return properties[name]; +} + +QDeclarativeParser::Object::DynamicProperty::DynamicProperty() +: isDefaultProperty(false), type(Variant), defaultValue(0) +{ +} + +QDeclarativeParser::Object::DynamicProperty::DynamicProperty(const DynamicProperty &o) +: isDefaultProperty(o.isDefaultProperty), + type(o.type), + customType(o.customType), + name(o.name), + defaultValue(o.defaultValue), + location(o.location) +{ +} + +QDeclarativeParser::Object::DynamicSignal::DynamicSignal() +{ +} + +QDeclarativeParser::Object::DynamicSignal::DynamicSignal(const DynamicSignal &o) +: name(o.name), parameterTypes(o.parameterTypes), + parameterNames(o.parameterNames) +{ +} + +QDeclarativeParser::Object::DynamicSlot::DynamicSlot() +{ +} + +QDeclarativeParser::Object::DynamicSlot::DynamicSlot(const DynamicSlot &o) +: name(o.name), body(o.body), parameterNames(o.parameterNames), location(o.location) +{ +} + +QDeclarativeParser::Property::Property() +: parent(0), type(0), index(-1), value(0), isDefault(true), isDeferred(false), + isValueTypeSubProperty(false), isAlias(false) +{ +} + +QDeclarativeParser::Property::Property(const QByteArray &n) +: parent(0), type(0), index(-1), value(0), name(n), isDefault(false), + isDeferred(false), isValueTypeSubProperty(false), isAlias(false) +{ +} + +QDeclarativeParser::Property::~Property() +{ + foreach(Value *value, values) + value->release(); + foreach(Value *value, onValues) + value->release(); + if (value) value->release(); +} + +QDeclarativeParser::Object *QDeclarativeParser::Property::getValue(const LocationSpan &l) +{ + if (!value) { value = new QDeclarativeParser::Object; value->location = l; } + return value; +} + +void QDeclarativeParser::Property::addValue(Value *v) +{ + values << v; +} + +void QDeclarativeParser::Property::addOnValue(Value *v) +{ + onValues << v; +} + +bool QDeclarativeParser::Property::isEmpty() const +{ + return !value && values.isEmpty() && onValues.isEmpty(); +} + +QDeclarativeParser::Value::Value() +: type(Unknown), object(0) +{ +} + +QDeclarativeParser::Value::~Value() +{ + if (object) object->release(); +} + +QDeclarativeParser::Variant::Variant() +: t(Invalid) {} + +QDeclarativeParser::Variant::Variant(const Variant &o) +: t(o.t), d(o.d), s(o.s) +{ +} + +QDeclarativeParser::Variant::Variant(bool v) +: t(Boolean), b(v) +{ +} + +QDeclarativeParser::Variant::Variant(double v, const QString &asWritten) +: t(Number), d(v), s(asWritten) +{ +} + +QDeclarativeParser::Variant::Variant(const QString &v) +: t(String), s(v) +{ +} + +QDeclarativeParser::Variant::Variant(const QString &v, QDeclarativeJS::AST::Node *n) +: t(Script), n(n), s(v) +{ +} + +QDeclarativeParser::Variant &QDeclarativeParser::Variant::operator=(const Variant &o) +{ + t = o.t; + d = o.d; + s = o.s; + return *this; +} + +QDeclarativeParser::Variant::Type QDeclarativeParser::Variant::type() const +{ + return t; +} + +bool QDeclarativeParser::Variant::asBoolean() const +{ + return b; +} + +QString QDeclarativeParser::Variant::asString() const +{ + return s; +} + +double QDeclarativeParser::Variant::asNumber() const +{ + return d; +} + +//reverse of Lexer::singleEscape() +QString escapedString(const QString &string) +{ + QString tmp = QLatin1String("\""); + for (int i = 0; i < string.length(); ++i) { + const QChar &c = string.at(i); + switch(c.unicode()) { + case 0x08: + tmp += QLatin1String("\\b"); + break; + case 0x09: + tmp += QLatin1String("\\t"); + break; + case 0x0A: + tmp += QLatin1String("\\n"); + break; + case 0x0B: + tmp += QLatin1String("\\v"); + break; + case 0x0C: + tmp += QLatin1String("\\f"); + break; + case 0x0D: + tmp += QLatin1String("\\r"); + break; + case 0x22: + tmp += QLatin1String("\\\""); + break; + case 0x27: + tmp += QLatin1String("\\\'"); + break; + case 0x5C: + tmp += QLatin1String("\\\\"); + break; + default: + tmp += c; + break; + } + } + tmp += QLatin1Char('\"'); + return tmp; +} + +QString QDeclarativeParser::Variant::asScript() const +{ + switch(type()) { + default: + case Invalid: + return QString(); + case Boolean: + return b?QLatin1String("true"):QLatin1String("false"); + case Number: + if (s.isEmpty()) + return QString::number(d); + else + return s; + case String: + return escapedString(s); + case Script: + return s; + } +} + +QDeclarativeJS::AST::Node *QDeclarativeParser::Variant::asAST() const +{ + if (type() == Script) + return n; + else + return 0; +} + +bool QDeclarativeParser::Variant::isStringList() const +{ + if (isString()) + return true; + + if (type() != Script || !n) + return false; + + AST::ArrayLiteral *array = AST::cast(n); + if (!array) + return false; + + AST::ElementList *elements = array->elements; + + while (elements) { + + if (!AST::cast(elements->expression)) + return false; + + elements = elements->next; + } + + return true; +} + +QStringList QDeclarativeParser::Variant::asStringList() const +{ + QStringList rv; + if (isString()) { + rv << asString(); + return rv; + } + + AST::ArrayLiteral *array = AST::cast(n); + if (!array) + return rv; + + AST::ElementList *elements = array->elements; + while (elements) { + + AST::StringLiteral *string = AST::cast(elements->expression); + if (!string) + return QStringList(); + rv.append(string->value->asString()); + + elements = elements->next; + } + + return rv; +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativeparser_p.h b/src/declarative/qml/qdeclarativeparser_p.h new file mode 100644 index 00000000..6e4d6cd3 --- /dev/null +++ b/src/declarative/qml/qdeclarativeparser_p.h @@ -0,0 +1,381 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPARSER_P_H +#define QDECLARATIVEPARSER_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 "qdeclarative.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativePropertyCache; +namespace QDeclarativeJS { namespace AST { class Node; } } + +/* + XXX + + These types are created (and owned) by the QDeclarativeXmlParser and consumed by the + QDeclarativeCompiler. During the compilation phase the compiler will update some of + the fields for both its own use and for the use of the upcoming QDeclarativeDom API. + + The types are part of the generic sounding "QDeclarativeParser" namespace for legacy + reasons (there used to be more in this namespace) and will be cleaned up and + migrated into a more appropriate location shortly. +*/ +namespace QDeclarativeParser +{ + struct Location + { + Location() : line(-1), column(-1) {} + int line; + int column; + }; + + struct LocationRange + { + LocationRange() : offset(0), length(0) {} + quint32 offset; + quint32 length; + }; + + struct LocationSpan + { + Location start; + Location end; + LocationRange range; + + bool operator<(LocationSpan &o) const { + return (start.line < o.start.line) || + (start.line == o.start.line && start.column < o.start.column); + } + }; + + class Property; + class Object : public QDeclarativeRefCount + { + public: + Object(); + virtual ~Object(); + + // Type of the object. The integer is an index into the + // QDeclarativeCompiledData::types array, or -1 if the object is a property + // group. + int type; + // The url of this object if it is an external type. Used by the DOM + QUrl url; + + // version information if type is defined in library or C++ + int majorVersion; + int minorVersion; + + // The fully-qualified name of this type + QByteArray typeName; + // The class name + QByteArray className; + // The id assigned to the object (if any). Set by the QDeclarativeCompiler + QString id; + // The id index assigned to the object (if any). Set by the QDeclarativeCompiler + int idIndex; + // Custom parsed data + QByteArray custom; + // Bit mask of the properties assigned bindings + QByteArray bindingBitmask; + void setBindingBit(int); + // Returns the metaobject for this type, or 0 if not available. + // Internally selectd between the metatype and extObject variables + const QMetaObject *metaObject() const; + + // The compile time metaobject for this type + const QMetaObject *metatype; + // The synthesized metaobject, if QML added signals or properties to + // this type. Otherwise null + QAbstractDynamicMetaObject extObject; + QByteArray metadata; // Generated by compiler + QByteArray synthdata; // Generated by compiler + QDeclarativePropertyCache *synthCache; // Generated by compiler + + Property *getDefaultProperty(); + Property *getProperty(const QByteArray &name, bool create=true); + + Property *defaultProperty; + QHash properties; + + // Output of the compilation phase (these properties continue to exist + // in either the defaultProperty or properties members too) + void addValueProperty(Property *); + void addSignalProperty(Property *); + void addAttachedProperty(Property *); + void addGroupedProperty(Property *); + void addValueTypeProperty(Property *); + void addScriptStringProperty(Property *, int = 0); + QList valueProperties; + QList signalProperties; + QList attachedProperties; + QList groupedProperties; + QList valueTypeProperties; + QList > scriptStringProperties; + + // Script blocks that were nested under this object + struct ScriptBlock { + enum Pragma { + None = 0x00000000, + Shared = 0x00000001 + }; + Q_DECLARE_FLAGS(Pragmas, Pragma) + + QString code; + QString file; + Pragmas pragmas; + }; + + // The bytes to cast instances by to get to the QDeclarativeParserStatus + // interface. -1 indicates the type doesn't support this interface. + // Set by the QDeclarativeCompiler. + int parserStatusCast; + + LocationSpan location; + + struct DynamicProperty { + DynamicProperty(); + DynamicProperty(const DynamicProperty &); + + enum Type { Variant, Int, Bool, Real, String, Url, Color, Time, Date, DateTime, Alias, Custom, CustomList }; + + bool isDefaultProperty; + Type type; + QByteArray customType; + QByteArray name; + QDeclarativeParser::Property *defaultValue; + LocationSpan location; + }; + struct DynamicSignal { + DynamicSignal(); + DynamicSignal(const DynamicSignal &); + + QByteArray name; + QList parameterTypes; + QList parameterNames; + }; + struct DynamicSlot { + DynamicSlot(); + DynamicSlot(const DynamicSlot &); + + QByteArray name; + QString body; + QList parameterNames; + LocationSpan location; + }; + + // The list of dynamic properties + QList dynamicProperties; + // The list of dynamic signals + QList dynamicSignals; + // The list of dynamic slots + QList dynamicSlots; + }; + + class Q_DECLARATIVE_EXPORT Variant + { + public: + enum Type { + Invalid, + Boolean, + Number, + String, + Script + }; + + Variant(); + Variant(const Variant &); + Variant(bool); + Variant(double, const QString &asWritten=QString()); + Variant(const QString &); + Variant(const QString &, QDeclarativeJS::AST::Node *); + Variant &operator=(const Variant &); + + Type type() const; + + bool isBoolean() const { return type() == Boolean; } + bool isNumber() const { return type() == Number; } + bool isString() const { return type() == String; } + bool isScript() const { return type() == Script; } + bool isStringList() const; + + bool asBoolean() const; + QString asString() const; + double asNumber() const; + QString asScript() const; + QDeclarativeJS::AST::Node *asAST() const; + QStringList asStringList() const; + + private: + Type t; + union { + bool b; + double d; + QDeclarativeJS::AST::Node *n; + }; + QString s; + }; + + class Value : public QDeclarativeRefCount + { + public: + Value(); + virtual ~Value(); + + enum Type { + // The type of this value assignment is not yet known + Unknown, + // This is used as a literal property assignment + Literal, + // This is used as a property binding assignment + PropertyBinding, + // This is used as a QDeclarativePropertyValueSource assignment + ValueSource, + // This is used as a QDeclarativePropertyValueInterceptor assignment + ValueInterceptor, + // This is used as a property QObject assignment + CreatedObject, + // This is used as a signal object assignment + SignalObject, + // This is used as a signal expression assignment + SignalExpression, + // This is used as an id assignment only + Id + }; + Type type; + + // ### Temporary (for id only) + QString primitive() const { return value.isString() ? value.asString() : value.asScript(); } + + // Primitive value + Variant value; + // Object value + Object *object; + + LocationSpan location; + }; + + class Property : public QDeclarativeRefCount + { + public: + Property(); + Property(const QByteArray &n); + virtual ~Property(); + + // The Object to which this property is attached + Object *parent; + + Object *getValue(const LocationSpan &); + void addValue(Value *v); + void addOnValue(Value *v); + + // The QVariant::Type of the property, or 0 (QVariant::Invalid) if + // unknown. + int type; + // The metaobject index of this property, or -1 if unknown. + int index; + + // Returns true if this is an empty property - both value and values + // are unset. + bool isEmpty() const; + // The list of values assigned to this property. Content in values + // and value are mutually exclusive + QList values; + // The list of values assigned to this property using the "on" syntax + QList onValues; + // The accessed property. This is used to represent dot properties. + // Content in value and values are mutually exclusive. + Object *value; + // The property name + QByteArray name; + // True if this property was accessed as the default property. + bool isDefault; + // True if the setting of this property will be deferred. Set by the + // QDeclarativeCompiler + bool isDeferred; + // True if this property is a value-type pseudo-property + bool isValueTypeSubProperty; + // True if this property is a property alias. Set by the + // QDeclarativeCompiler + bool isAlias; + + LocationSpan location; + LocationRange listValueRange; + QList listCommaPositions; + }; +} + +Q_DECLARE_OPERATORS_FOR_FLAGS(QDeclarativeParser::Object::ScriptBlock::Pragmas); + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QDeclarativeParser::Variant) + +QT_END_HEADER + +#endif // QDECLARATIVEPARSER_P_H diff --git a/src/declarative/qml/qdeclarativeparserstatus.cpp b/src/declarative/qml/qdeclarativeparserstatus.cpp new file mode 100644 index 00000000..ebf47152 --- /dev/null +++ b/src/declarative/qml/qdeclarativeparserstatus.cpp @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativeparserstatus.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QDeclarativeParserStatus + \since 4.7 + \brief The QDeclarativeParserStatus class provides updates on the QML parser state. + + QDeclarativeParserStatus provides a mechanism for classes instantiated by + a QDeclarativeEngine to receive notification at key points in their creation. + + This class is often used for optimization purposes, as it allows you to defer an + expensive operation until after all the properties have been set on an + object. For example, QML's \l {Text} element uses the parser status + to defer text layout until all of its properties have been set (we + don't want to layout when the \c text is assigned, and then relayout + when the \c font is assigned, and relayout again when the \c width is assigned, + and so on). + + To use QDeclarativeParserStatus, you must inherit both a QObject-derived class + and QDeclarativeParserStatus, and use the Q_INTERFACES() macro. + + \code + class MyObject : public QObject, public QDeclarativeParserStatus + { + Q_OBJECT + Q_INTERFACES(QDeclarativeParserStatus) + + public: + MyObject(QObject *parent = 0); + ... + void classBegin(); + void componentComplete(); + } + \endcode +*/ + +/*! \internal */ +QDeclarativeParserStatus::QDeclarativeParserStatus() +: d(0) +{ +} + +/*! \internal */ +QDeclarativeParserStatus::~QDeclarativeParserStatus() +{ + if(d) + (*d) = 0; +} + +/*! + \fn void QDeclarativeParserStatus::classBegin() + + Invoked after class creation, but before any properties have been set. +*/ + +/*! + \fn void QDeclarativeParserStatus::componentComplete() + + Invoked after the root component that caused this instantiation has + completed construction. At this point all static values and binding values + have been assigned to the class. +*/ + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativeparserstatus.h b/src/declarative/qml/qdeclarativeparserstatus.h new file mode 100644 index 00000000..460600af --- /dev/null +++ b/src/declarative/qml/qdeclarativeparserstatus.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPARSERSTATUS_H +#define QDECLARATIVEPARSERSTATUS_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class Q_DECLARATIVE_EXPORT QDeclarativeParserStatus +{ +public: + QDeclarativeParserStatus(); + virtual ~QDeclarativeParserStatus(); + + virtual void classBegin()=0; + virtual void componentComplete()=0; + +private: + friend class QDeclarativeVME; + friend class QDeclarativeComponent; + friend class QDeclarativeComponentPrivate; + friend class QDeclarativeEnginePrivate; + QDeclarativeParserStatus **d; +}; +Q_DECLARE_INTERFACE(QDeclarativeParserStatus, "com.trolltech.qml.QDeclarativeParserStatus") + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEPARSERSTATUS_H diff --git a/src/declarative/qml/qdeclarativeprivate.h b/src/declarative/qml/qdeclarativeprivate.h new file mode 100644 index 00000000..20dcca0e --- /dev/null +++ b/src/declarative/qml/qdeclarativeprivate.h @@ -0,0 +1,249 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPRIVATE_H +#define QDECLARATIVEPRIVATE_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 +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +typedef QObject *(*QDeclarativeAttachedPropertiesFunc)(QObject *); + +template +class QDeclarativeTypeInfo +{ +public: + enum { + hasAttachedProperties = 0 + }; +}; + + +class QDeclarativeCustomParser; +namespace QDeclarativePrivate +{ + void Q_DECLARATIVE_EXPORT qdeclarativeelement_destructor(QObject *); + template + class QDeclarativeElement : public T + { + public: + virtual ~QDeclarativeElement() { + QDeclarativePrivate::qdeclarativeelement_destructor(this); + } + }; + + template + void createInto(void *memory) { new (memory) QDeclarativeElement; } + + template + QObject *createParent(QObject *p) { return new T(p); } + + template + struct StaticCastSelectorClass + { + static inline int cast() { return -1; } + }; + + template + struct StaticCastSelectorClass + { + static inline int cast() { return int(reinterpret_cast(static_cast(reinterpret_cast(0x10000000)))) - 0x10000000; } + }; + + template + struct StaticCastSelector + { + typedef int yes_type; + typedef char no_type; + + static yes_type check(To *); + static no_type check(...); + + static inline int cast() + { + return StaticCastSelectorClass(0)))>::cast(); + } + }; + + template + struct has_attachedPropertiesMember + { + static bool const value = QDeclarativeTypeInfo::hasAttachedProperties; + }; + + template + class has_attachedPropertiesMethod + { + public: + typedef int yes_type; + typedef char no_type; + + template + static yes_type check(ReturnType *(*)(QObject *)); + static no_type check(...); + + static bool const value = sizeof(check(&T::qmlAttachedProperties)) == sizeof(yes_type); + }; + + template + class has_attachedPropertiesMethod + { + public: + static bool const value = false; + }; + + template + class AttachedPropertySelector + { + public: + static inline QDeclarativeAttachedPropertiesFunc func() { return 0; } + static inline const QMetaObject *metaObject() { return 0; } + }; + template + class AttachedPropertySelector + { + static inline QObject *attachedProperties(QObject *obj) { + return T::qmlAttachedProperties(obj); + } + template + static inline const QMetaObject *attachedPropertiesMetaObject(ReturnType *(*)(QObject *)) { + return &ReturnType::staticMetaObject; + } + public: + static inline QDeclarativeAttachedPropertiesFunc func() { + return &attachedProperties; + } + static inline const QMetaObject *metaObject() { + return attachedPropertiesMetaObject(&T::qmlAttachedProperties); + } + }; + + template + inline QDeclarativeAttachedPropertiesFunc attachedPropertiesFunc() + { + return AttachedPropertySelector::value>::value>::func(); + } + + template + inline const QMetaObject *attachedPropertiesMetaObject() + { + return AttachedPropertySelector::value>::value>::metaObject(); + } + + enum AutoParentResult { Parented, IncompatibleObject, IncompatibleParent }; + typedef AutoParentResult (*AutoParentFunction)(QObject *object, QObject *parent); + + struct RegisterType { + int version; + + int typeId; + int listId; + int objectSize; + void (*create)(void *); + QString noCreationReason; + + const char *uri; + int versionMajor; + int versionMinor; + const char *elementName; + const QMetaObject *metaObject; + + QDeclarativeAttachedPropertiesFunc attachedPropertiesFunction; + const QMetaObject *attachedPropertiesMetaObject; + + int parserStatusCast; + int valueSourceCast; + int valueInterceptorCast; + + QObject *(*extensionObjectCreate)(QObject *); + const QMetaObject *extensionMetaObject; + + QDeclarativeCustomParser *customParser; + int revision; + // If this is extended ensure "version" is bumped!!! + }; + + struct RegisterInterface { + int version; + + int typeId; + int listId; + + const char *iid; + }; + + struct RegisterAutoParent { + int version; + + AutoParentFunction function; + }; + + enum RegistrationType { + TypeRegistration = 0, + InterfaceRegistration = 1, + AutoParentRegistration = 2 + }; + + int Q_DECLARATIVE_EXPORT qmlregister(RegistrationType, void *); +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEPRIVATE_H diff --git a/src/declarative/qml/qdeclarativeproperty.cpp b/src/declarative/qml/qdeclarativeproperty.cpp new file mode 100644 index 00000000..05bec635 --- /dev/null +++ b/src/declarative/qml/qdeclarativeproperty.cpp @@ -0,0 +1,1654 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativeproperty.h" +#include "private/qdeclarativeproperty_p.h" + +#include "qdeclarative.h" +#include "private/qdeclarativebinding_p.h" +#include "qdeclarativecontext.h" +#include "private/qdeclarativecontext_p.h" +#include "private/qdeclarativeboundsignal_p.h" +#include "qdeclarativeengine.h" +#include "private/qdeclarativeengine_p.h" +#include "private/qdeclarativedata_p.h" +#include "private/qdeclarativestringconverters_p.h" +#include "private/qdeclarativelist_p.h" +#include "private/qdeclarativecompiler_p.h" +#include "private/qdeclarativevmemetaobject_p.h" + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +/*! +\class QDeclarativeProperty +\since 4.7 +\brief The QDeclarativeProperty class abstracts accessing properties on objects created from QML. + +As QML uses Qt's meta-type system all of the existing QMetaObject classes can be used to introspect +and interact with objects created by QML. However, some of the new features provided by QML - such +as type safety and attached properties - are most easily used through the QDeclarativeProperty class +that simplifies some of their natural complexity. + +Unlike QMetaProperty which represents a property on a class type, QDeclarativeProperty encapsulates +a property on a specific object instance. To read a property's value, programmers create a +QDeclarativeProperty instance and call the read() method. Likewise to write a property value the +write() method is used. + +For example, for the following QML code: + +\qml +// MyItem.qml +import QtQuick 1.0 + +Text { text: "A bit of text" } +\endqml + +The \l Text object's properties could be accessed using QDeclarativeProperty, like this: + +\code +#include +#include + +... + +QDeclarativeView view(QUrl::fromLocalFile("MyItem.qml")); +QDeclarativeProperty property(view.rootObject(), "font.pixelSize"); +qWarning() << "Current pixel size:" << property.read().toInt(); +property.write(24); +qWarning() << "Pixel size should now be 24:" << property.read().toInt(); +\endcode +*/ + +/*! + Create an invalid QDeclarativeProperty. +*/ +QDeclarativeProperty::QDeclarativeProperty() +: d(0) +{ +} + +/*! \internal */ +QDeclarativeProperty::~QDeclarativeProperty() +{ + if (d) + d->release(); + d = 0; +} + +/*! + Creates a QDeclarativeProperty for the default property of \a obj. If there is no + default property, an invalid QDeclarativeProperty will be created. + */ +QDeclarativeProperty::QDeclarativeProperty(QObject *obj) +: d(new QDeclarativePropertyPrivate) +{ + d->initDefault(obj); +} + +/*! + Creates a QDeclarativeProperty for the default property of \a obj + using the \l{QDeclarativeContext} {context} \a ctxt. If there is + no default property, an invalid QDeclarativeProperty will be + created. + */ +QDeclarativeProperty::QDeclarativeProperty(QObject *obj, QDeclarativeContext *ctxt) +: d(new QDeclarativePropertyPrivate) +{ + d->context = ctxt?QDeclarativeContextData::get(ctxt):0; + d->engine = ctxt?ctxt->engine():0; + d->initDefault(obj); +} + +/*! + Creates a QDeclarativeProperty for the default property of \a obj + using the environment for instantiating QML components that is + provided by \a engine. If there is no default property, an + invalid QDeclarativeProperty will be created. + */ +QDeclarativeProperty::QDeclarativeProperty(QObject *obj, QDeclarativeEngine *engine) + : d(new QDeclarativePropertyPrivate) +{ + d->context = 0; + d->engine = engine; + d->initDefault(obj); +} + +/*! + Initialize from the default property of \a obj +*/ +void QDeclarativePropertyPrivate::initDefault(QObject *obj) +{ + if (!obj) + return; + + QMetaProperty p = QDeclarativeMetaType::defaultProperty(obj); + core.load(p); + if (core.isValid()) + object = obj; +} + +/*! + Creates a QDeclarativeProperty for the property \a name of \a obj. + */ +QDeclarativeProperty::QDeclarativeProperty(QObject *obj, const QString &name) +: d(new QDeclarativePropertyPrivate) +{ + d->initProperty(obj, name); + if (!isValid()) d->object = 0; +} + +/*! + Creates a QDeclarativeProperty for the property \a name of \a obj + using the \l{QDeclarativeContext} {context} \a ctxt. + + Creating a QDeclarativeProperty without a context will render some + properties - like attached properties - inaccessible. +*/ +QDeclarativeProperty::QDeclarativeProperty(QObject *obj, const QString &name, QDeclarativeContext *ctxt) +: d(new QDeclarativePropertyPrivate) +{ + d->context = ctxt?QDeclarativeContextData::get(ctxt):0; + d->engine = ctxt?ctxt->engine():0; + d->initProperty(obj, name); + if (!isValid()) { d->object = 0; d->context = 0; d->engine = 0; } +} + +/*! + Creates a QDeclarativeProperty for the property \a name of \a obj + using the environment for instantiating QML components that is + provided by \a engine. + */ +QDeclarativeProperty::QDeclarativeProperty(QObject *obj, const QString &name, QDeclarativeEngine *engine) +: d(new QDeclarativePropertyPrivate) +{ + d->context = 0; + d->engine = engine; + d->initProperty(obj, name); + if (!isValid()) { d->object = 0; d->context = 0; d->engine = 0; } +} + +Q_GLOBAL_STATIC(QDeclarativeValueTypeFactory, qmlValueTypes); + +void QDeclarativePropertyPrivate::initProperty(QObject *obj, const QString &name) +{ + if (!obj) return; + + QDeclarativeTypeNameCache *typeNameCache = context?context->imports:0; + + QStringList path = name.split(QLatin1Char('.')); + if (path.isEmpty()) return; + + QObject *currentObject = obj; + + // Everything up to the last property must be an "object type" property + for (int ii = 0; ii < path.count() - 1; ++ii) { + const QString &pathName = path.at(ii); + + if (QDeclarativeTypeNameCache::Data *data = typeNameCache?typeNameCache->data(pathName):0) { + if (data->type) { + QDeclarativeAttachedPropertiesFunc func = data->type->attachedPropertiesFunction(); + if (!func) return; // Not an attachable type + + currentObject = qmlAttachedPropertiesObjectById(data->type->attachedPropertiesId(), currentObject); + if (!currentObject) return; // Something is broken with the attachable type + } else { + Q_ASSERT(data->typeNamespace); + if ((ii + 1) == path.count()) return; // No type following the namespace + + ++ii; data = data->typeNamespace->data(path.at(ii)); + if (!data || !data->type) return; // Invalid type in namespace + + QDeclarativeAttachedPropertiesFunc func = data->type->attachedPropertiesFunction(); + if (!func) return; // Not an attachable type + + currentObject = qmlAttachedPropertiesObjectById(data->type->attachedPropertiesId(), currentObject); + if (!currentObject) return; // Something is broken with the attachable type + } + } else { + + QDeclarativePropertyCache::Data local; + QDeclarativePropertyCache::Data *property = + QDeclarativePropertyCache::property(engine, obj, pathName, local); + + if (!property) return; // Not a property + if (property->flags & QDeclarativePropertyCache::Data::IsFunction) + return; // Not an object property + + if (ii == (path.count() - 2) && QDeclarativeValueTypeFactory::isValueType(property->propType)) { + // We're now at a value type property. We can use a global valuetypes array as we + // never actually use the objects, just look up their properties. + QObject *typeObject = (*qmlValueTypes())[property->propType]; + if (!typeObject) return; // Not a value type + + int idx = typeObject->metaObject()->indexOfProperty(path.last().toUtf8().constData()); + if (idx == -1) return; // Value type property does not exist + + QMetaProperty vtProp = typeObject->metaObject()->property(idx); + + object = currentObject; + core = *property; + valueType.flags = QDeclarativePropertyCache::Data::flagsForProperty(vtProp); + valueType.valueTypeCoreIdx = idx; + valueType.valueTypePropType = vtProp.userType(); + + return; + } else { + if (!(property->flags & QDeclarativePropertyCache::Data::IsQObjectDerived)) + return; // Not an object property + + void *args[] = { ¤tObject, 0 }; + QMetaObject::metacall(currentObject, QMetaObject::ReadProperty, property->coreIndex, args); + if (!currentObject) return; // No value + + } + } + + } + + const QString &terminal = path.last(); + + if (terminal.count() >= 3 && + terminal.at(0) == QLatin1Char('o') && + terminal.at(1) == QLatin1Char('n') && + terminal.at(2).isUpper()) { + + QString signalName = terminal.mid(2); + signalName[0] = signalName.at(0).toLower(); + + QMetaMethod method = findSignalByName(currentObject->metaObject(), signalName.toLatin1().constData()); + if (method.signature()) { + object = currentObject; + core.load(method); + return; + } + } + + // Property + QDeclarativePropertyCache::Data local; + QDeclarativePropertyCache::Data *property = + QDeclarativePropertyCache::property(engine, currentObject, terminal, local); + if (property && !(property->flags & QDeclarativePropertyCache::Data::IsFunction)) { + object = currentObject; + core = *property; + nameCache = terminal; + isNameCached = true; + } +} + +/*! + Create a copy of \a other. +*/ +QDeclarativeProperty::QDeclarativeProperty(const QDeclarativeProperty &other) +{ + d = other.d; + if (d) + d->addref(); +} + +/*! + \enum QDeclarativeProperty::PropertyTypeCategory + + This enum specifies a category of QML property. + + \value InvalidCategory The property is invalid, or is a signal property. + \value List The property is a QDeclarativeListProperty list property + \value Object The property is a QObject derived type pointer + \value Normal The property is a normal value property. + */ + +/*! + \enum QDeclarativeProperty::Type + + This enum specifies a type of QML property. + + \value Invalid The property is invalid. + \value Property The property is a regular Qt property. + \value SignalProperty The property is a signal property. +*/ + +/*! + Returns the property category. +*/ +QDeclarativeProperty::PropertyTypeCategory QDeclarativeProperty::propertyTypeCategory() const +{ + return d ? d->propertyTypeCategory() : InvalidCategory; +} + +QDeclarativeProperty::PropertyTypeCategory +QDeclarativePropertyPrivate::propertyTypeCategory() const +{ + uint type = this->type(); + + if (isValueType()) { + return QDeclarativeProperty::Normal; + } else if (type & QDeclarativeProperty::Property) { + int type = propertyType(); + if (type == QVariant::Invalid) + return QDeclarativeProperty::InvalidCategory; + else if (QDeclarativeValueTypeFactory::isValueType((uint)type)) + return QDeclarativeProperty::Normal; + else if (core.flags & QDeclarativePropertyCache::Data::IsQObjectDerived) + return QDeclarativeProperty::Object; + else if (core.flags & QDeclarativePropertyCache::Data::IsQList) + return QDeclarativeProperty::List; + else + return QDeclarativeProperty::Normal; + } else { + return QDeclarativeProperty::InvalidCategory; + } +} + +/*! + Returns the type name of the property, or 0 if the property has no type + name. +*/ +const char *QDeclarativeProperty::propertyTypeName() const +{ + if (!d) + return 0; + if (d->isValueType()) { + + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(d->context); + QDeclarativeValueType *valueType = 0; + if (ep) valueType = ep->valueTypes[d->core.propType]; + else valueType = QDeclarativeValueTypeFactory::valueType(d->core.propType); + Q_ASSERT(valueType); + + const char *rv = valueType->metaObject()->property(d->valueType.valueTypeCoreIdx).typeName(); + + if (!ep) delete valueType; + + return rv; + } else if (d->object && type() & Property && d->core.isValid()) { + return d->object->metaObject()->property(d->core.coreIndex).typeName(); + } else { + return 0; + } +} + +/*! + Returns true if \a other and this QDeclarativeProperty represent the same + property. +*/ +bool QDeclarativeProperty::operator==(const QDeclarativeProperty &other) const +{ + if (!d || !other.d) + return false; + // category is intentially omitted here as it is generated + // from the other members + return d->object == other.d->object && + d->core == other.d->core && + d->valueType == other.d->valueType; +} + +/*! + Returns the QVariant type of the property, or QVariant::Invalid if the + property has no QVariant type. +*/ +int QDeclarativeProperty::propertyType() const +{ + return d ? d->propertyType() : int(QVariant::Invalid); +} + +bool QDeclarativePropertyPrivate::isValueType() const +{ + return valueType.valueTypeCoreIdx != -1; +} + +int QDeclarativePropertyPrivate::propertyType() const +{ + uint type = this->type(); + if (isValueType()) { + return valueType.valueTypePropType; + } else if (type & QDeclarativeProperty::Property) { + if (core.propType == (int)QVariant::LastType) + return qMetaTypeId(); + else + return core.propType; + } else { + return QVariant::Invalid; + } +} + +QDeclarativeProperty::Type QDeclarativePropertyPrivate::type() const +{ + if (core.flags & QDeclarativePropertyCache::Data::IsFunction) + return QDeclarativeProperty::SignalProperty; + else if (core.isValid()) + return QDeclarativeProperty::Property; + else + return QDeclarativeProperty::Invalid; +} + +/*! + Returns the type of the property. +*/ +QDeclarativeProperty::Type QDeclarativeProperty::type() const +{ + return d ? d->type() : Invalid; +} + +/*! + Returns true if this QDeclarativeProperty represents a regular Qt property. +*/ +bool QDeclarativeProperty::isProperty() const +{ + return type() & Property; +} + +/*! + Returns true if this QDeclarativeProperty represents a QML signal property. +*/ +bool QDeclarativeProperty::isSignalProperty() const +{ + return type() & SignalProperty; +} + +/*! + Returns the QDeclarativeProperty's QObject. +*/ +QObject *QDeclarativeProperty::object() const +{ + return d ? d->object : 0; +} + +/*! + Assign \a other to this QDeclarativeProperty. +*/ +QDeclarativeProperty &QDeclarativeProperty::operator=(const QDeclarativeProperty &other) +{ + if (d) + d->release(); + d = other.d; + if (d) + d->addref(); + + return *this; +} + +/*! + Returns true if the property is writable, otherwise false. +*/ +bool QDeclarativeProperty::isWritable() const +{ + if (!d) + return false; + if (!d->object) + return false; + if (d->core.flags & QDeclarativePropertyCache::Data::IsQList) //list + return true; + else if (d->core.flags & QDeclarativePropertyCache::Data::IsFunction) //signal handler + return false; + else if (d->core.isValid()) //normal property + return d->core.flags & QDeclarativePropertyCache::Data::IsWritable; + else + return false; +} + +/*! + Returns true if the property is designable, otherwise false. +*/ +bool QDeclarativeProperty::isDesignable() const +{ + if (!d) + return false; + if (type() & Property && d->core.isValid() && d->object) + return d->object->metaObject()->property(d->core.coreIndex).isDesignable(); + else + return false; +} + +/*! + Returns true if the property is resettable, otherwise false. +*/ +bool QDeclarativeProperty::isResettable() const +{ + if (!d) + return false; + if (type() & Property && d->core.isValid() && d->object) + return d->core.flags & QDeclarativePropertyCache::Data::IsResettable; + else + return false; +} + +/*! + Returns true if the QDeclarativeProperty refers to a valid property, otherwise + false. +*/ +bool QDeclarativeProperty::isValid() const +{ + if (!d) + return false; + return type() != Invalid; +} + +/*! + Return the name of this QML property. +*/ +QString QDeclarativeProperty::name() const +{ + if (!d) + return QString(); + if (!d->isNameCached) { + // ### + if (!d->object) { + } else if (d->isValueType()) { + QString rv = d->core.name(d->object) + QLatin1Char('.'); + + QDeclarativeEnginePrivate *ep = d->engine?QDeclarativeEnginePrivate::get(d->engine):0; + QDeclarativeValueType *valueType = 0; + if (ep) valueType = ep->valueTypes[d->core.propType]; + else valueType = QDeclarativeValueTypeFactory::valueType(d->core.propType); + Q_ASSERT(valueType); + + rv += QString::fromUtf8(valueType->metaObject()->property(d->valueType.valueTypeCoreIdx).name()); + + if (!ep) delete valueType; + + d->nameCache = rv; + } else if (type() & SignalProperty) { + QString name = QLatin1String("on") + d->core.name(d->object); + name[2] = name.at(2).toUpper(); + d->nameCache = name; + } else { + d->nameCache = d->core.name(d->object); + } + d->isNameCached = true; + } + + return d->nameCache; +} + +/*! + Returns the \l{QMetaProperty} {Qt property} associated with + this QML property. + */ +QMetaProperty QDeclarativeProperty::property() const +{ + if (!d) + return QMetaProperty(); + if (type() & Property && d->core.isValid() && d->object) + return d->object->metaObject()->property(d->core.coreIndex); + else + return QMetaProperty(); +} + +/*! + Return the QMetaMethod for this property if it is a SignalProperty, + otherwise returns an invalid QMetaMethod. +*/ +QMetaMethod QDeclarativeProperty::method() const +{ + if (!d) + return QMetaMethod(); + if (type() & SignalProperty && d->object) + return d->object->metaObject()->method(d->core.coreIndex); + else + return QMetaMethod(); +} + +/*! + Returns the binding associated with this property, or 0 if no binding + exists. +*/ +QDeclarativeAbstractBinding * +QDeclarativePropertyPrivate::binding(const QDeclarativeProperty &that) +{ + if (!that.d || !that.isProperty() || !that.d->object) + return 0; + + return binding(that.d->object, that.d->core.coreIndex, that.d->valueType.valueTypeCoreIdx); +} + +/*! + Set the binding associated with this property to \a newBinding. Returns + the existing binding (if any), otherwise 0. + + \a newBinding will be enabled, and the returned binding (if any) will be + disabled. + + Ownership of \a newBinding transfers to QML. Ownership of the return value + is assumed by the caller. + + \a flags is passed through to the binding and is used for the initial update (when + the binding sets the initial value, it will use these flags for the write). +*/ +QDeclarativeAbstractBinding * +QDeclarativePropertyPrivate::setBinding(const QDeclarativeProperty &that, + QDeclarativeAbstractBinding *newBinding, + WriteFlags flags) +{ + if (!that.d || !that.isProperty() || !that.d->object) { + if (newBinding) + newBinding->destroy(); + return 0; + } + + return that.d->setBinding(that.d->object, that.d->core.coreIndex, + that.d->valueType.valueTypeCoreIdx, newBinding, flags); +} + +QDeclarativeAbstractBinding * +QDeclarativePropertyPrivate::binding(QObject *object, int coreIndex, int valueTypeIndex) +{ + QDeclarativeData *data = QDeclarativeData::get(object); + if (!data) + return 0; + + QDeclarativePropertyCache::Data *propertyData = + data->propertyCache?data->propertyCache->property(coreIndex):0; + if (propertyData && propertyData->flags & QDeclarativePropertyCache::Data::IsAlias) { + const QDeclarativeVMEMetaObject *vme = + static_cast(metaObjectForProperty(object->metaObject(), coreIndex)); + + QObject *aObject = 0; int aCoreIndex = -1; int aValueTypeIndex = -1; + if (!vme->aliasTarget(coreIndex, &aObject, &aCoreIndex, &aValueTypeIndex) || aCoreIndex == -1) + return 0; + + // This will either be a value type sub-reference or an alias to a value-type sub-reference not both + Q_ASSERT(valueTypeIndex == -1 || aValueTypeIndex == -1); + return binding(aObject, aCoreIndex, (valueTypeIndex == -1)?aValueTypeIndex:valueTypeIndex); + } + + if (!data->hasBindingBit(coreIndex)) + return 0; + + QDeclarativeAbstractBinding *binding = data->bindings; + while (binding && binding->propertyIndex() != coreIndex) + binding = binding->m_nextBinding; + + if (binding && valueTypeIndex != -1) { + if (binding->bindingType() == QDeclarativeAbstractBinding::ValueTypeProxy) { + int index = coreIndex | (valueTypeIndex << 24); + binding = static_cast(binding)->binding(index); + } + } + + return binding; +} + +void QDeclarativePropertyPrivate::findAliasTarget(QObject *object, int bindingIndex, + QObject **targetObject, int *targetBindingIndex) +{ + int coreIndex = bindingIndex & 0xFFFFFF; + int valueTypeIndex = bindingIndex >> 24; + if (valueTypeIndex == 0) valueTypeIndex = -1; + + QDeclarativeData *data = QDeclarativeData::get(object, false); + if (data) { + QDeclarativePropertyCache::Data *propertyData = + data->propertyCache?data->propertyCache->property(coreIndex):0; + if (propertyData && propertyData->flags & QDeclarativePropertyCache::Data::IsAlias) { + const QDeclarativeVMEMetaObject *vme = + static_cast(metaObjectForProperty(object->metaObject(), coreIndex)); + QObject *aObject = 0; int aCoreIndex = -1; int aValueTypeIndex = -1; + if (vme->aliasTarget(coreIndex, &aObject, &aCoreIndex, &aValueTypeIndex)) { + // This will either be a value type sub-reference or an alias to a value-type sub-reference not both + Q_ASSERT(valueTypeIndex == -1 || aValueTypeIndex == -1); + + int aBindingIndex = aCoreIndex; + if (aValueTypeIndex != -1) + aBindingIndex |= aValueTypeIndex << 24; + else if (valueTypeIndex != -1) + aBindingIndex |= valueTypeIndex << 24; + + findAliasTarget(aObject, aBindingIndex, targetObject, targetBindingIndex); + return; + } + } + } + + *targetObject = object; + *targetBindingIndex = bindingIndex; +} + +QDeclarativeAbstractBinding * +QDeclarativePropertyPrivate::setBinding(QObject *object, int coreIndex, int valueTypeIndex, + QDeclarativeAbstractBinding *newBinding, WriteFlags flags) +{ + QDeclarativeData *data = QDeclarativeData::get(object, 0 != newBinding); + QDeclarativeAbstractBinding *binding = 0; + + if (data) { + QDeclarativePropertyCache::Data *propertyData = + data->propertyCache?data->propertyCache->property(coreIndex):0; + if (propertyData && propertyData->flags & QDeclarativePropertyCache::Data::IsAlias) { + const QDeclarativeVMEMetaObject *vme = + static_cast(metaObjectForProperty(object->metaObject(), coreIndex)); + + QObject *aObject = 0; int aCoreIndex = -1; int aValueTypeIndex = -1; + if (!vme->aliasTarget(coreIndex, &aObject, &aCoreIndex, &aValueTypeIndex)) { + if (newBinding) newBinding->destroy(); + return 0; + } + + // This will either be a value type sub-reference or an alias to a value-type sub-reference not both + Q_ASSERT(valueTypeIndex == -1 || aValueTypeIndex == -1); + return setBinding(aObject, aCoreIndex, (valueTypeIndex == -1)?aValueTypeIndex:valueTypeIndex, + newBinding, flags); + } + } + + if (data && data->hasBindingBit(coreIndex)) { + binding = data->bindings; + + while (binding && binding->propertyIndex() != coreIndex) + binding = binding->m_nextBinding; + } + + int index = coreIndex; + if (valueTypeIndex != -1) + index |= (valueTypeIndex << 24); + + if (binding && valueTypeIndex != -1 && binding->bindingType() == QDeclarativeAbstractBinding::ValueTypeProxy) + binding = static_cast(binding)->binding(index); + + if (binding) { + binding->removeFromObject(); + binding->setEnabled(false, 0); + } + + if (newBinding) { + newBinding->addToObject(object, index); + newBinding->setEnabled(true, flags); + } + + return binding; +} + +QDeclarativeAbstractBinding * +QDeclarativePropertyPrivate::setBindingNoEnable(QObject *object, int coreIndex, int valueTypeIndex, + QDeclarativeAbstractBinding *newBinding) +{ + QDeclarativeData *data = QDeclarativeData::get(object, 0 != newBinding); + QDeclarativeAbstractBinding *binding = 0; + + if (data) { + QDeclarativePropertyCache::Data *propertyData = + data->propertyCache?data->propertyCache->property(coreIndex):0; + if (propertyData && propertyData->flags & QDeclarativePropertyCache::Data::IsAlias) { + const QDeclarativeVMEMetaObject *vme = + static_cast(metaObjectForProperty(object->metaObject(), coreIndex)); + + QObject *aObject = 0; int aCoreIndex = -1; int aValueTypeIndex = -1; + if (!vme->aliasTarget(coreIndex, &aObject, &aCoreIndex, &aValueTypeIndex)) { + if (newBinding) newBinding->destroy(); + return 0; + } + + // This will either be a value type sub-reference or an alias to a value-type sub-reference not both + Q_ASSERT(valueTypeIndex == -1 || aValueTypeIndex == -1); + return setBindingNoEnable(aObject, aCoreIndex, (valueTypeIndex == -1)?aValueTypeIndex:valueTypeIndex, + newBinding); + } + } + + if (data && data->hasBindingBit(coreIndex)) { + binding = data->bindings; + + while (binding && binding->propertyIndex() != coreIndex) + binding = binding->m_nextBinding; + } + + int index = coreIndex; + if (valueTypeIndex != -1) + index |= (valueTypeIndex << 24); + + if (binding && valueTypeIndex != -1 && binding->bindingType() == QDeclarativeAbstractBinding::ValueTypeProxy) + binding = static_cast(binding)->binding(index); + + if (binding) + binding->removeFromObject(); + + if (newBinding) + newBinding->addToObject(object, index); + + return binding; +} + +/*! + Returns the expression associated with this signal property, or 0 if no + signal expression exists. +*/ +QDeclarativeExpression * +QDeclarativePropertyPrivate::signalExpression(const QDeclarativeProperty &that) +{ + if (!(that.type() & QDeclarativeProperty::SignalProperty)) + return 0; + + const QObjectList &children = that.d->object->children(); + + for (int ii = 0; ii < children.count(); ++ii) { + QObject *child = children.at(ii); + + QDeclarativeBoundSignal *signal = QDeclarativeBoundSignal::cast(child); + if (signal && signal->index() == that.index()) + return signal->expression(); + } + + return 0; +} + +/*! + Set the signal expression associated with this signal property to \a expr. + Returns the existing signal expression (if any), otherwise 0. + + Ownership of \a expr transfers to QML. Ownership of the return value is + assumed by the caller. +*/ +QDeclarativeExpression * +QDeclarativePropertyPrivate::setSignalExpression(const QDeclarativeProperty &that, + QDeclarativeExpression *expr) +{ + if (!(that.type() & QDeclarativeProperty::SignalProperty)) { + delete expr; + return 0; + } + + const QObjectList &children = that.d->object->children(); + + for (int ii = 0; ii < children.count(); ++ii) { + QObject *child = children.at(ii); + + QDeclarativeBoundSignal *signal = QDeclarativeBoundSignal::cast(child); + if (signal && signal->index() == that.index()) + return signal->setExpression(expr); + } + + if (expr) { + QDeclarativeBoundSignal *signal = new QDeclarativeBoundSignal(that.d->object, that.method(), that.d->object); + return signal->setExpression(expr); + } else { + return 0; + } +} + +/*! + Returns the property value. +*/ +QVariant QDeclarativeProperty::read() const +{ + if (!d) + return QVariant(); + if (!d->object) + return QVariant(); + + if (type() & SignalProperty) { + + return QVariant(); + + } else if (type() & Property) { + + return d->readValueProperty(); + + } + return QVariant(); +} + +/*! +Return the \a name property value of \a object. This method is equivalent to: +\code + QDeclarativeProperty p(object, name); + p.read(); +\endcode +*/ +QVariant QDeclarativeProperty::read(QObject *object, const QString &name) +{ + QDeclarativeProperty p(object, name); + return p.read(); +} + +/*! + Return the \a name property value of \a object using the + \l{QDeclarativeContext} {context} \a ctxt. This method is + equivalent to: + + \code + QDeclarativeProperty p(object, name, context); + p.read(); + \endcode +*/ +QVariant QDeclarativeProperty::read(QObject *object, const QString &name, QDeclarativeContext *ctxt) +{ + QDeclarativeProperty p(object, name, ctxt); + return p.read(); +} + +/*! + + Return the \a name property value of \a object using the environment + for instantiating QML components that is provided by \a engine. . + This method is equivalent to: + + \code + QDeclarativeProperty p(object, name, engine); + p.read(); + \endcode +*/ +QVariant QDeclarativeProperty::read(QObject *object, const QString &name, QDeclarativeEngine *engine) +{ + QDeclarativeProperty p(object, name, engine); + return p.read(); +} + +QVariant QDeclarativePropertyPrivate::readValueProperty() +{ + if (isValueType()) { + + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(context); + QDeclarativeValueType *valueType = 0; + if (ep) valueType = ep->valueTypes[core.propType]; + else valueType = QDeclarativeValueTypeFactory::valueType(core.propType); + Q_ASSERT(valueType); + + valueType->read(object, core.coreIndex); + + QVariant rv = + valueType->metaObject()->property(this->valueType.valueTypeCoreIdx).read(valueType); + + if (!ep) delete valueType; + return rv; + + } else if (core.flags & QDeclarativePropertyCache::Data::IsQList) { + + QDeclarativeListProperty prop; + void *args[] = { &prop, 0 }; + QMetaObject::metacall(object, QMetaObject::ReadProperty, core.coreIndex, args); + return QVariant::fromValue(QDeclarativeListReferencePrivate::init(prop, core.propType, engine)); + + } else if (core.flags & QDeclarativePropertyCache::Data::IsQObjectDerived) { + + QObject *rv = 0; + void *args[] = { &rv, 0 }; + QMetaObject::metacall(object, QMetaObject::ReadProperty, core.coreIndex, args); + return QVariant::fromValue(rv); + + } else { + + return object->metaObject()->property(core.coreIndex).read(object.data()); + + } +} + +//writeEnumProperty MIRRORS the relelvant bit of QMetaProperty::write AND MUST BE KEPT IN SYNC! +bool QDeclarativePropertyPrivate::writeEnumProperty(const QMetaProperty &prop, int idx, QObject *object, const QVariant &value, int flags) +{ + if (!object || !prop.isWritable()) + return false; + + QVariant v = value; + if (prop.isEnumType()) { + QMetaEnum menum = prop.enumerator(); + if (v.userType() == QVariant::String +#ifdef QT3_SUPPORT + || v.userType() == QVariant::CString +#endif + ) { + if (prop.isFlagType()) + v = QVariant(menum.keysToValue(value.toByteArray())); + else + v = QVariant(menum.keyToValue(value.toByteArray())); + } else if (v.userType() != QVariant::Int && v.userType() != QVariant::UInt) { + int enumMetaTypeId = QMetaType::type(QByteArray(menum.scope() + QByteArray("::") + menum.name())); + if ((enumMetaTypeId == 0) || (v.userType() != enumMetaTypeId) || !v.constData()) + return false; + v = QVariant(*reinterpret_cast(v.constData())); + } + v.convert(QVariant::Int); + } + + // the status variable is changed by qt_metacall to indicate what it did + // this feature is currently only used by QtDBus and should not be depended + // upon. Don't change it without looking into QDBusAbstractInterface first + // -1 (unchanged): normal qt_metacall, result stored in argv[0] + // changed: result stored directly in value, return the value of status + int status = -1; + void *argv[] = { v.data(), &v, &status, &flags }; + QMetaObject::metacall(object, QMetaObject::WriteProperty, idx, argv); + return status; +} + +bool QDeclarativePropertyPrivate::writeValueProperty(const QVariant &value, WriteFlags flags) +{ + // Remove any existing bindings on this property + if (!(flags & DontRemoveBinding) && + (type() & QDeclarativeProperty::Property) && object) { + QDeclarativeAbstractBinding *binding = setBinding(object, core.coreIndex, + valueType.valueTypeCoreIdx, 0, flags); + if (binding) binding->destroy(); + } + + bool rv = false; + if (isValueType()) { + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(context); + + QDeclarativeValueType *writeBack = 0; + if (ep) { + writeBack = ep->valueTypes[core.propType]; + } else { + writeBack = QDeclarativeValueTypeFactory::valueType(core.propType); + } + + writeBack->read(object, core.coreIndex); + + QDeclarativePropertyCache::Data data = core; + data.flags = valueType.flags; + data.coreIndex = valueType.valueTypeCoreIdx; + data.propType = valueType.valueTypePropType; + rv = write(writeBack, data, value, context, flags); + + writeBack->write(object, core.coreIndex, flags); + if (!ep) delete writeBack; + + } else { + + rv = write(object, core, value, context, flags); + + } + + return rv; +} + +bool QDeclarativePropertyPrivate::write(QObject *object, const QDeclarativePropertyCache::Data &property, + const QVariant &value, QDeclarativeContextData *context, + WriteFlags flags) +{ + int coreIdx = property.coreIndex; + int status = -1; //for dbus + + if (property.flags & QDeclarativePropertyCache::Data::IsEnumType) { + QMetaProperty prop = object->metaObject()->property(property.coreIndex); + QVariant v = value; + // Enum values come through the script engine as doubles + if (value.userType() == QVariant::Double) { + double integral; + double fractional = modf(value.toDouble(), &integral); + if (qFuzzyIsNull(fractional)) + v.convert(QVariant::Int); + } + return writeEnumProperty(prop, coreIdx, object, v, flags); + } + + int propertyType = property.propType; + int variantType = value.userType(); + + QDeclarativeEnginePrivate *enginePriv = QDeclarativeEnginePrivate::get(context); + + if (propertyType == QVariant::Url) { + + QUrl u; + bool found = false; + if (variantType == QVariant::Url) { + u = value.toUrl(); + found = true; + } else if (variantType == QVariant::ByteArray) { + u = QUrl(QString::fromUtf8(value.toByteArray())); + found = true; + } else if (variantType == QVariant::String) { + u = QUrl(value.toString()); + found = true; + } + + if (!found) + return false; + + if (context && u.isRelative() && !u.isEmpty()) + u = context->resolvedUrl(u); + int status = -1; + void *argv[] = { &u, 0, &status, &flags }; + QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, argv); + + } else if (variantType == propertyType) { + + void *a[] = { (void *)value.constData(), 0, &status, &flags }; + QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, a); + + } else if (qMetaTypeId() == propertyType) { + + void *a[] = { (void *)&value, 0, &status, &flags }; + QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, a); + + } else if (property.flags & QDeclarativePropertyCache::Data::IsQObjectDerived) { + + const QMetaObject *valMo = rawMetaObjectForType(enginePriv, value.userType()); + + if (!valMo) + return false; + + QObject *o = *(QObject **)value.constData(); + const QMetaObject *propMo = rawMetaObjectForType(enginePriv, propertyType); + + if (o) valMo = o->metaObject(); + + if (canConvert(valMo, propMo)) { + void *args[] = { &o, 0, &status, &flags }; + QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, + args); + } else if (!o && canConvert(propMo, valMo)) { + // In the case of a null QObject, we assign the null if there is + // any change that the null variant type could be up or down cast to + // the property type. + void *args[] = { &o, 0, &status, &flags }; + QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, + args); + } else { + return false; + } + + } else if (property.flags & QDeclarativePropertyCache::Data::IsQList) { + + const QMetaObject *listType = 0; + if (enginePriv) { + listType = enginePriv->rawMetaObjectForType(enginePriv->listType(property.propType)); + } else { + QDeclarativeType *type = QDeclarativeMetaType::qmlType(QDeclarativeMetaType::listType(property.propType)); + if (!type) return false; + listType = type->baseMetaObject(); + } + if (!listType) return false; + + QDeclarativeListProperty prop; + void *args[] = { &prop, 0 }; + QMetaObject::metacall(object, QMetaObject::ReadProperty, coreIdx, args); + + if (!prop.clear) return false; + + prop.clear(&prop); + + if (value.userType() == qMetaTypeId >()) { + const QList &list = qvariant_cast >(value); + + for (int ii = 0; ii < list.count(); ++ii) { + QObject *o = list.at(ii); + if (o && !canConvert(o->metaObject(), listType)) + o = 0; + prop.append(&prop, (void *)o); + } + } else { + QObject *o = enginePriv?enginePriv->toQObject(value):QDeclarativeMetaType::toQObject(value); + if (o && !canConvert(o->metaObject(), listType)) + o = 0; + prop.append(&prop, (void *)o); + } + + } else { + Q_ASSERT(variantType != propertyType); + + bool ok = false; + QVariant v; + if (variantType == QVariant::String) + v = QDeclarativeStringConverters::variantFromString(value.toString(), propertyType, &ok); + if (!ok) { + v = value; + if (v.convert((QVariant::Type)propertyType)) { + ok = true; + } else if ((uint)propertyType >= QVariant::UserType && variantType == QVariant::String) { + QDeclarativeMetaType::StringConverter con = QDeclarativeMetaType::customStringConverter(propertyType); + if (con) { + v = con(value.toString()); + if (v.userType() == propertyType) + ok = true; + } + } + } + if (ok) { + void *a[] = { (void *)v.constData(), 0, &status, &flags}; + QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, a); + } else { + return false; + } + } + + return true; +} + +const QMetaObject *QDeclarativePropertyPrivate::rawMetaObjectForType(QDeclarativeEnginePrivate *engine, int userType) +{ + if (engine) { + return engine->rawMetaObjectForType(userType); + } else { + QDeclarativeType *type = QDeclarativeMetaType::qmlType(userType); + return type?type->baseMetaObject():0; + } +} + +/*! + Sets the property value to \a value and returns true. + Returns false if the property can't be set because the + \a value is the wrong type, for example. + */ +bool QDeclarativeProperty::write(const QVariant &value) const +{ + return QDeclarativePropertyPrivate::write(*this, value, 0); +} + +/*! + Writes \a value to the \a name property of \a object. This method + is equivalent to: + + \code + QDeclarativeProperty p(object, name); + p.write(value); + \endcode +*/ +bool QDeclarativeProperty::write(QObject *object, const QString &name, const QVariant &value) +{ + QDeclarativeProperty p(object, name); + return p.write(value); +} + +/*! + Writes \a value to the \a name property of \a object using the + \l{QDeclarativeContext} {context} \a ctxt. This method is + equivalent to: + + \code + QDeclarativeProperty p(object, name, ctxt); + p.write(value); + \endcode +*/ +bool QDeclarativeProperty::write(QObject *object, + const QString &name, + const QVariant &value, + QDeclarativeContext *ctxt) +{ + QDeclarativeProperty p(object, name, ctxt); + return p.write(value); +} + +/*! + + Writes \a value to the \a name property of \a object using the + environment for instantiating QML components that is provided by + \a engine. This method is equivalent to: + + \code + QDeclarativeProperty p(object, name, engine); + p.write(value); + \endcode +*/ +bool QDeclarativeProperty::write(QObject *object, const QString &name, const QVariant &value, + QDeclarativeEngine *engine) +{ + QDeclarativeProperty p(object, name, engine); + return p.write(value); +} + +/*! + Resets the property and returns true if the property is + resettable. If the property is not resettable, nothing happens + and false is returned. +*/ +bool QDeclarativeProperty::reset() const +{ + if (isResettable()) { + void *args[] = { 0 }; + QMetaObject::metacall(d->object, QMetaObject::ResetProperty, d->core.coreIndex, args); + return true; + } else { + return false; + } +} + +bool QDeclarativePropertyPrivate::write(const QDeclarativeProperty &that, + const QVariant &value, WriteFlags flags) +{ + if (!that.d) + return false; + if (that.d->object && that.type() & QDeclarativeProperty::Property && + that.d->core.isValid() && that.isWritable()) + return that.d->writeValueProperty(value, flags); + else + return false; +} + +/*! + Returns true if the property has a change notifier signal, otherwise false. +*/ +bool QDeclarativeProperty::hasNotifySignal() const +{ + if (type() & Property && d->object) { + return d->object->metaObject()->property(d->core.coreIndex).hasNotifySignal(); + } + return false; +} + +/*! + Returns true if the property needs a change notifier signal for bindings + to remain upto date, false otherwise. + + Some properties, such as attached properties or those whose value never + changes, do not require a change notifier. +*/ +bool QDeclarativeProperty::needsNotifySignal() const +{ + return type() & Property && !property().isConstant(); +} + +/*! + Connects the property's change notifier signal to the + specified \a method of the \a dest object and returns + true. Returns false if this metaproperty does not + represent a regular Qt property or if it has no + change notifier signal, or if the \a dest object does + not have the specified \a method. +*/ +bool QDeclarativeProperty::connectNotifySignal(QObject *dest, int method) const +{ + if (!(type() & Property) || !d->object) + return false; + + QMetaProperty prop = d->object->metaObject()->property(d->core.coreIndex); + if (prop.hasNotifySignal()) { + return QDeclarativePropertyPrivate::connect(d->object, prop.notifySignalIndex(), dest, method, Qt::DirectConnection); + } else { + return false; + } +} + +/*! + Connects the property's change notifier signal to the + specified \a slot of the \a dest object and returns + true. Returns false if this metaproperty does not + represent a regular Qt property or if it has no + change notifier signal, or if the \a dest object does + not have the specified \a slot. +*/ +bool QDeclarativeProperty::connectNotifySignal(QObject *dest, const char *slot) const +{ + if (!(type() & Property) || !d->object) + return false; + + QMetaProperty prop = d->object->metaObject()->property(d->core.coreIndex); + if (prop.hasNotifySignal()) { + QByteArray signal(QByteArray("2") + prop.notifySignal().signature()); + return QObject::connect(d->object, signal.constData(), dest, slot); + } else { + return false; + } +} + +/*! + Return the Qt metaobject index of the property. +*/ +int QDeclarativeProperty::index() const +{ + return d ? d->core.coreIndex : -1; +} + +int QDeclarativePropertyPrivate::valueTypeCoreIndex(const QDeclarativeProperty &that) +{ + return that.d ? that.d->valueType.valueTypeCoreIdx : -1; +} + +/*! + Returns the "property index" for use in bindings. The top 8 bits are the value type + offset, and 0 otherwise. The bottom 24-bits are the regular property index. +*/ +int QDeclarativePropertyPrivate::bindingIndex(const QDeclarativeProperty &that) +{ + if (!that.d) + return -1; + int rv = that.d->core.coreIndex; + if (rv != -1 && that.d->valueType.valueTypeCoreIdx != -1) + rv = rv | (that.d->valueType.valueTypeCoreIdx << 24); + return rv; +} + +struct SerializedData { + bool isValueType; + QDeclarativePropertyCache::Data core; +}; + +struct ValueTypeSerializedData : public SerializedData { + QDeclarativePropertyCache::ValueTypeData valueType; +}; + +QByteArray QDeclarativePropertyPrivate::saveValueType(const QMetaObject *metaObject, int index, + const QMetaObject *subObject, int subIndex) +{ + QMetaProperty prop = metaObject->property(index); + QMetaProperty subProp = subObject->property(subIndex); + + ValueTypeSerializedData sd; + memset(&sd, 0, sizeof(sd)); + sd.isValueType = true; + sd.core.load(metaObject->property(index)); + sd.valueType.flags = QDeclarativePropertyCache::Data::flagsForProperty(subProp); + sd.valueType.valueTypeCoreIdx = subIndex; + sd.valueType.valueTypePropType = subProp.userType(); + + QByteArray rv((const char *)&sd, sizeof(sd)); + + return rv; +} + +QByteArray QDeclarativePropertyPrivate::saveProperty(const QMetaObject *metaObject, int index) +{ + SerializedData sd; + memset(&sd, 0, sizeof(sd)); + sd.isValueType = false; + sd.core.load(metaObject->property(index)); + + QByteArray rv((const char *)&sd, sizeof(sd)); + return rv; +} + +QDeclarativeProperty +QDeclarativePropertyPrivate::restore(const QByteArray &data, QObject *object, QDeclarativeContextData *ctxt) +{ + QDeclarativeProperty prop; + + if (data.isEmpty()) + return prop; + + const SerializedData *sd = (const SerializedData *)data.constData(); + if (sd->isValueType) { + const ValueTypeSerializedData *vt = (const ValueTypeSerializedData *)sd; + return restore(vt->core, vt->valueType, object, ctxt); + } else { + QDeclarativePropertyCache::ValueTypeData data; + return restore(sd->core, data, object, ctxt); + } +} + +QDeclarativeProperty +QDeclarativePropertyPrivate::restore(const QDeclarativePropertyCache::Data &data, const QDeclarativePropertyCache::ValueTypeData &valueType, QObject *object, QDeclarativeContextData *ctxt) +{ + QDeclarativeProperty prop; + + prop.d = new QDeclarativePropertyPrivate; + prop.d->object = object; + prop.d->context = ctxt; + prop.d->engine = ctxt->engine; + + prop.d->core = data; + prop.d->valueType = valueType; + + return prop; +} + +/*! + Returns true if lhs and rhs refer to the same metaobject data +*/ +bool QDeclarativePropertyPrivate::equal(const QMetaObject *lhs, const QMetaObject *rhs) +{ + return lhs == rhs || (1 && lhs && rhs && lhs->d.stringdata == rhs->d.stringdata); +} + +/*! + Returns true if from inherits to. +*/ +bool QDeclarativePropertyPrivate::canConvert(const QMetaObject *from, const QMetaObject *to) +{ + if (from && to == &QObject::staticMetaObject) + return true; + + while (from) { + if (equal(from, to)) + return true; + from = from->superClass(); + } + + return false; +} + +/*! + Return the signal corresponding to \a name +*/ +QMetaMethod QDeclarativePropertyPrivate::findSignalByName(const QMetaObject *mo, const QByteArray &name) +{ + Q_ASSERT(mo); + int methods = mo->methodCount(); + for (int ii = methods - 1; ii >= 2; --ii) { // >= 2 to block the destroyed signal + QMetaMethod method = mo->method(ii); + QByteArray methodName = method.signature(); + int idx = methodName.indexOf('('); + methodName = methodName.left(idx); + + if (methodName == name) + return method; + } + + // If no signal is found, but the signal is of the form "onBlahChanged", + // return the notify signal for the property "Blah" + if (name.endsWith("Changed")) { + QByteArray propName = name.mid(0, name.length() - 7); + int propIdx = mo->indexOfProperty(propName.constData()); + if (propIdx >= 0) { + QMetaProperty prop = mo->property(propIdx); + if (prop.hasNotifySignal()) + return prop.notifySignal(); + } + } + + return QMetaMethod(); +} + +static inline int QMetaObject_methods(const QMetaObject *metaObject) +{ + struct Private + { + int revision; + int className; + int classInfoCount, classInfoData; + int methodCount, methodData; + int propertyCount, propertyData; + }; + + return reinterpret_cast(metaObject->d.data)->methodCount; +} + +static inline int QMetaObject_properties(const QMetaObject *metaObject) +{ + struct Private + { + int revision; + int className; + int classInfoCount, classInfoData; + int methodCount, methodData; + int propertyCount, propertyData; + }; + + return reinterpret_cast(metaObject->d.data)->propertyCount; +} + +static inline void flush_vme_signal(const QObject *object, int index) +{ + QDeclarativeData *data = static_cast(QObjectPrivate::get(const_cast(object))->declarativeData); + if (data && data->propertyCache) { + QDeclarativePropertyCache::Data *property = data->propertyCache->method(index); + + if (property && property->flags & QDeclarativePropertyCache::Data::IsVMESignal) { + const QMetaObject *metaObject = object->metaObject(); + int methodOffset = metaObject->methodOffset(); + + while (methodOffset > index) { + metaObject = metaObject->d.superdata; + methodOffset -= QMetaObject_methods(metaObject); + } + + QDeclarativeVMEMetaObject *vme = + static_cast(const_cast(metaObject)); + + vme->connectAliasSignal(index); + } + } +} + +/*! +Connect \a sender \a signal_index to \a receiver \a method_index with the specified +\a type and \a types. This behaves identically to QMetaObject::connect() except that +it connects any lazy "proxy" signal connections set up by QML. + +It is possible that this logic should be moved to QMetaObject::connect(). +*/ +bool QDeclarativePropertyPrivate::connect(const QObject *sender, int signal_index, + const QObject *receiver, int method_index, + int type, int *types) +{ + flush_vme_signal(sender, signal_index); + flush_vme_signal(receiver, method_index); + + return QMetaObject::connect(sender, signal_index, receiver, method_index, type, types); +} + +/*! +Return \a metaObject's [super] meta object that provides data for \a property. +*/ +const QMetaObject *QDeclarativePropertyPrivate::metaObjectForProperty(const QMetaObject *metaObject, int property) +{ + int propertyOffset = metaObject->propertyOffset(); + + while (propertyOffset > property) { + metaObject = metaObject->d.superdata; + propertyOffset -= QMetaObject_properties(metaObject); + } + + return metaObject; +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativeproperty.h b/src/declarative/qml/qdeclarativeproperty.h new file mode 100644 index 00000000..b58d7800 --- /dev/null +++ b/src/declarative/qml/qdeclarativeproperty.h @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPROPERTY_H +#define QDECLARATIVEPROPERTY_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QObject; +class QVariant; +class QDeclarativeContext; +class QDeclarativeEngine; + +class QDeclarativePropertyPrivate; +class Q_DECLARATIVE_EXPORT QDeclarativeProperty +{ +public: + enum PropertyTypeCategory { + InvalidCategory, + List, + Object, + Normal + }; + + enum Type { + Invalid, + Property, + SignalProperty + }; + + QDeclarativeProperty(); + ~QDeclarativeProperty(); + + QDeclarativeProperty(QObject *); + QDeclarativeProperty(QObject *, QDeclarativeContext *); + QDeclarativeProperty(QObject *, QDeclarativeEngine *); + + QDeclarativeProperty(QObject *, const QString &); + QDeclarativeProperty(QObject *, const QString &, QDeclarativeContext *); + QDeclarativeProperty(QObject *, const QString &, QDeclarativeEngine *); + + QDeclarativeProperty(const QDeclarativeProperty &); + QDeclarativeProperty &operator=(const QDeclarativeProperty &); + + bool operator==(const QDeclarativeProperty &) const; + + Type type() const; + bool isValid() const; + bool isProperty() const; + bool isSignalProperty() const; + + int propertyType() const; + PropertyTypeCategory propertyTypeCategory() const; + const char *propertyTypeName() const; + + QString name() const; + + QVariant read() const; + static QVariant read(QObject *, const QString &); + static QVariant read(QObject *, const QString &, QDeclarativeContext *); + static QVariant read(QObject *, const QString &, QDeclarativeEngine *); + + bool write(const QVariant &) const; + static bool write(QObject *, const QString &, const QVariant &); + static bool write(QObject *, const QString &, const QVariant &, QDeclarativeContext *); + static bool write(QObject *, const QString &, const QVariant &, QDeclarativeEngine *); + + bool reset() const; + + bool hasNotifySignal() const; + bool needsNotifySignal() const; + bool connectNotifySignal(QObject *dest, const char *slot) const; + bool connectNotifySignal(QObject *dest, int method) const; + + bool isWritable() const; + bool isDesignable() const; + bool isResettable() const; + QObject *object() const; + + int index() const; + QMetaProperty property() const; + QMetaMethod method() const; + +private: + friend class QDeclarativePropertyPrivate; + QDeclarativePropertyPrivate *d; +}; +typedef QList QDeclarativeProperties; + +inline uint qHash (const QDeclarativeProperty &key) +{ + return qHash(key.object()) + qHash(key.name()); +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEPROPERTY_H diff --git a/src/declarative/qml/qdeclarativeproperty_p.h b/src/declarative/qml/qdeclarativeproperty_p.h new file mode 100644 index 00000000..40b25ca2 --- /dev/null +++ b/src/declarative/qml/qdeclarativeproperty_p.h @@ -0,0 +1,147 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPROPERTY_P_H +#define QDECLARATIVEPROPERTY_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 "qdeclarativeproperty.h" + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeContext; +class QDeclarativeEnginePrivate; +class QDeclarativeExpression; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativePropertyPrivate : public QDeclarativeRefCount +{ +public: + enum WriteFlag { BypassInterceptor = 0x01, DontRemoveBinding = 0x02, RemoveBindingOnAliasWrite = 0x04 }; + Q_DECLARE_FLAGS(WriteFlags, WriteFlag) + + QDeclarativePropertyPrivate() + : context(0), engine(0), object(0), isNameCached(false) {} + + QDeclarativeContextData *context; + QDeclarativeEngine *engine; + QDeclarativeGuard object; + + bool isNameCached:1; + QDeclarativePropertyCache::Data core; + QString nameCache; + + // Describes the "virtual" value-type sub-property. + QDeclarativePropertyCache::ValueTypeData valueType; + + void initProperty(QObject *obj, const QString &name); + void initDefault(QObject *obj); + + bool isValueType() const; + int propertyType() const; + QDeclarativeProperty::Type type() const; + QDeclarativeProperty::PropertyTypeCategory propertyTypeCategory() const; + + QVariant readValueProperty(); + bool writeValueProperty(const QVariant &, WriteFlags); + + static const QMetaObject *rawMetaObjectForType(QDeclarativeEnginePrivate *, int); + static bool writeEnumProperty(const QMetaProperty &prop, int idx, QObject *object, + const QVariant &value, int flags); + static bool write(QObject *, const QDeclarativePropertyCache::Data &, const QVariant &, + QDeclarativeContextData *, WriteFlags flags = 0); + static void findAliasTarget(QObject *, int, QObject **, int *); + static QDeclarativeAbstractBinding *setBinding(QObject *, int coreIndex, int valueTypeIndex /* -1 */, + QDeclarativeAbstractBinding *, + WriteFlags flags = DontRemoveBinding); + static QDeclarativeAbstractBinding *setBindingNoEnable(QObject *, int coreIndex, int valueTypeIndex /* -1 */, + QDeclarativeAbstractBinding *); + static QDeclarativeAbstractBinding *binding(QObject *, int coreIndex, int valueTypeIndex /* -1 */); + + static QByteArray saveValueType(const QMetaObject *, int, + const QMetaObject *, int); + static QByteArray saveProperty(const QMetaObject *, int); + + static QDeclarativeProperty restore(const QByteArray &, QObject *, QDeclarativeContextData *); + static QDeclarativeProperty restore(const QDeclarativePropertyCache::Data &, + const QDeclarativePropertyCache::ValueTypeData &, + QObject *, + QDeclarativeContextData *); + + static bool equal(const QMetaObject *, const QMetaObject *); + static bool canConvert(const QMetaObject *from, const QMetaObject *to); + + // "Public" (to QML) methods + static QDeclarativeAbstractBinding *binding(const QDeclarativeProperty &that); + static QDeclarativeAbstractBinding *setBinding(const QDeclarativeProperty &that, + QDeclarativeAbstractBinding *, + WriteFlags flags = DontRemoveBinding); + static QDeclarativeExpression *signalExpression(const QDeclarativeProperty &that); + static QDeclarativeExpression *setSignalExpression(const QDeclarativeProperty &that, + QDeclarativeExpression *) ; + static bool write(const QDeclarativeProperty &that, const QVariant &, WriteFlags); + static int valueTypeCoreIndex(const QDeclarativeProperty &that); + static int bindingIndex(const QDeclarativeProperty &that); + static QMetaMethod findSignalByName(const QMetaObject *mo, const QByteArray &); + static bool connect(const QObject *sender, int signal_index, + const QObject *receiver, int method_index, + int type = 0, int *types = 0); + static const QMetaObject *metaObjectForProperty(const QMetaObject *, int); +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QDeclarativePropertyPrivate::WriteFlags) + +QT_END_NAMESPACE + +#endif // QDECLARATIVEPROPERTY_P_H diff --git a/src/declarative/qml/qdeclarativepropertycache.cpp b/src/declarative/qml/qdeclarativepropertycache.cpp new file mode 100644 index 00000000..7ce5ab9e --- /dev/null +++ b/src/declarative/qml/qdeclarativepropertycache.cpp @@ -0,0 +1,471 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativepropertycache_p.h" + +#include "private/qdeclarativeengine_p.h" +#include "private/qdeclarativebinding_p.h" +#include + +Q_DECLARE_METATYPE(QScriptValue) + +QT_BEGIN_NAMESPACE + +QDeclarativePropertyCache::Data::Flags QDeclarativePropertyCache::Data::flagsForProperty(const QMetaProperty &p, QDeclarativeEngine *engine) +{ + int propType = p.userType(); + + Flags flags; + + if (p.isConstant()) + flags |= Data::IsConstant; + if (p.isWritable()) + flags |= Data::IsWritable; + if (p.isResettable()) + flags |= Data::IsResettable; + + if (propType == qMetaTypeId()) { + flags |= Data::IsQmlBinding; + } else if (propType == qMetaTypeId()) { + flags |= Data::IsQScriptValue; + } else if (p.isEnumType()) { + flags |= Data::IsEnumType; + } else { + QDeclarativeMetaType::TypeCategory cat = engine ? QDeclarativeEnginePrivate::get(engine)->typeCategory(propType) + : QDeclarativeMetaType::typeCategory(propType); + if (cat == QDeclarativeMetaType::Object) + flags |= Data::IsQObjectDerived; + else if (cat == QDeclarativeMetaType::List) + flags |= Data::IsQList; + } + + return flags; +} + +void QDeclarativePropertyCache::Data::load(const QMetaProperty &p, QDeclarativeEngine *engine) +{ + propType = p.userType(); + if (QVariant::Type(propType) == QVariant::LastType) + propType = qMetaTypeId(); + coreIndex = p.propertyIndex(); + notifyIndex = p.notifySignalIndex(); + flags = flagsForProperty(p, engine); + revision = p.revision(); +} + +void QDeclarativePropertyCache::Data::load(const QMetaMethod &m) +{ + coreIndex = m.methodIndex(); + relatedIndex = -1; + flags |= Data::IsFunction; + if (m.methodType() == QMetaMethod::Signal) + flags |= Data::IsSignal; + propType = QVariant::Invalid; + + const char *returnType = m.typeName(); + if (returnType) + propType = QMetaType::type(returnType); + + QList params = m.parameterTypes(); + if (!params.isEmpty()) + flags |= Data::HasArguments; + revision = m.revision(); +} + + +/*! +Creates a new empty QDeclarativePropertyCache. +*/ +QDeclarativePropertyCache::QDeclarativePropertyCache(QDeclarativeEngine *e) +: QDeclarativeCleanup(e), engine(e) +{ + Q_ASSERT(engine); +} + +/*! +Creates a new QDeclarativePropertyCache of \a metaObject. +*/ +QDeclarativePropertyCache::QDeclarativePropertyCache(QDeclarativeEngine *e, const QMetaObject *metaObject) +: QDeclarativeCleanup(e), engine(e) +{ + Q_ASSERT(engine); + Q_ASSERT(metaObject); + + update(engine, metaObject); +} + +QDeclarativePropertyCache::~QDeclarativePropertyCache() +{ + clear(); +} + +void QDeclarativePropertyCache::clear() +{ + for (int ii = 0; ii < indexCache.count(); ++ii) { + if (indexCache.at(ii)) indexCache.at(ii)->release(); + } + + for (int ii = 0; ii < methodIndexCache.count(); ++ii) { + RData *data = methodIndexCache.at(ii); + if (data) data->release(); + } + + for (StringCache::ConstIterator iter = stringCache.begin(); + iter != stringCache.end(); ++iter) { + RData *data = (*iter); + data->release(); + } + + for (IdentifierCache::ConstIterator iter = identifierCache.begin(); + iter != identifierCache.end(); ++iter) { + RData *data = (*iter); + data->release(); + } + + indexCache.clear(); + methodIndexCache.clear(); + stringCache.clear(); + identifierCache.clear(); +} + +QDeclarativePropertyCache::Data QDeclarativePropertyCache::create(const QMetaObject *metaObject, + const QString &property) +{ + Q_ASSERT(metaObject); + + QDeclarativePropertyCache::Data rv; + { + const QMetaObject *cmo = metaObject; + while (cmo) { + int idx = metaObject->indexOfProperty(property.toUtf8()); + if (idx != -1) { + QMetaProperty p = metaObject->property(idx); + if (p.isScriptable()) { + rv.load(metaObject->property(idx)); + return rv; + } else { + while (cmo && cmo->propertyOffset() >= idx) + cmo = cmo->superClass(); + } + } else { + cmo = 0; + } + } + } + + int methodCount = metaObject->methodCount(); + for (int ii = methodCount - 1; ii >= 3; --ii) { // >=3 to block the destroyed signal and deleteLater() slot + QMetaMethod m = metaObject->method(ii); + if (m.access() == QMetaMethod::Private) + continue; + QString methodName = QString::fromUtf8(m.signature()); + + int parenIdx = methodName.indexOf(QLatin1Char('(')); + Q_ASSERT(parenIdx != -1); + QStringRef methodNameRef = methodName.leftRef(parenIdx); + + if (methodNameRef == property) { + rv.load(m); + return rv; + } + } + + return rv; +} + +QDeclarativePropertyCache *QDeclarativePropertyCache::copy() const +{ + QDeclarativePropertyCache *cache = new QDeclarativePropertyCache(engine); + cache->indexCache = indexCache; + cache->methodIndexCache = methodIndexCache; + cache->stringCache = stringCache; + cache->identifierCache = identifierCache; + cache->allowedRevisionCache = allowedRevisionCache; + + for (int ii = 0; ii < indexCache.count(); ++ii) { + if (indexCache.at(ii)) indexCache.at(ii)->addref(); + } + for (int ii = 0; ii < methodIndexCache.count(); ++ii) { + if (methodIndexCache.at(ii)) methodIndexCache.at(ii)->addref(); + } + for (StringCache::ConstIterator iter = stringCache.begin(); iter != stringCache.end(); ++iter) + (*iter)->addref(); + for (IdentifierCache::ConstIterator iter = identifierCache.begin(); iter != identifierCache.end(); ++iter) + (*iter)->addref(); + + return cache; +} + +void QDeclarativePropertyCache::append(QDeclarativeEngine *engine, const QMetaObject *metaObject, + Data::Flag propertyFlags, Data::Flag methodFlags, Data::Flag signalFlags) +{ + append(engine, metaObject, -1, propertyFlags, methodFlags, signalFlags); +} + +void QDeclarativePropertyCache::append(QDeclarativeEngine *engine, const QMetaObject *metaObject, + int revision, + Data::Flag propertyFlags, Data::Flag methodFlags, Data::Flag signalFlags) +{ + Q_UNUSED(revision); + + allowedRevisionCache.append(0); + + QDeclarativeEnginePrivate *enginePriv = QDeclarativeEnginePrivate::get(engine); + int methodCount = metaObject->methodCount(); + // 3 to block the destroyed signal and the deleteLater() slot + int methodOffset = qMax(3, metaObject->methodOffset()); + + methodIndexCache.resize(methodCount); + for (int ii = methodOffset; ii < methodCount; ++ii) { + QMetaMethod m = metaObject->method(ii); + if (m.access() == QMetaMethod::Private) + continue; + QString methodName = QString::fromUtf8(m.signature()); + + int parenIdx = methodName.indexOf(QLatin1Char('(')); + Q_ASSERT(parenIdx != -1); + methodName = methodName.left(parenIdx); + + RData *data = new RData; + data->identifier = enginePriv->objectClass->createPersistentIdentifier(methodName); + methodIndexCache[ii] = data; + + data->load(m); + if (m.methodType() == QMetaMethod::Slot || m.methodType() == QMetaMethod::Method) + data->flags |= methodFlags; + else if (m.methodType() == QMetaMethod::Signal) + data->flags |= signalFlags; + + data->metaObjectOffset = allowedRevisionCache.count() - 1; + + if (stringCache.contains(methodName)) { + RData *old = stringCache[methodName]; + // We only overload methods in the same class, exactly like C++ + if (old->flags & Data::IsFunction && old->coreIndex >= methodOffset) + data->relatedIndex = old->coreIndex; + data->overrideIndexIsProperty = !bool(old->flags & Data::IsFunction); + data->overrideIndex = old->coreIndex; + stringCache[methodName]->release(); + identifierCache[data->identifier.identifier]->release(); + } + + stringCache.insert(methodName, data); + identifierCache.insert(data->identifier.identifier, data); + data->addref(); + data->addref(); + } + + int propCount = metaObject->propertyCount(); + int propOffset = metaObject->propertyOffset(); + + indexCache.resize(propCount); + for (int ii = propOffset; ii < propCount; ++ii) { + QMetaProperty p = metaObject->property(ii); + if (!p.isScriptable()) + continue; + + QString propName = QString::fromUtf8(p.name()); + + RData *data = new RData; + data->identifier = enginePriv->objectClass->createPersistentIdentifier(propName); + indexCache[ii] = data; + + data->load(p, engine); + data->flags |= propertyFlags; + + data->metaObjectOffset = allowedRevisionCache.count() - 1; + + if (stringCache.contains(propName)) { + RData *old = stringCache[propName]; + data->overrideIndexIsProperty = !bool(old->flags & Data::IsFunction); + data->overrideIndex = old->coreIndex; + stringCache[propName]->release(); + identifierCache[data->identifier.identifier]->release(); + } + + stringCache.insert(propName, data); + identifierCache.insert(data->identifier.identifier, data); + data->addref(); + data->addref(); + } +} + +void QDeclarativePropertyCache::updateRecur(QDeclarativeEngine *engine, const QMetaObject *metaObject) +{ + if (!metaObject) + return; + + updateRecur(engine, metaObject->superClass()); + + append(engine, metaObject); +} + +void QDeclarativePropertyCache::update(QDeclarativeEngine *engine, const QMetaObject *metaObject) +{ + Q_ASSERT(engine); + Q_ASSERT(metaObject); + + clear(); + + // Optimization to prevent unnecessary reallocation of lists + indexCache.reserve(metaObject->propertyCount()); + methodIndexCache.reserve(metaObject->methodCount()); + + updateRecur(engine,metaObject); +} + +QDeclarativePropertyCache::Data * +QDeclarativePropertyCache::property(int index) const +{ + if (index < 0 || index >= indexCache.count()) + return 0; + + return indexCache.at(index); +} + +QDeclarativePropertyCache::Data * +QDeclarativePropertyCache::method(int index) const +{ + if (index < 0 || index >= methodIndexCache.count()) + return 0; + + return methodIndexCache.at(index); +} + +QDeclarativePropertyCache::Data * +QDeclarativePropertyCache::property(const QString &str) const +{ + return stringCache.value(str); +} + +QString QDeclarativePropertyCache::Data::name(QObject *object) +{ + if (!object) + return QString(); + + return name(object->metaObject()); +} + +QString QDeclarativePropertyCache::Data::name(const QMetaObject *metaObject) +{ + if (!metaObject || coreIndex == -1) + return QString(); + + if (flags & IsFunction) { + QMetaMethod m = metaObject->method(coreIndex); + + QString name = QString::fromUtf8(m.signature()); + int parenIdx = name.indexOf(QLatin1Char('(')); + if (parenIdx != -1) + name = name.left(parenIdx); + return name; + } else { + QMetaProperty p = metaObject->property(coreIndex); + return QString::fromUtf8(p.name()); + } +} + +QStringList QDeclarativePropertyCache::propertyNames() const +{ + return stringCache.keys(); +} + +QDeclarativePropertyCache::Data *QDeclarativePropertyCache::property(QDeclarativeEngine *engine, QObject *obj, + const QScriptDeclarativeClass::Identifier &name, Data &local) +{ + QDeclarativePropertyCache::Data *rv = 0; + + QDeclarativeEnginePrivate *enginePrivate = QDeclarativeEnginePrivate::get(engine); + + QDeclarativePropertyCache *cache = 0; + QDeclarativeData *ddata = QDeclarativeData::get(obj); + if (ddata && ddata->propertyCache && ddata->propertyCache->qmlEngine() == engine) + cache = ddata->propertyCache; + if (!cache) { + cache = enginePrivate->cache(obj); + if (cache && ddata && !ddata->propertyCache) { cache->addref(); ddata->propertyCache = cache; } + } + + if (cache) { + rv = cache->property(name); + } else { + local = QDeclarativePropertyCache::create(obj->metaObject(), enginePrivate->objectClass->toString(name)); + if (local.isValid()) + rv = &local; + } + + return rv; +} + +QDeclarativePropertyCache::Data *QDeclarativePropertyCache::property(QDeclarativeEngine *engine, QObject *obj, + const QString &name, Data &local) +{ + QDeclarativePropertyCache::Data *rv = 0; + + if (!engine) { + local = QDeclarativePropertyCache::create(obj->metaObject(), name); + if (local.isValid()) + rv = &local; + } else { + QDeclarativeEnginePrivate *enginePrivate = QDeclarativeEnginePrivate::get(engine); + + QDeclarativePropertyCache *cache = 0; + QDeclarativeData *ddata = QDeclarativeData::get(obj); + if (ddata && ddata->propertyCache && ddata->propertyCache->qmlEngine() == engine) + cache = ddata->propertyCache; + if (!cache) { + cache = enginePrivate->cache(obj); + if (cache && ddata && !ddata->propertyCache) { cache->addref(); ddata->propertyCache = cache; } + } + + if (cache) { + rv = cache->property(name); + } else { + local = QDeclarativePropertyCache::create(obj->metaObject(), name); + if (local.isValid()) + rv = &local; + } + } + + return rv; +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativepropertycache_p.h b/src/declarative/qml/qdeclarativepropertycache_p.h new file mode 100644 index 00000000..7ac69254 --- /dev/null +++ b/src/declarative/qml/qdeclarativepropertycache_p.h @@ -0,0 +1,240 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPROPERTYCACHE_P_H +#define QDECLARATIVEPROPERTYCACHE_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 "private/qdeclarativerefcount_p.h" +#include "private/qdeclarativecleanup_p.h" +#include "private/qdeclarativenotifier_p.h" + +#include + +#include +QT_BEGIN_NAMESPACE + +class QDeclarativeEngine; +class QMetaProperty; + +class Q_AUTOTEST_EXPORT QDeclarativePropertyCache : public QDeclarativeRefCount, public QDeclarativeCleanup +{ +public: + QDeclarativePropertyCache(QDeclarativeEngine *); + QDeclarativePropertyCache(QDeclarativeEngine *, const QMetaObject *); + virtual ~QDeclarativePropertyCache(); + + struct Data { + inline Data(); + inline bool operator==(const Data &); + + enum Flag { + NoFlags = 0x00000000, + + // Can apply to all properties, except IsFunction + IsConstant = 0x00000001, + IsWritable = 0x00000002, + IsResettable = 0x00000004, + IsAlias = 0x00000008, + + // These are mutualy exclusive + IsFunction = 0x00000010, + IsQObjectDerived = 0x00000020, + IsEnumType = 0x00000040, + IsQList = 0x00000080, + IsQmlBinding = 0x00000100, + IsQScriptValue = 0x00000200, + + // Apply only to IsFunctions + IsVMEFunction = 0x00000400, + HasArguments = 0x00000800, + IsSignal = 0x00001000, + IsVMESignal = 0x00002000 + }; + Q_DECLARE_FLAGS(Flags, Flag) + + bool isValid() const { return coreIndex != -1; } + + Flags flags; + int propType; + int coreIndex; + union { + int notifyIndex; // When !IsFunction + int relatedIndex; // When IsFunction + }; + uint overrideIndexIsProperty : 1; + signed int overrideIndex : 31; + int revision; + int metaObjectOffset; + + static Flags flagsForProperty(const QMetaProperty &, QDeclarativeEngine *engine = 0); + void load(const QMetaProperty &, QDeclarativeEngine *engine = 0); + void load(const QMetaMethod &); + QString name(QObject *); + QString name(const QMetaObject *); + }; + + struct ValueTypeData { + inline ValueTypeData(); + inline bool operator==(const ValueTypeData &); + Data::Flags flags; // flags of the access property on the value type proxy object + int valueTypeCoreIdx; // The prop index of the access property on the value type proxy object + int valueTypePropType; // The QVariant::Type of access property on the value type proxy object + }; + + void update(QDeclarativeEngine *, const QMetaObject *); + + QDeclarativePropertyCache *copy() const; + void append(QDeclarativeEngine *, const QMetaObject *, Data::Flag propertyFlags = Data::NoFlags, + Data::Flag methodFlags = Data::NoFlags, Data::Flag signalFlags = Data::NoFlags); + void append(QDeclarativeEngine *, const QMetaObject *, int revision, Data::Flag propertyFlags = Data::NoFlags, + Data::Flag methodFlags = Data::NoFlags, Data::Flag signalFlags = Data::NoFlags); + + static Data create(const QMetaObject *, const QString &); + + inline Data *property(const QScriptDeclarativeClass::Identifier &id) const; + Data *property(const QString &) const; + Data *property(int) const; + Data *method(int) const; + QStringList propertyNames() const; + + inline Data *overrideData(Data *) const; + inline bool isAllowedInRevision(Data *) const; + + inline QDeclarativeEngine *qmlEngine() const; + static Data *property(QDeclarativeEngine *, QObject *, const QScriptDeclarativeClass::Identifier &, Data &); + static Data *property(QDeclarativeEngine *, QObject *, const QString &, Data &); + +protected: + virtual void clear(); + +private: + friend class QDeclarativeEnginePrivate; + + struct RData : public Data, public QDeclarativeRefCount { + QScriptDeclarativeClass::PersistentIdentifier identifier; + }; + + typedef QVector IndexCache; + typedef QHash StringCache; + typedef QHash IdentifierCache; + typedef QVector AllowedRevisionCache; + + void updateRecur(QDeclarativeEngine *, const QMetaObject *); + + QDeclarativeEngine *engine; + IndexCache indexCache; + IndexCache methodIndexCache; + StringCache stringCache; + IdentifierCache identifierCache; + AllowedRevisionCache allowedRevisionCache; +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(QDeclarativePropertyCache::Data::Flags); + +QDeclarativePropertyCache::Data::Data() +: flags(0), propType(0), coreIndex(-1), notifyIndex(-1), overrideIndexIsProperty(false), overrideIndex(-1), + revision(0), metaObjectOffset(-1) +{ +} + +bool QDeclarativePropertyCache::Data::operator==(const QDeclarativePropertyCache::Data &other) +{ + return flags == other.flags && + propType == other.propType && + coreIndex == other.coreIndex && + notifyIndex == other.notifyIndex && + revision == other.revision; +} + +QDeclarativePropertyCache::Data * +QDeclarativePropertyCache::overrideData(Data *data) const +{ + if (data->overrideIndex < 0) + return 0; + + if (data->overrideIndexIsProperty) + return indexCache.at(data->overrideIndex); + else + return methodIndexCache.at(data->overrideIndex); +} + +QDeclarativePropertyCache::Data * +QDeclarativePropertyCache::property(const QScriptDeclarativeClass::Identifier &id) const +{ + return identifierCache.value(id); +} + +QDeclarativePropertyCache::ValueTypeData::ValueTypeData() +: flags(QDeclarativePropertyCache::Data::NoFlags), valueTypeCoreIdx(-1), valueTypePropType(0) +{ +} + +bool QDeclarativePropertyCache::ValueTypeData::operator==(const ValueTypeData &o) +{ + return flags == o.flags && + valueTypeCoreIdx == o.valueTypeCoreIdx && + valueTypePropType == o.valueTypePropType; +} + +bool QDeclarativePropertyCache::isAllowedInRevision(Data *data) const +{ + return (data->metaObjectOffset == -1 && data->revision == 0) || + (allowedRevisionCache[data->metaObjectOffset] >= data->revision); +} + +QDeclarativeEngine *QDeclarativePropertyCache::qmlEngine() const +{ + return engine; +} + +QT_END_NAMESPACE + +#endif // QDECLARATIVEPROPERTYCACHE_P_H diff --git a/src/declarative/qml/qdeclarativepropertyvalueinterceptor.cpp b/src/declarative/qml/qdeclarativepropertyvalueinterceptor.cpp new file mode 100644 index 00000000..d0c920ee --- /dev/null +++ b/src/declarative/qml/qdeclarativepropertyvalueinterceptor.cpp @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativepropertyvalueinterceptor.h" + +#include "qdeclarative.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QDeclarativePropertyValueInterceptor + \brief The QDeclarativePropertyValueInterceptor class is inherited by property interceptors such as Behavior. + \internal + + This class intercepts property writes, allowing for custom handling. For example, Behavior uses this + interception to provide a default animation for all changes to a property's value. + */ + +/*! + Constructs a QDeclarativePropertyValueInterceptor. +*/ +QDeclarativePropertyValueInterceptor::QDeclarativePropertyValueInterceptor() +{ +} + +QDeclarativePropertyValueInterceptor::~QDeclarativePropertyValueInterceptor() +{ +} + +/*! + \fn void QDeclarativePropertyValueInterceptor::setTarget(const QDeclarativeProperty &property) + Set the target \a property for the value interceptor. This method will + be called by the QML engine when assigning a value interceptor. +*/ + +/*! + \fn void QDeclarativePropertyValueInterceptor::write(const QVariant &value) + This method will be called when a new \a value is assigned to the property being intercepted. +*/ + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativepropertyvalueinterceptor.h b/src/declarative/qml/qdeclarativepropertyvalueinterceptor.h new file mode 100644 index 00000000..49d214b2 --- /dev/null +++ b/src/declarative/qml/qdeclarativepropertyvalueinterceptor.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPROPERTYVALUEINTERCEPTOR_H +#define QDECLARATIVEPROPERTYVALUEINTERCEPTOR_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeProperty; +class Q_DECLARATIVE_EXPORT QDeclarativePropertyValueInterceptor +{ +public: + QDeclarativePropertyValueInterceptor(); + virtual ~QDeclarativePropertyValueInterceptor(); + virtual void setTarget(const QDeclarativeProperty &property) = 0; + virtual void write(const QVariant &value) = 0; +}; +Q_DECLARE_INTERFACE(QDeclarativePropertyValueInterceptor, "com.trolltech.qml.QDeclarativePropertyValueInterceptor") + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEPROPERTYVALUEINTERCEPTOR_H diff --git a/src/declarative/qml/qdeclarativepropertyvaluesource.cpp b/src/declarative/qml/qdeclarativepropertyvaluesource.cpp new file mode 100644 index 00000000..ee6269d0 --- /dev/null +++ b/src/declarative/qml/qdeclarativepropertyvaluesource.cpp @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativepropertyvaluesource.h" + +#include "qdeclarative.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QDeclarativePropertyValueSource + \brief The QDeclarativePropertyValueSource class is an interface for property value sources such as animations and bindings. + + See \l{Property Value Sources} for information on writing custom property + value sources. + */ + +/*! + Constructs a QDeclarativePropertyValueSource. +*/ +QDeclarativePropertyValueSource::QDeclarativePropertyValueSource() +{ +} + +/*! + Destroys the value source. +*/ +QDeclarativePropertyValueSource::~QDeclarativePropertyValueSource() +{ +} + +/*! + \fn void QDeclarativePropertyValueSource::setTarget(const QDeclarativeProperty &property) + Set the target \a property for the value source. This method will + be called by the QML engine when assigning a value source. +*/ + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativepropertyvaluesource.h b/src/declarative/qml/qdeclarativepropertyvaluesource.h new file mode 100644 index 00000000..deb01912 --- /dev/null +++ b/src/declarative/qml/qdeclarativepropertyvaluesource.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPROPERTYVALUESOURCE_H +#define QDECLARATIVEPROPERTYVALUESOURCE_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeProperty; +class Q_DECLARATIVE_EXPORT QDeclarativePropertyValueSource +{ +public: + QDeclarativePropertyValueSource(); + virtual ~QDeclarativePropertyValueSource(); + virtual void setTarget(const QDeclarativeProperty &) = 0; +}; +Q_DECLARE_INTERFACE(QDeclarativePropertyValueSource, "com.trolltech.qml.QDeclarativePropertyValueSource") + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEPROPERTYVALUESOURCE_H diff --git a/src/declarative/qml/qdeclarativeproxymetaobject.cpp b/src/declarative/qml/qdeclarativeproxymetaobject.cpp new file mode 100644 index 00000000..ca2f3aeb --- /dev/null +++ b/src/declarative/qml/qdeclarativeproxymetaobject.cpp @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativeproxymetaobject_p.h" +#include "private/qdeclarativeproperty_p.h" + +QT_BEGIN_NAMESPACE + +QDeclarativeProxyMetaObject::QDeclarativeProxyMetaObject(QObject *obj, QList *mList) +: metaObjects(mList), proxies(0), parent(0), object(obj) +{ + *static_cast(this) = *metaObjects->first().metaObject; + + QObjectPrivate *op = QObjectPrivate::get(obj); + if (op->metaObject) + parent = static_cast(op->metaObject); + + op->metaObject = this; +} + +QDeclarativeProxyMetaObject::~QDeclarativeProxyMetaObject() +{ + if (parent) + delete parent; + parent = 0; + + if (proxies) + delete [] proxies; + proxies = 0; +} + +int QDeclarativeProxyMetaObject::metaCall(QMetaObject::Call c, int id, void **a) +{ + if ((c == QMetaObject::ReadProperty || + c == QMetaObject::WriteProperty) && + id >= metaObjects->last().propertyOffset) { + + for (int ii = 0; ii < metaObjects->count(); ++ii) { + const ProxyData &data = metaObjects->at(ii); + if (id >= data.propertyOffset) { + if (!proxies) { + proxies = new QObject*[metaObjects->count()]; + ::memset(proxies, 0, + sizeof(QObject *) * metaObjects->count()); + } + + if (!proxies[ii]) { + QObject *proxy = data.createFunc(object); + const QMetaObject *metaObject = proxy->metaObject(); + proxies[ii] = proxy; + + int localOffset = data.metaObject->methodOffset(); + int methodOffset = metaObject->methodOffset(); + int methods = metaObject->methodCount() - methodOffset; + + // ### - Can this be done more optimally? + for (int jj = 0; jj < methods; ++jj) { + QMetaMethod method = + metaObject->method(jj + methodOffset); + if (method.methodType() == QMetaMethod::Signal) + QDeclarativePropertyPrivate::connect(proxy, methodOffset + jj, object, localOffset + jj); + } + } + + int proxyOffset = proxies[ii]->metaObject()->propertyOffset(); + int proxyId = id - data.propertyOffset + proxyOffset; + + return proxies[ii]->qt_metacall(c, proxyId, a); + } + } + } else if (c == QMetaObject::InvokeMetaMethod && + id >= metaObjects->last().methodOffset) { + QMetaMethod m = object->metaObject()->method(id); + if (m.methodType() == QMetaMethod::Signal) { + QMetaObject::activate(object, id, a); + return -1; + } + } + + if (parent) + return parent->metaCall(c, id, a); + else + return object->qt_metacall(c, id, a); +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativeproxymetaobject_p.h b/src/declarative/qml/qdeclarativeproxymetaobject_p.h new file mode 100644 index 00000000..19a9365e --- /dev/null +++ b/src/declarative/qml/qdeclarativeproxymetaobject_p.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPROXYMETAOBJECT_P_H +#define QDECLARATIVEPROXYMETAOBJECT_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 "private/qmetaobjectbuilder_p.h" +#include "qdeclarative.h" + +#include +#include + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeProxyMetaObject : public QAbstractDynamicMetaObject +{ +public: + struct ProxyData { + typedef QObject *(*CreateFunc)(QObject *); + QMetaObject *metaObject; + CreateFunc createFunc; + int propertyOffset; + int methodOffset; + }; + + QDeclarativeProxyMetaObject(QObject *, QList *); + virtual ~QDeclarativeProxyMetaObject(); + +protected: + virtual int metaCall(QMetaObject::Call _c, int _id, void **_a); + +private: + QList *metaObjects; + QObject **proxies; + + QAbstractDynamicMetaObject *parent; + QObject *object; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEPROXYMETAOBJECT_P_H + diff --git a/src/declarative/qml/qdeclarativerefcount.cpp b/src/declarative/qml/qdeclarativerefcount.cpp new file mode 100644 index 00000000..8fbc0bab --- /dev/null +++ b/src/declarative/qml/qdeclarativerefcount.cpp @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativerefcount_p.h" + +QT_BEGIN_NAMESPACE + +QDeclarativeRefCount::QDeclarativeRefCount() +: refCount(1) +{ +} + +QDeclarativeRefCount::~QDeclarativeRefCount() +{ +} + +void QDeclarativeRefCount::addref() +{ + Q_ASSERT(refCount > 0); + ++refCount; +} + +void QDeclarativeRefCount::release() +{ + Q_ASSERT(refCount > 0); + --refCount; + if (refCount == 0) + delete this; +} + +QT_END_NAMESPACE + diff --git a/src/declarative/qml/qdeclarativerefcount_p.h b/src/declarative/qml/qdeclarativerefcount_p.h new file mode 100644 index 00000000..ca09c337 --- /dev/null +++ b/src/declarative/qml/qdeclarativerefcount_p.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEREFCOUNT_P_H +#define QDECLARATIVEREFCOUNT_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 + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class Q_DECLARATIVE_EXPORT QDeclarativeRefCount +{ +public: + QDeclarativeRefCount(); + virtual ~QDeclarativeRefCount(); + void addref(); + void release(); + +private: + int refCount; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEREFCOUNT_P_H diff --git a/src/declarative/qml/qdeclarativerewrite.cpp b/src/declarative/qml/qdeclarativerewrite.cpp new file mode 100644 index 00000000..ef104115 --- /dev/null +++ b/src/declarative/qml/qdeclarativerewrite.cpp @@ -0,0 +1,273 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativerewrite_p.h" + +#include "private/qdeclarativeglobal_p.h" + +#include + +QT_BEGIN_NAMESPACE + +DEFINE_BOOL_CONFIG_OPTION(rewriteDump, QML_REWRITE_DUMP); + +namespace QDeclarativeRewrite { + +bool SharedBindingTester::isSharable(const QString &code) +{ + Engine engine; + NodePool pool(QString(), &engine); + Lexer lexer(&engine); + Parser parser(&engine); + lexer.setCode(code, 0); + parser.parseStatement(); + if (!parser.statement()) + return false; + + return isSharable(parser.statement()); +} + +bool SharedBindingTester::isSharable(AST::Node *node) +{ + _sharable = true; + AST::Node::acceptChild(node, this); + return _sharable; +} + +QString RewriteBinding::operator()(const QString &code, bool *ok, bool *sharable) +{ + Engine engine; + NodePool pool(QString(), &engine); + Lexer lexer(&engine); + Parser parser(&engine); + lexer.setCode(code, 0); + parser.parseStatement(); + if (!parser.statement()) { + if (ok) *ok = false; + return QString(); + } else { + if (ok) *ok = true; + if (sharable) { + SharedBindingTester tester; + *sharable = tester.isSharable(parser.statement()); + } + } + return rewrite(code, 0, parser.statement()); +} + +QString RewriteBinding::operator()(QDeclarativeJS::AST::Node *node, const QString &code, bool *sharable) +{ + if (!node) + return code; + + if (sharable) { + SharedBindingTester tester; + *sharable = tester.isSharable(node); + } + + QDeclarativeJS::AST::ExpressionNode *expression = node->expressionCast(); + QDeclarativeJS::AST::Statement *statement = node->statementCast(); + if(!expression && !statement) + return code; + + TextWriter w; + _writer = &w; + _position = expression ? expression->firstSourceLocation().begin() : statement->firstSourceLocation().begin(); + _inLoop = 0; + + accept(node); + + unsigned startOfStatement = 0; + unsigned endOfStatement = (expression ? expression->lastSourceLocation().end() : statement->lastSourceLocation().end()) - _position; + + QString startString = QLatin1String("(function ") + QString::fromUtf8(_name) + QLatin1String("() { "); + if (expression) + startString += QLatin1String("return "); + _writer->replace(startOfStatement, 0, startString); + _writer->replace(endOfStatement, 0, QLatin1String(" })")); + + if (rewriteDump()) { + qWarning() << "============================================================="; + qWarning() << "Rewrote:"; + qWarning() << qPrintable(code); + } + + QString codeCopy = code; + w.write(&codeCopy); + + if (rewriteDump()) { + qWarning() << "To:"; + qWarning() << qPrintable(code); + qWarning() << "============================================================="; + } + + return codeCopy; +} + +void RewriteBinding::accept(AST::Node *node) +{ + AST::Node::acceptChild(node, this); +} + +QString RewriteBinding::rewrite(QString code, unsigned position, + AST::Statement *node) +{ + TextWriter w; + _writer = &w; + _position = position; + _inLoop = 0; + + accept(node); + + unsigned startOfStatement = node->firstSourceLocation().begin() - _position; + unsigned endOfStatement = node->lastSourceLocation().end() - _position; + + _writer->replace(startOfStatement, 0, QLatin1String("(function ") + QString::fromUtf8(_name) + QLatin1String("() { ")); + _writer->replace(endOfStatement, 0, QLatin1String(" })")); + + if (rewriteDump()) { + qWarning() << "============================================================="; + qWarning() << "Rewrote:"; + qWarning() << qPrintable(code); + } + + w.write(&code); + + if (rewriteDump()) { + qWarning() << "To:"; + qWarning() << qPrintable(code); + qWarning() << "============================================================="; + } + + return code; +} + +bool RewriteBinding::visit(AST::Block *ast) +{ + for (AST::StatementList *it = ast->statements; it; it = it->next) { + if (! it->next) { + // we need to rewrite only the last statement of a block. + accept(it->statement); + } + } + + return false; +} + +bool RewriteBinding::visit(AST::ExpressionStatement *ast) +{ + if (! _inLoop) { + unsigned startOfExpressionStatement = ast->firstSourceLocation().begin() - _position; + _writer->replace(startOfExpressionStatement, 0, QLatin1String("return ")); + } + + return false; +} + +bool RewriteBinding::visit(AST::DoWhileStatement *) +{ + ++_inLoop; + return true; +} + +void RewriteBinding::endVisit(AST::DoWhileStatement *) +{ + --_inLoop; +} + +bool RewriteBinding::visit(AST::WhileStatement *) +{ + ++_inLoop; + return true; +} + +void RewriteBinding::endVisit(AST::WhileStatement *) +{ + --_inLoop; +} + +bool RewriteBinding::visit(AST::ForStatement *) +{ + ++_inLoop; + return true; +} + +void RewriteBinding::endVisit(AST::ForStatement *) +{ + --_inLoop; +} + +bool RewriteBinding::visit(AST::LocalForStatement *) +{ + ++_inLoop; + return true; +} + +void RewriteBinding::endVisit(AST::LocalForStatement *) +{ + --_inLoop; +} + +bool RewriteBinding::visit(AST::ForEachStatement *) +{ + ++_inLoop; + return true; +} + +void RewriteBinding::endVisit(AST::ForEachStatement *) +{ + --_inLoop; +} + +bool RewriteBinding::visit(AST::LocalForEachStatement *) +{ + ++_inLoop; + return true; +} + +void RewriteBinding::endVisit(AST::LocalForEachStatement *) +{ + --_inLoop; +} + +} // namespace QDeclarativeRewrite + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativerewrite_p.h b/src/declarative/qml/qdeclarativerewrite_p.h new file mode 100644 index 00000000..76efb584 --- /dev/null +++ b/src/declarative/qml/qdeclarativerewrite_p.h @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEREWRITE_P_H +#define QDECLARATIVEREWRITE_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 "rewriter/textwriter_p.h" +#include "parser/qdeclarativejslexer_p.h" +#include "parser/qdeclarativejsparser_p.h" +#include "parser/qdeclarativejsnodepool_p.h" + +QT_BEGIN_NAMESPACE + +namespace QDeclarativeRewrite { +using namespace QDeclarativeJS; + +class SharedBindingTester : protected AST::Visitor +{ + bool _sharable; +public: + bool isSharable(const QString &code); + bool isSharable(AST::Node *Node); + + virtual bool visit(AST::FunctionDeclaration *) { _sharable = false; return false; } + virtual bool visit(AST::FunctionExpression *) { _sharable = false; return false; } + virtual bool visit(AST::CallExpression *) { _sharable = false; return false; } +}; + +class RewriteBinding: protected AST::Visitor +{ + unsigned _position; + TextWriter *_writer; + QByteArray _name; + +public: + QString operator()(const QString &code, bool *ok = 0, bool *sharable = 0); + QString operator()(QDeclarativeJS::AST::Node *node, const QString &code, bool *sharable = 0); + + //name of the function: used for the debugger + void setName(const QByteArray &name) { _name = name; } + +protected: + using AST::Visitor::visit; + + void accept(AST::Node *node); + QString rewrite(QString code, unsigned position, AST::Statement *node); + + virtual bool visit(AST::Block *ast); + virtual bool visit(AST::ExpressionStatement *ast); + + virtual bool visit(AST::DoWhileStatement *ast); + virtual void endVisit(AST::DoWhileStatement *ast); + + virtual bool visit(AST::WhileStatement *ast); + virtual void endVisit(AST::WhileStatement *ast); + + virtual bool visit(AST::ForStatement *ast); + virtual void endVisit(AST::ForStatement *ast); + + virtual bool visit(AST::LocalForStatement *ast); + virtual void endVisit(AST::LocalForStatement *ast); + + virtual bool visit(AST::ForEachStatement *ast); + virtual void endVisit(AST::ForEachStatement *ast); + + virtual bool visit(AST::LocalForEachStatement *ast); + virtual void endVisit(AST::LocalForEachStatement *ast); + +private: + int _inLoop; +}; + +} // namespace QDeclarativeRewrite + +QT_END_NAMESPACE + +#endif // QDECLARATIVEREWRITE_P_H + diff --git a/src/declarative/qml/qdeclarativescriptparser.cpp b/src/declarative/qml/qdeclarativescriptparser.cpp new file mode 100644 index 00000000..373842cd --- /dev/null +++ b/src/declarative/qml/qdeclarativescriptparser.cpp @@ -0,0 +1,1206 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativescriptparser_p.h" + +#include "private/qdeclarativeparser_p.h" +#include "parser/qdeclarativejsengine_p.h" +#include "parser/qdeclarativejsparser_p.h" +#include "parser/qdeclarativejslexer_p.h" +#include "parser/qdeclarativejsnodepool_p.h" +#include "parser/qdeclarativejsastvisitor_p.h" +#include "parser/qdeclarativejsast_p.h" +#include "private/qdeclarativerewrite_p.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +using namespace QDeclarativeJS; +using namespace QDeclarativeParser; + +namespace { + +class ProcessAST: protected AST::Visitor +{ + struct State { + State() : object(0), property(0) {} + State(QDeclarativeParser::Object *o) : object(o), property(0) {} + State(QDeclarativeParser::Object *o, Property *p) : object(o), property(p) {} + + QDeclarativeParser::Object *object; + Property *property; + }; + + struct StateStack : public QStack + { + void pushObject(QDeclarativeParser::Object *obj) + { + push(State(obj)); + } + + void pushProperty(const QString &name, const LocationSpan &location) + { + const State &state = top(); + if (state.property) { + State s(state.property->getValue(location), + state.property->getValue(location)->getProperty(name.toUtf8())); + s.property->location = location; + push(s); + } else { + State s(state.object, + state.object->getProperty(name.toUtf8())); + + s.property->location = location; + push(s); + } + } + }; + +public: + ProcessAST(QDeclarativeScriptParser *parser); + virtual ~ProcessAST(); + + void operator()(const QString &code, AST::Node *node); + +protected: + + QDeclarativeParser::Object *defineObjectBinding(AST::UiQualifiedId *propertyName, bool onAssignment, + const QString &objectType, + AST::SourceLocation typeLocation, + LocationSpan location, + AST::UiObjectInitializer *initializer = 0); + + QDeclarativeParser::Variant getVariant(AST::ExpressionNode *expr); + + LocationSpan location(AST::SourceLocation start, AST::SourceLocation end); + LocationSpan location(AST::UiQualifiedId *); + + using AST::Visitor::visit; + using AST::Visitor::endVisit; + + virtual bool visit(AST::UiProgram *node); + virtual bool visit(AST::UiImport *node); + virtual bool visit(AST::UiObjectDefinition *node); + virtual bool visit(AST::UiPublicMember *node); + virtual bool visit(AST::UiObjectBinding *node); + + virtual bool visit(AST::UiScriptBinding *node); + virtual bool visit(AST::UiArrayBinding *node); + virtual bool visit(AST::UiSourceElement *node); + + void accept(AST::Node *node); + + QString asString(AST::UiQualifiedId *node) const; + + const State state() const; + QDeclarativeParser::Object *currentObject() const; + Property *currentProperty() const; + + QString qualifiedNameId() const; + + QString textAt(const AST::SourceLocation &loc) const + { return _contents.mid(loc.offset, loc.length); } + + + QString textAt(const AST::SourceLocation &first, + const AST::SourceLocation &last) const + { return _contents.mid(first.offset, last.offset + last.length - first.offset); } + + QString asString(AST::ExpressionNode *expr) + { + if (! expr) + return QString(); + + return textAt(expr->firstSourceLocation(), expr->lastSourceLocation()); + } + + QString asString(AST::Statement *stmt) + { + if (! stmt) + return QString(); + + QString s = textAt(stmt->firstSourceLocation(), stmt->lastSourceLocation()); + s += QLatin1Char('\n'); + return s; + } + +private: + QDeclarativeScriptParser *_parser; + StateStack _stateStack; + QStringList _scope; + QString _contents; +}; + +ProcessAST::ProcessAST(QDeclarativeScriptParser *parser) + : _parser(parser) +{ +} + +ProcessAST::~ProcessAST() +{ +} + +void ProcessAST::operator()(const QString &code, AST::Node *node) +{ + _contents = code; + accept(node); +} + +void ProcessAST::accept(AST::Node *node) +{ + AST::Node::acceptChild(node, this); +} + +const ProcessAST::State ProcessAST::state() const +{ + if (_stateStack.isEmpty()) + return State(); + + return _stateStack.back(); +} + +QDeclarativeParser::Object *ProcessAST::currentObject() const +{ + return state().object; +} + +Property *ProcessAST::currentProperty() const +{ + return state().property; +} + +QString ProcessAST::qualifiedNameId() const +{ + return _scope.join(QLatin1String("/")); +} + +QString ProcessAST::asString(AST::UiQualifiedId *node) const +{ + QString s; + + for (AST::UiQualifiedId *it = node; it; it = it->next) { + s.append(it->name->asString()); + + if (it->next) + s.append(QLatin1Char('.')); + } + + return s; +} + +QDeclarativeParser::Object * +ProcessAST::defineObjectBinding(AST::UiQualifiedId *propertyName, + bool onAssignment, + const QString &objectType, + AST::SourceLocation typeLocation, + LocationSpan location, + AST::UiObjectInitializer *initializer) +{ + int lastTypeDot = objectType.lastIndexOf(QLatin1Char('.')); + bool isType = !objectType.isEmpty() && + (objectType.at(0).isUpper() || + (lastTypeDot >= 0 && objectType.at(lastTypeDot+1).isUpper())); + + int propertyCount = 0; + for (AST::UiQualifiedId *name = propertyName; name; name = name->next){ + ++propertyCount; + _stateStack.pushProperty(name->name->asString(), + this->location(name)); + } + + if (!onAssignment && propertyCount && currentProperty() && currentProperty()->values.count()) { + QDeclarativeError error; + error.setDescription(QCoreApplication::translate("QDeclarativeParser","Property value set multiple times")); + error.setLine(this->location(propertyName).start.line); + error.setColumn(this->location(propertyName).start.column); + _parser->_errors << error; + return 0; + } + + if (!isType) { + + if(propertyCount || !currentObject()) { + QDeclarativeError error; + error.setDescription(QCoreApplication::translate("QDeclarativeParser","Expected type name")); + error.setLine(typeLocation.startLine); + error.setColumn(typeLocation.startColumn); + _parser->_errors << error; + return 0; + } + + LocationSpan loc = ProcessAST::location(typeLocation, typeLocation); + if (propertyName) + loc = ProcessAST::location(propertyName); + + _stateStack.pushProperty(objectType, loc); + accept(initializer); + _stateStack.pop(); + + return 0; + + } else { + // Class + + QString resolvableObjectType = objectType; + if (lastTypeDot >= 0) + resolvableObjectType.replace(QLatin1Char('.'),QLatin1Char('/')); + + QDeclarativeParser::Object *obj = new QDeclarativeParser::Object; + + QDeclarativeScriptParser::TypeReference *typeRef = _parser->findOrCreateType(resolvableObjectType); + obj->type = typeRef->id; + + typeRef->refObjects.append(obj); + + // XXX this doesn't do anything (_scope never builds up) + _scope.append(resolvableObjectType); + obj->typeName = qualifiedNameId().toUtf8(); + _scope.removeLast(); + + obj->location = location; + + if (propertyCount) { + Property *prop = currentProperty(); + QDeclarativeParser::Value *v = new QDeclarativeParser::Value; + v->object = obj; + v->location = obj->location; + if (onAssignment) + prop->addOnValue(v); + else + prop->addValue(v); + + while (propertyCount--) + _stateStack.pop(); + + } else { + + if (! _parser->tree()) { + _parser->setTree(obj); + } else { + const State state = _stateStack.top(); + QDeclarativeParser::Value *v = new QDeclarativeParser::Value; + v->object = obj; + v->location = obj->location; + if (state.property) { + state.property->addValue(v); + } else { + Property *defaultProp = state.object->getDefaultProperty(); + if (defaultProp->location.start.line == -1) { + defaultProp->location = v->location; + defaultProp->location.end = defaultProp->location.start; + defaultProp->location.range.length = 0; + } + defaultProp->addValue(v); + } + } + } + + _stateStack.pushObject(obj); + accept(initializer); + _stateStack.pop(); + + return obj; + } +} + +LocationSpan ProcessAST::location(AST::UiQualifiedId *id) +{ + return location(id->identifierToken, id->identifierToken); +} + +LocationSpan ProcessAST::location(AST::SourceLocation start, AST::SourceLocation end) +{ + LocationSpan rv; + rv.start.line = start.startLine; + rv.start.column = start.startColumn; + rv.end.line = end.startLine; + rv.end.column = end.startColumn + end.length - 1; + rv.range.offset = start.offset; + rv.range.length = end.offset + end.length - start.offset; + return rv; +} + +// UiProgram: UiImportListOpt UiObjectMemberList ; +bool ProcessAST::visit(AST::UiProgram *node) +{ + accept(node->imports); + accept(node->members->member); + return false; +} + +// UiImport: T_IMPORT T_STRING_LITERAL ; +bool ProcessAST::visit(AST::UiImport *node) +{ + QString uri; + QDeclarativeScriptParser::Import import; + + if (node->fileName) { + uri = node->fileName->asString(); + + if (uri.endsWith(QLatin1String(".js"))) { + import.type = QDeclarativeScriptParser::Import::Script; + } else { + import.type = QDeclarativeScriptParser::Import::File; + } + } else { + import.type = QDeclarativeScriptParser::Import::Library; + uri = asString(node->importUri); + } + + AST::SourceLocation startLoc = node->importToken; + AST::SourceLocation endLoc = node->semicolonToken; + + // Qualifier + if (node->importId) { + import.qualifier = node->importId->asString(); + if (!import.qualifier.at(0).isUpper()) { + QDeclarativeError error; + error.setDescription(QCoreApplication::translate("QDeclarativeParser","Invalid import qualifier ID")); + error.setLine(node->importIdToken.startLine); + error.setColumn(node->importIdToken.startColumn); + _parser->_errors << error; + return false; + } + if (import.qualifier == QLatin1String("Qt")) { + QDeclarativeError error; + error.setDescription(QCoreApplication::translate("QDeclarativeParser","Reserved name \"Qt\" cannot be used as an qualifier")); + error.setLine(node->importIdToken.startLine); + error.setColumn(node->importIdToken.startColumn); + _parser->_errors << error; + return false; + } + + // Check for script qualifier clashes + bool isScript = import.type == QDeclarativeScriptParser::Import::Script; + for (int ii = 0; ii < _parser->_imports.count(); ++ii) { + const QDeclarativeScriptParser::Import &other = _parser->_imports.at(ii); + bool otherIsScript = other.type == QDeclarativeScriptParser::Import::Script; + + if ((isScript || otherIsScript) && import.qualifier == other.qualifier) { + QDeclarativeError error; + error.setDescription(QCoreApplication::translate("QDeclarativeParser","Script import qualifiers must be unique.")); + error.setLine(node->importIdToken.startLine); + error.setColumn(node->importIdToken.startColumn); + _parser->_errors << error; + return false; + } + } + + } else if (import.type == QDeclarativeScriptParser::Import::Script) { + QDeclarativeError error; + error.setDescription(QCoreApplication::translate("QDeclarativeParser","Script import requires a qualifier")); + error.setLine(node->fileNameToken.startLine); + error.setColumn(node->fileNameToken.startColumn); + _parser->_errors << error; + return false; + } + + if (node->versionToken.isValid()) { + import.version = textAt(node->versionToken); + } else if (import.type == QDeclarativeScriptParser::Import::Library) { + QDeclarativeError error; + error.setDescription(QCoreApplication::translate("QDeclarativeParser","Library import requires a version")); + error.setLine(node->importIdToken.startLine); + error.setColumn(node->importIdToken.startColumn); + _parser->_errors << error; + return false; + } + + + import.location = location(startLoc, endLoc); + import.uri = uri; + + _parser->_imports << import; + + return false; +} + +bool ProcessAST::visit(AST::UiPublicMember *node) +{ + const struct TypeNameToType { + const char *name; + Object::DynamicProperty::Type type; + const char *qtName; + } propTypeNameToTypes[] = { + { "int", Object::DynamicProperty::Int, "int" }, + { "bool", Object::DynamicProperty::Bool, "bool" }, + { "double", Object::DynamicProperty::Real, "double" }, + { "real", Object::DynamicProperty::Real, "qreal" }, + { "string", Object::DynamicProperty::String, "QString" }, + { "url", Object::DynamicProperty::Url, "QUrl" }, + { "color", Object::DynamicProperty::Color, "QColor" }, + // Internally QTime, QDate and QDateTime are all supported. + // To be more consistent with JavaScript we expose only + // QDateTime as it matches closely with the Date JS type. + // We also call it "date" to match. + // { "time", Object::DynamicProperty::Time, "QTime" }, + // { "date", Object::DynamicProperty::Date, "QDate" }, + { "date", Object::DynamicProperty::DateTime, "QDateTime" }, + { "variant", Object::DynamicProperty::Variant, "QVariant" } + }; + const int propTypeNameToTypesCount = sizeof(propTypeNameToTypes) / + sizeof(propTypeNameToTypes[0]); + + if(node->type == AST::UiPublicMember::Signal) { + const QString name = node->name->asString(); + + Object::DynamicSignal signal; + signal.name = name.toUtf8(); + + AST::UiParameterList *p = node->parameters; + while (p) { + const QString memberType = p->type->asString(); + const char *qtType = 0; + for(int ii = 0; !qtType && ii < propTypeNameToTypesCount; ++ii) { + if(QLatin1String(propTypeNameToTypes[ii].name) == memberType) + qtType = propTypeNameToTypes[ii].qtName; + } + + if (!qtType) { + QDeclarativeError error; + error.setDescription(QCoreApplication::translate("QDeclarativeParser","Expected parameter type")); + error.setLine(node->typeToken.startLine); + error.setColumn(node->typeToken.startColumn); + _parser->_errors << error; + return false; + } + + signal.parameterTypes << qtType; + signal.parameterNames << p->name->asString().toUtf8(); + p = p->finish(); + } + + _stateStack.top().object->dynamicSignals << signal; + } else { + const QString memberType = node->memberType->asString(); + const QString name = node->name->asString(); + + bool typeFound = false; + Object::DynamicProperty::Type type; + + if (memberType == QLatin1String("alias")) { + type = Object::DynamicProperty::Alias; + typeFound = true; + } + + for(int ii = 0; !typeFound && ii < propTypeNameToTypesCount; ++ii) { + if(QLatin1String(propTypeNameToTypes[ii].name) == memberType) { + type = propTypeNameToTypes[ii].type; + typeFound = true; + } + } + + if (!typeFound && memberType.at(0).isUpper()) { + QString typemodifier; + if(node->typeModifier) + typemodifier = node->typeModifier->asString(); + if (typemodifier.isEmpty()) { + type = Object::DynamicProperty::Custom; + } else if(typemodifier == QLatin1String("list")) { + type = Object::DynamicProperty::CustomList; + } else { + QDeclarativeError error; + error.setDescription(QCoreApplication::translate("QDeclarativeParser","Invalid property type modifier")); + error.setLine(node->typeModifierToken.startLine); + error.setColumn(node->typeModifierToken.startColumn); + _parser->_errors << error; + return false; + } + typeFound = true; + } else if (node->typeModifier) { + QDeclarativeError error; + error.setDescription(QCoreApplication::translate("QDeclarativeParser","Unexpected property type modifier")); + error.setLine(node->typeModifierToken.startLine); + error.setColumn(node->typeModifierToken.startColumn); + _parser->_errors << error; + return false; + } + + if(!typeFound) { + QDeclarativeError error; + error.setDescription(QCoreApplication::translate("QDeclarativeParser","Expected property type")); + error.setLine(node->typeToken.startLine); + error.setColumn(node->typeToken.startColumn); + _parser->_errors << error; + return false; + } + + if (node->isReadonlyMember) { + QDeclarativeError error; + error.setDescription(QCoreApplication::translate("QDeclarativeParser","Readonly not yet supported")); + error.setLine(node->readonlyToken.startLine); + error.setColumn(node->readonlyToken.startColumn); + _parser->_errors << error; + return false; + + } + Object::DynamicProperty property; + property.isDefaultProperty = node->isDefaultMember; + property.type = type; + if (type >= Object::DynamicProperty::Custom) { + QDeclarativeScriptParser::TypeReference *typeRef = + _parser->findOrCreateType(memberType); + typeRef->refObjects.append(_stateStack.top().object); + } + property.customType = memberType.toUtf8(); + property.name = name.toUtf8(); + property.location = location(node->firstSourceLocation(), + node->lastSourceLocation()); + + if (node->expression) { // default value + property.defaultValue = new Property; + property.defaultValue->parent = _stateStack.top().object; + property.defaultValue->location = + location(node->expression->firstSourceLocation(), + node->expression->lastSourceLocation()); + QDeclarativeParser::Value *value = new QDeclarativeParser::Value; + value->location = location(node->expression->firstSourceLocation(), + node->expression->lastSourceLocation()); + value->value = getVariant(node->expression); + property.defaultValue->values << value; + } + + _stateStack.top().object->dynamicProperties << property; + + // process QML-like initializers (e.g. property Object o: Object {}) + accept(node->binding); + } + + return false; +} + + +// UiObjectMember: UiQualifiedId UiObjectInitializer ; +bool ProcessAST::visit(AST::UiObjectDefinition *node) +{ + LocationSpan l = location(node->firstSourceLocation(), + node->lastSourceLocation()); + + const QString objectType = asString(node->qualifiedTypeNameId); + const AST::SourceLocation typeLocation = node->qualifiedTypeNameId->identifierToken; + + defineObjectBinding(/*propertyName = */ 0, false, objectType, + typeLocation, l, node->initializer); + + return false; +} + + +// UiObjectMember: UiQualifiedId T_COLON UiQualifiedId UiObjectInitializer ; +bool ProcessAST::visit(AST::UiObjectBinding *node) +{ + LocationSpan l = location(node->qualifiedTypeNameId->identifierToken, + node->initializer->rbraceToken); + + const QString objectType = asString(node->qualifiedTypeNameId); + const AST::SourceLocation typeLocation = node->qualifiedTypeNameId->identifierToken; + + defineObjectBinding(node->qualifiedId, node->hasOnToken, objectType, + typeLocation, l, node->initializer); + + return false; +} + +QDeclarativeParser::Variant ProcessAST::getVariant(AST::ExpressionNode *expr) +{ + if (AST::StringLiteral *lit = AST::cast(expr)) { + return QDeclarativeParser::Variant(lit->value->asString()); + } else if (expr->kind == AST::Node::Kind_TrueLiteral) { + return QDeclarativeParser::Variant(true); + } else if (expr->kind == AST::Node::Kind_FalseLiteral) { + return QDeclarativeParser::Variant(false); + } else if (AST::NumericLiteral *lit = AST::cast(expr)) { + return QDeclarativeParser::Variant(lit->value, asString(expr)); + } else { + + if (AST::UnaryMinusExpression *unaryMinus = AST::cast(expr)) { + if (AST::NumericLiteral *lit = AST::cast(unaryMinus->expression)) { + return QDeclarativeParser::Variant(-lit->value, asString(expr)); + } + } + + return QDeclarativeParser::Variant(asString(expr), expr); + } +} + + +// UiObjectMember: UiQualifiedId T_COLON Statement ; +bool ProcessAST::visit(AST::UiScriptBinding *node) +{ + int propertyCount = 0; + AST::UiQualifiedId *propertyName = node->qualifiedId; + for (AST::UiQualifiedId *name = propertyName; name; name = name->next){ + ++propertyCount; + _stateStack.pushProperty(name->name->asString(), + location(name)); + } + + Property *prop = currentProperty(); + + if (prop->values.count()) { + QDeclarativeError error; + error.setDescription(QCoreApplication::translate("QDeclarativeParser","Property value set multiple times")); + error.setLine(this->location(propertyName).start.line); + error.setColumn(this->location(propertyName).start.column); + _parser->_errors << error; + return 0; + } + + QDeclarativeParser::Variant primitive; + + if (AST::ExpressionStatement *stmt = AST::cast(node->statement)) { + primitive = getVariant(stmt->expression); + } else { // do binding + primitive = QDeclarativeParser::Variant(asString(node->statement), + node->statement); + } + + prop->location.range.length = prop->location.range.offset + prop->location.range.length - node->qualifiedId->identifierToken.offset; + prop->location.range.offset = node->qualifiedId->identifierToken.offset; + QDeclarativeParser::Value *v = new QDeclarativeParser::Value; + v->value = primitive; + v->location = location(node->statement->firstSourceLocation(), + node->statement->lastSourceLocation()); + + prop->addValue(v); + + while (propertyCount--) + _stateStack.pop(); + + return true; +} + +static QList collectCommas(AST::UiArrayMemberList *members) +{ + QList commas; + + if (members) { + for (AST::UiArrayMemberList *it = members->next; it; it = it->next) { + commas.append(it->commaToken.offset); + } + } + + return commas; +} + +// UiObjectMember: UiQualifiedId T_COLON T_LBRACKET UiArrayMemberList T_RBRACKET ; +bool ProcessAST::visit(AST::UiArrayBinding *node) +{ + int propertyCount = 0; + AST::UiQualifiedId *propertyName = node->qualifiedId; + for (AST::UiQualifiedId *name = propertyName; name; name = name->next){ + ++propertyCount; + _stateStack.pushProperty(name->name->asString(), + location(name)); + } + + Property* prop = currentProperty(); + + if (prop->values.count()) { + QDeclarativeError error; + error.setDescription(QCoreApplication::translate("QDeclarativeParser","Property value set multiple times")); + error.setLine(this->location(propertyName).start.line); + error.setColumn(this->location(propertyName).start.column); + _parser->_errors << error; + return 0; + } + + accept(node->members); + + // For the DOM, store the position of the T_LBRACKET upto the T_RBRACKET as the range: + prop->listValueRange.offset = node->lbracketToken.offset; + prop->listValueRange.length = node->rbracketToken.offset + node->rbracketToken.length - node->lbracketToken.offset; + + // Store the positions of the comma token too, again for the DOM to be able to retrieve it. + prop->listCommaPositions = collectCommas(node->members); + + while (propertyCount--) + _stateStack.pop(); + + return false; +} + +bool ProcessAST::visit(AST::UiSourceElement *node) +{ + QDeclarativeParser::Object *obj = currentObject(); + + if (AST::FunctionDeclaration *funDecl = AST::cast(node->sourceElement)) { + + Object::DynamicSlot slot; + slot.location = location(funDecl->firstSourceLocation(), funDecl->lastSourceLocation()); + + AST::FormalParameterList *f = funDecl->formals; + while (f) { + slot.parameterNames << f->name->asString().toUtf8(); + f = f->finish(); + } + + AST::SourceLocation loc = funDecl->rparenToken; + loc.offset = loc.end(); + loc.startColumn += 1; + QString body = textAt(loc, funDecl->rbraceToken); + slot.name = funDecl->name->asString().toUtf8(); + slot.body = body; + obj->dynamicSlots << slot; + + } else { + QDeclarativeError error; + error.setDescription(QCoreApplication::translate("QDeclarativeParser","JavaScript declaration outside Script element")); + error.setLine(node->firstSourceLocation().startLine); + error.setColumn(node->firstSourceLocation().startColumn); + _parser->_errors << error; + } + return false; +} + +} // end of anonymous namespace + + +QDeclarativeScriptParser::QDeclarativeScriptParser() +: root(0), data(0) +{ + +} + +QDeclarativeScriptParser::~QDeclarativeScriptParser() +{ + clear(); +} + +class QDeclarativeScriptParserJsASTData +{ +public: + QDeclarativeScriptParserJsASTData(const QString &filename) + : nodePool(filename, &engine) {} + + Engine engine; + NodePool nodePool; +}; + +bool QDeclarativeScriptParser::parse(const QByteArray &qmldata, const QUrl &url) +{ + clear(); + + const QString fileName = url.toString(); + _scriptFile = fileName; + + QTextStream stream(qmldata, QIODevice::ReadOnly); +#ifndef QT_NO_TEXTCODEC + stream.setCodec("UTF-8"); +#endif + const QString code = stream.readAll(); + + data = new QDeclarativeScriptParserJsASTData(fileName); + + Lexer lexer(&data->engine); + lexer.setCode(code, /*line = */ 1); + + Parser parser(&data->engine); + + if (! parser.parse() || !_errors.isEmpty()) { + + // Extract errors from the parser + foreach (const DiagnosticMessage &m, parser.diagnosticMessages()) { + + if (m.isWarning()) + continue; + + QDeclarativeError error; + error.setUrl(url); + error.setDescription(m.message); + error.setLine(m.loc.startLine); + error.setColumn(m.loc.startColumn); + _errors << error; + + } + } + + if (_errors.isEmpty()) { + ProcessAST process(this); + process(code, parser.ast()); + + // Set the url for process errors + for(int ii = 0; ii < _errors.count(); ++ii) + _errors[ii].setUrl(url); + } + + return _errors.isEmpty(); +} + +QList QDeclarativeScriptParser::referencedTypes() const +{ + return _refTypes; +} + +QDeclarativeParser::Object *QDeclarativeScriptParser::tree() const +{ + return root; +} + +QList QDeclarativeScriptParser::imports() const +{ + return _imports; +} + +QList QDeclarativeScriptParser::errors() const +{ + return _errors; +} + +static void replaceWithSpace(QString &str, int idx, int n) +{ + QChar *data = str.data() + idx; + const QChar space(QLatin1Char(' ')); + for (int ii = 0; ii < n; ++ii) + *data++ = space; +} + +/* +Searches for ".pragma " declarations within \a script. Currently supported pragmas +are: + library +*/ +QDeclarativeParser::Object::ScriptBlock::Pragmas QDeclarativeScriptParser::extractPragmas(QString &script) +{ + QDeclarativeParser::Object::ScriptBlock::Pragmas rv = QDeclarativeParser::Object::ScriptBlock::None; + + const QString pragma(QLatin1String("pragma")); + const QString library(QLatin1String("library")); + + QDeclarativeJS::Lexer l(0); + l.setCode(script, 0); + + int token = l.lex(); + + while (true) { + if (token != QDeclarativeJSGrammar::T_DOT) + return rv; + + int startOffset = l.tokenOffset(); + int startLine = l.currentLineNo(); + + token = l.lex(); + + if (token != QDeclarativeJSGrammar::T_IDENTIFIER || + l.currentLineNo() != startLine || + script.mid(l.tokenOffset(), l.tokenLength()) != pragma) + return rv; + + token = l.lex(); + + if (token != QDeclarativeJSGrammar::T_IDENTIFIER || + l.currentLineNo() != startLine) + return rv; + + QString pragmaValue = script.mid(l.tokenOffset(), l.tokenLength()); + int endOffset = l.tokenLength() + l.tokenOffset(); + + token = l.lex(); + if (l.currentLineNo() == startLine) + return rv; + + if (pragmaValue == library) { + rv |= QDeclarativeParser::Object::ScriptBlock::Shared; + replaceWithSpace(script, startOffset, endOffset - startOffset); + } else { + return rv; + } + } + return rv; +} + +#define CHECK_LINE if(l.currentLineNo() != startLine) return rv; +#define CHECK_TOKEN(t) if (token != QDeclarativeJSGrammar:: t) return rv; + +static const int uriTokens[] = { + QDeclarativeJSGrammar::T_IDENTIFIER, + QDeclarativeJSGrammar::T_PROPERTY, + QDeclarativeJSGrammar::T_SIGNAL, + QDeclarativeJSGrammar::T_READONLY, + QDeclarativeJSGrammar::T_ON, + QDeclarativeJSGrammar::T_BREAK, + QDeclarativeJSGrammar::T_CASE, + QDeclarativeJSGrammar::T_CATCH, + QDeclarativeJSGrammar::T_CONTINUE, + QDeclarativeJSGrammar::T_DEFAULT, + QDeclarativeJSGrammar::T_DELETE, + QDeclarativeJSGrammar::T_DO, + QDeclarativeJSGrammar::T_ELSE, + QDeclarativeJSGrammar::T_FALSE, + QDeclarativeJSGrammar::T_FINALLY, + QDeclarativeJSGrammar::T_FOR, + QDeclarativeJSGrammar::T_FUNCTION, + QDeclarativeJSGrammar::T_IF, + QDeclarativeJSGrammar::T_IN, + QDeclarativeJSGrammar::T_INSTANCEOF, + QDeclarativeJSGrammar::T_NEW, + QDeclarativeJSGrammar::T_NULL, + QDeclarativeJSGrammar::T_RETURN, + QDeclarativeJSGrammar::T_SWITCH, + QDeclarativeJSGrammar::T_THIS, + QDeclarativeJSGrammar::T_THROW, + QDeclarativeJSGrammar::T_TRUE, + QDeclarativeJSGrammar::T_TRY, + QDeclarativeJSGrammar::T_TYPEOF, + QDeclarativeJSGrammar::T_VAR, + QDeclarativeJSGrammar::T_VOID, + QDeclarativeJSGrammar::T_WHILE, + QDeclarativeJSGrammar::T_CONST, + QDeclarativeJSGrammar::T_DEBUGGER, + QDeclarativeJSGrammar::T_RESERVED_WORD, + QDeclarativeJSGrammar::T_WITH, + + QDeclarativeJSGrammar::EOF_SYMBOL +}; +static inline bool isUriToken(int token) +{ + const int *current = uriTokens; + while (*current != QDeclarativeJSGrammar::EOF_SYMBOL) { + if (*current == token) + return true; + ++current; + } + return false; +} + +QDeclarativeScriptParser::JavaScriptMetaData QDeclarativeScriptParser::extractMetaData(QString &script) +{ + JavaScriptMetaData rv; + + QDeclarativeParser::Object::ScriptBlock::Pragmas &pragmas = rv.pragmas; + + const QString pragma(QLatin1String("pragma")); + const QString js(QLatin1String(".js")); + const QString library(QLatin1String("library")); + + QDeclarativeJS::Lexer l(0); + l.setCode(script, 0); + + int token = l.lex(); + + while (true) { + if (token != QDeclarativeJSGrammar::T_DOT) + return rv; + + int startOffset = l.tokenOffset(); + int startLine = l.currentLineNo(); + + token = l.lex(); + + CHECK_LINE; + + if (token == QDeclarativeJSGrammar::T_IMPORT) { + + // .import as + // .import as + + token = l.lex(); + + CHECK_LINE; + + if (token == QDeclarativeJSGrammar::T_STRING_LITERAL) { + + QString file(l.characterBuffer(), l.characterCount()); + if (!file.endsWith(js)) + return rv; + + token = l.lex(); + + CHECK_TOKEN(T_AS); + CHECK_LINE; + + token = l.lex(); + + CHECK_TOKEN(T_IDENTIFIER); + CHECK_LINE; + + int endOffset = l.tokenLength() + l.tokenOffset(); + + QString importId = script.mid(l.tokenOffset(), l.tokenLength()); + + if (!importId.at(0).isUpper()) + return rv; + + token = l.lex(); + if (l.currentLineNo() == startLine) + return rv; + + replaceWithSpace(script, startOffset, endOffset - startOffset); + + Import import; + import.type = Import::Script; + import.uri = file; + import.qualifier = importId; + + rv.imports << import; + + } else { + // URI + QString uri; + QString version; + + while (true) { + if (!isUriToken(token)) + return rv; + + uri.append(QString(l.characterBuffer(), l.characterCount())); + + token = l.lex(); + CHECK_LINE; + if (token != QDeclarativeJSGrammar::T_DOT) + break; + + uri.append(QLatin1Char('.')); + + token = l.lex(); + CHECK_LINE; + } + + CHECK_TOKEN(T_NUMERIC_LITERAL); + version = script.mid(l.tokenOffset(), l.tokenLength()); + + token = l.lex(); + + CHECK_TOKEN(T_AS); + CHECK_LINE; + + token = l.lex(); + + CHECK_TOKEN(T_IDENTIFIER); + CHECK_LINE; + + int endOffset = l.tokenLength() + l.tokenOffset(); + + QString importId = script.mid(l.tokenOffset(), l.tokenLength()); + + if (!importId.at(0).isUpper()) + return rv; + + token = l.lex(); + if (l.currentLineNo() == startLine) + return rv; + + replaceWithSpace(script, startOffset, endOffset - startOffset); + + Import import; + import.type = Import::Library; + import.uri = uri; + import.version = version; + import.qualifier = importId; + + rv.imports << import; + } + + } else if (token == QDeclarativeJSGrammar::T_IDENTIFIER && + script.mid(l.tokenOffset(), l.tokenLength()) == pragma) { + + token = l.lex(); + + CHECK_TOKEN(T_IDENTIFIER); + CHECK_LINE; + + QString pragmaValue = script.mid(l.tokenOffset(), l.tokenLength()); + int endOffset = l.tokenLength() + l.tokenOffset(); + + if (pragmaValue == QLatin1String("library")) { + pragmas |= QDeclarativeParser::Object::ScriptBlock::Shared; + replaceWithSpace(script, startOffset, endOffset - startOffset); + } else { + return rv; + } + + token = l.lex(); + if (l.currentLineNo() == startLine) + return rv; + + } else { + return rv; + } + } + return rv; +} + +void QDeclarativeScriptParser::clear() +{ + if (root) { + root->release(); + root = 0; + } + _imports.clear(); + qDeleteAll(_refTypes); + _refTypes.clear(); + _errors.clear(); + + if (data) { + delete data; + data = 0; + } +} + +QDeclarativeScriptParser::TypeReference *QDeclarativeScriptParser::findOrCreateType(const QString &name) +{ + TypeReference *type = 0; + int i = 0; + for (; i < _refTypes.size(); ++i) { + if (_refTypes.at(i)->name == name) { + type = _refTypes.at(i); + break; + } + } + if (!type) { + type = new TypeReference(i, name); + _refTypes.append(type); + } + + return type; +} + +void QDeclarativeScriptParser::setTree(QDeclarativeParser::Object *tree) +{ + Q_ASSERT(! root); + + root = tree; +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativescriptparser_p.h b/src/declarative/qml/qdeclarativescriptparser_p.h new file mode 100644 index 00000000..39958e10 --- /dev/null +++ b/src/declarative/qml/qdeclarativescriptparser_p.h @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QDECLARATIVESCRIPTPARSER_P_H +#define QDECLARATIVESCRIPTPARSER_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 "qdeclarativeerror.h" +#include "private/qdeclarativeparser_p.h" + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QByteArray; + +class QDeclarativeScriptParserJsASTData; +class QDeclarativeScriptParser +{ +public: + class Import + { + public: + Import() : type(Library) {} + + enum Type { Library, File, Script }; + Type type; + + QString uri; + QString qualifier; + QString version; + + QDeclarativeParser::LocationSpan location; + }; + + class TypeReference + { + public: + TypeReference(int typeId, const QString &typeName) : id(typeId), name(typeName) {} + + int id; + // type as it has been referenced in Qml + QString name; + // objects in parse tree referencing the type + QList refObjects; + }; + + QDeclarativeScriptParser(); + ~QDeclarativeScriptParser(); + + bool parse(const QByteArray &data, const QUrl &url = QUrl()); + + QList referencedTypes() const; + + QDeclarativeParser::Object *tree() const; + QList imports() const; + + void clear(); + + QList errors() const; + + class JavaScriptMetaData { + public: + JavaScriptMetaData() + : pragmas(QDeclarativeParser::Object::ScriptBlock::None) {} + + QDeclarativeParser::Object::ScriptBlock::Pragmas pragmas; + QList imports; + }; + + static QDeclarativeParser::Object::ScriptBlock::Pragmas extractPragmas(QString &); + static JavaScriptMetaData extractMetaData(QString &); + + +// ### private: + TypeReference *findOrCreateType(const QString &name); + void setTree(QDeclarativeParser::Object *tree); + + void setScriptFile(const QString &filename) {_scriptFile = filename; } + QString scriptFile() const { return _scriptFile; } + +// ### private: + QList _errors; + + QDeclarativeParser::Object *root; + QList _imports; + QList _refTypes; + QString _scriptFile; + QDeclarativeScriptParserJsASTData *data; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVESCRIPTPARSER_P_H diff --git a/src/declarative/qml/qdeclarativescriptstring.cpp b/src/declarative/qml/qdeclarativescriptstring.cpp new file mode 100644 index 00000000..4f9da3a1 --- /dev/null +++ b/src/declarative/qml/qdeclarativescriptstring.cpp @@ -0,0 +1,166 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativescriptstring.h" + +QT_BEGIN_NAMESPACE + +class QDeclarativeScriptStringPrivate : public QSharedData +{ +public: + QDeclarativeScriptStringPrivate() : context(0), scope(0) {} + + QDeclarativeContext *context; + QObject *scope; + QString script; +}; + +/*! +\class QDeclarativeScriptString +\since 4.7 +\brief The QDeclarativeScriptString class encapsulates a script and its context. + +QDeclarativeScriptString is used to create QObject properties that accept a script "assignment" from QML. + +Normally, the following QML would result in a binding being established for the \c script +property; i.e. \c script would be assigned the value obtained from running \c {myObj.value = Math.max(myValue, 100)} + +\qml +MyType { + script: myObj.value = Math.max(myValue, 100) +} +\endqml + +If instead the property had a type of QDeclarativeScriptString, +the script itself -- \e {myObj.value = Math.max(myValue, 100)} -- would be passed to the \c script property +and the class could choose how to handle it. Typically, the class will evaluate +the script at some later time using a QDeclarativeExpression. + +\code +QDeclarativeExpression expr(scriptString.context(), scriptString.script(), scriptStr.scopeObject()); +expr.value(); +\endcode + +\sa QDeclarativeExpression +*/ + +/*! +Constructs an empty instance. +*/ +QDeclarativeScriptString::QDeclarativeScriptString() +: d(new QDeclarativeScriptStringPrivate) +{ +} + +/*! +Copies \a other. +*/ +QDeclarativeScriptString::QDeclarativeScriptString(const QDeclarativeScriptString &other) +: d(other.d) +{ +} + +/*! +\internal +*/ +QDeclarativeScriptString::~QDeclarativeScriptString() +{ +} + +/*! +Assigns \a other to this. +*/ +QDeclarativeScriptString &QDeclarativeScriptString::operator=(const QDeclarativeScriptString &other) +{ + d = other.d; + return *this; +} + +/*! +Returns the context for the script. +*/ +QDeclarativeContext *QDeclarativeScriptString::context() const +{ + return d->context; +} + +/*! +Sets the \a context for the script. +*/ +void QDeclarativeScriptString::setContext(QDeclarativeContext *context) +{ + d->context = context; +} + +/*! +Returns the scope object for the script. +*/ +QObject *QDeclarativeScriptString::scopeObject() const +{ + return d->scope; +} + +/*! +Sets the scope \a object for the script. +*/ +void QDeclarativeScriptString::setScopeObject(QObject *object) +{ + d->scope = object; +} + +/*! +Returns the script text. +*/ +QString QDeclarativeScriptString::script() const +{ + return d->script; +} + +/*! +Sets the \a script text. +*/ +void QDeclarativeScriptString::setScript(const QString &script) +{ + d->script = script; +} + +QT_END_NAMESPACE + diff --git a/src/declarative/qml/qdeclarativescriptstring.h b/src/declarative/qml/qdeclarativescriptstring.h new file mode 100644 index 00000000..cda847ff --- /dev/null +++ b/src/declarative/qml/qdeclarativescriptstring.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESCRIPTSTRING_H +#define QDECLARATIVESCRIPTSTRING_H + +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QObject; +class QDeclarativeContext; +class QDeclarativeScriptStringPrivate; +class Q_DECLARATIVE_EXPORT QDeclarativeScriptString +{ +public: + QDeclarativeScriptString(); + QDeclarativeScriptString(const QDeclarativeScriptString &); + ~QDeclarativeScriptString(); + + QDeclarativeScriptString &operator=(const QDeclarativeScriptString &); + + QDeclarativeContext *context() const; + void setContext(QDeclarativeContext *); + + QObject *scopeObject() const; + void setScopeObject(QObject *); + + QString script() const; + void setScript(const QString &); + +private: + QSharedDataPointer d; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QDeclarativeScriptString) + +QT_END_HEADER + +#endif // QDECLARATIVESCRIPTSTRING_H + diff --git a/src/declarative/qml/qdeclarativesqldatabase.cpp b/src/declarative/qml/qdeclarativesqldatabase.cpp new file mode 100644 index 00000000..ed44b207 --- /dev/null +++ b/src/declarative/qml/qdeclarativesqldatabase.cpp @@ -0,0 +1,448 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativesqldatabase_p.h" + +#include "qdeclarativeengine.h" +#include "private/qdeclarativeengine_p.h" +#include "private/qdeclarativerefcount_p.h" +#include "private/qdeclarativeengine_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +Q_DECLARE_METATYPE(QSqlDatabase) +Q_DECLARE_METATYPE(QSqlQuery) + +QT_BEGIN_NAMESPACE + +class QDeclarativeSqlQueryScriptClass: public QScriptClass { +public: + QDeclarativeSqlQueryScriptClass(QScriptEngine *engine) : QScriptClass(engine) + { + str_length = engine->toStringHandle(QLatin1String("length")); + str_forwardOnly = engine->toStringHandle(QLatin1String("forwardOnly")); // not in HTML5 (an optimization) + } + + QueryFlags queryProperty(const QScriptValue &, + const QScriptString &name, + QueryFlags flags, uint *) + { + if (flags & HandlesReadAccess) { + if (name == str_length) { + return HandlesReadAccess; + } else if (name == str_forwardOnly) { + return flags; + } + } + if (flags & HandlesWriteAccess) + if (name == str_forwardOnly) + return flags; + return 0; + } + + QScriptValue property(const QScriptValue &object, + const QScriptString &name, uint) + { + QSqlQuery query = qscriptvalue_cast(object.data()); + if (name == str_length) { + int s = query.size(); + if (s<0) { + // Inefficient. + if (query.last()) { + return query.at()+1; + } else { + return 0; + } + } else { + return s; + } + } else if (name == str_forwardOnly) { + return query.isForwardOnly(); + } + return engine()->undefinedValue(); + } + + void setProperty(QScriptValue &object, + const QScriptString &name, uint, const QScriptValue & value) + { + if (name == str_forwardOnly) { + QSqlQuery query = qscriptvalue_cast(object.data()); + query.setForwardOnly(value.toBool()); + } + } + + QScriptValue::PropertyFlags propertyFlags(const QScriptValue &/*object*/, const QScriptString &name, uint /*id*/) + { + if (name == str_length) { + return QScriptValue::Undeletable + | QScriptValue::SkipInEnumeration; + } + return QScriptValue::Undeletable; + } + +private: + QScriptString str_length; + QScriptString str_forwardOnly; +}; + +// If the spec changes to allow iteration, check git history... +// class QDeclarativeSqlQueryScriptClassPropertyIterator : public QScriptClassPropertyIterator + + + +enum SqlException { + UNKNOWN_ERR, + DATABASE_ERR, + VERSION_ERR, + TOO_LARGE_ERR, + QUOTA_ERR, + SYNTAX_ERR, + CONSTRAINT_ERR, + TIMEOUT_ERR +}; + +static const char* sqlerror[] = { + "UNKNOWN_ERR", + "DATABASE_ERR", + "VERSION_ERR", + "TOO_LARGE_ERR", + "QUOTA_ERR", + "SYNTAX_ERR", + "CONSTRAINT_ERR", + "TIMEOUT_ERR", + 0 +}; + +#define THROW_SQL(error, desc) \ +{ \ + QScriptValue errorValue = context->throwError(desc); \ + errorValue.setProperty(QLatin1String("code"), error); \ + return errorValue; \ +} + +static QString qmlsqldatabase_databasesPath(QScriptEngine *engine) +{ + QDeclarativeScriptEngine *qmlengine = static_cast(engine); + return QDir::toNativeSeparators(qmlengine->offlineStoragePath) + + QDir::separator() + QLatin1String("Databases"); +} + +static void qmlsqldatabase_initDatabasesPath(QScriptEngine *engine) +{ + QDir().mkpath(qmlsqldatabase_databasesPath(engine)); +} + +static QString qmlsqldatabase_databaseFile(const QString& connectionName, QScriptEngine *engine) +{ + return qmlsqldatabase_databasesPath(engine) + QDir::separator() + + connectionName; +} + + +static QScriptValue qmlsqldatabase_item(QScriptContext *context, QScriptEngine *engine) +{ + QSqlQuery query = qscriptvalue_cast(context->thisObject().data()); + int i = context->argument(0).toNumber(); + if (query.at() == i || query.seek(i)) { // Qt 4.6 doesn't optimize seek(at()) + QSqlRecord r = query.record(); + QScriptValue row = engine->newObject(); + for (int j=0; jundefinedValue(); +} + +static QScriptValue qmlsqldatabase_executeSql_outsidetransaction(QScriptContext *context, QScriptEngine * /*engine*/) +{ + THROW_SQL(DATABASE_ERR,QDeclarativeEngine::tr("executeSql called outside transaction()")); +} + +static QScriptValue qmlsqldatabase_executeSql(QScriptContext *context, QScriptEngine *engine) +{ + QSqlDatabase db = qscriptvalue_cast(context->thisObject()); + QString sql = context->argument(0).toString(); + QSqlQuery query(db); + bool err = false; + + QScriptValue result; + + if (query.prepare(sql)) { + if (context->argumentCount() > 1) { + QScriptValue values = context->argument(1); + if (values.isObject()) { + if (values.isArray()) { + int size = values.property(QLatin1String("length")).toInt32(); + for (int i = 0; i < size; ++i) + query.bindValue(i, values.property(i).toVariant()); + } else { + for (QScriptValueIterator it(values); it.hasNext();) { + it.next(); + query.bindValue(it.name(),it.value().toVariant()); + } + } + } else { + query.bindValue(0,values.toVariant()); + } + } + if (query.exec()) { + result = engine->newObject(); + QDeclarativeScriptEngine *qmlengine = static_cast(engine); + if (!qmlengine->sqlQueryClass) + qmlengine->sqlQueryClass = new QDeclarativeSqlQueryScriptClass(engine); + QScriptValue rows = engine->newObject(qmlengine->sqlQueryClass); + rows.setData(engine->newVariant(QVariant::fromValue(query))); + rows.setProperty(QLatin1String("item"), engine->newFunction(qmlsqldatabase_item,1), QScriptValue::SkipInEnumeration); + result.setProperty(QLatin1String("rows"),rows); + result.setProperty(QLatin1String("rowsAffected"),query.numRowsAffected()); + result.setProperty(QLatin1String("insertId"),query.lastInsertId().toString()); + } else { + err = true; + } + } else { + err = true; + } + if (err) + THROW_SQL(DATABASE_ERR,query.lastError().text()); + return result; +} + +static QScriptValue qmlsqldatabase_executeSql_readonly(QScriptContext *context, QScriptEngine *engine) +{ + QString sql = context->argument(0).toString(); + if (sql.startsWith(QLatin1String("SELECT"),Qt::CaseInsensitive)) { + return qmlsqldatabase_executeSql(context,engine); + } else { + THROW_SQL(SYNTAX_ERR,QDeclarativeEngine::tr("Read-only Transaction")) + } +} + +static QScriptValue qmlsqldatabase_change_version(QScriptContext *context, QScriptEngine *engine) +{ + if (context->argumentCount() < 2) + return engine->undefinedValue(); + + QSqlDatabase db = qscriptvalue_cast(context->thisObject()); + QString from_version = context->argument(0).toString(); + QString to_version = context->argument(1).toString(); + QScriptValue callback = context->argument(2); + + QScriptValue instance = engine->newObject(); + instance.setProperty(QLatin1String("executeSql"), engine->newFunction(qmlsqldatabase_executeSql,1)); + QScriptValue tx = engine->newVariant(instance,QVariant::fromValue(db)); + + QString foundvers = context->thisObject().property(QLatin1String("version")).toString(); + if (from_version!=foundvers) { + THROW_SQL(VERSION_ERR,QDeclarativeEngine::tr("Version mismatch: expected %1, found %2").arg(from_version).arg(foundvers)); + return engine->undefinedValue(); + } + + bool ok = true; + if (callback.isFunction()) { + ok = false; + db.transaction(); + callback.call(QScriptValue(), QScriptValueList() << tx); + if (engine->hasUncaughtException()) { + db.rollback(); + } else { + if (!db.commit()) { + db.rollback(); + THROW_SQL(UNKNOWN_ERR,QDeclarativeEngine::tr("SQL transaction failed")); + } else { + ok = true; + } + } + } + + if (ok) { + context->thisObject().setProperty(QLatin1String("version"), to_version, QScriptValue::ReadOnly); +#ifndef QT_NO_SETTINGS + QSettings ini(qmlsqldatabase_databaseFile(db.connectionName(),engine) + QLatin1String(".ini"), QSettings::IniFormat); + ini.setValue(QLatin1String("Version"), to_version); +#endif + } + + return engine->undefinedValue(); +} + +static QScriptValue qmlsqldatabase_transaction_shared(QScriptContext *context, QScriptEngine *engine, bool readOnly) +{ + QSqlDatabase db = qscriptvalue_cast(context->thisObject()); + QScriptValue callback = context->argument(0); + if (!callback.isFunction()) + THROW_SQL(UNKNOWN_ERR,QDeclarativeEngine::tr("transaction: missing callback")); + + QScriptValue instance = engine->newObject(); + instance.setProperty(QLatin1String("executeSql"), + engine->newFunction(readOnly ? qmlsqldatabase_executeSql_readonly : qmlsqldatabase_executeSql,1)); + QScriptValue tx = engine->newVariant(instance,QVariant::fromValue(db)); + + db.transaction(); + callback.call(QScriptValue(), QScriptValueList() << tx); + instance.setProperty(QLatin1String("executeSql"), + engine->newFunction(qmlsqldatabase_executeSql_outsidetransaction)); + if (engine->hasUncaughtException()) { + db.rollback(); + } else { + if (!db.commit()) + db.rollback(); + } + return engine->undefinedValue(); +} + +static QScriptValue qmlsqldatabase_transaction(QScriptContext *context, QScriptEngine *engine) +{ + return qmlsqldatabase_transaction_shared(context,engine,false); +} +static QScriptValue qmlsqldatabase_read_transaction(QScriptContext *context, QScriptEngine *engine) +{ + return qmlsqldatabase_transaction_shared(context,engine,true); +} + +/* + Currently documented in doc/src/declarative/globalobject.qdoc +*/ +static QScriptValue qmlsqldatabase_open_sync(QScriptContext *context, QScriptEngine *engine) +{ +#ifndef QT_NO_SETTINGS + qmlsqldatabase_initDatabasesPath(engine); + + QSqlDatabase database; + + QString dbname = context->argument(0).toString(); + QString dbversion = context->argument(1).toString(); + QString dbdescription = context->argument(2).toString(); + int dbestimatedsize = context->argument(3).toNumber(); + QScriptValue dbcreationCallback = context->argument(4); + + QCryptographicHash md5(QCryptographicHash::Md5); + md5.addData(dbname.toUtf8()); + QString dbid(QLatin1String(md5.result().toHex())); + + QString basename = qmlsqldatabase_databaseFile(dbid, engine); + bool created = false; + QString version = dbversion; + + { + QSettings ini(basename+QLatin1String(".ini"),QSettings::IniFormat); + + if (QSqlDatabase::connectionNames().contains(dbid)) { + database = QSqlDatabase::database(dbid); + version = ini.value(QLatin1String("Version")).toString(); + if (version != dbversion && !dbversion.isEmpty() && !version.isEmpty()) + THROW_SQL(VERSION_ERR,QDeclarativeEngine::tr("SQL: database version mismatch")); + } else { + created = !QFile::exists(basename+QLatin1String(".sqlite")); + database = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), dbid); + if (created) { + ini.setValue(QLatin1String("Name"), dbname); + if (dbcreationCallback.isFunction()) + version = QString(); + ini.setValue(QLatin1String("Version"), version); + ini.setValue(QLatin1String("Description"), dbdescription); + ini.setValue(QLatin1String("EstimatedSize"), dbestimatedsize); + ini.setValue(QLatin1String("Driver"), QLatin1String("QSQLITE")); + } else { + if (!dbversion.isEmpty() && ini.value(QLatin1String("Version")) != dbversion) { + // Incompatible + THROW_SQL(VERSION_ERR,QDeclarativeEngine::tr("SQL: database version mismatch")); + } + version = ini.value(QLatin1String("Version")).toString(); + } + database.setDatabaseName(basename+QLatin1String(".sqlite")); + } + if (!database.isOpen()) + database.open(); + } + + QScriptValue instance = engine->newObject(); + instance.setProperty(QLatin1String("transaction"), engine->newFunction(qmlsqldatabase_transaction,1)); + instance.setProperty(QLatin1String("readTransaction"), engine->newFunction(qmlsqldatabase_read_transaction,1)); + instance.setProperty(QLatin1String("version"), version, QScriptValue::ReadOnly); + instance.setProperty(QLatin1String("changeVersion"), engine->newFunction(qmlsqldatabase_change_version,3)); + + QScriptValue result = engine->newVariant(instance,QVariant::fromValue(database)); + + if (created && dbcreationCallback.isFunction()) { + dbcreationCallback.call(QScriptValue(), QScriptValueList() << result); + } + + return result; +#else + return engine->undefinedValue(); +#endif // QT_NO_SETTINGS +} + +void qt_add_qmlsqldatabase(QScriptEngine *engine) +{ + QScriptValue openDatabase = engine->newFunction(qmlsqldatabase_open_sync, 4); + engine->globalObject().setProperty(QLatin1String("openDatabaseSync"), openDatabase); + + QScriptValue sqlExceptionPrototype = engine->newObject(); + for (int i=0; sqlerror[i]; ++i) + sqlExceptionPrototype.setProperty(QLatin1String(sqlerror[i]), + i,QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); + + engine->globalObject().setProperty(QLatin1String("SQLException"), sqlExceptionPrototype); +} + +/* +HTML5 "spec" says "rs.rows[n]", but WebKit only impelments "rs.rows.item(n)". We do both (and property iterator). +We add a "forwardOnly" property that stops Qt caching results (code promises to only go forward +through the data. +*/ + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativesqldatabase_p.h b/src/declarative/qml/qdeclarativesqldatabase_p.h new file mode 100644 index 00000000..cad0f3cd --- /dev/null +++ b/src/declarative/qml/qdeclarativesqldatabase_p.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESQLDATABASE_P_H +#define QDECLARATIVESQLDATABASE_P_H + +#include +// +// 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 + +QT_BEGIN_NAMESPACE + +class QScriptEngine; +void qt_add_qmlsqldatabase(QScriptEngine *engine); + +QT_END_NAMESPACE + +#endif // QDECLARATIVESQLDATABASE_P_H + diff --git a/src/declarative/qml/qdeclarativestringconverters.cpp b/src/declarative/qml/qdeclarativestringconverters.cpp new file mode 100644 index 00000000..0ffae2f0 --- /dev/null +++ b/src/declarative/qml/qdeclarativestringconverters.cpp @@ -0,0 +1,278 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativestringconverters_p.h" + +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +static uchar fromHex(const uchar c, const uchar c2) +{ + uchar rv = 0; + if (c >= '0' && c <= '9') + rv += (c - '0') * 16; + else if (c >= 'A' && c <= 'F') + rv += (c - 'A' + 10) * 16; + else if (c >= 'a' && c <= 'f') + rv += (c - 'a' + 10) * 16; + + if (c2 >= '0' && c2 <= '9') + rv += (c2 - '0'); + else if (c2 >= 'A' && c2 <= 'F') + rv += (c2 - 'A' + 10); + else if (c2 >= 'a' && c2 <= 'f') + rv += (c2 - 'a' + 10); + + return rv; +} + +static uchar fromHex(const QString &s, int idx) +{ + uchar c = s.at(idx).toAscii(); + uchar c2 = s.at(idx + 1).toAscii(); + return fromHex(c, c2); +} + +QVariant QDeclarativeStringConverters::variantFromString(const QString &s) +{ + if (s.isEmpty()) + return QVariant(s); + bool ok = false; + QRectF r = rectFFromString(s, &ok); + if (ok) return QVariant(r); + QColor c = colorFromString(s, &ok); + if (ok) return QVariant(c); + QPointF p = pointFFromString(s, &ok); + if (ok) return QVariant(p); + QSizeF sz = sizeFFromString(s, &ok); + if (ok) return QVariant(sz); + QVector3D v = vector3DFromString(s, &ok); + if (ok) return QVariant::fromValue(v); + + return QVariant(s); +} + +QVariant QDeclarativeStringConverters::variantFromString(const QString &s, int preferredType, bool *ok) +{ + switch (preferredType) { + case QMetaType::Int: + return QVariant(int(qRound(s.toDouble(ok)))); + case QMetaType::UInt: + return QVariant(uint(qRound(s.toDouble(ok)))); + case QMetaType::QColor: + return QVariant::fromValue(colorFromString(s, ok)); +#ifndef QT_NO_DATESTRING + case QMetaType::QDate: + return QVariant::fromValue(dateFromString(s, ok)); + case QMetaType::QTime: + return QVariant::fromValue(timeFromString(s, ok)); + case QMetaType::QDateTime: + return QVariant::fromValue(dateTimeFromString(s, ok)); +#endif // QT_NO_DATESTRING + case QMetaType::QPointF: + return QVariant::fromValue(pointFFromString(s, ok)); + case QMetaType::QPoint: + return QVariant::fromValue(pointFFromString(s, ok).toPoint()); + case QMetaType::QSizeF: + return QVariant::fromValue(sizeFFromString(s, ok)); + case QMetaType::QSize: + return QVariant::fromValue(sizeFFromString(s, ok).toSize()); + case QMetaType::QRectF: + return QVariant::fromValue(rectFFromString(s, ok)); + case QMetaType::QRect: + return QVariant::fromValue(rectFFromString(s, ok).toRect()); + case QMetaType::QVector3D: + return QVariant::fromValue(vector3DFromString(s, ok)); + default: + if (ok) *ok = false; + return QVariant(); + } +} + +QColor QDeclarativeStringConverters::colorFromString(const QString &s, bool *ok) +{ + if (s.length() == 9 && s.startsWith(QLatin1Char('#'))) { + uchar a = fromHex(s, 1); + uchar r = fromHex(s, 3); + uchar g = fromHex(s, 5); + uchar b = fromHex(s, 7); + if (ok) *ok = true; + return QColor(r, g, b, a); + } else { + QColor rv(s); + if (ok) *ok = rv.isValid(); + return rv; + } +} + +#ifndef QT_NO_DATESTRING +QDate QDeclarativeStringConverters::dateFromString(const QString &s, bool *ok) +{ + QDate d = QDate::fromString(s, Qt::ISODate); + if (ok) *ok = d.isValid(); + return d; +} + +QTime QDeclarativeStringConverters::timeFromString(const QString &s, bool *ok) +{ + QTime t = QTime::fromString(s, Qt::ISODate); + if (ok) *ok = t.isValid(); + return t; +} + +QDateTime QDeclarativeStringConverters::dateTimeFromString(const QString &s, bool *ok) +{ + QDateTime d = QDateTime::fromString(s, Qt::ISODate); + if (ok) *ok = d.isValid(); + return d; +} +#endif // QT_NO_DATESTRING + +//expects input of "x,y" +QPointF QDeclarativeStringConverters::pointFFromString(const QString &s, bool *ok) +{ + if (s.count(QLatin1Char(',')) != 1) { + if (ok) + *ok = false; + return QPointF(); + } + + bool xGood, yGood; + int index = s.indexOf(QLatin1Char(',')); + qreal xCoord = s.left(index).toDouble(&xGood); + qreal yCoord = s.mid(index+1).toDouble(&yGood); + if (!xGood || !yGood) { + if (ok) + *ok = false; + return QPointF(); + } + + if (ok) + *ok = true; + return QPointF(xCoord, yCoord); +} + +//expects input of "widthxheight" +QSizeF QDeclarativeStringConverters::sizeFFromString(const QString &s, bool *ok) +{ + if (s.count(QLatin1Char('x')) != 1) { + if (ok) + *ok = false; + return QSizeF(); + } + + bool wGood, hGood; + int index = s.indexOf(QLatin1Char('x')); + qreal width = s.left(index).toDouble(&wGood); + qreal height = s.mid(index+1).toDouble(&hGood); + if (!wGood || !hGood) { + if (ok) + *ok = false; + return QSizeF(); + } + + if (ok) + *ok = true; + return QSizeF(width, height); +} + +//expects input of "x,y,widthxheight" //### use space instead of second comma? +QRectF QDeclarativeStringConverters::rectFFromString(const QString &s, bool *ok) +{ + if (s.count(QLatin1Char(',')) != 2 || s.count(QLatin1Char('x')) != 1) { + if (ok) + *ok = false; + return QRectF(); + } + + bool xGood, yGood, wGood, hGood; + int index = s.indexOf(QLatin1Char(',')); + qreal x = s.left(index).toDouble(&xGood); + int index2 = s.indexOf(QLatin1Char(','), index+1); + qreal y = s.mid(index+1, index2-index-1).toDouble(&yGood); + index = s.indexOf(QLatin1Char('x'), index2+1); + qreal width = s.mid(index2+1, index-index2-1).toDouble(&wGood); + qreal height = s.mid(index+1).toDouble(&hGood); + if (!xGood || !yGood || !wGood || !hGood) { + if (ok) + *ok = false; + return QRectF(); + } + + if (ok) + *ok = true; + return QRectF(x, y, width, height); +} + +//expects input of "x,y,z" +QVector3D QDeclarativeStringConverters::vector3DFromString(const QString &s, bool *ok) +{ + if (s.count(QLatin1Char(',')) != 2) { + if (ok) + *ok = false; + return QVector3D(); + } + + bool xGood, yGood, zGood; + int index = s.indexOf(QLatin1Char(',')); + int index2 = s.indexOf(QLatin1Char(','), index+1); + qreal xCoord = s.left(index).toDouble(&xGood); + qreal yCoord = s.mid(index+1, index2-index-1).toDouble(&yGood); + qreal zCoord = s.mid(index2+1).toDouble(&zGood); + if (!xGood || !yGood || !zGood) { + if (ok) + *ok = false; + return QVector3D(); + } + + if (ok) + *ok = true; + return QVector3D(xCoord, yCoord, zCoord); +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativestringconverters_p.h b/src/declarative/qml/qdeclarativestringconverters_p.h new file mode 100644 index 00000000..afb519ec --- /dev/null +++ b/src/declarative/qml/qdeclarativestringconverters_p.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESTRINGCONVERTERS_P_H +#define QDECLARATIVESTRINGCONVERTERS_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 +#include + +#include + +QT_BEGIN_NAMESPACE + +class QColor; +class QPointF; +class QSizeF; +class QRectF; +class QString; +class QByteArray; +class QVector3D; + +// XXX - Bauhaus currently uses these methods which is why they're exported +namespace QDeclarativeStringConverters +{ + QVariant Q_DECLARATIVE_PRIVATE_EXPORT variantFromString(const QString &); + QVariant Q_DECLARATIVE_PRIVATE_EXPORT variantFromString(const QString &, int preferredType, bool *ok = 0); + + QColor Q_DECLARATIVE_PRIVATE_EXPORT colorFromString(const QString &, bool *ok = 0); +#ifndef QT_NO_DATESTRING + QDate Q_DECLARATIVE_PRIVATE_EXPORT dateFromString(const QString &, bool *ok = 0); + QTime Q_DECLARATIVE_PRIVATE_EXPORT timeFromString(const QString &, bool *ok = 0); + QDateTime Q_DECLARATIVE_PRIVATE_EXPORT dateTimeFromString(const QString &, bool *ok = 0); +#endif + QPointF Q_DECLARATIVE_PRIVATE_EXPORT pointFFromString(const QString &, bool *ok = 0); + QSizeF Q_DECLARATIVE_PRIVATE_EXPORT sizeFFromString(const QString &, bool *ok = 0); + QRectF Q_DECLARATIVE_PRIVATE_EXPORT rectFFromString(const QString &, bool *ok = 0); + QVector3D Q_DECLARATIVE_PRIVATE_EXPORT vector3DFromString(const QString &, bool *ok = 0); +} + +QT_END_NAMESPACE + +#endif // QDECLARATIVESTRINGCONVERTERS_P_H diff --git a/src/declarative/qml/qdeclarativetypeloader.cpp b/src/declarative/qml/qdeclarativetypeloader.cpp new file mode 100644 index 00000000..4fa45102 --- /dev/null +++ b/src/declarative/qml/qdeclarativetypeloader.cpp @@ -0,0 +1,1196 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativetypeloader_p.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/* +Returns the set of QML files in path (qmldir, *.qml, *.js). The caller +is responsible for deleting the returned data. +*/ +static QSet *qmlFilesInDirectory(const QString &path) +{ + QDirIterator dir(path, QDir::Files); + if (!dir.hasNext()) + return 0; + QSet *files = new QSet; + while (dir.hasNext()) { + dir.next(); + QString fileName = dir.fileName(); + if (fileName == QLatin1String("qmldir") + || fileName.endsWith(QLatin1String(".qml")) + || fileName.endsWith(QLatin1String(".js"))) { +#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_DARWIN) || defined(Q_OS_SYMBIAN) + fileName = fileName.toLower(); +#endif + files->insert(fileName); + } + } + return files; +} + + +/*! +\class QDeclarativeDataBlob +\brief The QDeclarativeDataBlob encapsulates a data request that can be issued to a QDeclarativeDataLoader. +\internal + +QDeclarativeDataBlob's are loaded by a QDeclarativeDataLoader. The user creates the QDeclarativeDataBlob +and then calls QDeclarativeDataLoader::load() or QDeclarativeDataLoader::loadWithStaticData() to load it. +The QDeclarativeDataLoader invokes callbacks on the QDeclarativeDataBlob as data becomes available. +*/ + +/*! + \class QDeclarativeTypeLoader + \internal +*/ + +/*! +\enum QDeclarativeDataBlob::Status + +This enum describes the status of the data blob. + +\list +\o Null The blob has not yet been loaded by a QDeclarativeDataLoader +\o Loading The blob is loading network data. The QDeclarativeDataBlob::setData() callback has not yet been +invoked or has not yet returned. +\o WaitingForDependencies The blob is waiting for dependencies to be done before continueing. This status +only occurs after the QDeclarativeDataBlob::setData() callback has been made, and when the blob has outstanding +dependencies. +\o Complete The blob's data has been loaded and all dependencies are done. +\o Error An error has been set on this blob. +\endlist +*/ + +/*! +\enum QDeclarativeDataBlob::Type + +This enum describes the type of the data blob. + +\list +\o QmlFile This is a QDeclarativeTypeData +\o JavaScriptFile This is a QDeclarativeScriptData +\o QmldirFile This is a QDeclarativeQmldirData +\endlist +*/ + +/*! +Create a new QDeclarativeDataBlob for \a url and of the provided \a type. +*/ +QDeclarativeDataBlob::QDeclarativeDataBlob(const QUrl &url, Type type) +: m_type(type), m_status(Null), m_progress(0), m_url(url), m_finalUrl(url), m_manager(0), + m_redirectCount(0), m_inCallback(false), m_isDone(false) +{ +} + +/*! \internal */ +QDeclarativeDataBlob::~QDeclarativeDataBlob() +{ + Q_ASSERT(m_waitingOnMe.isEmpty()); + + cancelAllWaitingFor(); +} + +/*! +Returns the type provided to the constructor. +*/ +QDeclarativeDataBlob::Type QDeclarativeDataBlob::type() const +{ + return m_type; +} + +/*! +Returns the blob's status. +*/ +QDeclarativeDataBlob::Status QDeclarativeDataBlob::status() const +{ + return m_status; +} + +/*! +Returns true if the status is Null. +*/ +bool QDeclarativeDataBlob::isNull() const +{ + return m_status == Null; +} + +/*! +Returns true if the status is Loading. +*/ +bool QDeclarativeDataBlob::isLoading() const +{ + return m_status == Loading; +} + +/*! +Returns true if the status is WaitingForDependencies. +*/ +bool QDeclarativeDataBlob::isWaiting() const +{ + return m_status == WaitingForDependencies; +} + +/*! +Returns true if the status is Complete. +*/ +bool QDeclarativeDataBlob::isComplete() const +{ + return m_status == Complete; +} + +/*! +Returns true if the status is Error. +*/ +bool QDeclarativeDataBlob::isError() const +{ + return m_status == Error; +} + +/*! +Returns true if the status is Complete or Error. +*/ +bool QDeclarativeDataBlob::isCompleteOrError() const +{ + return isComplete() || isError(); +} + +/*! +Returns the data download progress from 0 to 1. +*/ +qreal QDeclarativeDataBlob::progress() const +{ + return m_progress; +} + +/*! +Returns the blob url passed to the constructor. If a network redirect +happens while fetching the data, this url remains the same. + +\sa finalUrl() +*/ +QUrl QDeclarativeDataBlob::url() const +{ + return m_url; +} + +/*! +Returns the final url of the data. Initially this is the same as +url(), but if a network redirect happens while fetching the data, this url +is updated to reflect the new location. +*/ +QUrl QDeclarativeDataBlob::finalUrl() const +{ + return m_finalUrl; +} + +/*! +Return the errors on this blob. +*/ +QList QDeclarativeDataBlob::errors() const +{ + return m_errors; +} + +/*! +Mark this blob as having \a errors. + +All outstanding dependencies will be cancelled. Requests to add new dependencies +will be ignored. Entry into the Error state is irreversable, although you can change the +specific errors by additional calls to setError. +*/ +void QDeclarativeDataBlob::setError(const QDeclarativeError &errors) +{ + QList l; + l << errors; + setError(l); +} + +/*! +\overload +*/ +void QDeclarativeDataBlob::setError(const QList &errors) +{ + m_status = Error; + m_errors = errors; + + cancelAllWaitingFor(); + + if (!m_inCallback) + tryDone(); +} + +/*! +Wait for \a blob to become complete or to error. If \a blob is already +complete or in error, or this blob is already complete, this has no effect. +*/ +void QDeclarativeDataBlob::addDependency(QDeclarativeDataBlob *blob) +{ + Q_ASSERT(status() != Null); + + if (!blob || + blob->status() == Error || blob->status() == Complete || + status() == Error || status() == Complete || + m_waitingFor.contains(blob)) + return; + + blob->addref(); + m_status = WaitingForDependencies; + m_waitingFor.append(blob); + blob->m_waitingOnMe.append(this); +} + +/*! +\fn void QDeclarativeDataBlob::dataReceived(const QByteArray &data) + +Invoked when data for the blob is received. Implementors should use this callback +to determine a blob's dependencies. Within this callback you may call setError() +or addDependency(). +*/ + +/*! +Invoked once data has either been received or a network error occurred, and all +dependencies are complete. + +You can set an error in this method, but you cannot add new dependencies. Implementors +should use this callback to finalize processing of data. + +The default implementation does nothing. +*/ +void QDeclarativeDataBlob::done() +{ +} + +/*! +Invoked if there is a network error while fetching this blob. + +The default implementation sets an appropriate QDeclarativeError. +*/ +void QDeclarativeDataBlob::networkError(QNetworkReply::NetworkError networkError) +{ + Q_UNUSED(networkError); + + QDeclarativeError error; + error.setUrl(m_finalUrl); + + const char *errorString = 0; + switch (networkError) { + default: + errorString = "Network error"; + break; + case QNetworkReply::ConnectionRefusedError: + errorString = "Connection refused"; + break; + case QNetworkReply::RemoteHostClosedError: + errorString = "Remote host closed the connection"; + break; + case QNetworkReply::HostNotFoundError: + errorString = "Host not found"; + break; + case QNetworkReply::TimeoutError: + errorString = "Timeout"; + break; + case QNetworkReply::ProxyConnectionRefusedError: + case QNetworkReply::ProxyConnectionClosedError: + case QNetworkReply::ProxyNotFoundError: + case QNetworkReply::ProxyTimeoutError: + case QNetworkReply::ProxyAuthenticationRequiredError: + case QNetworkReply::UnknownProxyError: + errorString = "Proxy error"; + break; + case QNetworkReply::ContentAccessDenied: + errorString = "Access denied"; + break; + case QNetworkReply::ContentNotFoundError: + errorString = "File not found"; + break; + case QNetworkReply::AuthenticationRequiredError: + errorString = "Authentication required"; + break; + }; + + error.setDescription(QLatin1String(errorString)); + + setError(error); +} + +/*! +Called if \a blob, which was previously waited for, has an error. + +The default implementation does nothing. +*/ +void QDeclarativeDataBlob::dependencyError(QDeclarativeDataBlob *blob) +{ + Q_UNUSED(blob); +} + +/*! +Called if \a blob, which was previously waited for, has completed. + +The default implementation does nothing. +*/ +void QDeclarativeDataBlob::dependencyComplete(QDeclarativeDataBlob *blob) +{ + Q_UNUSED(blob); +} + +/*! +Called when all blobs waited for have completed. This occurs regardless of +whether they are in error, or complete state. + +The default implementation does nothing. +*/ +void QDeclarativeDataBlob::allDependenciesDone() +{ +} + +/*! +Called when the download progress of this blob changes. \a progress goes +from 0 to 1. +*/ +void QDeclarativeDataBlob::downloadProgressChanged(qreal progress) +{ + Q_UNUSED(progress); +} + +void QDeclarativeDataBlob::tryDone() +{ + if (status() != Loading && m_waitingFor.isEmpty() && !m_isDone) { + if (status() != Error) + m_status = Complete; + + m_isDone = true; + done(); + notifyAllWaitingOnMe(); + } +} + +void QDeclarativeDataBlob::cancelAllWaitingFor() +{ + while (m_waitingFor.count()) { + QDeclarativeDataBlob *blob = m_waitingFor.takeLast(); + + Q_ASSERT(blob->m_waitingOnMe.contains(this)); + + blob->m_waitingOnMe.removeOne(this); + + blob->release(); + } +} + +void QDeclarativeDataBlob::notifyAllWaitingOnMe() +{ + while (m_waitingOnMe.count()) { + QDeclarativeDataBlob *blob = m_waitingOnMe.takeLast(); + + Q_ASSERT(blob->m_waitingFor.contains(this)); + + blob->notifyComplete(this); + } +} + +void QDeclarativeDataBlob::notifyComplete(QDeclarativeDataBlob *blob) +{ + Q_ASSERT(m_waitingFor.contains(blob)); + Q_ASSERT(blob->status() == Error || blob->status() == Complete); + + m_inCallback = true; + + if (blob->status() == Error) { + dependencyError(blob); + } else if (blob->status() == Complete) { + dependencyComplete(blob); + } + + m_waitingFor.removeOne(blob); + blob->release(); + + if (!isError() && m_waitingFor.isEmpty()) + allDependenciesDone(); + + m_inCallback = false; + + tryDone(); +} + +/*! +\class QDeclarativeDataLoader +\brief The QDeclarativeDataLoader class abstracts loading files and their dependencies over the network. +\internal + +The QDeclarativeDataLoader class is provided for the exclusive use of the QDeclarativeTypeLoader class. + +Clients create QDeclarativeDataBlob instances and submit them to the QDeclarativeDataLoader class +through the QDeclarativeDataLoader::load() or QDeclarativeDataLoader::loadWithStaticData() methods. +The loader then fetches the data over the network or from the local file system in an efficient way. +QDeclarativeDataBlob is an abstract class, so should always be specialized. + +Once data is received, the QDeclarativeDataBlob::dataReceived() method is invoked on the blob. The +derived class should use this callback to process the received data. Processing of the data can +result in an error being set (QDeclarativeDataBlob::setError()), or one or more dependencies being +created (QDeclarativeDataBlob::addDependency()). Dependencies are other QDeclarativeDataBlob's that +are required before processing can fully complete. + +To complete processing, the QDeclarativeDataBlob::done() callback is invoked. done() is called when +one of these three preconditions are met. + +\list 1 +\o The QDeclarativeDataBlob has no dependencies. +\o The QDeclarativeDataBlob has an error set. +\o All the QDeclarativeDataBlob's dependencies are themselves "done()". +\endlist + +Thus QDeclarativeDataBlob::done() will always eventually be called, even if the blob has an error set. +*/ + +/*! +Create a new QDeclarativeDataLoader for \a engine. +*/ +QDeclarativeDataLoader::QDeclarativeDataLoader(QDeclarativeEngine *engine) +: m_engine(engine) +{ +} + +/*! \internal */ +QDeclarativeDataLoader::~QDeclarativeDataLoader() +{ + for (NetworkReplies::Iterator iter = m_networkReplies.begin(); iter != m_networkReplies.end(); ++iter) + (*iter)->release(); +} + +/*! +Load the provided \a blob from the network or filesystem. +*/ +void QDeclarativeDataLoader::load(QDeclarativeDataBlob *blob) +{ + Q_ASSERT(blob->status() == QDeclarativeDataBlob::Null); + Q_ASSERT(blob->m_manager == 0); + + blob->m_status = QDeclarativeDataBlob::Loading; + + if (blob->m_url.isEmpty()) { + QDeclarativeError error; + error.setDescription(QLatin1String("Invalid null URL")); + blob->setError(error); + return; + } + + QString lf = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(blob->m_url); + + if (!lf.isEmpty()) { + if (!QDeclarative_isFileCaseCorrect(lf)) { + QDeclarativeError error; + error.setUrl(blob->m_url); + error.setDescription(QLatin1String("File name case mismatch")); + blob->setError(error); + return; + } + QFile file(lf); + if (file.open(QFile::ReadOnly)) { + QByteArray data = file.readAll(); + + blob->m_progress = 1.; + blob->downloadProgressChanged(1.); + + setData(blob, data); + } else { + blob->networkError(QNetworkReply::ContentNotFoundError); + } + + } else { + + blob->m_manager = this; + QNetworkReply *reply = m_engine->networkAccessManager()->get(QNetworkRequest(blob->m_url)); + QObject::connect(reply, SIGNAL(downloadProgress(qint64,qint64)), + this, SLOT(networkReplyProgress(qint64,qint64))); + QObject::connect(reply, SIGNAL(finished()), + this, SLOT(networkReplyFinished())); + m_networkReplies.insert(reply, blob); + + blob->addref(); + } +} + +#define DATALOADER_MAXIMUM_REDIRECT_RECURSION 16 + +void QDeclarativeDataLoader::networkReplyFinished() +{ + QNetworkReply *reply = static_cast(sender()); + reply->deleteLater(); + + QDeclarativeDataBlob *blob = m_networkReplies.take(reply); + + Q_ASSERT(blob); + + blob->m_redirectCount++; + + if (blob->m_redirectCount < DATALOADER_MAXIMUM_REDIRECT_RECURSION) { + QVariant redirect = reply->attribute(QNetworkRequest::RedirectionTargetAttribute); + if (redirect.isValid()) { + QUrl url = reply->url().resolved(redirect.toUrl()); + blob->m_finalUrl = url; + + QNetworkReply *reply = m_engine->networkAccessManager()->get(QNetworkRequest(url)); + QObject::connect(reply, SIGNAL(finished()), this, SLOT(networkReplyFinished())); + m_networkReplies.insert(reply, blob); + return; + } + } + + if (reply->error()) { + blob->networkError(reply->error()); + } else { + QByteArray data = reply->readAll(); + setData(blob, data); + } + + blob->release(); +} + +void QDeclarativeDataLoader::networkReplyProgress(qint64 bytesReceived, qint64 bytesTotal) +{ + QNetworkReply *reply = static_cast(sender()); + QDeclarativeDataBlob *blob = m_networkReplies.value(reply); + + Q_ASSERT(blob); + + if (bytesTotal != 0) { + blob->m_progress = bytesReceived / bytesTotal; + blob->downloadProgressChanged(blob->m_progress); + } +} + +/*! +Load the provided \a blob with \a data. The blob's URL is not used by the data loader in this case. +*/ +void QDeclarativeDataLoader::loadWithStaticData(QDeclarativeDataBlob *blob, const QByteArray &data) +{ + Q_ASSERT(blob->status() == QDeclarativeDataBlob::Null); + Q_ASSERT(blob->m_manager == 0); + + blob->m_status = QDeclarativeDataBlob::Loading; + + setData(blob, data); +} + +/*! +Return the QDeclarativeEngine associated with this loader +*/ +QDeclarativeEngine *QDeclarativeDataLoader::engine() const +{ + return m_engine; +} + +void QDeclarativeDataLoader::setData(QDeclarativeDataBlob *blob, const QByteArray &data) +{ + blob->m_inCallback = true; + + blob->dataReceived(data); + + if (!blob->isError() && !blob->isWaiting()) + blob->allDependenciesDone(); + + if (blob->status() != QDeclarativeDataBlob::Error) + blob->m_status = QDeclarativeDataBlob::WaitingForDependencies; + + blob->m_inCallback = false; + + blob->tryDone(); +} + +/*! +Constructs a new type loader that uses the given \a engine. +*/ +QDeclarativeTypeLoader::QDeclarativeTypeLoader(QDeclarativeEngine *engine) +: QDeclarativeDataLoader(engine) +{ +} + +/*! +Destroys the type loader, first clearing the cache of any information about +loaded files. +*/ +QDeclarativeTypeLoader::~QDeclarativeTypeLoader() +{ + clearCache(); +} + +/*! +\enum QDeclarativeTypeLoader::Option + +This enum defines the options that control the way type data is handled. + +\value None The default value, indicating that no other options + are enabled. +\value PreserveParser The parser used to handle the type data is preserved + after the data has been parsed. +*/ + +/*! +Returns a QDeclarativeTypeData for the specified \a url. The QDeclarativeTypeData may be cached. +*/ +QDeclarativeTypeData *QDeclarativeTypeLoader::get(const QUrl &url) +{ + Q_ASSERT(!url.isRelative() && + (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url).isEmpty() || + !QDir::isRelativePath(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url)))); + + QDeclarativeTypeData *typeData = m_typeCache.value(url); + + if (!typeData) { + typeData = new QDeclarativeTypeData(url, None, this); + m_typeCache.insert(url, typeData); + QDeclarativeDataLoader::load(typeData); + } + + typeData->addref(); + return typeData; +} + +/*! +Returns a QDeclarativeTypeData for the given \a data with the provided base \a url. The +QDeclarativeTypeData will not be cached. + +The specified \a options control how the loader handles type data. +*/ +QDeclarativeTypeData *QDeclarativeTypeLoader::get(const QByteArray &data, const QUrl &url, Options options) +{ + QDeclarativeTypeData *typeData = new QDeclarativeTypeData(url, options, this); + QDeclarativeDataLoader::loadWithStaticData(typeData, data); + return typeData; +} + +/*! +Returns a QDeclarativeScriptData for \a url. The QDeclarativeScriptData may be cached. +*/ +QDeclarativeScriptData *QDeclarativeTypeLoader::getScript(const QUrl &url) +{ + Q_ASSERT(!url.isRelative() && + (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url).isEmpty() || + !QDir::isRelativePath(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url)))); + + QDeclarativeScriptData *scriptData = m_scriptCache.value(url); + + if (!scriptData) { + scriptData = new QDeclarativeScriptData(url); + m_scriptCache.insert(url, scriptData); + QDeclarativeDataLoader::load(scriptData); + } + + scriptData->addref(); + return scriptData; +} + +/*! +Returns a QDeclarativeQmldirData for \a url. The QDeclarativeQmldirData may be cached. +*/ +QDeclarativeQmldirData *QDeclarativeTypeLoader::getQmldir(const QUrl &url) +{ + Q_ASSERT(!url.isRelative() && + (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url).isEmpty() || + !QDir::isRelativePath(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url)))); + + QDeclarativeQmldirData *qmldirData = m_qmldirCache.value(url); + + if (!qmldirData) { + qmldirData = new QDeclarativeQmldirData(url); + m_qmldirCache.insert(url, qmldirData); + QDeclarativeDataLoader::load(qmldirData); + } + + qmldirData->addref(); + return qmldirData; +} + +/*! +Returns the absolute filename of path via a directory cache for files named +"qmldir", "*.qml", "*.js" +Returns a empty string if the path does not exist. +*/ +QString QDeclarativeTypeLoader::absoluteFilePath(const QString &path) +{ + if (path.isEmpty()) + return QString(); + if (path.at(0) == QLatin1Char(':')) { + // qrc resource + QFileInfo fileInfo(path); + return fileInfo.isFile() ? fileInfo.absoluteFilePath() : QString(); + } +#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_DARWIN) || defined(Q_OS_SYMBIAN) + QString lowPath(path.toLower()); +#else + QString lowPath(path); +#endif + int lastSlash = lowPath.lastIndexOf(QLatin1Char('/')); + QString dirPath = lowPath.left(lastSlash); + + StringSet *fileSet = 0; + QHash::const_iterator it = m_importDirCache.find(dirPath); + if (it == m_importDirCache.end()) { + StringSet *files = qmlFilesInDirectory(path.left(lastSlash)); + m_importDirCache.insert(dirPath, files); + fileSet = files; + } else { + fileSet = *it; + } + if (!fileSet) + return QString(); + + QString absoluteFilePath = fileSet->contains(QString(lowPath.constData()+lastSlash+1, lowPath.length()-lastSlash-1)) ? path : QString(); + if (absoluteFilePath.length() > 2 && absoluteFilePath.at(0) != QLatin1Char('/') && absoluteFilePath.at(1) != QLatin1Char(':')) + absoluteFilePath = QFileInfo(absoluteFilePath).absoluteFilePath(); + + return absoluteFilePath; +} + +/*! +Return a QDeclarativeDirParser for absoluteFilePath. The QDeclarativeDirParser may be cached. +*/ +const QDeclarativeDirParser *QDeclarativeTypeLoader::qmlDirParser(const QString &absoluteFilePath) +{ + QDeclarativeDirParser *qmldirParser; + QHash::const_iterator it = m_importQmlDirCache.find(absoluteFilePath); + if (it == m_importQmlDirCache.end()) { + qmldirParser = new QDeclarativeDirParser; + qmldirParser->setFileSource(absoluteFilePath); + qmldirParser->setUrl(QUrl::fromLocalFile(absoluteFilePath)); + qmldirParser->parse(); + m_importQmlDirCache.insert(absoluteFilePath, qmldirParser); + } else { + qmldirParser = *it; + } + + return qmldirParser; +} + +/* +Clears cached information about loaded files, including any type data, scripts +and qmldir information. +*/ +void QDeclarativeTypeLoader::clearCache() +{ + for (TypeCache::Iterator iter = m_typeCache.begin(); iter != m_typeCache.end(); ++iter) + (*iter)->release(); + for (ScriptCache::Iterator iter = m_scriptCache.begin(); iter != m_scriptCache.end(); ++iter) + (*iter)->release(); + for (QmldirCache::Iterator iter = m_qmldirCache.begin(); iter != m_qmldirCache.end(); ++iter) + (*iter)->release(); + qDeleteAll(m_importDirCache); + qDeleteAll(m_importQmlDirCache); + + m_typeCache.clear(); + m_scriptCache.clear(); + m_qmldirCache.clear(); + m_importDirCache.clear(); + m_importQmlDirCache.clear(); +} + + +QDeclarativeTypeData::QDeclarativeTypeData(const QUrl &url, QDeclarativeTypeLoader::Options options, + QDeclarativeTypeLoader *manager) +: QDeclarativeDataBlob(url, QmlFile), m_options(options), m_imports(manager), m_typesResolved(false), + m_compiledData(0), m_typeLoader(manager) +{ +} + +QDeclarativeTypeData::~QDeclarativeTypeData() +{ + for (int ii = 0; ii < m_scripts.count(); ++ii) + m_scripts.at(ii).script->release(); + for (int ii = 0; ii < m_qmldirs.count(); ++ii) + m_qmldirs.at(ii)->release(); + for (int ii = 0; ii < m_types.count(); ++ii) + if (m_types.at(ii).typeData) m_types.at(ii).typeData->release(); + if (m_compiledData) + m_compiledData->release(); +} + +QDeclarativeTypeLoader *QDeclarativeTypeData::typeLoader() const +{ + return m_typeLoader; +} + +const QDeclarativeImports &QDeclarativeTypeData::imports() const +{ + return m_imports; +} + +const QDeclarativeScriptParser &QDeclarativeTypeData::parser() const +{ + return scriptParser; +} + +const QList &QDeclarativeTypeData::resolvedTypes() const +{ + return m_types; +} + +const QList &QDeclarativeTypeData::resolvedScripts() const +{ + return m_scripts; +} + +QDeclarativeCompiledData *QDeclarativeTypeData::compiledData() const +{ + if (m_compiledData) + m_compiledData->addref(); + + return m_compiledData; +} + +void QDeclarativeTypeData::registerCallback(TypeDataCallback *callback) +{ + Q_ASSERT(!m_callbacks.contains(callback)); + m_callbacks.append(callback); +} + +void QDeclarativeTypeData::unregisterCallback(TypeDataCallback *callback) +{ + Q_ASSERT(m_callbacks.contains(callback)); + m_callbacks.removeOne(callback); + Q_ASSERT(!m_callbacks.contains(callback)); +} + +void QDeclarativeTypeData::done() +{ + addref(); + + // Check all script dependencies for errors + for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) { + const ScriptReference &script = m_scripts.at(ii); + Q_ASSERT(script.script->isCompleteOrError()); + if (script.script->isError()) { + QList errors = script.script->errors(); + QDeclarativeError error; + error.setUrl(finalUrl()); + error.setLine(script.location.line); + error.setColumn(script.location.column); + error.setDescription(QDeclarativeTypeLoader::tr("Script %1 unavailable").arg(script.script->url().toString())); + errors.prepend(error); + setError(errors); + } + } + + // Check all type dependencies for errors + for (int ii = 0; !isError() && ii < m_types.count(); ++ii) { + const TypeReference &type = m_types.at(ii); + Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError()); + if (type.typeData && type.typeData->isError()) { + QString typeName = scriptParser.referencedTypes().at(ii)->name; + + QList errors = type.typeData->errors(); + QDeclarativeError error; + error.setUrl(finalUrl()); + error.setLine(type.location.line); + error.setColumn(type.location.column); + error.setDescription(QDeclarativeTypeLoader::tr("Type %1 unavailable").arg(typeName)); + errors.prepend(error); + setError(errors); + } + } + + // Compile component + if (!isError()) + compile(); + + if (!(m_options & QDeclarativeTypeLoader::PreserveParser)) + scriptParser.clear(); + + // Notify callbacks + while (!m_callbacks.isEmpty()) { + TypeDataCallback *callback = m_callbacks.takeFirst(); + callback->typeDataReady(this); + } + + release(); +} + +void QDeclarativeTypeData::dataReceived(const QByteArray &data) +{ + if (!scriptParser.parse(data, finalUrl())) { + setError(scriptParser.errors()); + return; + } + + m_imports.setBaseUrl(finalUrl()); + + foreach (const QDeclarativeScriptParser::Import &import, scriptParser.imports()) { + if (import.type == QDeclarativeScriptParser::Import::File && import.qualifier.isEmpty()) { + QUrl importUrl = finalUrl().resolved(QUrl(import.uri + QLatin1String("/qmldir"))); + if (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(importUrl).isEmpty()) { + QDeclarativeQmldirData *data = typeLoader()->getQmldir(importUrl); + addDependency(data); + m_qmldirs << data; + } + } else if (import.type == QDeclarativeScriptParser::Import::Script) { + QUrl scriptUrl = finalUrl().resolved(QUrl(import.uri)); + QDeclarativeScriptData *data = typeLoader()->getScript(scriptUrl); + addDependency(data); + + ScriptReference ref; + ref.location = import.location.start; + ref.qualifier = import.qualifier; + ref.script = data; + m_scripts << ref; + + } + } + + if (!finalUrl().scheme().isEmpty()) { + QUrl importUrl = finalUrl().resolved(QUrl(QLatin1String("qmldir"))); + if (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(importUrl).isEmpty()) { + QDeclarativeQmldirData *data = typeLoader()->getQmldir(importUrl); + addDependency(data); + m_qmldirs << data; + } + } +} + +void QDeclarativeTypeData::allDependenciesDone() +{ + if (!m_typesResolved) { + resolveTypes(); + m_typesResolved = true; + } +} + +void QDeclarativeTypeData::downloadProgressChanged(qreal p) +{ + for (int ii = 0; ii < m_callbacks.count(); ++ii) { + TypeDataCallback *callback = m_callbacks.at(ii); + callback->typeDataProgress(this, p); + } +} + +void QDeclarativeTypeData::compile() +{ + Q_ASSERT(m_compiledData == 0); + QDeclarativeDebugTrace::startRange(QDeclarativeDebugTrace::Compiling); + + m_compiledData = new QDeclarativeCompiledData(typeLoader()->engine()); + m_compiledData->url = m_imports.baseUrl(); + m_compiledData->name = m_compiledData->url.toString(); + QDeclarativeDebugTrace::rangeData(QDeclarativeDebugTrace::Compiling, m_compiledData->name); + + QDeclarativeCompiler compiler; + if (!compiler.compile(typeLoader()->engine(), this, m_compiledData)) { + setError(compiler.errors()); + m_compiledData->release(); + m_compiledData = 0; + } + QDeclarativeDebugTrace::endRange(QDeclarativeDebugTrace::Compiling); +} + +void QDeclarativeTypeData::resolveTypes() +{ + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(m_typeLoader->engine()); + QDeclarativeImportDatabase *importDatabase = &ep->importDatabase; + + // For local urls, add an implicit import "." as first (most overridden) lookup. + // This will also trigger the loading of the qmldir and the import of any native + // types from available plugins. + if (QDeclarativeQmldirData *qmldir = qmldirForUrl(finalUrl().resolved(QUrl(QLatin1String("./qmldir"))))) { + m_imports.addImport(importDatabase, QLatin1String("."), + QString(), -1, -1, QDeclarativeScriptParser::Import::File, + qmldir->dirComponents(), 0); + } else { + m_imports.addImport(importDatabase, QLatin1String("."), + QString(), -1, -1, QDeclarativeScriptParser::Import::File, + QDeclarativeDirComponents(), 0); + } + + foreach (const QDeclarativeScriptParser::Import &import, scriptParser.imports()) { + QDeclarativeDirComponents qmldircomponentsnetwork; + if (import.type == QDeclarativeScriptParser::Import::Script) + continue; + + if (import.type == QDeclarativeScriptParser::Import::File && import.qualifier.isEmpty()) { + QUrl qmldirUrl = finalUrl().resolved(QUrl(import.uri + QLatin1String("/qmldir"))); + if (QDeclarativeQmldirData *qmldir = qmldirForUrl(qmldirUrl)) + qmldircomponentsnetwork = qmldir->dirComponents(); + } + + int vmaj = -1; + int vmin = -1; + + if (!import.version.isEmpty()) { + int dot = import.version.indexOf(QLatin1Char('.')); + if (dot < 0) { + vmaj = import.version.toInt(); + vmin = 0; + } else { + vmaj = import.version.left(dot).toInt(); + vmin = import.version.mid(dot+1).toInt(); + } + } + + QString errorString; + if (!m_imports.addImport(importDatabase, import.uri, import.qualifier, + vmaj, vmin, import.type, qmldircomponentsnetwork, &errorString)) { + QDeclarativeError error; + error.setUrl(m_imports.baseUrl()); + error.setDescription(errorString); + error.setLine(import.location.start.line); + error.setColumn(import.location.start.column); + + setError(error); + return; + } + } + + foreach (QDeclarativeScriptParser::TypeReference *parserRef, scriptParser.referencedTypes()) { + QByteArray typeName = parserRef->name.toUtf8(); + + TypeReference ref; + + QUrl url; + int majorVersion; + int minorVersion; + QDeclarativeImportedNamespace *typeNamespace = 0; + QString errorString; + + if (!m_imports.resolveType(typeName, &ref.type, &url, &majorVersion, &minorVersion, + &typeNamespace, &errorString) || typeNamespace) { + // Known to not be a type: + // - known to be a namespace (Namespace {}) + // - type with unknown namespace (UnknownNamespace.SomeType {}) + QDeclarativeError error; + error.setUrl(m_imports.baseUrl()); + QString userTypeName = parserRef->name; + userTypeName.replace(QLatin1Char('/'),QLatin1Char('.')); + if (typeNamespace) + error.setDescription(QDeclarativeTypeLoader::tr("Namespace %1 cannot be used as a type").arg(userTypeName)); + else + error.setDescription(QDeclarativeTypeLoader::tr("%1 %2").arg(userTypeName).arg(errorString)); + + if (!parserRef->refObjects.isEmpty()) { + QDeclarativeParser::Object *obj = parserRef->refObjects.first(); + error.setLine(obj->location.start.line); + error.setColumn(obj->location.start.column); + } + + setError(error); + return; + } + + if (ref.type) { + ref.majorVersion = majorVersion; + ref.minorVersion = minorVersion; + foreach (QDeclarativeParser::Object *obj, parserRef->refObjects) { + // store namespace for DOM + obj->majorVersion = majorVersion; + obj->minorVersion = minorVersion; + } + } else { + ref.typeData = typeLoader()->get(url); + addDependency(ref.typeData); + } + + if (parserRef->refObjects.count()) + ref.location = parserRef->refObjects.first()->location.start; + + m_types << ref; + } +} + +QDeclarativeQmldirData *QDeclarativeTypeData::qmldirForUrl(const QUrl &url) +{ + for (int ii = 0; ii < m_qmldirs.count(); ++ii) { + if (m_qmldirs.at(ii)->url() == url) + return m_qmldirs.at(ii); + } + return 0; +} + +QDeclarativeScriptData::QDeclarativeScriptData(const QUrl &url) +: QDeclarativeDataBlob(url, JavaScriptFile), m_pragmas(QDeclarativeParser::Object::ScriptBlock::None) +{ +} + +QDeclarativeParser::Object::ScriptBlock::Pragmas QDeclarativeScriptData::pragmas() const +{ + return m_pragmas; +} + +QString QDeclarativeScriptData::scriptSource() const +{ + return m_source; +} + +void QDeclarativeScriptData::dataReceived(const QByteArray &data) +{ + m_source = QString::fromUtf8(data); + m_pragmas = QDeclarativeScriptParser::extractPragmas(m_source); +} + +QDeclarativeQmldirData::QDeclarativeQmldirData(const QUrl &url) +: QDeclarativeDataBlob(url, QmldirFile) +{ +} + +const QDeclarativeDirComponents &QDeclarativeQmldirData::dirComponents() const +{ + return m_components; +} + +void QDeclarativeQmldirData::dataReceived(const QByteArray &data) +{ + QDeclarativeDirParser parser; + parser.setSource(QString::fromUtf8(data)); + parser.parse(); + m_components = parser.components(); +} + +QT_END_NAMESPACE + diff --git a/src/declarative/qml/qdeclarativetypeloader_p.h b/src/declarative/qml/qdeclarativetypeloader_p.h new file mode 100644 index 00000000..d377b4b9 --- /dev/null +++ b/src/declarative/qml/qdeclarativetypeloader_p.h @@ -0,0 +1,330 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVETYPELOADER_P_H +#define QDECLARATIVETYPELOADER_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 +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeScriptData; +class QDeclarativeQmldirData; +class QDeclarativeTypeLoader; +class QDeclarativeCompiledData; +class QDeclarativeComponentPrivate; +class QDeclarativeTypeData; +class QDeclarativeDataLoader; + +class Q_AUTOTEST_EXPORT QDeclarativeDataBlob : public QDeclarativeRefCount +{ +public: + enum Status { + Null, // Prior to QDeclarativeDataLoader::load() + Loading, // Prior to data being received and dataReceived() being called + WaitingForDependencies, // While there are outstanding addDependency()s + Complete, // Finished + Error // Error + }; + + enum Type { + QmlFile, + JavaScriptFile, + QmldirFile + }; + + QDeclarativeDataBlob(const QUrl &, Type); + virtual ~QDeclarativeDataBlob(); + + Type type() const; + + Status status() const; + bool isNull() const; + bool isLoading() const; + bool isWaiting() const; + bool isComplete() const; + bool isError() const; + bool isCompleteOrError() const; + + qreal progress() const; + + QUrl url() const; + QUrl finalUrl() const; + + QList errors() const; + + void setError(const QDeclarativeError &); + void setError(const QList &errors); + + void addDependency(QDeclarativeDataBlob *); + +protected: + virtual void dataReceived(const QByteArray &) = 0; + + virtual void done(); + virtual void networkError(QNetworkReply::NetworkError); + + virtual void dependencyError(QDeclarativeDataBlob *); + virtual void dependencyComplete(QDeclarativeDataBlob *); + virtual void allDependenciesDone(); + + virtual void downloadProgressChanged(qreal); + +private: + friend class QDeclarativeDataLoader; + void tryDone(); + void cancelAllWaitingFor(); + void notifyAllWaitingOnMe(); + void notifyComplete(QDeclarativeDataBlob *); + + Type m_type; + Status m_status; + qreal m_progress; + + QUrl m_url; + QUrl m_finalUrl; + + // List of QDeclarativeDataBlob's that are waiting for me to complete. + QList m_waitingOnMe; + + // List of QDeclarativeDataBlob's that I am waiting for to complete. + QList m_waitingFor; + + // Manager that is currently fetching data for me + QDeclarativeDataLoader *m_manager; + int m_redirectCount:30; + bool m_inCallback:1; + bool m_isDone:1; + + QList m_errors; +}; + +class Q_AUTOTEST_EXPORT QDeclarativeDataLoader : public QObject +{ + Q_OBJECT +public: + QDeclarativeDataLoader(QDeclarativeEngine *); + ~QDeclarativeDataLoader(); + + void load(QDeclarativeDataBlob *); + void loadWithStaticData(QDeclarativeDataBlob *, const QByteArray &); + + QDeclarativeEngine *engine() const; + +private slots: + void networkReplyFinished(); + void networkReplyProgress(qint64,qint64); + +private: + void setData(QDeclarativeDataBlob *, const QByteArray &); + + QDeclarativeEngine *m_engine; + typedef QHash NetworkReplies; + NetworkReplies m_networkReplies; +}; + + +class Q_AUTOTEST_EXPORT QDeclarativeTypeLoader : public QDeclarativeDataLoader +{ + Q_OBJECT +public: + QDeclarativeTypeLoader(QDeclarativeEngine *); + ~QDeclarativeTypeLoader(); + + enum Option { + None, + PreserveParser + }; + Q_DECLARE_FLAGS(Options, Option) + + QDeclarativeTypeData *get(const QUrl &url); + QDeclarativeTypeData *get(const QByteArray &, const QUrl &url, Options = None); + void clearCache(); + + QDeclarativeScriptData *getScript(const QUrl &); + QDeclarativeQmldirData *getQmldir(const QUrl &); + + QString absoluteFilePath(const QString &path); + const QDeclarativeDirParser *qmlDirParser(const QString &absoluteFilePath); + +private: + typedef QHash TypeCache; + typedef QHash ScriptCache; + typedef QHash QmldirCache; + typedef QSet StringSet; + typedef QHash ImportDirCache; + typedef QHash ImportQmlDirCache; + + TypeCache m_typeCache; + ScriptCache m_scriptCache; + QmldirCache m_qmldirCache; + ImportDirCache m_importDirCache; + ImportQmlDirCache m_importQmlDirCache; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QDeclarativeTypeLoader::Options) + +class Q_AUTOTEST_EXPORT QDeclarativeTypeData : public QDeclarativeDataBlob +{ +public: + struct TypeReference + { + TypeReference() : type(0), majorVersion(0), minorVersion(0), typeData(0) {} + + QDeclarativeParser::Location location; + QDeclarativeType *type; + int majorVersion; + int minorVersion; + QDeclarativeTypeData *typeData; + }; + + struct ScriptReference + { + ScriptReference() : script(0) {} + + QDeclarativeParser::Location location; + QString qualifier; + QDeclarativeScriptData *script; + }; + + QDeclarativeTypeData(const QUrl &, QDeclarativeTypeLoader::Options, QDeclarativeTypeLoader *); + ~QDeclarativeTypeData(); + + QDeclarativeTypeLoader *typeLoader() const; + + const QDeclarativeImports &imports() const; + const QDeclarativeScriptParser &parser() const; + + const QList &resolvedTypes() const; + const QList &resolvedScripts() const; + + QDeclarativeCompiledData *compiledData() const; + + // Used by QDeclarativeComponent to get notifications + struct TypeDataCallback { + ~TypeDataCallback() {} + virtual void typeDataProgress(QDeclarativeTypeData *, qreal) {} + virtual void typeDataReady(QDeclarativeTypeData *) {} + }; + void registerCallback(TypeDataCallback *); + void unregisterCallback(TypeDataCallback *); + +protected: + virtual void done(); + virtual void dataReceived(const QByteArray &); + virtual void allDependenciesDone(); + virtual void downloadProgressChanged(qreal); + +private: + void resolveTypes(); + void compile(); + + QDeclarativeTypeLoader::Options m_options; + + QDeclarativeQmldirData *qmldirForUrl(const QUrl &); + + QDeclarativeScriptParser scriptParser; + QDeclarativeImports m_imports; + + QList m_scripts; + QList m_qmldirs; + + QList m_types; + bool m_typesResolved:1; + + QDeclarativeCompiledData *m_compiledData; + + QList m_callbacks; + + QDeclarativeTypeLoader *m_typeLoader; +}; + +class Q_AUTOTEST_EXPORT QDeclarativeScriptData : public QDeclarativeDataBlob +{ +public: + QDeclarativeScriptData(const QUrl &); + + QDeclarativeParser::Object::ScriptBlock::Pragmas pragmas() const; + QString scriptSource() const; + +protected: + virtual void dataReceived(const QByteArray &); + +private: + QDeclarativeParser::Object::ScriptBlock::Pragmas m_pragmas; + QString m_source; +}; + +class Q_AUTOTEST_EXPORT QDeclarativeQmldirData : public QDeclarativeDataBlob +{ +public: + QDeclarativeQmldirData(const QUrl &); + + const QDeclarativeDirComponents &dirComponents() const; + +protected: + virtual void dataReceived(const QByteArray &); + +private: + QDeclarativeDirComponents m_components; + +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVETYPELOADER_P_H diff --git a/src/declarative/qml/qdeclarativetypenamecache.cpp b/src/declarative/qml/qdeclarativetypenamecache.cpp new file mode 100644 index 00000000..29b88750 --- /dev/null +++ b/src/declarative/qml/qdeclarativetypenamecache.cpp @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativetypenamecache_p.h" + +#include "private/qdeclarativeengine_p.h" + +QT_BEGIN_NAMESPACE + +QDeclarativeTypeNameCache::QDeclarativeTypeNameCache(QDeclarativeEngine *e) +: QDeclarativeCleanup(e), engine(e) +{ +} + +QDeclarativeTypeNameCache::~QDeclarativeTypeNameCache() +{ + clear(); +} + +void QDeclarativeTypeNameCache::clear() +{ + qDeleteAll(stringCache); + stringCache.clear(); + identifierCache.clear(); + engine = 0; +} + +void QDeclarativeTypeNameCache::add(const QString &name, int importedScriptIndex) +{ + if (stringCache.contains(name)) + return; + + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); + + RData *data = new RData; + // ### Use typename class + data->identifier = ep->objectClass->createPersistentIdentifier(name); + data->importedScriptIndex = importedScriptIndex; + stringCache.insert(name, data); + identifierCache.insert(data->identifier.identifier, data); +} + +void QDeclarativeTypeNameCache::add(const QString &name, QDeclarativeType *type) +{ + if (stringCache.contains(name)) + return; + + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); + + RData *data = new RData; + // ### Use typename class + data->identifier = ep->objectClass->createPersistentIdentifier(name); + data->type = type; + stringCache.insert(name, data); + identifierCache.insert(data->identifier.identifier, data); +} + +void QDeclarativeTypeNameCache::add(const QString &name, QDeclarativeTypeNameCache *typeNamespace) +{ + if (stringCache.contains(name)) + return; + + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); + + RData *data = new RData; + // ### Use typename class + data->identifier = ep->objectClass->createPersistentIdentifier(name); + data->typeNamespace = typeNamespace; + stringCache.insert(name, data); + identifierCache.insert(data->identifier.identifier, data); + typeNamespace->addref(); +} + +QDeclarativeTypeNameCache::Data *QDeclarativeTypeNameCache::data(const QString &id) const +{ + return stringCache.value(id); +} + +QT_END_NAMESPACE + diff --git a/src/declarative/qml/qdeclarativetypenamecache_p.h b/src/declarative/qml/qdeclarativetypenamecache_p.h new file mode 100644 index 00000000..f86629bb --- /dev/null +++ b/src/declarative/qml/qdeclarativetypenamecache_p.h @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVETYPENAMECACHE_P_H +#define QDECLARATIVETYPENAMECACHE_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 "private/qdeclarativerefcount_p.h" +#include "private/qdeclarativecleanup_p.h" + +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeType; +class QDeclarativeEngine; +class QDeclarativeTypeNameCache : public QDeclarativeRefCount, public QDeclarativeCleanup +{ +public: + QDeclarativeTypeNameCache(QDeclarativeEngine *); + virtual ~QDeclarativeTypeNameCache(); + + struct Data { + inline Data(); + inline ~Data(); + QDeclarativeType *type; + QDeclarativeTypeNameCache *typeNamespace; + int importedScriptIndex; + }; + + void add(const QString &, int); + void add(const QString &, QDeclarativeType *); + void add(const QString &, QDeclarativeTypeNameCache *); + + Data *data(const QString &) const; + inline Data *data(const QScriptDeclarativeClass::Identifier &id) const; + +protected: + virtual void clear(); + +private: + struct RData : public Data { + QScriptDeclarativeClass::PersistentIdentifier identifier; + }; + typedef QHash StringCache; + typedef QHash IdentifierCache; + + StringCache stringCache; + IdentifierCache identifierCache; + QDeclarativeEngine *engine; +}; + +QDeclarativeTypeNameCache::Data::Data() +: type(0), typeNamespace(0), importedScriptIndex(-1) +{ +} + +QDeclarativeTypeNameCache::Data::~Data() +{ + if (typeNamespace) typeNamespace->release(); +} + +QDeclarativeTypeNameCache::Data *QDeclarativeTypeNameCache::data(const QScriptDeclarativeClass::Identifier &id) const +{ + return identifierCache.value(id); +} + +QT_END_NAMESPACE + +#endif // QDECLARATIVETYPENAMECACHE_P_H + diff --git a/src/declarative/qml/qdeclarativetypenamescriptclass.cpp b/src/declarative/qml/qdeclarativetypenamescriptclass.cpp new file mode 100644 index 00000000..585f12d6 --- /dev/null +++ b/src/declarative/qml/qdeclarativetypenamescriptclass.cpp @@ -0,0 +1,165 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativetypenamescriptclass_p.h" + +#include "private/qdeclarativeengine_p.h" +#include "private/qdeclarativetypenamecache_p.h" + +QT_BEGIN_NAMESPACE + +struct TypeNameData : public QScriptDeclarativeClass::Object { + TypeNameData(QObject *o, QDeclarativeType *t, QDeclarativeTypeNameScriptClass::TypeNameMode m) : object(o), type(t), typeNamespace(0), mode(m) {} + TypeNameData(QObject *o, QDeclarativeTypeNameCache *n, QDeclarativeTypeNameScriptClass::TypeNameMode m) : object(o), type(0), typeNamespace(n), mode(m) { + if (typeNamespace) typeNamespace->addref(); + } + ~TypeNameData() { + if (typeNamespace) typeNamespace->release(); + } + + QObject *object; + QDeclarativeType *type; + QDeclarativeTypeNameCache *typeNamespace; + QDeclarativeTypeNameScriptClass::TypeNameMode mode; +}; + +QDeclarativeTypeNameScriptClass::QDeclarativeTypeNameScriptClass(QDeclarativeEngine *bindEngine) +: QScriptDeclarativeClass(QDeclarativeEnginePrivate::getScriptEngine(bindEngine)), + engine(bindEngine), object(0), type(0) +{ +} + +QDeclarativeTypeNameScriptClass::~QDeclarativeTypeNameScriptClass() +{ +} + +QScriptValue QDeclarativeTypeNameScriptClass::newObject(QObject *object, QDeclarativeType *type, TypeNameMode mode) +{ + QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine); + + return QScriptDeclarativeClass::newObject(scriptEngine, this, new TypeNameData(object, type, mode)); +} + +QScriptValue QDeclarativeTypeNameScriptClass::newObject(QObject *object, QDeclarativeTypeNameCache *ns, TypeNameMode mode) +{ + QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine); + + return QScriptDeclarativeClass::newObject(scriptEngine, this, new TypeNameData(object, ns, mode)); +} + +QScriptClass::QueryFlags +QDeclarativeTypeNameScriptClass::queryProperty(Object *obj, const Identifier &name, + QScriptClass::QueryFlags flags) +{ + Q_UNUSED(flags); + + TypeNameData *data = (TypeNameData *)obj; + + object = 0; + type = 0; + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); + + if (data->typeNamespace) { + + QDeclarativeTypeNameCache::Data *d = data->typeNamespace->data(name); + if (d && d->type) { + type = d->type; + return QScriptClass::HandlesReadAccess; + } else { + return 0; + } + + } else if (data->type) { + + if (startsWithUpper(name)) { + QString strName = toString(name); + // Must be an enum + if (data->mode == IncludeEnums) { + // ### Optimize + QByteArray enumName = strName.toUtf8(); + const QMetaObject *metaObject = data->type->baseMetaObject(); + for (int ii = metaObject->enumeratorCount() - 1; ii >= 0; --ii) { + QMetaEnum e = metaObject->enumerator(ii); + int value = e.keyToValue(enumName.constData()); + if (value != -1) { + enumValue = value; + return QScriptClass::HandlesReadAccess; + } + } + } + return 0; + } else if (data->object) { + // Must be an attached property + object = qmlAttachedPropertiesObjectById(data->type->attachedPropertiesId(), data->object); + if (!object) return 0; + return ep->objectClass->queryProperty(object, name, flags, 0); + } + + } + + return 0; +} + +QDeclarativeTypeNameScriptClass::Value +QDeclarativeTypeNameScriptClass::property(Object *obj, const Identifier &name) +{ + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); + QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine); + if (type) { + return Value(scriptEngine, newObject(((TypeNameData *)obj)->object, type, ((TypeNameData *)obj)->mode)); + } else if (object) { + return ep->objectClass->property(object, name); + } else { + return Value(scriptEngine, enumValue); + } +} + +void QDeclarativeTypeNameScriptClass::setProperty(Object *, const Identifier &n, const QScriptValue &v) +{ + Q_ASSERT(object); + Q_ASSERT(!type); + + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); + ep->objectClass->setProperty(object, n, v, context()); +} + +QT_END_NAMESPACE + diff --git a/src/declarative/qml/qdeclarativetypenamescriptclass_p.h b/src/declarative/qml/qdeclarativetypenamescriptclass_p.h new file mode 100644 index 00000000..292e8934 --- /dev/null +++ b/src/declarative/qml/qdeclarativetypenamescriptclass_p.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVETYPENAMESCRIPTCLASS_P_H +#define QDECLARATIVETYPENAMESCRIPTCLASS_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 "private/qdeclarativeengine_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeEngine; +class QDeclarativeType; +class QDeclarativeTypeNameCache; +class QDeclarativeTypeNameScriptClass : public QScriptDeclarativeClass +{ +public: + QDeclarativeTypeNameScriptClass(QDeclarativeEngine *); + ~QDeclarativeTypeNameScriptClass(); + + enum TypeNameMode { IncludeEnums, ExcludeEnums }; + QScriptValue newObject(QObject *, QDeclarativeType *, TypeNameMode = IncludeEnums); + QScriptValue newObject(QObject *, QDeclarativeTypeNameCache *, TypeNameMode = IncludeEnums); + +protected: + virtual QScriptClass::QueryFlags queryProperty(Object *, const Identifier &, + QScriptClass::QueryFlags flags); + + virtual Value property(Object *, const Identifier &); + virtual void setProperty(Object *, const Identifier &name, const QScriptValue &); + +private: + QDeclarativeEngine *engine; + QObject *object; + QDeclarativeType *type; + quint32 enumValue; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVETYPENAMESCRIPTCLASS_P_H + diff --git a/src/declarative/qml/qdeclarativetypenotavailable.cpp b/src/declarative/qml/qdeclarativetypenotavailable.cpp new file mode 100644 index 00000000..20be6d4d --- /dev/null +++ b/src/declarative/qml/qdeclarativetypenotavailable.cpp @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativetypenotavailable_p.h" + +QT_BEGIN_NAMESPACE + +int qmlRegisterTypeNotAvailable(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& message) +{ + return qmlRegisterUncreatableType(uri,versionMajor,versionMinor,qmlName,message); +} + +QDeclarativeTypeNotAvailable::QDeclarativeTypeNotAvailable() { } + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativetypenotavailable_p.h b/src/declarative/qml/qdeclarativetypenotavailable_p.h new file mode 100644 index 00000000..7adce6c9 --- /dev/null +++ b/src/declarative/qml/qdeclarativetypenotavailable_p.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVETYPENOTAVAILABLE_H +#define QDECLARATIVETYPENOTAVAILABLE_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeTypeNotAvailable : public QObject { + Q_OBJECT +public: + QDeclarativeTypeNotAvailable(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeTypeNotAvailable) + +QT_END_HEADER + +#endif // QDECLARATIVETYPENOTAVAILABLE_H diff --git a/src/declarative/qml/qdeclarativevaluetype.cpp b/src/declarative/qml/qdeclarativevaluetype.cpp new file mode 100644 index 00000000..850928e3 --- /dev/null +++ b/src/declarative/qml/qdeclarativevaluetype.cpp @@ -0,0 +1,1011 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativevaluetype_p.h" + +#include "private/qdeclarativemetatype_p.h" +#include "private/qfont_p.h" + +#include + +QT_BEGIN_NAMESPACE + +template +int qmlRegisterValueTypeEnums(const char *uri, int versionMajor, int versionMinor, const char *qmlName) +{ + QByteArray name(T::staticMetaObject.className()); + + QByteArray pointerName(name + '*'); + + QDeclarativePrivate::RegisterType type = { + 0, + + qRegisterMetaType(pointerName.constData()), 0, 0, 0, + + QString(), + + uri, versionMajor, versionMinor, qmlName, &T::staticMetaObject, + + 0, 0, + + 0, 0, 0, + + 0, 0, + + 0, + 0 + }; + + return QDeclarativePrivate::qmlregister(QDeclarativePrivate::TypeRegistration, &type); +} + +QDeclarativeValueTypeFactory::QDeclarativeValueTypeFactory() +{ + // ### Optimize + for (unsigned int ii = 0; ii < (QVariant::UserType - 1); ++ii) + valueTypes[ii] = valueType(ii); +} + +QDeclarativeValueTypeFactory::~QDeclarativeValueTypeFactory() +{ + for (unsigned int ii = 0; ii < (QVariant::UserType - 1); ++ii) + delete valueTypes[ii]; +} + +bool QDeclarativeValueTypeFactory::isValueType(int idx) +{ + if ((uint)idx < QVariant::UserType) + return true; + return false; +} + +void QDeclarativeValueTypeFactory::registerValueTypes() +{ + qmlRegisterValueTypeEnums("QtQuick",1,0,"Easing"); + qmlRegisterValueTypeEnums("QtQuick",1,0,"Font"); +#ifndef QT_NO_IMPORT_QT47_QML + qmlRegisterValueTypeEnums("Qt",4,7,"Easing"); + qmlRegisterValueTypeEnums("Qt",4,7,"Font"); +#endif +} + +QDeclarativeValueType *QDeclarativeValueTypeFactory::valueType(int t) +{ + QDeclarativeValueType *rv = 0; + + switch (t) { + case QVariant::Point: + rv = new QDeclarativePointValueType; + break; + case QVariant::PointF: + rv = new QDeclarativePointFValueType; + break; + case QVariant::Size: + rv = new QDeclarativeSizeValueType; + break; + case QVariant::SizeF: + rv = new QDeclarativeSizeFValueType; + break; + case QVariant::Rect: + rv = new QDeclarativeRectValueType; + break; + case QVariant::RectF: + rv = new QDeclarativeRectFValueType; + break; + case QVariant::Vector2D: + rv = new QDeclarativeVector2DValueType; + break; + case QVariant::Vector3D: + rv = new QDeclarativeVector3DValueType; + break; + case QVariant::Vector4D: + rv = new QDeclarativeVector4DValueType; + break; + case QVariant::Quaternion: + rv = new QDeclarativeQuaternionValueType; + break; + case QVariant::Matrix4x4: + rv = new QDeclarativeMatrix4x4ValueType; + break; + case QVariant::EasingCurve: + rv = new QDeclarativeEasingValueType; + break; + case QVariant::Font: + rv = new QDeclarativeFontValueType; + break; + default: + break; + } + + Q_ASSERT(!rv || rv->metaObject()->propertyCount() < 32); + return rv; +} + +QDeclarativeValueType::QDeclarativeValueType(QObject *parent) +: QObject(parent) +{ +} + +QDeclarativePointFValueType::QDeclarativePointFValueType(QObject *parent) +: QDeclarativeValueType(parent) +{ +} + +void QDeclarativePointFValueType::read(QObject *obj, int idx) +{ + void *a[] = { &point, 0 }; + QMetaObject::metacall(obj, QMetaObject::ReadProperty, idx, a); +} + +void QDeclarativePointFValueType::write(QObject *obj, int idx, QDeclarativePropertyPrivate::WriteFlags flags) +{ + int status = -1; + void *a[] = { &point, 0, &status, &flags }; + QMetaObject::metacall(obj, QMetaObject::WriteProperty, idx, a); +} + +QVariant QDeclarativePointFValueType::value() +{ + return QVariant(point); +} + +void QDeclarativePointFValueType::setValue(QVariant value) +{ + point = qvariant_cast(value); +} + +qreal QDeclarativePointFValueType::x() const +{ + return point.x(); +} + +qreal QDeclarativePointFValueType::y() const +{ + return point.y(); +} + +void QDeclarativePointFValueType::setX(qreal x) +{ + point.setX(x); +} + +void QDeclarativePointFValueType::setY(qreal y) +{ + point.setY(y); +} + +QDeclarativePointValueType::QDeclarativePointValueType(QObject *parent) +: QDeclarativeValueType(parent) +{ +} + +void QDeclarativePointValueType::read(QObject *obj, int idx) +{ + void *a[] = { &point, 0 }; + QMetaObject::metacall(obj, QMetaObject::ReadProperty, idx, a); +} + +void QDeclarativePointValueType::write(QObject *obj, int idx, QDeclarativePropertyPrivate::WriteFlags flags) +{ + int status = -1; + void *a[] = { &point, 0, &status, &flags }; + QMetaObject::metacall(obj, QMetaObject::WriteProperty, idx, a); +} + +QVariant QDeclarativePointValueType::value() +{ + return QVariant(point); +} + +void QDeclarativePointValueType::setValue(QVariant value) +{ + point = qvariant_cast(value); +} + +int QDeclarativePointValueType::x() const +{ + return point.x(); +} + +int QDeclarativePointValueType::y() const +{ + return point.y(); +} + +void QDeclarativePointValueType::setX(int x) +{ + point.setX(x); +} + +void QDeclarativePointValueType::setY(int y) +{ + point.setY(y); +} + +QDeclarativeSizeFValueType::QDeclarativeSizeFValueType(QObject *parent) +: QDeclarativeValueType(parent) +{ +} + +void QDeclarativeSizeFValueType::read(QObject *obj, int idx) +{ + void *a[] = { &size, 0 }; + QMetaObject::metacall(obj, QMetaObject::ReadProperty, idx, a); +} + +void QDeclarativeSizeFValueType::write(QObject *obj, int idx, QDeclarativePropertyPrivate::WriteFlags flags) +{ + int status = -1; + void *a[] = { &size, 0, &status, &flags }; + QMetaObject::metacall(obj, QMetaObject::WriteProperty, idx, a); +} + +QVariant QDeclarativeSizeFValueType::value() +{ + return QVariant(size); +} + +void QDeclarativeSizeFValueType::setValue(QVariant value) +{ + size = qvariant_cast(value); +} + +qreal QDeclarativeSizeFValueType::width() const +{ + return size.width(); +} + +qreal QDeclarativeSizeFValueType::height() const +{ + return size.height(); +} + +void QDeclarativeSizeFValueType::setWidth(qreal w) +{ + size.setWidth(w); +} + +void QDeclarativeSizeFValueType::setHeight(qreal h) +{ + size.setHeight(h); +} + +QDeclarativeSizeValueType::QDeclarativeSizeValueType(QObject *parent) +: QDeclarativeValueType(parent) +{ +} + +void QDeclarativeSizeValueType::read(QObject *obj, int idx) +{ + void *a[] = { &size, 0 }; + QMetaObject::metacall(obj, QMetaObject::ReadProperty, idx, a); +} + +void QDeclarativeSizeValueType::write(QObject *obj, int idx, QDeclarativePropertyPrivate::WriteFlags flags) +{ + int status = -1; + void *a[] = { &size, 0, &status, &flags }; + QMetaObject::metacall(obj, QMetaObject::WriteProperty, idx, a); +} + +QVariant QDeclarativeSizeValueType::value() +{ + return QVariant(size); +} + +void QDeclarativeSizeValueType::setValue(QVariant value) +{ + size = qvariant_cast(value); +} + +int QDeclarativeSizeValueType::width() const +{ + return size.width(); +} + +int QDeclarativeSizeValueType::height() const +{ + return size.height(); +} + +void QDeclarativeSizeValueType::setWidth(int w) +{ + size.setWidth(w); +} + +void QDeclarativeSizeValueType::setHeight(int h) +{ + size.setHeight(h); +} + +QDeclarativeRectFValueType::QDeclarativeRectFValueType(QObject *parent) +: QDeclarativeValueType(parent) +{ +} + +void QDeclarativeRectFValueType::read(QObject *obj, int idx) +{ + void *a[] = { &rect, 0 }; + QMetaObject::metacall(obj, QMetaObject::ReadProperty, idx, a); +} + +void QDeclarativeRectFValueType::write(QObject *obj, int idx, QDeclarativePropertyPrivate::WriteFlags flags) +{ + int status = -1; + void *a[] = { &rect, 0, &status, &flags }; + QMetaObject::metacall(obj, QMetaObject::WriteProperty, idx, a); +} + +QVariant QDeclarativeRectFValueType::value() +{ + return QVariant(rect); +} + +void QDeclarativeRectFValueType::setValue(QVariant value) +{ + rect = qvariant_cast(value); +} + +qreal QDeclarativeRectFValueType::x() const +{ + return rect.x(); +} + +qreal QDeclarativeRectFValueType::y() const +{ + return rect.y(); +} + +void QDeclarativeRectFValueType::setX(qreal x) +{ + rect.moveLeft(x); +} + +void QDeclarativeRectFValueType::setY(qreal y) +{ + rect.moveTop(y); +} + +qreal QDeclarativeRectFValueType::width() const +{ + return rect.width(); +} + +qreal QDeclarativeRectFValueType::height() const +{ + return rect.height(); +} + +void QDeclarativeRectFValueType::setWidth(qreal w) +{ + rect.setWidth(w); +} + +void QDeclarativeRectFValueType::setHeight(qreal h) +{ + rect.setHeight(h); +} + +QDeclarativeRectValueType::QDeclarativeRectValueType(QObject *parent) +: QDeclarativeValueType(parent) +{ +} + +void QDeclarativeRectValueType::read(QObject *obj, int idx) +{ + void *a[] = { &rect, 0 }; + QMetaObject::metacall(obj, QMetaObject::ReadProperty, idx, a); +} + +void QDeclarativeRectValueType::write(QObject *obj, int idx, QDeclarativePropertyPrivate::WriteFlags flags) +{ + int status = -1; + void *a[] = { &rect, 0, &status, &flags }; + QMetaObject::metacall(obj, QMetaObject::WriteProperty, idx, a); +} + +QVariant QDeclarativeRectValueType::value() +{ + return QVariant(rect); +} + +void QDeclarativeRectValueType::setValue(QVariant value) +{ + rect = qvariant_cast(value); +} + +int QDeclarativeRectValueType::x() const +{ + return rect.x(); +} + +int QDeclarativeRectValueType::y() const +{ + return rect.y(); +} + +void QDeclarativeRectValueType::setX(int x) +{ + rect.moveLeft(x); +} + +void QDeclarativeRectValueType::setY(int y) +{ + rect.moveTop(y); +} + +int QDeclarativeRectValueType::width() const +{ + return rect.width(); +} + +int QDeclarativeRectValueType::height() const +{ + return rect.height(); +} + +void QDeclarativeRectValueType::setWidth(int w) +{ + rect.setWidth(w); +} + +void QDeclarativeRectValueType::setHeight(int h) +{ + rect.setHeight(h); +} + +QDeclarativeVector2DValueType::QDeclarativeVector2DValueType(QObject *parent) +: QDeclarativeValueType(parent) +{ +} + +void QDeclarativeVector2DValueType::read(QObject *obj, int idx) +{ + void *a[] = { &vector, 0 }; + QMetaObject::metacall(obj, QMetaObject::ReadProperty, idx, a); +} + +void QDeclarativeVector2DValueType::write(QObject *obj, int idx, QDeclarativePropertyPrivate::WriteFlags flags) +{ + int status = -1; + void *a[] = { &vector, 0, &status, &flags }; + QMetaObject::metacall(obj, QMetaObject::WriteProperty, idx, a); +} + +QVariant QDeclarativeVector2DValueType::value() +{ + return QVariant(vector); +} + +void QDeclarativeVector2DValueType::setValue(QVariant value) +{ + vector = qvariant_cast(value); +} + +qreal QDeclarativeVector2DValueType::x() const +{ + return vector.x(); +} + +qreal QDeclarativeVector2DValueType::y() const +{ + return vector.y(); +} + +void QDeclarativeVector2DValueType::setX(qreal x) +{ + vector.setX(x); +} + +void QDeclarativeVector2DValueType::setY(qreal y) +{ + vector.setY(y); +} + +QDeclarativeVector3DValueType::QDeclarativeVector3DValueType(QObject *parent) +: QDeclarativeValueType(parent) +{ +} + +void QDeclarativeVector3DValueType::read(QObject *obj, int idx) +{ + void *a[] = { &vector, 0 }; + QMetaObject::metacall(obj, QMetaObject::ReadProperty, idx, a); +} + +void QDeclarativeVector3DValueType::write(QObject *obj, int idx, QDeclarativePropertyPrivate::WriteFlags flags) +{ + int status = -1; + void *a[] = { &vector, 0, &status, &flags }; + QMetaObject::metacall(obj, QMetaObject::WriteProperty, idx, a); +} + +QVariant QDeclarativeVector3DValueType::value() +{ + return QVariant(vector); +} + +void QDeclarativeVector3DValueType::setValue(QVariant value) +{ + vector = qvariant_cast(value); +} + +qreal QDeclarativeVector3DValueType::x() const +{ + return vector.x(); +} + +qreal QDeclarativeVector3DValueType::y() const +{ + return vector.y(); +} + +qreal QDeclarativeVector3DValueType::z() const +{ + return vector.z(); +} + +void QDeclarativeVector3DValueType::setX(qreal x) +{ + vector.setX(x); +} + +void QDeclarativeVector3DValueType::setY(qreal y) +{ + vector.setY(y); +} + +void QDeclarativeVector3DValueType::setZ(qreal z) +{ + vector.setZ(z); +} + +QDeclarativeVector4DValueType::QDeclarativeVector4DValueType(QObject *parent) +: QDeclarativeValueType(parent) +{ +} + +void QDeclarativeVector4DValueType::read(QObject *obj, int idx) +{ + void *a[] = { &vector, 0 }; + QMetaObject::metacall(obj, QMetaObject::ReadProperty, idx, a); +} + +void QDeclarativeVector4DValueType::write(QObject *obj, int idx, QDeclarativePropertyPrivate::WriteFlags flags) +{ + int status = -1; + void *a[] = { &vector, 0, &status, &flags }; + QMetaObject::metacall(obj, QMetaObject::WriteProperty, idx, a); +} + +QVariant QDeclarativeVector4DValueType::value() +{ + return QVariant(vector); +} + +void QDeclarativeVector4DValueType::setValue(QVariant value) +{ + vector = qvariant_cast(value); +} + +qreal QDeclarativeVector4DValueType::x() const +{ + return vector.x(); +} + +qreal QDeclarativeVector4DValueType::y() const +{ + return vector.y(); +} + +qreal QDeclarativeVector4DValueType::z() const +{ + return vector.z(); +} + +qreal QDeclarativeVector4DValueType::w() const +{ + return vector.w(); +} + +void QDeclarativeVector4DValueType::setX(qreal x) +{ + vector.setX(x); +} + +void QDeclarativeVector4DValueType::setY(qreal y) +{ + vector.setY(y); +} + +void QDeclarativeVector4DValueType::setZ(qreal z) +{ + vector.setZ(z); +} + +void QDeclarativeVector4DValueType::setW(qreal w) +{ + vector.setW(w); +} + +QDeclarativeQuaternionValueType::QDeclarativeQuaternionValueType(QObject *parent) +: QDeclarativeValueType(parent) +{ +} + +void QDeclarativeQuaternionValueType::read(QObject *obj, int idx) +{ + void *a[] = { &quaternion, 0 }; + QMetaObject::metacall(obj, QMetaObject::ReadProperty, idx, a); +} + +void QDeclarativeQuaternionValueType::write(QObject *obj, int idx, QDeclarativePropertyPrivate::WriteFlags flags) +{ + int status = -1; + void *a[] = { &quaternion, 0, &status, &flags }; + QMetaObject::metacall(obj, QMetaObject::WriteProperty, idx, a); +} + +QVariant QDeclarativeQuaternionValueType::value() +{ + return QVariant(quaternion); +} + +void QDeclarativeQuaternionValueType::setValue(QVariant value) +{ + quaternion = qvariant_cast(value); +} + +qreal QDeclarativeQuaternionValueType::scalar() const +{ + return quaternion.scalar(); +} + +qreal QDeclarativeQuaternionValueType::x() const +{ + return quaternion.x(); +} + +qreal QDeclarativeQuaternionValueType::y() const +{ + return quaternion.y(); +} + +qreal QDeclarativeQuaternionValueType::z() const +{ + return quaternion.z(); +} + +void QDeclarativeQuaternionValueType::setScalar(qreal scalar) +{ + quaternion.setScalar(scalar); +} + +void QDeclarativeQuaternionValueType::setX(qreal x) +{ + quaternion.setX(x); +} + +void QDeclarativeQuaternionValueType::setY(qreal y) +{ + quaternion.setY(y); +} + +void QDeclarativeQuaternionValueType::setZ(qreal z) +{ + quaternion.setZ(z); +} + +QDeclarativeMatrix4x4ValueType::QDeclarativeMatrix4x4ValueType(QObject *parent) +: QDeclarativeValueType(parent) +{ +} + +void QDeclarativeMatrix4x4ValueType::read(QObject *obj, int idx) +{ + void *a[] = { &matrix, 0 }; + QMetaObject::metacall(obj, QMetaObject::ReadProperty, idx, a); +} + +void QDeclarativeMatrix4x4ValueType::write(QObject *obj, int idx, QDeclarativePropertyPrivate::WriteFlags flags) +{ + int status = -1; + void *a[] = { &matrix, 0, &status, &flags }; + QMetaObject::metacall(obj, QMetaObject::WriteProperty, idx, a); +} + +QVariant QDeclarativeMatrix4x4ValueType::value() +{ + return QVariant(matrix); +} + +void QDeclarativeMatrix4x4ValueType::setValue(QVariant value) +{ + matrix = qvariant_cast(value); +} + +QDeclarativeEasingValueType::QDeclarativeEasingValueType(QObject *parent) +: QDeclarativeValueType(parent) +{ +} + +void QDeclarativeEasingValueType::read(QObject *obj, int idx) +{ + void *a[] = { &easing, 0 }; + QMetaObject::metacall(obj, QMetaObject::ReadProperty, idx, a); +} + +void QDeclarativeEasingValueType::write(QObject *obj, int idx, QDeclarativePropertyPrivate::WriteFlags flags) +{ + int status = -1; + void *a[] = { &easing, 0, &status, &flags }; + QMetaObject::metacall(obj, QMetaObject::WriteProperty, idx, a); +} + +QVariant QDeclarativeEasingValueType::value() +{ + return QVariant(easing); +} + +void QDeclarativeEasingValueType::setValue(QVariant value) +{ + easing = qvariant_cast(value); +} + +QDeclarativeEasingValueType::Type QDeclarativeEasingValueType::type() const +{ + return (QDeclarativeEasingValueType::Type)easing.type(); +} + +qreal QDeclarativeEasingValueType::amplitude() const +{ + return easing.amplitude(); +} + +qreal QDeclarativeEasingValueType::overshoot() const +{ + return easing.overshoot(); +} + +qreal QDeclarativeEasingValueType::period() const +{ + return easing.period(); +} + +void QDeclarativeEasingValueType::setType(QDeclarativeEasingValueType::Type type) +{ + easing.setType((QEasingCurve::Type)type); +} + +void QDeclarativeEasingValueType::setAmplitude(qreal amplitude) +{ + easing.setAmplitude(amplitude); +} + +void QDeclarativeEasingValueType::setOvershoot(qreal overshoot) +{ + easing.setOvershoot(overshoot); +} + +void QDeclarativeEasingValueType::setPeriod(qreal period) +{ + easing.setPeriod(period); +} + +QDeclarativeFontValueType::QDeclarativeFontValueType(QObject *parent) +: QDeclarativeValueType(parent), pixelSizeSet(false), pointSizeSet(false) +{ +} + +void QDeclarativeFontValueType::read(QObject *obj, int idx) +{ + void *a[] = { &font, 0 }; + QMetaObject::metacall(obj, QMetaObject::ReadProperty, idx, a); + pixelSizeSet = false; + pointSizeSet = false; +} + +void QDeclarativeFontValueType::write(QObject *obj, int idx, QDeclarativePropertyPrivate::WriteFlags flags) +{ + int status = -1; + void *a[] = { &font, 0, &status, &flags }; + QMetaObject::metacall(obj, QMetaObject::WriteProperty, idx, a); +} + +QVariant QDeclarativeFontValueType::value() +{ + return QVariant(font); +} + +void QDeclarativeFontValueType::setValue(QVariant value) +{ + font = qvariant_cast(value); +} + + +QString QDeclarativeFontValueType::family() const +{ + return font.family(); +} + +void QDeclarativeFontValueType::setFamily(const QString &family) +{ + font.setFamily(family); +} + +bool QDeclarativeFontValueType::bold() const +{ + return font.bold(); +} + +void QDeclarativeFontValueType::setBold(bool b) +{ + font.setBold(b); +} + +QDeclarativeFontValueType::FontWeight QDeclarativeFontValueType::weight() const +{ + return (QDeclarativeFontValueType::FontWeight)font.weight(); +} + +void QDeclarativeFontValueType::setWeight(QDeclarativeFontValueType::FontWeight w) +{ + font.setWeight((QFont::Weight)w); +} + +bool QDeclarativeFontValueType::italic() const +{ + return font.italic(); +} + +void QDeclarativeFontValueType::setItalic(bool b) +{ + font.setItalic(b); +} + +bool QDeclarativeFontValueType::underline() const +{ + return font.underline(); +} + +void QDeclarativeFontValueType::setUnderline(bool b) +{ + font.setUnderline(b); +} + +bool QDeclarativeFontValueType::overline() const +{ + return font.overline(); +} + +void QDeclarativeFontValueType::setOverline(bool b) +{ + font.setOverline(b); +} + +bool QDeclarativeFontValueType::strikeout() const +{ + return font.strikeOut(); +} + +void QDeclarativeFontValueType::setStrikeout(bool b) +{ + font.setStrikeOut(b); +} + +qreal QDeclarativeFontValueType::pointSize() const +{ + if (font.pointSizeF() == -1) { + if (dpi.isNull) + dpi = qt_defaultDpi(); + return font.pixelSize() * qreal(72.) / qreal(dpi); + } + return font.pointSizeF(); +} + +void QDeclarativeFontValueType::setPointSize(qreal size) +{ + if (pixelSizeSet) { + qWarning() << "Both point size and pixel size set. Using pixel size."; + return; + } + + if (size >= 0.0) { + pointSizeSet = true; + font.setPointSizeF(size); + } else { + pointSizeSet = false; + } +} + +int QDeclarativeFontValueType::pixelSize() const +{ + if (font.pixelSize() == -1) { + if (dpi.isNull) + dpi = qt_defaultDpi(); + return (font.pointSizeF() * dpi) / qreal(72.); + } + return font.pixelSize(); +} + +void QDeclarativeFontValueType::setPixelSize(int size) +{ + if (size >0) { + if (pointSizeSet) + qWarning() << "Both point size and pixel size set. Using pixel size."; + font.setPixelSize(size); + pixelSizeSet = true; + } else { + pixelSizeSet = false; + } +} + +QDeclarativeFontValueType::Capitalization QDeclarativeFontValueType::capitalization() const +{ + return (QDeclarativeFontValueType::Capitalization)font.capitalization(); +} + +void QDeclarativeFontValueType::setCapitalization(QDeclarativeFontValueType::Capitalization c) +{ + font.setCapitalization((QFont::Capitalization)c); +} + +qreal QDeclarativeFontValueType::letterSpacing() const +{ + return font.letterSpacing(); +} + +void QDeclarativeFontValueType::setLetterSpacing(qreal size) +{ + font.setLetterSpacing(QFont::AbsoluteSpacing, size); +} + +qreal QDeclarativeFontValueType::wordSpacing() const +{ + return font.wordSpacing(); +} + +void QDeclarativeFontValueType::setWordSpacing(qreal size) +{ + font.setWordSpacing(size); +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativevaluetype_p.h b/src/declarative/qml/qdeclarativevaluetype_p.h new file mode 100644 index 00000000..5c2cda7f --- /dev/null +++ b/src/declarative/qml/qdeclarativevaluetype_p.h @@ -0,0 +1,556 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEVALUETYPE_P_H +#define QDECLARATIVEVALUETYPE_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 "qdeclarativeproperty.h" +#include "private/qdeclarativeproperty_p.h" +#include "private/qdeclarativenullablevalue_p_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeValueType : public QObject +{ + Q_OBJECT +public: + QDeclarativeValueType(QObject *parent = 0); + virtual void read(QObject *, int) = 0; + virtual void write(QObject *, int, QDeclarativePropertyPrivate::WriteFlags flags) = 0; + virtual QVariant value() = 0; + virtual void setValue(QVariant) = 0; +}; + +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeValueTypeFactory +{ +public: + QDeclarativeValueTypeFactory(); + ~QDeclarativeValueTypeFactory(); + static bool isValueType(int); + static QDeclarativeValueType *valueType(int); + + static void registerValueTypes(); + + QDeclarativeValueType *operator[](int idx) const { + if (idx >= (int)QVariant::UserType) return 0; + else return valueTypes[idx]; + } + +private: + QDeclarativeValueType *valueTypes[QVariant::UserType - 1]; +}; + +class Q_AUTOTEST_EXPORT QDeclarativePointFValueType : public QDeclarativeValueType +{ + Q_PROPERTY(qreal x READ x WRITE setX) + Q_PROPERTY(qreal y READ y WRITE setY) + Q_OBJECT +public: + QDeclarativePointFValueType(QObject *parent = 0); + + virtual void read(QObject *, int); + virtual void write(QObject *, int, QDeclarativePropertyPrivate::WriteFlags); + virtual QVariant value(); + virtual void setValue(QVariant value); + + qreal x() const; + qreal y() const; + void setX(qreal); + void setY(qreal); + +private: + QPointF point; +}; + +class Q_AUTOTEST_EXPORT QDeclarativePointValueType : public QDeclarativeValueType +{ + Q_PROPERTY(int x READ x WRITE setX) + Q_PROPERTY(int y READ y WRITE setY) + Q_OBJECT +public: + QDeclarativePointValueType(QObject *parent = 0); + + virtual void read(QObject *, int); + virtual void write(QObject *, int, QDeclarativePropertyPrivate::WriteFlags); + virtual QVariant value(); + virtual void setValue(QVariant value); + + int x() const; + int y() const; + void setX(int); + void setY(int); + +private: + QPoint point; +}; + +class Q_AUTOTEST_EXPORT QDeclarativeSizeFValueType : public QDeclarativeValueType +{ + Q_PROPERTY(qreal width READ width WRITE setWidth) + Q_PROPERTY(qreal height READ height WRITE setHeight) + Q_OBJECT +public: + QDeclarativeSizeFValueType(QObject *parent = 0); + + virtual void read(QObject *, int); + virtual void write(QObject *, int, QDeclarativePropertyPrivate::WriteFlags); + virtual QVariant value(); + virtual void setValue(QVariant value); + + qreal width() const; + qreal height() const; + void setWidth(qreal); + void setHeight(qreal); + +private: + QSizeF size; +}; + +class Q_AUTOTEST_EXPORT QDeclarativeSizeValueType : public QDeclarativeValueType +{ + Q_PROPERTY(int width READ width WRITE setWidth) + Q_PROPERTY(int height READ height WRITE setHeight) + Q_OBJECT +public: + QDeclarativeSizeValueType(QObject *parent = 0); + + virtual void read(QObject *, int); + virtual void write(QObject *, int, QDeclarativePropertyPrivate::WriteFlags); + virtual QVariant value(); + virtual void setValue(QVariant value); + + int width() const; + int height() const; + void setWidth(int); + void setHeight(int); + +private: + QSize size; +}; + +class Q_AUTOTEST_EXPORT QDeclarativeRectFValueType : public QDeclarativeValueType +{ + Q_PROPERTY(qreal x READ x WRITE setX) + Q_PROPERTY(qreal y READ y WRITE setY) + Q_PROPERTY(qreal width READ width WRITE setWidth) + Q_PROPERTY(qreal height READ height WRITE setHeight) + Q_OBJECT +public: + QDeclarativeRectFValueType(QObject *parent = 0); + + virtual void read(QObject *, int); + virtual void write(QObject *, int, QDeclarativePropertyPrivate::WriteFlags); + virtual QVariant value(); + virtual void setValue(QVariant value); + + qreal x() const; + qreal y() const; + void setX(qreal); + void setY(qreal); + + qreal width() const; + qreal height() const; + void setWidth(qreal); + void setHeight(qreal); + +private: + QRectF rect; +}; + +class Q_AUTOTEST_EXPORT QDeclarativeRectValueType : public QDeclarativeValueType +{ + Q_PROPERTY(int x READ x WRITE setX) + Q_PROPERTY(int y READ y WRITE setY) + Q_PROPERTY(int width READ width WRITE setWidth) + Q_PROPERTY(int height READ height WRITE setHeight) + Q_OBJECT +public: + QDeclarativeRectValueType(QObject *parent = 0); + + virtual void read(QObject *, int); + virtual void write(QObject *, int, QDeclarativePropertyPrivate::WriteFlags); + virtual QVariant value(); + virtual void setValue(QVariant value); + + int x() const; + int y() const; + void setX(int); + void setY(int); + + int width() const; + int height() const; + void setWidth(int); + void setHeight(int); + +private: + QRect rect; +}; + +class Q_AUTOTEST_EXPORT QDeclarativeVector2DValueType : public QDeclarativeValueType +{ + Q_PROPERTY(qreal x READ x WRITE setX) + Q_PROPERTY(qreal y READ y WRITE setY) + Q_OBJECT +public: + QDeclarativeVector2DValueType(QObject *parent = 0); + + virtual void read(QObject *, int); + virtual void write(QObject *, int, QDeclarativePropertyPrivate::WriteFlags); + virtual QVariant value(); + virtual void setValue(QVariant value); + + qreal x() const; + qreal y() const; + void setX(qreal); + void setY(qreal); + +private: + QVector2D vector; +}; + +class Q_AUTOTEST_EXPORT QDeclarativeVector3DValueType : public QDeclarativeValueType +{ + Q_PROPERTY(qreal x READ x WRITE setX) + Q_PROPERTY(qreal y READ y WRITE setY) + Q_PROPERTY(qreal z READ z WRITE setZ) + Q_OBJECT +public: + QDeclarativeVector3DValueType(QObject *parent = 0); + + virtual void read(QObject *, int); + virtual void write(QObject *, int, QDeclarativePropertyPrivate::WriteFlags); + virtual QVariant value(); + virtual void setValue(QVariant value); + + qreal x() const; + qreal y() const; + qreal z() const; + void setX(qreal); + void setY(qreal); + void setZ(qreal); + +private: + QVector3D vector; +}; + +class Q_AUTOTEST_EXPORT QDeclarativeVector4DValueType : public QDeclarativeValueType +{ + Q_PROPERTY(qreal x READ x WRITE setX) + Q_PROPERTY(qreal y READ y WRITE setY) + Q_PROPERTY(qreal z READ z WRITE setZ) + Q_PROPERTY(qreal w READ w WRITE setW) + Q_OBJECT +public: + QDeclarativeVector4DValueType(QObject *parent = 0); + + virtual void read(QObject *, int); + virtual void write(QObject *, int, QDeclarativePropertyPrivate::WriteFlags); + virtual QVariant value(); + virtual void setValue(QVariant value); + + qreal x() const; + qreal y() const; + qreal z() const; + qreal w() const; + void setX(qreal); + void setY(qreal); + void setZ(qreal); + void setW(qreal); + +private: + QVector4D vector; +}; + +class Q_AUTOTEST_EXPORT QDeclarativeQuaternionValueType : public QDeclarativeValueType +{ + Q_PROPERTY(qreal scalar READ scalar WRITE setScalar) + Q_PROPERTY(qreal x READ x WRITE setX) + Q_PROPERTY(qreal y READ y WRITE setY) + Q_PROPERTY(qreal z READ z WRITE setZ) + Q_OBJECT +public: + QDeclarativeQuaternionValueType(QObject *parent = 0); + + virtual void read(QObject *, int); + virtual void write(QObject *, int, QDeclarativePropertyPrivate::WriteFlags); + virtual QVariant value(); + virtual void setValue(QVariant value); + + qreal scalar() const; + qreal x() const; + qreal y() const; + qreal z() const; + void setScalar(qreal); + void setX(qreal); + void setY(qreal); + void setZ(qreal); + +private: + QQuaternion quaternion; +}; + +class Q_AUTOTEST_EXPORT QDeclarativeMatrix4x4ValueType : public QDeclarativeValueType +{ + Q_PROPERTY(qreal m11 READ m11 WRITE setM11) + Q_PROPERTY(qreal m12 READ m12 WRITE setM12) + Q_PROPERTY(qreal m13 READ m13 WRITE setM13) + Q_PROPERTY(qreal m14 READ m14 WRITE setM14) + Q_PROPERTY(qreal m21 READ m21 WRITE setM21) + Q_PROPERTY(qreal m22 READ m22 WRITE setM22) + Q_PROPERTY(qreal m23 READ m23 WRITE setM23) + Q_PROPERTY(qreal m24 READ m24 WRITE setM24) + Q_PROPERTY(qreal m31 READ m31 WRITE setM31) + Q_PROPERTY(qreal m32 READ m32 WRITE setM32) + Q_PROPERTY(qreal m33 READ m33 WRITE setM33) + Q_PROPERTY(qreal m34 READ m34 WRITE setM34) + Q_PROPERTY(qreal m41 READ m41 WRITE setM41) + Q_PROPERTY(qreal m42 READ m42 WRITE setM42) + Q_PROPERTY(qreal m43 READ m43 WRITE setM43) + Q_PROPERTY(qreal m44 READ m44 WRITE setM44) + Q_OBJECT +public: + QDeclarativeMatrix4x4ValueType(QObject *parent = 0); + + virtual void read(QObject *, int); + virtual void write(QObject *, int, QDeclarativePropertyPrivate::WriteFlags); + virtual QVariant value(); + virtual void setValue(QVariant value); + + qreal m11() const { return matrix(0, 0); } + qreal m12() const { return matrix(0, 1); } + qreal m13() const { return matrix(0, 2); } + qreal m14() const { return matrix(0, 3); } + qreal m21() const { return matrix(1, 0); } + qreal m22() const { return matrix(1, 1); } + qreal m23() const { return matrix(1, 2); } + qreal m24() const { return matrix(1, 3); } + qreal m31() const { return matrix(2, 0); } + qreal m32() const { return matrix(2, 1); } + qreal m33() const { return matrix(2, 2); } + qreal m34() const { return matrix(2, 3); } + qreal m41() const { return matrix(3, 0); } + qreal m42() const { return matrix(3, 1); } + qreal m43() const { return matrix(3, 2); } + qreal m44() const { return matrix(3, 3); } + + void setM11(qreal value) { matrix(0, 0) = value; } + void setM12(qreal value) { matrix(0, 1) = value; } + void setM13(qreal value) { matrix(0, 2) = value; } + void setM14(qreal value) { matrix(0, 3) = value; } + void setM21(qreal value) { matrix(1, 0) = value; } + void setM22(qreal value) { matrix(1, 1) = value; } + void setM23(qreal value) { matrix(1, 2) = value; } + void setM24(qreal value) { matrix(1, 3) = value; } + void setM31(qreal value) { matrix(2, 0) = value; } + void setM32(qreal value) { matrix(2, 1) = value; } + void setM33(qreal value) { matrix(2, 2) = value; } + void setM34(qreal value) { matrix(2, 3) = value; } + void setM41(qreal value) { matrix(3, 0) = value; } + void setM42(qreal value) { matrix(3, 1) = value; } + void setM43(qreal value) { matrix(3, 2) = value; } + void setM44(qreal value) { matrix(3, 3) = value; } + +private: + QMatrix4x4 matrix; +}; + +class Q_AUTOTEST_EXPORT QDeclarativeEasingValueType : public QDeclarativeValueType +{ + Q_OBJECT + Q_ENUMS(Type) + + Q_PROPERTY(QDeclarativeEasingValueType::Type type READ type WRITE setType) + Q_PROPERTY(qreal amplitude READ amplitude WRITE setAmplitude) + Q_PROPERTY(qreal overshoot READ overshoot WRITE setOvershoot) + Q_PROPERTY(qreal period READ period WRITE setPeriod) +public: + enum Type { + Linear = QEasingCurve::Linear, + InQuad = QEasingCurve::InQuad, OutQuad = QEasingCurve::OutQuad, + InOutQuad = QEasingCurve::InOutQuad, OutInQuad = QEasingCurve::OutInQuad, + InCubic = QEasingCurve::InCubic, OutCubic = QEasingCurve::OutCubic, + InOutCubic = QEasingCurve::InOutCubic, OutInCubic = QEasingCurve::OutInCubic, + InQuart = QEasingCurve::InQuart, OutQuart = QEasingCurve::OutQuart, + InOutQuart = QEasingCurve::InOutQuart, OutInQuart = QEasingCurve::OutInQuart, + InQuint = QEasingCurve::InQuint, OutQuint = QEasingCurve::OutQuint, + InOutQuint = QEasingCurve::InOutQuint, OutInQuint = QEasingCurve::OutInQuint, + InSine = QEasingCurve::InSine, OutSine = QEasingCurve::OutSine, + InOutSine = QEasingCurve::InOutSine, OutInSine = QEasingCurve::OutInSine, + InExpo = QEasingCurve::InExpo, OutExpo = QEasingCurve::OutExpo, + InOutExpo = QEasingCurve::InOutExpo, OutInExpo = QEasingCurve::OutInExpo, + InCirc = QEasingCurve::InCirc, OutCirc = QEasingCurve::OutCirc, + InOutCirc = QEasingCurve::InOutCirc, OutInCirc = QEasingCurve::OutInCirc, + InElastic = QEasingCurve::InElastic, OutElastic = QEasingCurve::OutElastic, + InOutElastic = QEasingCurve::InOutElastic, OutInElastic = QEasingCurve::OutInElastic, + InBack = QEasingCurve::InBack, OutBack = QEasingCurve::OutBack, + InOutBack = QEasingCurve::InOutBack, OutInBack = QEasingCurve::OutInBack, + InBounce = QEasingCurve::InBounce, OutBounce = QEasingCurve::OutBounce, + InOutBounce = QEasingCurve::InOutBounce, OutInBounce = QEasingCurve::OutInBounce, + InCurve = QEasingCurve::InCurve, OutCurve = QEasingCurve::OutCurve, + SineCurve = QEasingCurve::SineCurve, CosineCurve = QEasingCurve::CosineCurve + }; + + QDeclarativeEasingValueType(QObject *parent = 0); + + virtual void read(QObject *, int); + virtual void write(QObject *, int, QDeclarativePropertyPrivate::WriteFlags); + virtual QVariant value(); + virtual void setValue(QVariant value); + + Type type() const; + qreal amplitude() const; + qreal overshoot() const; + qreal period() const; + void setType(Type); + void setAmplitude(qreal); + void setOvershoot(qreal); + void setPeriod(qreal); + +private: + QEasingCurve easing; +}; + +class Q_AUTOTEST_EXPORT QDeclarativeFontValueType : public QDeclarativeValueType +{ + Q_OBJECT + Q_ENUMS(FontWeight) + Q_ENUMS(Capitalization) + + Q_PROPERTY(QString family READ family WRITE setFamily) + Q_PROPERTY(bool bold READ bold WRITE setBold) + Q_PROPERTY(FontWeight weight READ weight WRITE setWeight) + Q_PROPERTY(bool italic READ italic WRITE setItalic) + Q_PROPERTY(bool underline READ underline WRITE setUnderline) + Q_PROPERTY(bool overline READ overline WRITE setOverline) + Q_PROPERTY(bool strikeout READ strikeout WRITE setStrikeout) + Q_PROPERTY(qreal pointSize READ pointSize WRITE setPointSize) + Q_PROPERTY(int pixelSize READ pixelSize WRITE setPixelSize) + Q_PROPERTY(Capitalization capitalization READ capitalization WRITE setCapitalization) + Q_PROPERTY(qreal letterSpacing READ letterSpacing WRITE setLetterSpacing) + Q_PROPERTY(qreal wordSpacing READ wordSpacing WRITE setWordSpacing) + +public: + enum FontWeight { Light = QFont::Light, + Normal = QFont::Normal, + DemiBold = QFont::DemiBold, + Bold = QFont::Bold, + Black = QFont::Black }; + enum Capitalization { MixedCase = QFont::MixedCase, + AllUppercase = QFont::AllUppercase, + AllLowercase = QFont::AllLowercase, + SmallCaps = QFont::SmallCaps, + Capitalize = QFont::Capitalize }; + + QDeclarativeFontValueType(QObject *parent = 0); + + virtual void read(QObject *, int); + virtual void write(QObject *, int, QDeclarativePropertyPrivate::WriteFlags); + virtual QVariant value(); + virtual void setValue(QVariant value); + + QString family() const; + void setFamily(const QString &); + + bool bold() const; + void setBold(bool b); + + FontWeight weight() const; + void setWeight(FontWeight); + + bool italic() const; + void setItalic(bool b); + + bool underline() const; + void setUnderline(bool b); + + bool overline() const; + void setOverline(bool b); + + bool strikeout() const; + void setStrikeout(bool b); + + qreal pointSize() const; + void setPointSize(qreal size); + + int pixelSize() const; + void setPixelSize(int size); + + Capitalization capitalization() const; + void setCapitalization(Capitalization); + + qreal letterSpacing() const; + void setLetterSpacing(qreal spacing); + + qreal wordSpacing() const; + void setWordSpacing(qreal spacing); + +private: + QFont font; + bool pixelSizeSet; + bool pointSizeSet; + mutable QDeclarativeNullableValue dpi; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVEVALUETYPE_P_H diff --git a/src/declarative/qml/qdeclarativevaluetypescriptclass.cpp b/src/declarative/qml/qdeclarativevaluetypescriptclass.cpp new file mode 100644 index 00000000..b1d4221d --- /dev/null +++ b/src/declarative/qml/qdeclarativevaluetypescriptclass.cpp @@ -0,0 +1,242 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativevaluetypescriptclass_p.h" + +#include "private/qdeclarativebinding_p.h" +#include "private/qdeclarativeproperty_p.h" +#include "private/qdeclarativeengine_p.h" +#include "private/qdeclarativeguard_p.h" + +#include + +QT_BEGIN_NAMESPACE + +struct QDeclarativeValueTypeObject : public QScriptDeclarativeClass::Object { + enum Type { Reference, Copy }; + QDeclarativeValueTypeObject(Type t) : objectType(t) {} + Type objectType; + QDeclarativeValueType *type; +}; + +struct QDeclarativeValueTypeReference : public QDeclarativeValueTypeObject { + QDeclarativeValueTypeReference() : QDeclarativeValueTypeObject(Reference) {} + QDeclarativeGuard object; + int property; +}; + +struct QDeclarativeValueTypeCopy : public QDeclarativeValueTypeObject { + QDeclarativeValueTypeCopy() : QDeclarativeValueTypeObject(Copy) {} + QVariant value; +}; + +QDeclarativeValueTypeScriptClass::QDeclarativeValueTypeScriptClass(QDeclarativeEngine *bindEngine) +: QScriptDeclarativeClass(QDeclarativeEnginePrivate::getScriptEngine(bindEngine)), engine(bindEngine) +{ +} + +QDeclarativeValueTypeScriptClass::~QDeclarativeValueTypeScriptClass() +{ +} + +QScriptValue QDeclarativeValueTypeScriptClass::newObject(QObject *object, int coreIndex, QDeclarativeValueType *type) +{ + QDeclarativeValueTypeReference *ref = new QDeclarativeValueTypeReference; + ref->type = type; + ref->object = object; + ref->property = coreIndex; + QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine); + return QScriptDeclarativeClass::newObject(scriptEngine, this, ref); +} + +QScriptValue QDeclarativeValueTypeScriptClass::newObject(const QVariant &v, QDeclarativeValueType *type) +{ + QDeclarativeValueTypeCopy *copy = new QDeclarativeValueTypeCopy; + copy->type = type; + copy->value = v; + QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine); + return QScriptDeclarativeClass::newObject(scriptEngine, this, copy); +} + +QScriptClass::QueryFlags +QDeclarativeValueTypeScriptClass::queryProperty(Object *obj, const Identifier &name, + QScriptClass::QueryFlags) +{ + QDeclarativeValueTypeObject *o = static_cast(obj); + + m_lastIndex = -1; + + QByteArray propName = toString(name).toUtf8(); + + m_lastIndex = o->type->metaObject()->indexOfProperty(propName.constData()); + if (m_lastIndex == -1) + return 0; + + QScriptClass::QueryFlags rv = 0; + + if (o->objectType == QDeclarativeValueTypeObject::Reference) { + QDeclarativeValueTypeReference *ref = static_cast(o); + + if (!ref->object) + return 0; + + QMetaProperty prop = ref->object->metaObject()->property(m_lastIndex); + + rv = QScriptClass::HandlesReadAccess; + if (prop.isWritable()) + rv |= QScriptClass::HandlesWriteAccess; + } else { + rv = QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess; + } + + return rv; +} + +QDeclarativeValueTypeScriptClass::Value QDeclarativeValueTypeScriptClass::property(Object *obj, const Identifier &) +{ + QDeclarativeValueTypeObject *o = static_cast(obj); + + QVariant rv; + if (o->objectType == QDeclarativeValueTypeObject::Reference) { + QDeclarativeValueTypeReference *ref = static_cast(obj); + + QMetaProperty p = ref->type->metaObject()->property(m_lastIndex); + ref->type->read(ref->object, ref->property); + rv = p.read(ref->type); + } else { + QDeclarativeValueTypeCopy *copy = static_cast(obj); + + QMetaProperty p = copy->type->metaObject()->property(m_lastIndex); + copy->type->setValue(copy->value); + rv = p.read(copy->type); + } + + QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine); + return Value(scriptEngine, static_cast(QObjectPrivate::get(engine))->scriptValueFromVariant(rv)); +} + +void QDeclarativeValueTypeScriptClass::setProperty(Object *obj, const Identifier &, + const QScriptValue &value) +{ + QDeclarativeValueTypeObject *o = static_cast(obj); + + QVariant v = QDeclarativeEnginePrivate::get(engine)->scriptValueToVariant(value); + + if (o->objectType == QDeclarativeValueTypeObject::Reference) { + QDeclarativeValueTypeReference *ref = static_cast(obj); + + ref->type->read(ref->object, ref->property); + QMetaProperty p = ref->type->metaObject()->property(m_lastIndex); + + QDeclarativeBinding *newBinding = 0; + if (value.isFunction() && !value.isRegExp()) { + QDeclarativeContextData *ctxt = QDeclarativeEnginePrivate::get(engine)->getContext(context()); + + QDeclarativePropertyCache::Data cacheData; + cacheData.flags = QDeclarativePropertyCache::Data::IsWritable; + cacheData.propType = ref->object->metaObject()->property(ref->property).userType(); + cacheData.coreIndex = ref->property; + + QDeclarativePropertyCache::ValueTypeData valueTypeData; + valueTypeData.valueTypeCoreIdx = m_lastIndex; + valueTypeData.valueTypePropType = p.userType(); + + newBinding = new QDeclarativeBinding(value, ref->object, ctxt); + QScriptContextInfo ctxtInfo(context()); + newBinding->setSourceLocation(ctxtInfo.fileName(), ctxtInfo.functionStartLineNumber()); + QDeclarativeProperty prop = QDeclarativePropertyPrivate::restore(cacheData, valueTypeData, ref->object, ctxt); + newBinding->setTarget(prop); + if (newBinding->expression().contains(QLatin1String("this"))) + newBinding->setEvaluateFlags(newBinding->evaluateFlags() | QDeclarativeBinding::RequiresThisObject); + } + + QDeclarativeAbstractBinding *delBinding = + QDeclarativePropertyPrivate::setBinding(ref->object, ref->property, m_lastIndex, newBinding); + if (delBinding) + delBinding->destroy(); + + if (p.isEnumType() && (QMetaType::Type)v.type() == QMetaType::Double) + v = v.toInt(); + p.write(ref->type, v); + ref->type->write(ref->object, ref->property, 0); + + } else { + QDeclarativeValueTypeCopy *copy = static_cast(obj); + copy->type->setValue(copy->value); + QMetaProperty p = copy->type->metaObject()->property(m_lastIndex); + p.write(copy->type, v); + copy->value = copy->type->value(); + } +} + +QVariant QDeclarativeValueTypeScriptClass::toVariant(Object *obj, bool *ok) +{ + QDeclarativeValueTypeObject *o = static_cast(obj); + + if (o->objectType == QDeclarativeValueTypeObject::Reference) { + QDeclarativeValueTypeReference *ref = static_cast(obj); + + if (ok) *ok = true; + + if (ref->object) { + ref->type->read(ref->object, ref->property); + return ref->type->value(); + } + } else { + QDeclarativeValueTypeCopy *copy = static_cast(obj); + + if (ok) *ok = true; + + return copy->value; + } + + return QVariant(); +} + +QVariant QDeclarativeValueTypeScriptClass::toVariant(const QScriptValue &value) +{ + Q_ASSERT(scriptClass(value) == this); + + return toVariant(object(value), 0); +} + +QT_END_NAMESPACE + diff --git a/src/declarative/qml/qdeclarativevaluetypescriptclass_p.h b/src/declarative/qml/qdeclarativevaluetypescriptclass_p.h new file mode 100644 index 00000000..781b7711 --- /dev/null +++ b/src/declarative/qml/qdeclarativevaluetypescriptclass_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEVALUETYPESCRIPTCLASS_P_H +#define QDECLARATIVEVALUETYPESCRIPTCLASS_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 + +QT_BEGIN_NAMESPACE + +class QDeclarativeEngine; +class QDeclarativeValueType; +class QDeclarativeValueTypeScriptClass : public QScriptDeclarativeClass +{ +public: + QDeclarativeValueTypeScriptClass(QDeclarativeEngine *); + ~QDeclarativeValueTypeScriptClass(); + + QScriptValue newObject(QObject *object, int coreIndex, QDeclarativeValueType *); + QScriptValue newObject(const QVariant &, QDeclarativeValueType *); + + virtual QScriptClass::QueryFlags queryProperty(Object *, const Identifier &, + QScriptClass::QueryFlags flags); + virtual Value property(Object *, const Identifier &); + virtual void setProperty(Object *, const Identifier &name, const QScriptValue &); + + virtual QVariant toVariant(Object *, bool *ok = 0); + QVariant toVariant(const QScriptValue &); +private: + QDeclarativeEngine *engine; + int m_lastIndex; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVEVALUETYPESCRIPTCLASS_P_H + diff --git a/src/declarative/qml/qdeclarativevme.cpp b/src/declarative/qml/qdeclarativevme.cpp new file mode 100644 index 00000000..59c9d2bc --- /dev/null +++ b/src/declarative/qml/qdeclarativevme.cpp @@ -0,0 +1,1124 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativevme_p.h" + +#include "private/qdeclarativecompiler_p.h" +#include "private/qdeclarativeboundsignal_p.h" +#include "private/qdeclarativestringconverters_p.h" +#include "private/qmetaobjectbuilder_p.h" +#include "private/qdeclarativedata_p.h" +#include "qdeclarative.h" +#include "private/qdeclarativecustomparser_p.h" +#include "qdeclarativeengine.h" +#include "qdeclarativecontext.h" +#include "qdeclarativecomponent.h" +#include "private/qdeclarativebinding_p.h" +#include "private/qdeclarativeengine_p.h" +#include "private/qdeclarativecomponent_p.h" +#include "private/qdeclarativevmemetaobject_p.h" +#include "private/qdeclarativebinding_p_p.h" +#include "private/qdeclarativecontext_p.h" +#include "private/qdeclarativecompiledbindings_p.h" +#include "private/qdeclarativeglobal_p.h" +#include "qdeclarativescriptstring.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +// A simple stack wrapper around QVarLengthArray +template +class QDeclarativeVMEStack : private QVarLengthArray +{ +private: + typedef QVarLengthArray VLA; + int _index; + +public: + inline QDeclarativeVMEStack(); + inline bool isEmpty() const; + inline const T &top() const; + inline void push(const T &o); + inline T pop(); + inline int count() const; + inline const T &at(int index) const; +}; + +// We do this so we can forward declare the type in the qdeclarativevme_p.h header +class QDeclarativeVMEObjectStack : public QDeclarativeVMEStack {}; + +QDeclarativeVME::QDeclarativeVME() +{ +} + +#define VME_EXCEPTION(desc) \ + { \ + QDeclarativeError error; \ + error.setDescription(desc.trimmed()); \ + error.setLine(instr.line); \ + error.setUrl(comp->url); \ + vmeErrors << error; \ + break; \ + } + +struct ListInstance +{ + ListInstance() + : type(0) {} + ListInstance(int t) + : type(t) {} + + int type; + QDeclarativeListProperty qListProperty; +}; + +Q_DECLARE_TYPEINFO(ListInstance, Q_PRIMITIVE_TYPE | Q_MOVABLE_TYPE); + +QObject *QDeclarativeVME::run(QDeclarativeContextData *ctxt, QDeclarativeCompiledData *comp, + int start, int count, const QBitField &bindingSkipList) +{ + QDeclarativeVMEObjectStack stack; + + if (start == -1) start = 0; + if (count == -1) count = comp->bytecode.count(); + + return run(stack, ctxt, comp, start, count, bindingSkipList); +} + +void QDeclarativeVME::runDeferred(QObject *object) +{ + QDeclarativeData *data = QDeclarativeData::get(object); + + if (!data || !data->context || !data->deferredComponent) + return; + + QDeclarativeContextData *ctxt = data->context; + QDeclarativeCompiledData *comp = data->deferredComponent; + int start = data->deferredIdx + 1; + int count = data->deferredComponent->bytecode.at(data->deferredIdx).defer.deferCount; + QDeclarativeVMEObjectStack stack; + stack.push(object); + + run(stack, ctxt, comp, start, count, QBitField()); +} + +inline bool fastHasBinding(QObject *o, int index) +{ + QDeclarativeData *ddata = static_cast(QObjectPrivate::get(o)->declarativeData); + + return ddata && (ddata->bindingBitsSize > index) && + (ddata->bindingBits[index / 32] & (1 << (index % 32))); +} + +static void removeBindingOnProperty(QObject *o, int index) +{ + QDeclarativeAbstractBinding *binding = QDeclarativePropertyPrivate::setBinding(o, index, -1, 0); + if (binding) binding->destroy(); +} + +#define CLEAN_PROPERTY(o, index) if (fastHasBinding(o, index)) removeBindingOnProperty(o, index) + +QObject *QDeclarativeVME::run(QDeclarativeVMEObjectStack &stack, + QDeclarativeContextData *ctxt, + QDeclarativeCompiledData *comp, + int start, int count, + const QBitField &bindingSkipList) +{ + Q_ASSERT(comp); + Q_ASSERT(ctxt); + const QList &types = comp->types; + const QList &primitives = comp->primitives; + const QList &datas = comp->datas; + const QList &customTypeData = comp->customTypeData; + const QList &intData = comp->intData; + const QList &floatData = comp->floatData; + const QList &propertyCaches = comp->propertyCaches; + const QList &scripts = comp->scripts; + const QList &urls = comp->urls; + + QDeclarativeEnginePrivate::SimpleList bindValues; + QDeclarativeEnginePrivate::SimpleList parserStatus; + + QDeclarativeVMEStack qliststack; + + vmeErrors.clear(); + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(ctxt->engine); + + int status = -1; //for dbus + QDeclarativePropertyPrivate::WriteFlags flags = QDeclarativePropertyPrivate::BypassInterceptor | + QDeclarativePropertyPrivate::RemoveBindingOnAliasWrite; + + for (int ii = start; !isError() && ii < (start + count); ++ii) { + const QDeclarativeInstruction &instr = comp->bytecode.at(ii); + + switch(instr.type) { + case QDeclarativeInstruction::Init: + { + if (instr.init.bindingsSize) + bindValues = QDeclarativeEnginePrivate::SimpleList(instr.init.bindingsSize); + if (instr.init.parserStatusSize) + parserStatus = QDeclarativeEnginePrivate::SimpleList(instr.init.parserStatusSize); + if (instr.init.contextCache != -1) + ctxt->setIdPropertyData(comp->contextCaches.at(instr.init.contextCache)); + if (instr.init.compiledBinding != -1) + ctxt->optimizedBindings = new QDeclarativeCompiledBindings(datas.at(instr.init.compiledBinding).constData(), ctxt, comp); + } + break; + + case QDeclarativeInstruction::CreateObject: + { + QBitField bindings; + if (instr.create.bindingBits != -1) { + const QByteArray &bits = datas.at(instr.create.bindingBits); + bindings = QBitField((const quint32*)bits.constData(), + bits.size() * 8); + } + if (stack.isEmpty()) + bindings = bindings.united(bindingSkipList); + + QObject *o = + types.at(instr.create.type).createInstance(ctxt, bindings, &vmeErrors); + + if (!o) { + VME_EXCEPTION(QCoreApplication::translate("QDeclarativeVME","Unable to create object of type %1").arg(QString::fromLatin1(types.at(instr.create.type).className))); + } + + QDeclarativeData *ddata = QDeclarativeData::get(o); + Q_ASSERT(ddata); + + if (stack.isEmpty()) { + if (ddata->context) { + Q_ASSERT(ddata->context != ctxt); + Q_ASSERT(ddata->outerContext); + Q_ASSERT(ddata->outerContext != ctxt); + QDeclarativeContextData *c = ddata->context; + while (c->linkedContext) c = c->linkedContext; + c->linkedContext = ctxt; + } else { + ctxt->addObject(o); + } + + ddata->ownContext = true; + } else if (!ddata->context) { + ctxt->addObject(o); + } + + ddata->setImplicitDestructible(); + ddata->outerContext = ctxt; + ddata->lineNumber = instr.line; + ddata->columnNumber = instr.create.column; + + if (instr.create.data != -1) { + QDeclarativeCustomParser *customParser = + types.at(instr.create.type).type->customParser(); + customParser->setCustomData(o, datas.at(instr.create.data)); + } + if (!stack.isEmpty()) { + QObject *parent = stack.top(); + if (o->isWidgetType()) { + QWidget *widget = static_cast(o); + if (parent->isWidgetType()) { + QWidget *parentWidget = static_cast(parent); + widget->setParent(parentWidget); + } else { + // TODO: parent might be a layout + } + } else { + QDeclarative_setParent_noEvent(o, parent); + } + } + stack.push(o); + } + break; + + case QDeclarativeInstruction::CreateSimpleObject: + { + QObject *o = (QObject *)operator new(instr.createSimple.typeSize + + sizeof(QDeclarativeData)); + ::memset(o, 0, instr.createSimple.typeSize + sizeof(QDeclarativeData)); + instr.createSimple.create(o); + + QDeclarativeData *ddata = (QDeclarativeData *)(((const char *)o) + instr.createSimple.typeSize); + const QDeclarativeCompiledData::TypeReference &ref = types.at(instr.createSimple.type); + if (!ddata->propertyCache && ref.typePropertyCache) { + ddata->propertyCache = ref.typePropertyCache; + ddata->propertyCache->addref(); + } + ddata->lineNumber = instr.line; + ddata->columnNumber = instr.createSimple.column; + + QObjectPrivate::get(o)->declarativeData = ddata; + ddata->context = ddata->outerContext = ctxt; + ddata->nextContextObject = ctxt->contextObjects; + if (ddata->nextContextObject) + ddata->nextContextObject->prevContextObject = &ddata->nextContextObject; + ddata->prevContextObject = &ctxt->contextObjects; + ctxt->contextObjects = ddata; + + QObject *parent = stack.top(); + QDeclarative_setParent_noEvent(o, parent); + + stack.push(o); + } + break; + + case QDeclarativeInstruction::SetId: + { + QObject *target = stack.top(); + ctxt->setIdProperty(instr.setId.index, target); + } + break; + + + case QDeclarativeInstruction::SetDefault: + { + ctxt->contextObject = stack.top(); + } + break; + + case QDeclarativeInstruction::CreateComponent: + { + QDeclarativeComponent *qcomp = + new QDeclarativeComponent(ctxt->engine, comp, ii + 1, instr.createComponent.count, + stack.isEmpty() ? 0 : stack.top()); + + QDeclarativeData *ddata = QDeclarativeData::get(qcomp, true); + Q_ASSERT(ddata); + + ctxt->addObject(qcomp); + + if (stack.isEmpty()) + ddata->ownContext = true; + + ddata->setImplicitDestructible(); + ddata->outerContext = ctxt; + ddata->lineNumber = instr.line; + ddata->columnNumber = instr.create.column; + + QDeclarativeComponentPrivate::get(qcomp)->creationContext = ctxt; + + stack.push(qcomp); + ii += instr.createComponent.count; + } + break; + + case QDeclarativeInstruction::StoreMetaObject: + { + QObject *target = stack.top(); + + QMetaObject mo; + const QByteArray &metadata = datas.at(instr.storeMeta.data); + QMetaObjectBuilder::fromRelocatableData(&mo, 0, metadata); + + const QDeclarativeVMEMetaData *data = + (const QDeclarativeVMEMetaData *)datas.at(instr.storeMeta.aliasData).constData(); + + (void)new QDeclarativeVMEMetaObject(target, &mo, data, comp); + + if (instr.storeMeta.propertyCache != -1) { + QDeclarativeData *ddata = QDeclarativeData::get(target, true); + if (ddata->propertyCache) ddata->propertyCache->release(); + ddata->propertyCache = propertyCaches.at(instr.storeMeta.propertyCache); + ddata->propertyCache->addref(); + } + } + break; + + case QDeclarativeInstruction::StoreVariant: + { + QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeString.propertyIndex); + + // XXX - can be more efficient + QVariant v = QDeclarativeStringConverters::variantFromString(primitives.at(instr.storeString.value)); + void *a[] = { &v, 0, &status, &flags }; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeString.propertyIndex, a); + } + break; + + case QDeclarativeInstruction::StoreVariantInteger: + { + QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeString.propertyIndex); + + QVariant v(instr.storeInteger.value); + void *a[] = { &v, 0, &status, &flags }; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeString.propertyIndex, a); + } + break; + + case QDeclarativeInstruction::StoreVariantDouble: + { + QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeString.propertyIndex); + + QVariant v(instr.storeDouble.value); + void *a[] = { &v, 0, &status, &flags }; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeString.propertyIndex, a); + } + break; + + case QDeclarativeInstruction::StoreVariantBool: + { + QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeString.propertyIndex); + + QVariant v(instr.storeBool.value); + void *a[] = { &v, 0, &status, &flags }; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeString.propertyIndex, a); + } + break; + + case QDeclarativeInstruction::StoreString: + { + QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeString.propertyIndex); + + void *a[] = { (void *)&primitives.at(instr.storeString.value), 0, &status, &flags }; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeString.propertyIndex, a); + } + break; + + case QDeclarativeInstruction::StoreUrl: + { + QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeUrl.propertyIndex); + + void *a[] = { (void *)&urls.at(instr.storeUrl.value), 0, &status, &flags }; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeUrl.propertyIndex, a); + } + break; + + case QDeclarativeInstruction::StoreFloat: + { + QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeFloat.propertyIndex); + + float f = instr.storeFloat.value; + void *a[] = { &f, 0, &status, &flags }; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeFloat.propertyIndex, a); + } + break; + + case QDeclarativeInstruction::StoreDouble: + { + QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeDouble.propertyIndex); + + double d = instr.storeDouble.value; + void *a[] = { &d, 0, &status, &flags }; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeDouble.propertyIndex, a); + } + break; + + case QDeclarativeInstruction::StoreBool: + { + QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeBool.propertyIndex); + + void *a[] = { (void *)&instr.storeBool.value, 0, &status, &flags }; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeBool.propertyIndex, a); + } + break; + + case QDeclarativeInstruction::StoreInteger: + { + QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeInteger.propertyIndex); + + void *a[] = { (void *)&instr.storeInteger.value, 0, &status, &flags }; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeInteger.propertyIndex, a); + } + break; + + case QDeclarativeInstruction::StoreColor: + { + QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeColor.propertyIndex); + + QColor c = QColor::fromRgba(instr.storeColor.value); + void *a[] = { &c, 0, &status, &flags }; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeColor.propertyIndex, a); + } + break; + + case QDeclarativeInstruction::StoreDate: + { + QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeDate.propertyIndex); + + QDate d = QDate::fromJulianDay(instr.storeDate.value); + void *a[] = { &d, 0, &status, &flags }; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeDate.propertyIndex, a); + } + break; + + case QDeclarativeInstruction::StoreTime: + { + QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeTime.propertyIndex); + + QTime t; + t.setHMS(intData.at(instr.storeTime.valueIndex), + intData.at(instr.storeTime.valueIndex+1), + intData.at(instr.storeTime.valueIndex+2), + intData.at(instr.storeTime.valueIndex+3)); + void *a[] = { &t, 0, &status, &flags }; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeTime.propertyIndex, a); + } + break; + + case QDeclarativeInstruction::StoreDateTime: + { + QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeDateTime.propertyIndex); + + QTime t; + t.setHMS(intData.at(instr.storeDateTime.valueIndex+1), + intData.at(instr.storeDateTime.valueIndex+2), + intData.at(instr.storeDateTime.valueIndex+3), + intData.at(instr.storeDateTime.valueIndex+4)); + QDateTime dt(QDate::fromJulianDay(intData.at(instr.storeDateTime.valueIndex)), t); + void *a[] = { &dt, 0, &status, &flags }; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeDateTime.propertyIndex, a); + } + break; + + case QDeclarativeInstruction::StorePoint: + { + QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeRealPair.propertyIndex); + + QPoint p = QPointF(floatData.at(instr.storeRealPair.valueIndex), + floatData.at(instr.storeRealPair.valueIndex+1)).toPoint(); + void *a[] = { &p, 0, &status, &flags }; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeRealPair.propertyIndex, a); + } + break; + + case QDeclarativeInstruction::StorePointF: + { + QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeRealPair.propertyIndex); + + QPointF p(floatData.at(instr.storeRealPair.valueIndex), + floatData.at(instr.storeRealPair.valueIndex+1)); + void *a[] = { &p, 0, &status, &flags }; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeRealPair.propertyIndex, a); + } + break; + + case QDeclarativeInstruction::StoreSize: + { + QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeRealPair.propertyIndex); + + QSize p = QSizeF(floatData.at(instr.storeRealPair.valueIndex), + floatData.at(instr.storeRealPair.valueIndex+1)).toSize(); + void *a[] = { &p, 0, &status, &flags }; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeRealPair.propertyIndex, a); + } + break; + + case QDeclarativeInstruction::StoreSizeF: + { + QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeRealPair.propertyIndex); + + QSizeF s(floatData.at(instr.storeRealPair.valueIndex), + floatData.at(instr.storeRealPair.valueIndex+1)); + void *a[] = { &s, 0, &status, &flags }; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeRealPair.propertyIndex, a); + } + break; + + case QDeclarativeInstruction::StoreRect: + { + QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeRect.propertyIndex); + + QRect r = QRectF(floatData.at(instr.storeRect.valueIndex), + floatData.at(instr.storeRect.valueIndex+1), + floatData.at(instr.storeRect.valueIndex+2), + floatData.at(instr.storeRect.valueIndex+3)).toRect(); + void *a[] = { &r, 0, &status, &flags }; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeRect.propertyIndex, a); + } + break; + + case QDeclarativeInstruction::StoreRectF: + { + QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeRect.propertyIndex); + + QRectF r(floatData.at(instr.storeRect.valueIndex), + floatData.at(instr.storeRect.valueIndex+1), + floatData.at(instr.storeRect.valueIndex+2), + floatData.at(instr.storeRect.valueIndex+3)); + void *a[] = { &r, 0, &status, &flags }; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeRect.propertyIndex, a); + } + break; + + case QDeclarativeInstruction::StoreVector3D: + { + QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeVector3D.propertyIndex); + + QVector3D p(floatData.at(instr.storeVector3D.valueIndex), + floatData.at(instr.storeVector3D.valueIndex+1), + floatData.at(instr.storeVector3D.valueIndex+2)); + void *a[] = { &p, 0, &status, &flags }; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeVector3D.propertyIndex, a); + } + break; + + case QDeclarativeInstruction::StoreObject: + { + QObject *assignObj = stack.pop(); + QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeObject.propertyIndex); + + void *a[] = { (void *)&assignObj, 0, &status, &flags }; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeObject.propertyIndex, a); + } + break; + + + case QDeclarativeInstruction::AssignCustomType: + { + QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.assignCustomType.propertyIndex); + + QDeclarativeCompiledData::CustomTypeData data = customTypeData.at(instr.assignCustomType.valueIndex); + const QString &primitive = primitives.at(data.index); + QDeclarativeMetaType::StringConverter converter = + QDeclarativeMetaType::customStringConverter(data.type); + QVariant v = (*converter)(primitive); + + QMetaProperty prop = + target->metaObject()->property(instr.assignCustomType.propertyIndex); + if (v.isNull() || ((int)prop.type() != data.type && prop.userType() != data.type)) + VME_EXCEPTION(QCoreApplication::translate("QDeclarativeVME","Cannot assign value %1 to property %2").arg(primitive).arg(QString::fromUtf8(prop.name()))); + + void *a[] = { (void *)v.data(), 0, &status, &flags }; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.assignCustomType.propertyIndex, a); + } + break; + + case QDeclarativeInstruction::AssignSignalObject: + { + // XXX optimize + + QObject *assign = stack.pop(); + QObject *target = stack.top(); + int sigIdx = instr.assignSignalObject.signal; + const QByteArray &pr = datas.at(sigIdx); + + QDeclarativeProperty prop(target, QString::fromUtf8(pr)); + if (prop.type() & QDeclarativeProperty::SignalProperty) { + + QMetaMethod method = QDeclarativeMetaType::defaultMethod(assign); + if (method.signature() == 0) + VME_EXCEPTION(QCoreApplication::translate("QDeclarativeVME","Cannot assign object type %1 with no default method").arg(QString::fromLatin1(assign->metaObject()->className()))); + + if (!QMetaObject::checkConnectArgs(prop.method().signature(), method.signature())) + VME_EXCEPTION(QCoreApplication::translate("QDeclarativeVME","Cannot connect mismatched signal/slot %1 %vs. %2").arg(QString::fromLatin1(method.signature())).arg(QString::fromLatin1(prop.method().signature()))); + + QDeclarativePropertyPrivate::connect(target, prop.index(), assign, method.methodIndex()); + + } else { + VME_EXCEPTION(QCoreApplication::translate("QDeclarativeVME","Cannot assign an object to signal property %1").arg(QString::fromUtf8(pr))); + } + + + } + break; + + case QDeclarativeInstruction::StoreSignal: + { + QObject *target = stack.top(); + QObject *context = stack.at(stack.count() - 1 - instr.storeSignal.context); + + QMetaMethod signal = target->metaObject()->method(instr.storeSignal.signalIndex); + + QDeclarativeBoundSignal *bs = new QDeclarativeBoundSignal(target, signal, target); + QDeclarativeExpression *expr = + new QDeclarativeExpression(ctxt, context, primitives.at(instr.storeSignal.value)); + expr->setSourceLocation(comp->name, instr.line); + static_cast(QObjectPrivate::get(expr))->name = datas.at(instr.storeSignal.name); + bs->setExpression(expr); + } + break; + + case QDeclarativeInstruction::StoreImportedScript: + { + ctxt->addImportedScript(scripts.at(instr.storeScript.value)); + } + break; + + case QDeclarativeInstruction::StoreScriptString: + { + QObject *target = stack.top(); + QObject *scope = stack.at(stack.count() - 1 - instr.storeScriptString.scope); + QDeclarativeScriptString ss; + ss.setContext(ctxt->asQDeclarativeContext()); + ss.setScopeObject(scope); + ss.setScript(primitives.at(instr.storeScriptString.value)); + + void *a[] = { &ss, 0, &status, &flags }; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeScriptString.propertyIndex, a); + } + break; + + case QDeclarativeInstruction::BeginObject: + { + QObject *target = stack.top(); + QDeclarativeParserStatus *status = reinterpret_cast(reinterpret_cast(target) + instr.begin.castValue); + parserStatus.append(status); + status->d = &parserStatus.values[parserStatus.count - 1]; + + status->classBegin(); + } + break; + + case QDeclarativeInstruction::StoreBinding: + case QDeclarativeInstruction::StoreBindingOnAlias: + { + QObject *target = + stack.at(stack.count() - 1 - instr.assignBinding.owner); + QObject *context = + stack.at(stack.count() - 1 - instr.assignBinding.context); + + QDeclarativeProperty mp = + QDeclarativePropertyPrivate::restore(datas.at(instr.assignBinding.property), target, ctxt); + + int coreIndex = mp.index(); + + if ((stack.count() - instr.assignBinding.owner) == 1 && bindingSkipList.testBit(coreIndex)) + break; + + QDeclarativeBinding *bind = new QDeclarativeBinding((void *)datas.at(instr.assignBinding.value).constData(), comp, context, ctxt, comp->name, instr.line, 0); + bindValues.append(bind); + bind->m_mePtr = &bindValues.values[bindValues.count - 1]; + bind->setTarget(mp); + + if (instr.type == QDeclarativeInstruction::StoreBindingOnAlias) { + QDeclarativeAbstractBinding *old = QDeclarativePropertyPrivate::setBindingNoEnable(target, coreIndex, QDeclarativePropertyPrivate::valueTypeCoreIndex(mp), bind); + if (old) { old->destroy(); } + } else { + bind->addToObject(target, QDeclarativePropertyPrivate::bindingIndex(mp)); + } + } + break; + + case QDeclarativeInstruction::StoreCompiledBinding: + { + QObject *target = + stack.at(stack.count() - 1 - instr.assignBinding.owner); + QObject *scope = + stack.at(stack.count() - 1 - instr.assignBinding.context); + + int property = instr.assignBinding.property; + if (stack.count() == 1 && bindingSkipList.testBit(property & 0xFFFF)) + break; + + QDeclarativeAbstractBinding *binding = + ctxt->optimizedBindings->configBinding(instr.assignBinding.value, target, scope, property); + bindValues.append(binding); + binding->m_mePtr = &bindValues.values[bindValues.count - 1]; + binding->addToObject(target, property); + } + break; + + case QDeclarativeInstruction::StoreValueSource: + { + QObject *obj = stack.pop(); + QDeclarativePropertyValueSource *vs = reinterpret_cast(reinterpret_cast(obj) + instr.assignValueSource.castValue); + QObject *target = stack.at(stack.count() - 1 - instr.assignValueSource.owner); + + QDeclarativeProperty prop = + QDeclarativePropertyPrivate::restore(datas.at(instr.assignValueSource.property), target, ctxt); + obj->setParent(target); + vs->setTarget(prop); + } + break; + + case QDeclarativeInstruction::StoreValueInterceptor: + { + QObject *obj = stack.pop(); + QDeclarativePropertyValueInterceptor *vi = reinterpret_cast(reinterpret_cast(obj) + instr.assignValueInterceptor.castValue); + QObject *target = stack.at(stack.count() - 1 - instr.assignValueInterceptor.owner); + QDeclarativeProperty prop = + QDeclarativePropertyPrivate::restore(datas.at(instr.assignValueInterceptor.property), target, ctxt); + obj->setParent(target); + vi->setTarget(prop); + QDeclarativeVMEMetaObject *mo = static_cast((QMetaObject*)target->metaObject()); + mo->registerInterceptor(prop.index(), QDeclarativePropertyPrivate::valueTypeCoreIndex(prop), vi); + } + break; + + case QDeclarativeInstruction::StoreObjectQList: + { + QObject *assign = stack.pop(); + + const ListInstance &list = qliststack.top(); + list.qListProperty.append((QDeclarativeListProperty*)&list.qListProperty, assign); + } + break; + + case QDeclarativeInstruction::AssignObjectList: + { + // This is only used for assigning interfaces + QObject *assign = stack.pop(); + const ListInstance &list = qliststack.top(); + + int type = list.type; + + void *ptr = 0; + + const char *iid = QDeclarativeMetaType::interfaceIId(type); + if (iid) + ptr = assign->qt_metacast(iid); + if (!ptr) + VME_EXCEPTION(QCoreApplication::translate("QDeclarativeVME","Cannot assign object to list")); + + + list.qListProperty.append((QDeclarativeListProperty*)&list.qListProperty, ptr); + } + break; + + case QDeclarativeInstruction::StoreVariantObject: + { + QObject *assign = stack.pop(); + QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeObject.propertyIndex); + + QVariant v = QVariant::fromValue(assign); + void *a[] = { &v, 0, &status, &flags }; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeObject.propertyIndex, a); + } + break; + + case QDeclarativeInstruction::StoreInterface: + { + QObject *assign = stack.pop(); + QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeObject.propertyIndex); + + int coreIdx = instr.storeObject.propertyIndex; + QMetaProperty prop = target->metaObject()->property(coreIdx); + int t = prop.userType(); + const char *iid = QDeclarativeMetaType::interfaceIId(t); + bool ok = false; + if (iid) { + void *ptr = assign->qt_metacast(iid); + if (ptr) { + void *a[] = { &ptr, 0, &status, &flags }; + QMetaObject::metacall(target, + QMetaObject::WriteProperty, + coreIdx, a); + ok = true; + } + } + + if (!ok) + VME_EXCEPTION(QCoreApplication::translate("QDeclarativeVME","Cannot assign object to interface property")); + } + break; + + case QDeclarativeInstruction::FetchAttached: + { + QObject *target = stack.top(); + + QObject *qmlObject = qmlAttachedPropertiesObjectById(instr.fetchAttached.id, target); + + if (!qmlObject) + VME_EXCEPTION(QCoreApplication::translate("QDeclarativeVME","Unable to create attached object")); + + stack.push(qmlObject); + } + break; + + case QDeclarativeInstruction::FetchQList: + { + QObject *target = stack.top(); + + qliststack.push(ListInstance(instr.fetchQmlList.type)); + + void *a[1]; + a[0] = (void *)&(qliststack.top().qListProperty); + QMetaObject::metacall(target, QMetaObject::ReadProperty, + instr.fetchQmlList.property, a); + } + break; + + case QDeclarativeInstruction::FetchObject: + { + QObject *target = stack.top(); + + QObject *obj = 0; + // NOTE: This assumes a cast to QObject does not alter the + // object pointer + void *a[1]; + a[0] = &obj; + QMetaObject::metacall(target, QMetaObject::ReadProperty, + instr.fetch.property, a); + + if (!obj) + VME_EXCEPTION(QCoreApplication::translate("QDeclarativeVME","Cannot set properties on %1 as it is null").arg(QString::fromUtf8(target->metaObject()->property(instr.fetch.property).name()))); + + stack.push(obj); + } + break; + + case QDeclarativeInstruction::PopQList: + { + qliststack.pop(); + } + break; + + case QDeclarativeInstruction::Defer: + { + if (instr.defer.deferCount) { + QObject *target = stack.top(); + QDeclarativeData *data = + QDeclarativeData::get(target, true); + comp->addref(); + data->deferredComponent = comp; + data->deferredIdx = ii; + ii += instr.defer.deferCount; + } + } + break; + + case QDeclarativeInstruction::PopFetchedObject: + { + stack.pop(); + } + break; + + case QDeclarativeInstruction::FetchValueType: + { + QObject *target = stack.top(); + + if (instr.fetchValue.bindingSkipList != 0) { + // Possibly need to clear bindings + QDeclarativeData *targetData = QDeclarativeData::get(target); + if (targetData) { + QDeclarativeAbstractBinding *binding = + QDeclarativePropertyPrivate::binding(target, instr.fetchValue.property, -1); + + if (binding && binding->bindingType() != QDeclarativeAbstractBinding::ValueTypeProxy) { + QDeclarativePropertyPrivate::setBinding(target, instr.fetchValue.property, -1, 0); + binding->destroy(); + } else if (binding) { + QDeclarativeValueTypeProxyBinding *proxy = + static_cast(binding); + proxy->removeBindings(instr.fetchValue.bindingSkipList); + } + } + } + + QDeclarativeValueType *valueHandler = ep->valueTypes[instr.fetchValue.type]; + valueHandler->read(target, instr.fetchValue.property); + stack.push(valueHandler); + } + break; + + case QDeclarativeInstruction::PopValueType: + { + QDeclarativeValueType *valueHandler = + static_cast(stack.pop()); + QObject *target = stack.top(); + valueHandler->write(target, instr.fetchValue.property, + QDeclarativePropertyPrivate::BypassInterceptor); + } + break; + + default: + qFatal("QDeclarativeCompiledData: Internal error - unknown instruction %d", instr.type); + break; + } + } + + if (isError()) { + if (!stack.isEmpty()) { + delete stack.at(0); // ### What about failures in deferred creation? + } else { + ctxt->destroy(); + } + + QDeclarativeEnginePrivate::clear(bindValues); + QDeclarativeEnginePrivate::clear(parserStatus); + return 0; + } + + if (bindValues.count) + ep->bindValues << bindValues; + else if (bindValues.values) + bindValues.clear(); + + if (parserStatus.count) + ep->parserStatus << parserStatus; + else if (parserStatus.values) + parserStatus.clear(); + + Q_ASSERT(stack.count() == 1); + return stack.top(); +} + +bool QDeclarativeVME::isError() const +{ + return !vmeErrors.isEmpty(); +} + +QList QDeclarativeVME::errors() const +{ + return vmeErrors; +} + +QObject * +QDeclarativeCompiledData::TypeReference::createInstance(QDeclarativeContextData *ctxt, + const QBitField &bindings, + QList *errors) const +{ + if (type) { + QObject *rv = 0; + void *memory = 0; + + type->create(&rv, &memory, sizeof(QDeclarativeData)); + QDeclarativeData *ddata = new (memory) QDeclarativeData; + ddata->ownMemory = false; + QObjectPrivate::get(rv)->declarativeData = ddata; + + if (typePropertyCache && !ddata->propertyCache) { + ddata->propertyCache = typePropertyCache; + ddata->propertyCache->addref(); + } + + return rv; + } else { + Q_ASSERT(component); + return QDeclarativeComponentPrivate::begin(ctxt, 0, component, -1, -1, 0, errors, bindings); + } +} + +template +QDeclarativeVMEStack::QDeclarativeVMEStack() +: _index(-1) +{ +} + +template +bool QDeclarativeVMEStack::isEmpty() const { + return _index == -1; +} + +template +const T &QDeclarativeVMEStack::top() const { + return at(_index); +} + +template +void QDeclarativeVMEStack::push(const T &o) { + _index++; + + Q_ASSERT(_index <= VLA::size()); + if (_index == VLA::size()) + VLA::append(o); + else + VLA::data()[_index] = o; +} + +template +T QDeclarativeVMEStack::pop() { + Q_ASSERT(_index >= 0); + --_index; + return VLA::data()[_index + 1]; +} + +template +int QDeclarativeVMEStack::count() const { + return _index + 1; +} + +template +const T &QDeclarativeVMEStack::at(int index) const { + return VLA::data()[index]; +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativevme_p.h b/src/declarative/qml/qdeclarativevme_p.h new file mode 100644 index 00000000..a00b4f12 --- /dev/null +++ b/src/declarative/qml/qdeclarativevme_p.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEVME_P_H +#define QDECLARATIVEVME_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 "qdeclarativeerror.h" +#include "private/qbitfield_p.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QObject; +class QDeclarativeInstruction; +class QDeclarativeCompiledData; +class QDeclarativeCompiledData; +class QDeclarativeContextData; + +class QDeclarativeVMEObjectStack; +class QDeclarativeVME +{ +public: + QDeclarativeVME(); + + QObject *run(QDeclarativeContextData *, QDeclarativeCompiledData *, + int start = -1, int count = -1, + const QBitField & = QBitField()); + void runDeferred(QObject *); + + bool isError() const; + QList errors() const; + +private: + QObject *run(QDeclarativeVMEObjectStack &, + QDeclarativeContextData *, QDeclarativeCompiledData *, + int start, int count, + const QBitField &); + QList vmeErrors; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVEVME_P_H diff --git a/src/declarative/qml/qdeclarativevmemetaobject.cpp b/src/declarative/qml/qdeclarativevmemetaobject.cpp new file mode 100644 index 00000000..f4cc8dbe --- /dev/null +++ b/src/declarative/qml/qdeclarativevmemetaobject.cpp @@ -0,0 +1,903 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativevmemetaobject_p.h" + +#include "qdeclarative.h" +#include "private/qdeclarativerefcount_p.h" +#include "qdeclarativeexpression.h" +#include "private/qdeclarativeexpression_p.h" +#include "private/qdeclarativecontext_p.h" +#include "private/qdeclarativebinding_p.h" + +Q_DECLARE_METATYPE(QScriptValue); + +QT_BEGIN_NAMESPACE + +class QDeclarativeVMEVariant +{ +public: + inline QDeclarativeVMEVariant(); + inline ~QDeclarativeVMEVariant(); + + inline const void *dataPtr() const; + inline void *dataPtr(); + inline int dataType() const; + + inline QObject *asQObject(); + inline const QVariant &asQVariant(); + inline int asInt(); + inline bool asBool(); + inline double asDouble(); + inline const QString &asQString(); + inline const QUrl &asQUrl(); + inline const QColor &asQColor(); + inline const QTime &asQTime(); + inline const QDate &asQDate(); + inline const QDateTime &asQDateTime(); + inline const QScriptValue &asQScriptValue(); + + inline void setValue(QObject *); + inline void setValue(const QVariant &); + inline void setValue(int); + inline void setValue(bool); + inline void setValue(double); + inline void setValue(const QString &); + inline void setValue(const QUrl &); + inline void setValue(const QColor &); + inline void setValue(const QTime &); + inline void setValue(const QDate &); + inline void setValue(const QDateTime &); + inline void setValue(const QScriptValue &); +private: + int type; + void *data[4]; // Large enough to hold all types + + inline void cleanup(); +}; + +QDeclarativeVMEVariant::QDeclarativeVMEVariant() +: type(QVariant::Invalid) +{ +} + +QDeclarativeVMEVariant::~QDeclarativeVMEVariant() +{ + cleanup(); +} + +void QDeclarativeVMEVariant::cleanup() +{ + if (type == QVariant::Invalid) { + } else if (type == QMetaType::Int || + type == QMetaType::Bool || + type == QMetaType::Double) { + type = QVariant::Invalid; + } else if (type == QMetaType::QObjectStar) { + ((QDeclarativeGuard*)dataPtr())->~QDeclarativeGuard(); + type = QVariant::Invalid; + } else if (type == QMetaType::QString) { + ((QString *)dataPtr())->~QString(); + type = QVariant::Invalid; + } else if (type == QMetaType::QUrl) { + ((QUrl *)dataPtr())->~QUrl(); + type = QVariant::Invalid; + } else if (type == QMetaType::QColor) { + ((QColor *)dataPtr())->~QColor(); + type = QVariant::Invalid; + } else if (type == QMetaType::QTime) { + ((QTime *)dataPtr())->~QTime(); + type = QVariant::Invalid; + } else if (type == QMetaType::QDate) { + ((QDate *)dataPtr())->~QDate(); + type = QVariant::Invalid; + } else if (type == QMetaType::QDateTime) { + ((QDateTime *)dataPtr())->~QDateTime(); + type = QVariant::Invalid; + } else if (type == qMetaTypeId()) { + ((QVariant *)dataPtr())->~QVariant(); + type = QVariant::Invalid; + } else if (type == qMetaTypeId()) { + ((QScriptValue *)dataPtr())->~QScriptValue(); + type = QVariant::Invalid; + } + +} + +int QDeclarativeVMEVariant::dataType() const +{ + return type; +} + +const void *QDeclarativeVMEVariant::dataPtr() const +{ + return &data; +} + +void *QDeclarativeVMEVariant::dataPtr() +{ + return &data; +} + +QObject *QDeclarativeVMEVariant::asQObject() +{ + if (type != QMetaType::QObjectStar) + setValue((QObject *)0); + + return *(QDeclarativeGuard *)(dataPtr()); +} + +const QVariant &QDeclarativeVMEVariant::asQVariant() +{ + if (type != QMetaType::QVariant) + setValue(QVariant()); + + return *(QVariant *)(dataPtr()); +} + +int QDeclarativeVMEVariant::asInt() +{ + if (type != QMetaType::Int) + setValue(int(0)); + + return *(int *)(dataPtr()); +} + +bool QDeclarativeVMEVariant::asBool() +{ + if (type != QMetaType::Bool) + setValue(bool(false)); + + return *(bool *)(dataPtr()); +} + +double QDeclarativeVMEVariant::asDouble() +{ + if (type != QMetaType::Double) + setValue(double(0)); + + return *(double *)(dataPtr()); +} + +const QString &QDeclarativeVMEVariant::asQString() +{ + if (type != QMetaType::QString) + setValue(QString()); + + return *(QString *)(dataPtr()); +} + +const QUrl &QDeclarativeVMEVariant::asQUrl() +{ + if (type != QMetaType::QUrl) + setValue(QUrl()); + + return *(QUrl *)(dataPtr()); +} + +const QColor &QDeclarativeVMEVariant::asQColor() +{ + if (type != QMetaType::QColor) + setValue(QColor()); + + return *(QColor *)(dataPtr()); +} + +const QTime &QDeclarativeVMEVariant::asQTime() +{ + if (type != QMetaType::QTime) + setValue(QTime()); + + return *(QTime *)(dataPtr()); +} + +const QDate &QDeclarativeVMEVariant::asQDate() +{ + if (type != QMetaType::QDate) + setValue(QDate()); + + return *(QDate *)(dataPtr()); +} + +const QDateTime &QDeclarativeVMEVariant::asQDateTime() +{ + if (type != QMetaType::QDateTime) + setValue(QDateTime()); + + return *(QDateTime *)(dataPtr()); +} + +const QScriptValue &QDeclarativeVMEVariant::asQScriptValue() +{ + if (type != qMetaTypeId()) + setValue(QScriptValue()); + + return *(QScriptValue *)(dataPtr()); +} + +void QDeclarativeVMEVariant::setValue(QObject *v) +{ + if (type != QMetaType::QObjectStar) { + cleanup(); + type = QMetaType::QObjectStar; + new (dataPtr()) QDeclarativeGuard(); + } + *(QDeclarativeGuard*)(dataPtr()) = v; +} + +void QDeclarativeVMEVariant::setValue(const QVariant &v) +{ + if (type != qMetaTypeId()) { + cleanup(); + type = qMetaTypeId(); + new (dataPtr()) QVariant(v); + } else { + *(QVariant *)(dataPtr()) = v; + } +} + +void QDeclarativeVMEVariant::setValue(int v) +{ + if (type != QMetaType::Int) { + cleanup(); + type = QMetaType::Int; + } + *(int *)(dataPtr()) = v; +} + +void QDeclarativeVMEVariant::setValue(bool v) +{ + if (type != QMetaType::Bool) { + cleanup(); + type = QMetaType::Bool; + } + *(bool *)(dataPtr()) = v; +} + +void QDeclarativeVMEVariant::setValue(double v) +{ + if (type != QMetaType::Double) { + cleanup(); + type = QMetaType::Double; + } + *(double *)(dataPtr()) = v; +} + +void QDeclarativeVMEVariant::setValue(const QString &v) +{ + if (type != QMetaType::QString) { + cleanup(); + type = QMetaType::QString; + new (dataPtr()) QString(v); + } else { + *(QString *)(dataPtr()) = v; + } +} + +void QDeclarativeVMEVariant::setValue(const QUrl &v) +{ + if (type != QMetaType::QUrl) { + cleanup(); + type = QMetaType::QUrl; + new (dataPtr()) QUrl(v); + } else { + *(QUrl *)(dataPtr()) = v; + } +} + +void QDeclarativeVMEVariant::setValue(const QColor &v) +{ + if (type != QMetaType::QColor) { + cleanup(); + type = QMetaType::QColor; + new (dataPtr()) QColor(v); + } else { + *(QColor *)(dataPtr()) = v; + } +} + +void QDeclarativeVMEVariant::setValue(const QTime &v) +{ + if (type != QMetaType::QTime) { + cleanup(); + type = QMetaType::QTime; + new (dataPtr()) QTime(v); + } else { + *(QTime *)(dataPtr()) = v; + } +} + +void QDeclarativeVMEVariant::setValue(const QDate &v) +{ + if (type != QMetaType::QDate) { + cleanup(); + type = QMetaType::QDate; + new (dataPtr()) QDate(v); + } else { + *(QDate *)(dataPtr()) = v; + } +} + +void QDeclarativeVMEVariant::setValue(const QDateTime &v) +{ + if (type != QMetaType::QDateTime) { + cleanup(); + type = QMetaType::QDateTime; + new (dataPtr()) QDateTime(v); + } else { + *(QDateTime *)(dataPtr()) = v; + } +} + +void QDeclarativeVMEVariant::setValue(const QScriptValue &v) +{ + if (type != qMetaTypeId()) { + cleanup(); + type = qMetaTypeId(); + new (dataPtr()) QScriptValue(v); + } else { + *(QScriptValue *)(dataPtr()) = v; + } +} + +QDeclarativeVMEMetaObject::QDeclarativeVMEMetaObject(QObject *obj, + const QMetaObject *other, + const QDeclarativeVMEMetaData *meta, + QDeclarativeCompiledData *cdata) +: object(obj), compiledData(cdata), ctxt(QDeclarativeData::get(obj, true)->outerContext), + metaData(meta), data(0), methods(0), parent(0) +{ + compiledData->addref(); + + *static_cast(this) = *other; + this->d.superdata = obj->metaObject(); + + QObjectPrivate *op = QObjectPrivate::get(obj); + if (op->metaObject) + parent = static_cast(op->metaObject); + op->metaObject = this; + + propOffset = QAbstractDynamicMetaObject::propertyOffset(); + methodOffset = QAbstractDynamicMetaObject::methodOffset(); + + data = new QDeclarativeVMEVariant[metaData->propertyCount]; + + aConnected.resize(metaData->aliasCount); + int list_type = qMetaTypeId >(); + + // ### Optimize + for (int ii = 0; ii < metaData->propertyCount; ++ii) { + int t = (metaData->propertyData() + ii)->propertyType; + if (t == list_type) { + listProperties.append(List(methodOffset + ii)); + data[ii].setValue(listProperties.count() - 1); + } + } +} + +QDeclarativeVMEMetaObject::~QDeclarativeVMEMetaObject() +{ + compiledData->release(); + delete parent; + delete [] data; + delete [] methods; +} + +int QDeclarativeVMEMetaObject::metaCall(QMetaObject::Call c, int _id, void **a) +{ + int id = _id; + if(c == QMetaObject::WriteProperty) { + int flags = *reinterpret_cast(a[3]); + if (!(flags & QDeclarativePropertyPrivate::BypassInterceptor) + && !aInterceptors.isEmpty() + && aInterceptors.testBit(id)) { + QPair pair = interceptors.value(id); + int valueIndex = pair.first; + QDeclarativePropertyValueInterceptor *vi = pair.second; + int type = property(id).userType(); + + if (type != QVariant::Invalid) { + if (valueIndex != -1) { + QDeclarativeEnginePrivate *ep = ctxt?QDeclarativeEnginePrivate::get(ctxt->engine):0; + QDeclarativeValueType *valueType = 0; + if (ep) valueType = ep->valueTypes[type]; + else valueType = QDeclarativeValueTypeFactory::valueType(type); + Q_ASSERT(valueType); + + valueType->setValue(QVariant(type, a[0])); + QMetaProperty valueProp = valueType->metaObject()->property(valueIndex); + vi->write(valueProp.read(valueType)); + + if (!ep) delete valueType; + return -1; + } else { + vi->write(QVariant(type, a[0])); + return -1; + } + } + } + } + if(c == QMetaObject::ReadProperty || c == QMetaObject::WriteProperty) { + if (id >= propOffset) { + id -= propOffset; + + if (id < metaData->propertyCount) { + int t = (metaData->propertyData() + id)->propertyType; + bool needActivate = false; + + if (t == -1) { + + if (c == QMetaObject::ReadProperty) { + *reinterpret_cast(a[0]) = readVarPropertyAsVariant(id); + } else if (c == QMetaObject::WriteProperty) { + writeVarProperty(id, *reinterpret_cast(a[0])); + } + + } else { + + if (c == QMetaObject::ReadProperty) { + switch(t) { + case QVariant::Int: + *reinterpret_cast(a[0]) = data[id].asInt(); + break; + case QVariant::Bool: + *reinterpret_cast(a[0]) = data[id].asBool(); + break; + case QVariant::Double: + *reinterpret_cast(a[0]) = data[id].asDouble(); + break; + case QVariant::String: + *reinterpret_cast(a[0]) = data[id].asQString(); + break; + case QVariant::Url: + *reinterpret_cast(a[0]) = data[id].asQUrl(); + break; + case QVariant::Color: + *reinterpret_cast(a[0]) = data[id].asQColor(); + break; + case QVariant::Date: + *reinterpret_cast(a[0]) = data[id].asQDate(); + break; + case QVariant::DateTime: + *reinterpret_cast(a[0]) = data[id].asQDateTime(); + break; + case QMetaType::QObjectStar: + *reinterpret_cast(a[0]) = data[id].asQObject(); + break; + default: + break; + } + if (t == qMetaTypeId >()) { + int listIndex = data[id].asInt(); + const List *list = &listProperties.at(listIndex); + *reinterpret_cast *>(a[0]) = + QDeclarativeListProperty(object, (void *)list, + list_append, list_count, list_at, + list_clear); + } + + } else if (c == QMetaObject::WriteProperty) { + + switch(t) { + case QVariant::Int: + needActivate = *reinterpret_cast(a[0]) != data[id].asInt(); + data[id].setValue(*reinterpret_cast(a[0])); + break; + case QVariant::Bool: + needActivate = *reinterpret_cast(a[0]) != data[id].asBool(); + data[id].setValue(*reinterpret_cast(a[0])); + break; + case QVariant::Double: + needActivate = *reinterpret_cast(a[0]) != data[id].asDouble(); + data[id].setValue(*reinterpret_cast(a[0])); + break; + case QVariant::String: + needActivate = *reinterpret_cast(a[0]) != data[id].asQString(); + data[id].setValue(*reinterpret_cast(a[0])); + break; + case QVariant::Url: + needActivate = *reinterpret_cast(a[0]) != data[id].asQUrl(); + data[id].setValue(*reinterpret_cast(a[0])); + break; + case QVariant::Color: + needActivate = *reinterpret_cast(a[0]) != data[id].asQColor(); + data[id].setValue(*reinterpret_cast(a[0])); + break; + case QVariant::Date: + needActivate = *reinterpret_cast(a[0]) != data[id].asQDate(); + data[id].setValue(*reinterpret_cast(a[0])); + break; + case QVariant::DateTime: + needActivate = *reinterpret_cast(a[0]) != data[id].asQDateTime(); + data[id].setValue(*reinterpret_cast(a[0])); + break; + case QMetaType::QObjectStar: + needActivate = *reinterpret_cast(a[0]) != data[id].asQObject(); + data[id].setValue(*reinterpret_cast(a[0])); + break; + default: + break; + } + } + + } + + if (c == QMetaObject::WriteProperty && needActivate) { + activate(object, methodOffset + id, 0); + } + + return -1; + } + + id -= metaData->propertyCount; + + if (id < metaData->aliasCount) { + + QDeclarativeVMEMetaData::AliasData *d = metaData->aliasData() + id; + + if (d->flags & QML_ALIAS_FLAG_PTR && c == QMetaObject::ReadProperty) + *reinterpret_cast(a[0]) = 0; + + if (!ctxt) return -1; + + QDeclarativeContext *context = ctxt->asQDeclarativeContext(); + QDeclarativeContextPrivate *ctxtPriv = QDeclarativeContextPrivate::get(context); + + QObject *target = ctxtPriv->data->idValues[d->contextIdx].data(); + if (!target) + return -1; + + connectAlias(id); + + if (d->isObjectAlias()) { + *reinterpret_cast(a[0]) = target; + return -1; + } + + // Remove binding (if any) on write + if(c == QMetaObject::WriteProperty) { + int flags = *reinterpret_cast(a[3]); + if (flags & QDeclarativePropertyPrivate::RemoveBindingOnAliasWrite) { + QDeclarativeData *targetData = QDeclarativeData::get(target); + if (targetData && targetData->hasBindingBit(d->propertyIndex())) { + QDeclarativeAbstractBinding *binding = QDeclarativePropertyPrivate::setBinding(target, d->propertyIndex(), d->isValueTypeAlias()?d->valueTypeIndex():-1, 0); + if (binding) binding->destroy(); + } + } + } + + if (d->isValueTypeAlias()) { + // Value type property + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(ctxt->engine); + + QDeclarativeValueType *valueType = ep->valueTypes[d->valueType()]; + Q_ASSERT(valueType); + + valueType->read(target, d->propertyIndex()); + int rv = QMetaObject::metacall(valueType, c, d->valueTypeIndex(), a); + + if (c == QMetaObject::WriteProperty) + valueType->write(target, d->propertyIndex(), 0x00); + + return rv; + + } else { + return QMetaObject::metacall(target, c, d->propertyIndex(), a); + } + + } + return -1; + + } + + } else if(c == QMetaObject::InvokeMetaMethod) { + + if (id >= methodOffset) { + + id -= methodOffset; + int plainSignals = metaData->signalCount + metaData->propertyCount + + metaData->aliasCount; + if (id < plainSignals) { + QMetaObject::activate(object, _id, a); + return -1; + } + + id -= plainSignals; + + if (id < metaData->methodCount) { + if (!ctxt->engine) + return -1; // We can't run the method + + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(ctxt->engine); + + QScriptValue function = method(id); + + QScriptValueList args; + QDeclarativeVMEMetaData::MethodData *data = metaData->methodData() + id; + if (data->parameterCount) { + for (int ii = 0; ii < data->parameterCount; ++ii) { + args << ep->scriptValueFromVariant(*(QVariant *)a[ii + 1]); + } + } + QScriptValue rv = function.call(ep->objectClass->newQObject(object), args); + + if (a[0]) *reinterpret_cast(a[0]) = ep->scriptValueToVariant(rv); + + return -1; + } + return -1; + } + } + + if (parent) + return parent->metaCall(c, _id, a); + else + return object->qt_metacall(c, _id, a); +} + +QScriptValue QDeclarativeVMEMetaObject::method(int index) +{ + if (!methods) + methods = new QScriptValue[metaData->methodCount]; + + if (!methods[index].isValid()) { + QDeclarativeVMEMetaData::MethodData *data = metaData->methodData() + index; + + const QChar *body = + (const QChar *)(((const char*)metaData) + data->bodyOffset); + + QString code = QString::fromRawData(body, data->bodyLength); + + // XXX Use QScriptProgram + // XXX We should evaluate all methods in a single big script block to + // improve the call time between dynamic methods defined on the same + // object + methods[index] = QDeclarativeExpressionPrivate::evalInObjectScope(ctxt, object, code, ctxt->url.toString(), + data->lineNumber, 0); + } + + return methods[index]; +} + +QScriptValue QDeclarativeVMEMetaObject::readVarProperty(int id) +{ + if (data[id].dataType() == qMetaTypeId()) + return data[id].asQScriptValue(); + else if (data[id].dataType() == QMetaType::QObjectStar) + return QDeclarativeEnginePrivate::get(ctxt->engine)->objectClass->newQObject(data[id].asQObject()); + else + return QDeclarativeEnginePrivate::get(ctxt->engine)->scriptValueFromVariant(data[id].asQVariant()); +} + +QVariant QDeclarativeVMEMetaObject::readVarPropertyAsVariant(int id) +{ + if (data[id].dataType() == qMetaTypeId()) + return QDeclarativeEnginePrivate::get(ctxt->engine)->scriptValueToVariant(data[id].asQScriptValue()); + else if (data[id].dataType() == QMetaType::QObjectStar) + return QVariant::fromValue(data[id].asQObject()); + else + return data[id].asQVariant(); +} + +void QDeclarativeVMEMetaObject::writeVarProperty(int id, const QScriptValue &value) +{ + data[id].setValue(value); + activate(object, methodOffset + id, 0); +} + +void QDeclarativeVMEMetaObject::writeVarProperty(int id, const QVariant &value) +{ + bool needActivate = false; + if (value.userType() == QMetaType::QObjectStar) { + QObject *o = qvariant_cast(value); + needActivate = (data[id].dataType() != QMetaType::QObjectStar || data[id].asQObject() != o); + data[id].setValue(qvariant_cast(value)); + } else { + needActivate = (data[id].dataType() != qMetaTypeId() || + data[id].asQVariant().userType() != value.userType() || + data[id].asQVariant() != value); + data[id].setValue(value); + } + if (needActivate) + activate(object, methodOffset + id, 0); +} + +void QDeclarativeVMEMetaObject::listChanged(int id) +{ + activate(object, methodOffset + id, 0); +} + +void QDeclarativeVMEMetaObject::list_append(QDeclarativeListProperty *prop, QObject *o) +{ + List *list = static_cast(prop->data); + list->append(o); + QMetaObject::activate(prop->object, list->notifyIndex, 0); +} + +int QDeclarativeVMEMetaObject::list_count(QDeclarativeListProperty *prop) +{ + return static_cast(prop->data)->count(); +} + +QObject *QDeclarativeVMEMetaObject::list_at(QDeclarativeListProperty *prop, int index) +{ + return static_cast(prop->data)->at(index); +} + +void QDeclarativeVMEMetaObject::list_clear(QDeclarativeListProperty *prop) +{ + List *list = static_cast(prop->data); + list->clear(); + QMetaObject::activate(prop->object, list->notifyIndex, 0); +} + +void QDeclarativeVMEMetaObject::registerInterceptor(int index, int valueIndex, QDeclarativePropertyValueInterceptor *interceptor) +{ + if (aInterceptors.isEmpty()) + aInterceptors.resize(propertyCount() + metaData->propertyCount); + aInterceptors.setBit(index); + interceptors.insert(index, qMakePair(valueIndex, interceptor)); +} + +int QDeclarativeVMEMetaObject::vmeMethodLineNumber(int index) +{ + if (index < methodOffset) { + Q_ASSERT(parent); + return static_cast(parent)->vmeMethodLineNumber(index); + } + + int plainSignals = metaData->signalCount + metaData->propertyCount + metaData->aliasCount; + Q_ASSERT(index >= (methodOffset + plainSignals) && index < (methodOffset + plainSignals + metaData->methodCount)); + + int rawIndex = index - methodOffset - plainSignals; + + QDeclarativeVMEMetaData::MethodData *data = metaData->methodData() + rawIndex; + return data->lineNumber; +} + +QScriptValue QDeclarativeVMEMetaObject::vmeMethod(int index) +{ + if (index < methodOffset) { + Q_ASSERT(parent); + return static_cast(parent)->vmeMethod(index); + } + int plainSignals = metaData->signalCount + metaData->propertyCount + metaData->aliasCount; + Q_ASSERT(index >= (methodOffset + plainSignals) && index < (methodOffset + plainSignals + metaData->methodCount)); + return method(index - methodOffset - plainSignals); +} + +void QDeclarativeVMEMetaObject::setVmeMethod(int index, const QScriptValue &value) +{ + if (index < methodOffset) { + Q_ASSERT(parent); + return static_cast(parent)->setVmeMethod(index, value); + } + int plainSignals = metaData->signalCount + metaData->propertyCount + metaData->aliasCount; + Q_ASSERT(index >= (methodOffset + plainSignals) && index < (methodOffset + plainSignals + metaData->methodCount)); + + if (!methods) + methods = new QScriptValue[metaData->methodCount]; + methods[index - methodOffset - plainSignals] = value; +} + +QScriptValue QDeclarativeVMEMetaObject::vmeProperty(int index) +{ + if (index < propOffset) { + Q_ASSERT(parent); + return static_cast(parent)->vmeProperty(index); + } + return readVarProperty(index - propOffset); +} + +void QDeclarativeVMEMetaObject::setVMEProperty(int index, const QScriptValue &v) +{ + if (index < propOffset) { + Q_ASSERT(parent); + static_cast(parent)->setVMEProperty(index, v); + } + return writeVarProperty(index - propOffset, v); +} + +bool QDeclarativeVMEMetaObject::aliasTarget(int index, QObject **target, int *coreIndex, int *valueTypeIndex) const +{ + Q_ASSERT(index >= propOffset + metaData->propertyCount); + + *target = 0; + *coreIndex = -1; + *valueTypeIndex = -1; + + if (!ctxt) + return false; + + QDeclarativeVMEMetaData::AliasData *d = metaData->aliasData() + (index - propOffset - metaData->propertyCount); + QDeclarativeContext *context = ctxt->asQDeclarativeContext(); + QDeclarativeContextPrivate *ctxtPriv = QDeclarativeContextPrivate::get(context); + + *target = ctxtPriv->data->idValues[d->contextIdx].data(); + if (!*target) + return false; + + if (d->isObjectAlias()) { + } else if (d->isValueTypeAlias()) { + *coreIndex = d->propertyIndex(); + *valueTypeIndex = d->valueTypeIndex(); + } else { + *coreIndex = d->propertyIndex(); + } + + return true; +} + +void QDeclarativeVMEMetaObject::connectAlias(int aliasId) +{ + if (!aConnected.testBit(aliasId)) { + aConnected.setBit(aliasId); + + QDeclarativeContext *context = ctxt->asQDeclarativeContext(); + QDeclarativeContextPrivate *ctxtPriv = QDeclarativeContextPrivate::get(context); + + QDeclarativeVMEMetaData::AliasData *d = metaData->aliasData() + aliasId; + + QObject *target = ctxtPriv->data->idValues[d->contextIdx].data(); + if (!target) + return; + + int sigIdx = methodOffset + aliasId + metaData->propertyCount; + QMetaObject::connect(context, d->contextIdx + ctxtPriv->notifyIndex, object, sigIdx); + + if (!d->isObjectAlias()) { + QMetaProperty prop = target->metaObject()->property(d->propertyIndex()); + if (prop.hasNotifySignal()) + QDeclarativePropertyPrivate::connect(target, prop.notifySignalIndex(), object, sigIdx); + } + } +} + +void QDeclarativeVMEMetaObject::connectAliasSignal(int index) +{ + int aliasId = (index - methodOffset) - metaData->propertyCount; + if (aliasId < 0 || aliasId >= metaData->aliasCount) + return; + + connectAlias(aliasId); +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativevmemetaobject_p.h b/src/declarative/qml/qdeclarativevmemetaobject_p.h new file mode 100644 index 00000000..3c6e4524 --- /dev/null +++ b/src/declarative/qml/qdeclarativevmemetaobject_p.h @@ -0,0 +1,197 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEVMEMETAOBJECT_P_H +#define QDECLARATIVEVMEMETAOBJECT_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 "qdeclarative.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "private/qdeclarativeguard_p.h" +#include "private/qdeclarativecompiler_p.h" +#include "private/qdeclarativecontext_p.h" + +QT_BEGIN_NAMESPACE + +#define QML_ALIAS_FLAG_PTR 0x00000001 + +struct QDeclarativeVMEMetaData +{ + short propertyCount; + short aliasCount; + short signalCount; + short methodCount; + + struct AliasData { + int contextIdx; + int propertyIdx; + int flags; + + bool isObjectAlias() const { + return propertyIdx == -1; + } + bool isPropertyAlias() const { + return !isObjectAlias() && !(propertyIdx & 0xFF000000); + } + bool isValueTypeAlias() const { + return !isObjectAlias() && (propertyIdx & 0xFF000000); + } + int propertyIndex() const { + return propertyIdx & 0x0000FFFF; + } + int valueTypeIndex() const { + return (propertyIdx & 0x00FF0000) >> 16; + } + int valueType() const { + return ((unsigned int)propertyIdx) >> 24; + } + }; + + struct PropertyData { + int propertyType; + }; + + struct MethodData { + int parameterCount; + int bodyOffset; + int bodyLength; + int lineNumber; + }; + + PropertyData *propertyData() const { + return (PropertyData *)(((const char *)this) + sizeof(QDeclarativeVMEMetaData)); + } + + AliasData *aliasData() const { + return (AliasData *)(propertyData() + propertyCount); + } + + MethodData *methodData() const { + return (MethodData *)(aliasData() + aliasCount); + } +}; + +class QDeclarativeVMEVariant; +class QDeclarativeRefCount; +class QDeclarativeVMEMetaObject : public QAbstractDynamicMetaObject +{ +public: + QDeclarativeVMEMetaObject(QObject *obj, const QMetaObject *other, const QDeclarativeVMEMetaData *data, + QDeclarativeCompiledData *compiledData); + ~QDeclarativeVMEMetaObject(); + + bool aliasTarget(int index, QObject **target, int *coreIndex, int *valueTypeIndex) const; + void registerInterceptor(int index, int valueIndex, QDeclarativePropertyValueInterceptor *interceptor); + QScriptValue vmeMethod(int index); + int vmeMethodLineNumber(int index); + void setVmeMethod(int index, const QScriptValue &); + QScriptValue vmeProperty(int index); + void setVMEProperty(int index, const QScriptValue &); + + void connectAliasSignal(int index); + +protected: + virtual int metaCall(QMetaObject::Call _c, int _id, void **_a); + +private: + QObject *object; + QDeclarativeCompiledData *compiledData; + QDeclarativeGuardedContextData ctxt; + + const QDeclarativeVMEMetaData *metaData; + int propOffset; + int methodOffset; + + QDeclarativeVMEVariant *data; + + void connectAlias(int aliasId); + QBitArray aConnected; + QBitArray aInterceptors; + QHash > interceptors; + + QScriptValue *methods; + QScriptValue method(int); + + QScriptValue readVarProperty(int); + QVariant readVarPropertyAsVariant(int); + void writeVarProperty(int, const QScriptValue &); + void writeVarProperty(int, const QVariant &); + + QAbstractDynamicMetaObject *parent; + + void listChanged(int); + class List : public QList + { + public: + List(int lpi) : notifyIndex(lpi) {} + int notifyIndex; + }; + QList listProperties; + + static void list_append(QDeclarativeListProperty *, QObject *); + static int list_count(QDeclarativeListProperty *); + static QObject *list_at(QDeclarativeListProperty *, int); + static void list_clear(QDeclarativeListProperty *); +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVEVMEMETAOBJECT_P_H diff --git a/src/declarative/qml/qdeclarativewatcher.cpp b/src/declarative/qml/qdeclarativewatcher.cpp new file mode 100644 index 00000000..de5f1489 --- /dev/null +++ b/src/declarative/qml/qdeclarativewatcher.cpp @@ -0,0 +1,188 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativewatcher_p.h" + +#include "qdeclarativeexpression.h" +#include "qdeclarativecontext.h" +#include "qdeclarative.h" + +#include +#include "private/qdeclarativeproperty_p.h" +#include "private/qdeclarativevaluetype_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + + +class QDeclarativeWatchProxy : public QObject +{ + Q_OBJECT +public: + QDeclarativeWatchProxy(int id, + QObject *object, + int debugId, + const QMetaProperty &prop, + QDeclarativeWatcher *parent = 0); + + QDeclarativeWatchProxy(int id, + QDeclarativeExpression *exp, + int debugId, + QDeclarativeWatcher *parent = 0); + +public slots: + void notifyValueChanged(); + +private: + friend class QDeclarativeWatcher; + int m_id; + QDeclarativeWatcher *m_watch; + QObject *m_object; + int m_debugId; + QMetaProperty m_property; + + QDeclarativeExpression *m_expr; +}; + +QDeclarativeWatchProxy::QDeclarativeWatchProxy(int id, + QDeclarativeExpression *exp, + int debugId, + QDeclarativeWatcher *parent) +: QObject(parent), m_id(id), m_watch(parent), m_object(0), m_debugId(debugId), m_expr(exp) +{ + QObject::connect(m_expr, SIGNAL(valueChanged()), this, SLOT(notifyValueChanged())); +} + +QDeclarativeWatchProxy::QDeclarativeWatchProxy(int id, + QObject *object, + int debugId, + const QMetaProperty &prop, + QDeclarativeWatcher *parent) +: QObject(parent), m_id(id), m_watch(parent), m_object(object), m_debugId(debugId), m_property(prop), m_expr(0) +{ + static int refreshIdx = -1; + if(refreshIdx == -1) + refreshIdx = QDeclarativeWatchProxy::staticMetaObject.indexOfMethod("notifyValueChanged()"); + + if (prop.hasNotifySignal()) + QDeclarativePropertyPrivate::connect(m_object, prop.notifySignalIndex(), this, refreshIdx); +} + +void QDeclarativeWatchProxy::notifyValueChanged() +{ + QVariant v; + if (m_expr) + v = m_expr->evaluate(); + else if (QDeclarativeValueTypeFactory::isValueType(m_property.userType())) + v = m_property.read(m_object); + + emit m_watch->propertyChanged(m_id, m_debugId, m_property, v); +} + + +QDeclarativeWatcher::QDeclarativeWatcher(QObject *parent) + : QObject(parent) +{ +} + +bool QDeclarativeWatcher::addWatch(int id, quint32 debugId) +{ + QObject *object = QDeclarativeDebugService::objectForId(debugId); + if (object) { + int propCount = object->metaObject()->propertyCount(); + for (int ii=0; iimetaObject()->property(ii)); + return true; + } + return false; +} + +bool QDeclarativeWatcher::addWatch(int id, quint32 debugId, const QByteArray &property) +{ + QObject *object = QDeclarativeDebugService::objectForId(debugId); + if (object) { + int index = object->metaObject()->indexOfProperty(property.constData()); + if (index >= 0) { + addPropertyWatch(id, object, debugId, object->metaObject()->property(index)); + return true; + } + } + return false; +} + +bool QDeclarativeWatcher::addWatch(int id, quint32 objectId, const QString &expr) +{ + QObject *object = QDeclarativeDebugService::objectForId(objectId); + QDeclarativeContext *context = qmlContext(object); + if (context) { + QDeclarativeExpression *exprObj = new QDeclarativeExpression(context, object, expr); + exprObj->setNotifyOnValueChanged(true); + QDeclarativeWatchProxy *proxy = new QDeclarativeWatchProxy(id, exprObj, objectId, this); + exprObj->setParent(proxy); + m_proxies[id].append(proxy); + proxy->notifyValueChanged(); + return true; + } + return false; +} + +void QDeclarativeWatcher::removeWatch(int id) +{ + if (!m_proxies.contains(id)) + return; + + QList > proxies = m_proxies.take(id); + qDeleteAll(proxies); +} + +void QDeclarativeWatcher::addPropertyWatch(int id, QObject *object, quint32 debugId, const QMetaProperty &property) +{ + QDeclarativeWatchProxy *proxy = new QDeclarativeWatchProxy(id, object, debugId, property, this); + m_proxies[id].append(proxy); + + proxy->notifyValueChanged(); +} + +QT_END_NAMESPACE + +#include diff --git a/src/declarative/qml/qdeclarativewatcher_p.h b/src/declarative/qml/qdeclarativewatcher_p.h new file mode 100644 index 00000000..c8b31424 --- /dev/null +++ b/src/declarative/qml/qdeclarativewatcher_p.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEWATCHER_P_H +#define QDECLARATIVEWATCHER_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 +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeWatchProxy; +class QDeclarativeExpression; +class QDeclarativeContext; +class QMetaProperty; + +class QDeclarativeWatcher : public QObject +{ + Q_OBJECT +public: + QDeclarativeWatcher(QObject * = 0); + + bool addWatch(int id, quint32 objectId); + bool addWatch(int id, quint32 objectId, const QByteArray &property); + bool addWatch(int id, quint32 objectId, const QString &expr); + + void removeWatch(int id); + +Q_SIGNALS: + void propertyChanged(int id, int objectId, const QMetaProperty &property, const QVariant &value); + +private: + friend class QDeclarativeWatchProxy; + void addPropertyWatch(int id, QObject *object, quint32 objectId, const QMetaProperty &property); + + QHash > > m_proxies; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVEWATCHER_P_H diff --git a/src/declarative/qml/qdeclarativeworkerscript.cpp b/src/declarative/qml/qdeclarativeworkerscript.cpp new file mode 100644 index 00000000..be283eae --- /dev/null +++ b/src/declarative/qml/qdeclarativeworkerscript.cpp @@ -0,0 +1,753 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativeworkerscript_p.h" +#include "private/qdeclarativelistmodel_p.h" +#include "private/qdeclarativelistmodelworkeragent_p.h" +#include "private/qdeclarativeengine_p.h" +#include "private/qdeclarativeexpression_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "qdeclarativenetworkaccessmanagerfactory.h" + + +QT_BEGIN_NAMESPACE + +class WorkerDataEvent : public QEvent +{ +public: + enum Type { WorkerData = QEvent::User }; + + WorkerDataEvent(int workerId, const QVariant &data); + virtual ~WorkerDataEvent(); + + int workerId() const; + QVariant data() const; + +private: + int m_id; + QVariant m_data; +}; + +class WorkerLoadEvent : public QEvent +{ +public: + enum Type { WorkerLoad = WorkerDataEvent::WorkerData + 1 }; + + WorkerLoadEvent(int workerId, const QUrl &url); + + int workerId() const; + QUrl url() const; + +private: + int m_id; + QUrl m_url; +}; + +class WorkerRemoveEvent : public QEvent +{ +public: + enum Type { WorkerRemove = WorkerLoadEvent::WorkerLoad + 1 }; + + WorkerRemoveEvent(int workerId); + + int workerId() const; + +private: + int m_id; +}; + +class WorkerErrorEvent : public QEvent +{ +public: + enum Type { WorkerError = WorkerRemoveEvent::WorkerRemove + 1 }; + + WorkerErrorEvent(const QDeclarativeError &error); + + QDeclarativeError error() const; + +private: + QDeclarativeError m_error; +}; + +class QDeclarativeWorkerScriptEnginePrivate : public QObject +{ + Q_OBJECT +public: + enum WorkerEventTypes { + WorkerDestroyEvent = QEvent::User + 100 + }; + + QDeclarativeWorkerScriptEnginePrivate(QDeclarativeEngine *eng); + + struct ScriptEngine : public QDeclarativeScriptEngine + { + ScriptEngine(QDeclarativeWorkerScriptEnginePrivate *parent) : QDeclarativeScriptEngine(0), p(parent), accessManager(0) {} + ~ScriptEngine() { delete accessManager; } + QDeclarativeWorkerScriptEnginePrivate *p; + QNetworkAccessManager *accessManager; + + virtual QNetworkAccessManager *networkAccessManager() { + if (!accessManager) { + if (p->qmlengine && p->qmlengine->networkAccessManagerFactory()) { + accessManager = p->qmlengine->networkAccessManagerFactory()->create(this); + } else { + accessManager = new QNetworkAccessManager(this); + } + } + return accessManager; + } + }; + ScriptEngine *workerEngine; + static QDeclarativeWorkerScriptEnginePrivate *get(QScriptEngine *e) { + return static_cast(e)->p; + } + + QDeclarativeEngine *qmlengine; + + QMutex m_lock; + QWaitCondition m_wait; + + struct WorkerScript { + WorkerScript(); + + int id; + QUrl source; + bool initialized; + QDeclarativeWorkerScript *owner; + QScriptValue object; + + QScriptValue callback; + }; + + QHash workers; + QScriptValue getWorker(int); + + int m_nextId; + + static QVariant scriptValueToVariant(const QScriptValue &); + static QScriptValue variantToScriptValue(const QVariant &, QScriptEngine *); + + static QScriptValue onMessage(QScriptContext *ctxt, QScriptEngine *engine); + static QScriptValue sendMessage(QScriptContext *ctxt, QScriptEngine *engine); + +signals: + void stopThread(); + +protected: + virtual bool event(QEvent *); + +private: + void processMessage(int, const QVariant &); + void processLoad(int, const QUrl &); + void reportScriptException(WorkerScript *); +}; + +QDeclarativeWorkerScriptEnginePrivate::QDeclarativeWorkerScriptEnginePrivate(QDeclarativeEngine *engine) +: workerEngine(0), qmlengine(engine), m_nextId(0) +{ +} + +QScriptValue QDeclarativeWorkerScriptEnginePrivate::onMessage(QScriptContext *ctxt, QScriptEngine *engine) +{ + QDeclarativeWorkerScriptEnginePrivate *p = QDeclarativeWorkerScriptEnginePrivate::get(engine); + + int id = ctxt->thisObject().data().toVariant().toInt(); + + WorkerScript *script = p->workers.value(id); + if (!script) + return engine->undefinedValue(); + + if (ctxt->argumentCount() >= 1) + script->callback = ctxt->argument(0); + + return script->callback; +} + +QScriptValue QDeclarativeWorkerScriptEnginePrivate::sendMessage(QScriptContext *ctxt, QScriptEngine *engine) +{ + if (!ctxt->argumentCount()) + return engine->undefinedValue(); + + QDeclarativeWorkerScriptEnginePrivate *p = QDeclarativeWorkerScriptEnginePrivate::get(engine); + + int id = ctxt->thisObject().data().toVariant().toInt(); + + WorkerScript *script = p->workers.value(id); + if (!script) + return engine->undefinedValue(); + + QMutexLocker(&p->m_lock); + + if (script->owner) + QCoreApplication::postEvent(script->owner, + new WorkerDataEvent(0, scriptValueToVariant(ctxt->argument(0)))); + + return engine->undefinedValue(); +} + +QScriptValue QDeclarativeWorkerScriptEnginePrivate::getWorker(int id) +{ + QHash::ConstIterator iter = workers.find(id); + + if (iter == workers.end()) + return workerEngine->nullValue(); + + WorkerScript *script = *iter; + if (!script->initialized) { + + script->initialized = true; + script->object = workerEngine->newObject(); + + QScriptValue api = workerEngine->newObject(); + api.setData(script->id); + + api.setProperty(QLatin1String("onMessage"), workerEngine->newFunction(onMessage), + QScriptValue::PropertyGetter | QScriptValue::PropertySetter); + api.setProperty(QLatin1String("sendMessage"), workerEngine->newFunction(sendMessage)); + + script->object.setProperty(QLatin1String("WorkerScript"), api); + } + + return script->object; +} + +bool QDeclarativeWorkerScriptEnginePrivate::event(QEvent *event) +{ + if (event->type() == (QEvent::Type)WorkerDataEvent::WorkerData) { + WorkerDataEvent *workerEvent = static_cast(event); + processMessage(workerEvent->workerId(), workerEvent->data()); + return true; + } else if (event->type() == (QEvent::Type)WorkerLoadEvent::WorkerLoad) { + WorkerLoadEvent *workerEvent = static_cast(event); + processLoad(workerEvent->workerId(), workerEvent->url()); + return true; + } else if (event->type() == (QEvent::Type)WorkerDestroyEvent) { + emit stopThread(); + return true; + } else { + return QObject::event(event); + } +} + +void QDeclarativeWorkerScriptEnginePrivate::processMessage(int id, const QVariant &data) +{ + WorkerScript *script = workers.value(id); + if (!script) + return; + + if (script->callback.isFunction()) { + QScriptValue args = workerEngine->newArray(1); + args.setProperty(0, variantToScriptValue(data, workerEngine)); + + script->callback.call(script->object, args); + + if (workerEngine->hasUncaughtException()) { + reportScriptException(script); + workerEngine->clearExceptions(); + } + } +} + +void QDeclarativeWorkerScriptEnginePrivate::processLoad(int id, const QUrl &url) +{ + if (url.isRelative()) + return; + + QString fileName = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url); + + QFile f(fileName); + if (f.open(QIODevice::ReadOnly)) { + QByteArray data = f.readAll(); + QString sourceCode = QString::fromUtf8(data); + + QScriptValue activation = getWorker(id); + + QScriptContext *ctxt = QScriptDeclarativeClass::pushCleanContext(workerEngine); + QScriptValue urlContext = workerEngine->newObject(); + urlContext.setData(QScriptValue(workerEngine, url.toString())); + ctxt->pushScope(urlContext); + ctxt->pushScope(activation); + ctxt->setActivationObject(activation); + QDeclarativeScriptParser::extractPragmas(sourceCode); + + workerEngine->baseUrl = url; + workerEngine->evaluate(sourceCode); + + WorkerScript *script = workers.value(id); + if (script) { + script->source = url; + if (workerEngine->hasUncaughtException()) { + reportScriptException(script); + workerEngine->clearExceptions(); + } + } + + workerEngine->popContext(); + } else { + qWarning().nospace() << "WorkerScript: Cannot find source file " << url.toString(); + } +} + +void QDeclarativeWorkerScriptEnginePrivate::reportScriptException(WorkerScript *script) +{ + if (!script || !workerEngine->hasUncaughtException()) + return; + + QDeclarativeError error; + QDeclarativeExpressionPrivate::exceptionToError(workerEngine, error); + error.setUrl(script->source); + + QDeclarativeWorkerScriptEnginePrivate *p = QDeclarativeWorkerScriptEnginePrivate::get(workerEngine); + + QMutexLocker(&p->m_lock); + if (script->owner) + QCoreApplication::postEvent(script->owner, new WorkerErrorEvent(error)); +} + +QVariant QDeclarativeWorkerScriptEnginePrivate::scriptValueToVariant(const QScriptValue &value) +{ + if (value.isBool()) { + return QVariant(value.toBool()); + } else if (value.isString()) { + return QVariant(value.toString()); + } else if (value.isNumber()) { + return QVariant((qreal)value.toNumber()); + } else if (value.isDate()) { + return QVariant(value.toDateTime()); +#ifndef QT_NO_REGEXP + } else if (value.isRegExp()) { + return QVariant(value.toRegExp()); +#endif + } else if (value.isArray()) { + QVariantList list; + + quint32 length = (quint32)value.property(QLatin1String("length")).toNumber(); + + for (quint32 ii = 0; ii < length; ++ii) { + QVariant v = scriptValueToVariant(value.property(ii)); + list << v; + } + + return QVariant(list); + } else if (value.isQObject()) { + QDeclarativeListModel *lm = qobject_cast(value.toQObject()); + if (lm) { + QDeclarativeListModelWorkerAgent *agent = lm->agent(); + if (agent) { + QDeclarativeListModelWorkerAgent::VariantRef v(agent); + return QVariant::fromValue(v); + } else { + return QVariant(); + } + } else { + // No other QObject's are allowed to be sent + return QVariant(); + } + } else if (value.isObject()) { + QVariantHash hash; + + QScriptValueIterator iter(value); + + while (iter.hasNext()) { + iter.next(); + hash.insert(iter.name(), scriptValueToVariant(iter.value())); + } + + return QVariant(hash); + } + + return QVariant(); + +} + +QScriptValue QDeclarativeWorkerScriptEnginePrivate::variantToScriptValue(const QVariant &value, QScriptEngine *engine) +{ + if (value.userType() == QVariant::Bool) { + return QScriptValue(value.toBool()); + } else if (value.userType() == QVariant::String) { + return QScriptValue(value.toString()); + } else if (value.userType() == QMetaType::QReal) { + return QScriptValue(value.toReal()); + } else if (value.userType() == QVariant::DateTime) { + return engine->newDate(value.toDateTime()); +#ifndef QT_NO_REGEXP + } else if (value.userType() == QVariant::RegExp) { + return engine->newRegExp(value.toRegExp()); +#endif + } else if (value.userType() == qMetaTypeId()) { + QDeclarativeListModelWorkerAgent::VariantRef vr = qvariant_cast(value); + if (vr.a->scriptEngine() == 0) + vr.a->setScriptEngine(engine); + else if (vr.a->scriptEngine() != engine) + return engine->nullValue(); + QScriptValue o = engine->newQObject(vr.a); + o.setData(engine->newVariant(value)); // Keeps the agent ref so that it is cleaned up on gc + return o; + } else if (value.userType() == QMetaType::QVariantList) { + QVariantList list = qvariant_cast(value); + QScriptValue rv = engine->newArray(list.count()); + + for (quint32 ii = 0; ii < quint32(list.count()); ++ii) + rv.setProperty(ii, variantToScriptValue(list.at(ii), engine)); + + return rv; + } else if (value.userType() == QMetaType::QVariantHash) { + + QVariantHash hash = qvariant_cast(value); + + QScriptValue rv = engine->newObject(); + + for (QVariantHash::ConstIterator iter = hash.begin(); iter != hash.end(); ++iter) + rv.setProperty(iter.key(), variantToScriptValue(iter.value(), engine)); + + return rv; + } else { + return engine->nullValue(); + } +} + +WorkerDataEvent::WorkerDataEvent(int workerId, const QVariant &data) +: QEvent((QEvent::Type)WorkerData), m_id(workerId), m_data(data) +{ +} + +WorkerDataEvent::~WorkerDataEvent() +{ +} + +int WorkerDataEvent::workerId() const +{ + return m_id; +} + +QVariant WorkerDataEvent::data() const +{ + return m_data; +} + +WorkerLoadEvent::WorkerLoadEvent(int workerId, const QUrl &url) +: QEvent((QEvent::Type)WorkerLoad), m_id(workerId), m_url(url) +{ +} + +int WorkerLoadEvent::workerId() const +{ + return m_id; +} + +QUrl WorkerLoadEvent::url() const +{ + return m_url; +} + +WorkerRemoveEvent::WorkerRemoveEvent(int workerId) +: QEvent((QEvent::Type)WorkerRemove), m_id(workerId) +{ +} + +int WorkerRemoveEvent::workerId() const +{ + return m_id; +} + +WorkerErrorEvent::WorkerErrorEvent(const QDeclarativeError &error) +: QEvent((QEvent::Type)WorkerError), m_error(error) +{ +} + +QDeclarativeError WorkerErrorEvent::error() const +{ + return m_error; +} + +QDeclarativeWorkerScriptEngine::QDeclarativeWorkerScriptEngine(QDeclarativeEngine *parent) +: QThread(parent), d(new QDeclarativeWorkerScriptEnginePrivate(parent)) +{ + d->m_lock.lock(); + connect(d, SIGNAL(stopThread()), this, SLOT(quit()), Qt::DirectConnection); + start(QThread::IdlePriority); + d->m_wait.wait(&d->m_lock); + d->moveToThread(this); + d->m_lock.unlock(); +} + +QDeclarativeWorkerScriptEngine::~QDeclarativeWorkerScriptEngine() +{ + d->m_lock.lock(); + qDeleteAll(d->workers); + d->workers.clear(); + QCoreApplication::postEvent(d, new QEvent((QEvent::Type)QDeclarativeWorkerScriptEnginePrivate::WorkerDestroyEvent)); + d->m_lock.unlock(); + + wait(); + d->deleteLater(); +} + +QDeclarativeWorkerScriptEnginePrivate::WorkerScript::WorkerScript() +: id(-1), initialized(false), owner(0) +{ +} + +int QDeclarativeWorkerScriptEngine::registerWorkerScript(QDeclarativeWorkerScript *owner) +{ + QDeclarativeWorkerScriptEnginePrivate::WorkerScript *script = new QDeclarativeWorkerScriptEnginePrivate::WorkerScript; + script->id = d->m_nextId++; + script->owner = owner; + + d->m_lock.lock(); + d->workers.insert(script->id, script); + d->m_lock.unlock(); + + return script->id; +} + +void QDeclarativeWorkerScriptEngine::removeWorkerScript(int id) +{ + QCoreApplication::postEvent(d, new WorkerRemoveEvent(id)); +} + +void QDeclarativeWorkerScriptEngine::executeUrl(int id, const QUrl &url) +{ + QCoreApplication::postEvent(d, new WorkerLoadEvent(id, url)); +} + +void QDeclarativeWorkerScriptEngine::sendMessage(int id, const QVariant &data) +{ + QCoreApplication::postEvent(d, new WorkerDataEvent(id, data)); +} + +void QDeclarativeWorkerScriptEngine::run() +{ + d->m_lock.lock(); + + d->workerEngine = new QDeclarativeWorkerScriptEnginePrivate::ScriptEngine(d); + + d->m_wait.wakeAll(); + + d->m_lock.unlock(); + + exec(); + + delete d->workerEngine; d->workerEngine = 0; +} + + +/*! + \qmlclass WorkerScript QDeclarativeWorkerScript + \ingroup qml-utility-elements + \brief The WorkerScript element enables the use of threads in QML. + + Use WorkerScript to run operations in a new thread. + This is useful for running operations in the background so + that the main GUI thread is not blocked. + + Messages can be passed between the new thread and the parent thread + using \l sendMessage() and the \l {WorkerScript::onMessage}{onMessage()} handler. + + An example: + + \snippet doc/src/snippets/declarative/workerscript.qml 0 + + The above worker script specifies a JavaScript file, "script.js", that handles + the operations to be performed in the new thread. Here is \c script.js: + + \quotefile doc/src/snippets/declarative/script.js + + When the user clicks anywhere within the rectangle, \c sendMessage() is + called, triggering the \tt WorkerScript.onMessage() handler in + \tt script.js. This in turn sends a reply message that is then received + by the \tt onMessage() handler of \tt myWorker. + + + \section3 Restrictions + + Since the \c WorkerScript.onMessage() function is run in a separate thread, the + JavaScript file is evaluated in a context separate from the main QML engine. This means + that unlike an ordinary JavaScript file that is imported into QML, the \c script.js + in the above example cannot access the properties, methods or other attributes + of the QML item, nor can it access any context properties set on the QML object + through QDeclarativeContext. + + Additionally, there are restrictions on the types of values that can be passed to and + from the worker script. See the sendMessage() documentation for details. + + \sa {declarative/threading/workerscript}{WorkerScript example}, + {declarative/threading/threadedlistmodel}{Threaded ListModel example} +*/ +QDeclarativeWorkerScript::QDeclarativeWorkerScript(QObject *parent) +: QObject(parent), m_engine(0), m_scriptId(-1), m_componentComplete(true) +{ +} + +QDeclarativeWorkerScript::~QDeclarativeWorkerScript() +{ + if (m_scriptId != -1) m_engine->removeWorkerScript(m_scriptId); +} + +/*! + \qmlproperty url WorkerScript::source + + This holds the url of the JavaScript file that implements the + \tt WorkerScript.onMessage() handler for threaded operations. +*/ +QUrl QDeclarativeWorkerScript::source() const +{ + return m_source; +} + +void QDeclarativeWorkerScript::setSource(const QUrl &source) +{ + if (m_source == source) + return; + + m_source = source; + + if (engine()) + m_engine->executeUrl(m_scriptId, m_source); + + emit sourceChanged(); +} + +/*! + \qmlmethod WorkerScript::sendMessage(jsobject message) + + Sends the given \a message to a worker script handler in another + thread. The other worker script handler can receive this message + through the onMessage() handler. + + The \c message object may only contain values of the following + types: + + \list + \o boolean, number, string + \o JavaScript objects and arrays + \o ListModel objects (any other type of QObject* is not allowed) + \endlist + + All objects and arrays are copied to the \c message. With the exception + of ListModel objects, any modifications by the other thread to an object + passed in \c message will not be reflected in the original object. +*/ +void QDeclarativeWorkerScript::sendMessage(const QScriptValue &message) +{ + if (!engine()) { + qWarning("QDeclarativeWorkerScript: Attempt to send message before WorkerScript establishment"); + return; + } + + m_engine->sendMessage(m_scriptId, QDeclarativeWorkerScriptEnginePrivate::scriptValueToVariant(message)); +} + +void QDeclarativeWorkerScript::classBegin() +{ + m_componentComplete = false; +} + +QDeclarativeWorkerScriptEngine *QDeclarativeWorkerScript::engine() +{ + if (m_engine) return m_engine; + if (m_componentComplete) { + QDeclarativeEngine *engine = qmlEngine(this); + if (!engine) { + qWarning("QDeclarativeWorkerScript: engine() called without qmlEngine() set"); + return 0; + } + + m_engine = QDeclarativeEnginePrivate::get(engine)->getWorkerScriptEngine(); + m_scriptId = m_engine->registerWorkerScript(this); + + if (m_source.isValid()) + m_engine->executeUrl(m_scriptId, m_source); + + return m_engine; + } + return 0; +} + +void QDeclarativeWorkerScript::componentComplete() +{ + m_componentComplete = true; + engine(); // Get it started now. +} + +/*! + \qmlsignal WorkerScript::onMessage(jsobject msg) + + This handler is called when a message \a msg is received from a worker + script in another thread through a call to sendMessage(). +*/ + +bool QDeclarativeWorkerScript::event(QEvent *event) +{ + if (event->type() == (QEvent::Type)WorkerDataEvent::WorkerData) { + QDeclarativeEngine *engine = qmlEngine(this); + if (engine) { + QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine); + WorkerDataEvent *workerEvent = static_cast(event); + QScriptValue value = + QDeclarativeWorkerScriptEnginePrivate::variantToScriptValue(workerEvent->data(), scriptEngine); + emit message(value); + } + return true; + } else if (event->type() == (QEvent::Type)WorkerErrorEvent::WorkerError) { + WorkerErrorEvent *workerEvent = static_cast(event); + QDeclarativeEnginePrivate::warning(qmlEngine(this), workerEvent->error()); + return true; + } else { + return QObject::event(event); + } +} + +QT_END_NAMESPACE + +#include + diff --git a/src/declarative/qml/qdeclarativeworkerscript_p.h b/src/declarative/qml/qdeclarativeworkerscript_p.h new file mode 100644 index 00000000..80482764 --- /dev/null +++ b/src/declarative/qml/qdeclarativeworkerscript_p.h @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEWORKERSCRIPT_P_H +#define QDECLARATIVEWORKERSCRIPT_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 "qdeclarative.h" +#include "qdeclarativeparserstatus.h" + +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeWorkerScript; +class QDeclarativeWorkerScriptEnginePrivate; +class QDeclarativeWorkerScriptEngine : public QThread +{ +Q_OBJECT +public: + QDeclarativeWorkerScriptEngine(QDeclarativeEngine *parent = 0); + virtual ~QDeclarativeWorkerScriptEngine(); + + int registerWorkerScript(QDeclarativeWorkerScript *); + void removeWorkerScript(int); + void executeUrl(int, const QUrl &); + void sendMessage(int, const QVariant &); + +protected: + virtual void run(); + +private: + QDeclarativeWorkerScriptEnginePrivate *d; +}; + +class Q_AUTOTEST_EXPORT QDeclarativeWorkerScript : public QObject, public QDeclarativeParserStatus +{ + Q_OBJECT + Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) + + Q_INTERFACES(QDeclarativeParserStatus) +public: + QDeclarativeWorkerScript(QObject *parent = 0); + virtual ~QDeclarativeWorkerScript(); + + QUrl source() const; + void setSource(const QUrl &); + +public slots: + void sendMessage(const QScriptValue &); + +signals: + void sourceChanged(); + void message(const QScriptValue &messageObject); + +protected: + virtual void classBegin(); + virtual void componentComplete(); + virtual bool event(QEvent *); + +private: + QDeclarativeWorkerScriptEngine *engine(); + QDeclarativeWorkerScriptEngine *m_engine; + int m_scriptId; + QUrl m_source; + bool m_componentComplete; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeWorkerScript) + +QT_END_HEADER + +#endif // QDECLARATIVEWORKERSCRIPT_P_H diff --git a/src/declarative/qml/qdeclarativexmlhttprequest.cpp b/src/declarative/qml/qdeclarativexmlhttprequest.cpp new file mode 100644 index 00000000..3348a7d2 --- /dev/null +++ b/src/declarative/qml/qdeclarativexmlhttprequest.cpp @@ -0,0 +1,1740 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativexmlhttprequest_p.h" + +#include "qdeclarativeengine.h" +#include "private/qdeclarativeengine_p.h" +#include "private/qdeclarativerefcount_p.h" +#include "private/qdeclarativeengine_p.h" +#include "private/qdeclarativeexpression_p.h" +#include "qdeclarativeglobal_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifndef QT_NO_XMLSTREAMREADER + +// From DOM-Level-3-Core spec +// http://www.w3.org/TR/DOM-Level-3-Core/core.html +#define INDEX_SIZE_ERR 1 +#define DOMSTRING_SIZE_ERR 2 +#define HIERARCHY_REQUEST_ERR 3 +#define WRONG_DOCUMENT_ERR 4 +#define INVALID_CHARACTER_ERR 5 +#define NO_DATA_ALLOWED_ERR 6 +#define NO_MODIFICATION_ALLOWED_ERR 7 +#define NOT_FOUND_ERR 8 +#define NOT_SUPPORTED_ERR 9 +#define INUSE_ATTRIBUTE_ERR 10 +#define INVALID_STATE_ERR 11 +#define SYNTAX_ERR 12 +#define INVALID_MODIFICATION_ERR 13 +#define NAMESPACE_ERR 14 +#define INVALID_ACCESS_ERR 15 +#define VALIDATION_ERR 16 +#define TYPE_MISMATCH_ERR 17 + +#define THROW_DOM(error, desc) \ +{ \ + QScriptValue errorValue = context->throwError(QLatin1String(desc)); \ + errorValue.setProperty(QLatin1String("code"), error); \ + return errorValue; \ +} + +#define THROW_SYNTAX(desc) \ + return context->throwError(QScriptContext::SyntaxError, QLatin1String(desc)); +#define THROW_REFERENCE(desc) \ + return context->throwError(QScriptContext::ReferenceError, QLatin1String(desc)); + +#define D(arg) (arg)->release() +#define A(arg) (arg)->addref() + +QT_BEGIN_NAMESPACE + +DEFINE_BOOL_CONFIG_OPTION(xhrDump, QML_XHR_DUMP); + +class DocumentImpl; +class NodeImpl +{ +public: + NodeImpl() : type(Element), document(0), parent(0) {} + virtual ~NodeImpl() { + for (int ii = 0; ii < children.count(); ++ii) + delete children.at(ii); + for (int ii = 0; ii < attributes.count(); ++ii) + delete attributes.at(ii); + } + + // These numbers are copied from the Node IDL definition + enum Type { + Attr = 2, + CDATA = 4, + Comment = 8, + Document = 9, + DocumentFragment = 11, + DocumentType = 10, + Element = 1, + Entity = 6, + EntityReference = 5, + Notation = 12, + ProcessingInstruction = 7, + Text = 3 + }; + Type type; + + QString namespaceUri; + QString name; + + QString data; + + void addref(); + void release(); + + DocumentImpl *document; + NodeImpl *parent; + + QList children; + QList attributes; +}; + +class DocumentImpl : public QDeclarativeRefCount, public NodeImpl +{ +public: + DocumentImpl() : root(0) { type = Document; } + virtual ~DocumentImpl() { + if (root) delete root; + } + + QString version; + QString encoding; + bool isStandalone; + + NodeImpl *root; + + void addref() { QDeclarativeRefCount::addref(); } + void release() { QDeclarativeRefCount::release(); } +}; + +class NamedNodeMap +{ +public: + // JS API + static QScriptValue length(QScriptContext *context, QScriptEngine *engine); + + // C++ API + static QScriptValue prototype(QScriptEngine *); + static QScriptValue create(QScriptEngine *, NodeImpl *, QList *); + + NamedNodeMap(); + NamedNodeMap(const NamedNodeMap &); + ~NamedNodeMap(); + bool isNull(); + + NodeImpl *d; + QList *list; +private: + NamedNodeMap &operator=(const NamedNodeMap &); +}; + +class NamedNodeMapClass : public QScriptClass +{ +public: + NamedNodeMapClass(QScriptEngine *engine) : QScriptClass(engine) {} + + virtual QueryFlags queryProperty(const QScriptValue &object, const QScriptString &name, QueryFlags flags, uint *id); + virtual QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); +}; + +class NodeList +{ +public: + // JS API + static QScriptValue length(QScriptContext *context, QScriptEngine *engine); + + // C++ API + static QScriptValue prototype(QScriptEngine *); + static QScriptValue create(QScriptEngine *, NodeImpl *); + + NodeList(); + NodeList(const NodeList &); + ~NodeList(); + bool isNull(); + + NodeImpl *d; +private: + NodeList &operator=(const NodeList &); +}; + +class NodeListClass : public QScriptClass +{ +public: + NodeListClass(QScriptEngine *engine) : QScriptClass(engine) {} + virtual QueryFlags queryProperty(const QScriptValue &object, const QScriptString &name, QueryFlags flags, uint *id); + virtual QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); +}; + +class Node +{ +public: + // JS API + static QScriptValue nodeName(QScriptContext *context, QScriptEngine *engine); + static QScriptValue nodeValue(QScriptContext *context, QScriptEngine *engine); + static QScriptValue nodeType(QScriptContext *context, QScriptEngine *engine); + + static QScriptValue parentNode(QScriptContext *context, QScriptEngine *engine); + static QScriptValue childNodes(QScriptContext *context, QScriptEngine *engine); + static QScriptValue firstChild(QScriptContext *context, QScriptEngine *engine); + static QScriptValue lastChild(QScriptContext *context, QScriptEngine *engine); + static QScriptValue previousSibling(QScriptContext *context, QScriptEngine *engine); + static QScriptValue nextSibling(QScriptContext *context, QScriptEngine *engine); + static QScriptValue attributes(QScriptContext *context, QScriptEngine *engine); + + //static QScriptValue ownerDocument(QScriptContext *context, QScriptEngine *engine); + //static QScriptValue namespaceURI(QScriptContext *context, QScriptEngine *engine); + //static QScriptValue prefix(QScriptContext *context, QScriptEngine *engine); + //static QScriptValue localName(QScriptContext *context, QScriptEngine *engine); + //static QScriptValue baseURI(QScriptContext *context, QScriptEngine *engine); + //static QScriptValue textContent(QScriptContext *context, QScriptEngine *engine); + + // C++ API + static QScriptValue prototype(QScriptEngine *); + static QScriptValue create(QScriptEngine *, NodeImpl *); + + Node(); + Node(const Node &o); + ~Node(); + bool isNull() const; + + NodeImpl *d; + +private: + Node &operator=(const Node &); +}; + +class Element : public Node +{ +public: + // C++ API + static QScriptValue prototype(QScriptEngine *); +}; + +class Attr : public Node +{ +public: + // JS API + static QScriptValue name(QScriptContext *context, QScriptEngine *engine); + static QScriptValue specified(QScriptContext *context, QScriptEngine *engine); + static QScriptValue value(QScriptContext *context, QScriptEngine *engine); + static QScriptValue ownerElement(QScriptContext *context, QScriptEngine *engine); + static QScriptValue schemaTypeInfo(QScriptContext *context, QScriptEngine *engine); + static QScriptValue isId(QScriptContext *context, QScriptEngine *engine); + + // C++ API + static QScriptValue prototype(QScriptEngine *); +}; + +class CharacterData : public Node +{ +public: + // JS API + static QScriptValue length(QScriptContext *context, QScriptEngine *engine); + + // C++ API + static QScriptValue prototype(QScriptEngine *); +}; + +class Text : public CharacterData +{ +public: + // JS API + static QScriptValue isElementContentWhitespace(QScriptContext *context, QScriptEngine *engine); + static QScriptValue wholeText(QScriptContext *context, QScriptEngine *engine); + + // C++ API + static QScriptValue prototype(QScriptEngine *); +}; + +class CDATA : public Text +{ +public: + // C++ API + static QScriptValue prototype(QScriptEngine *); +}; + +class Document : public Node +{ +public: + // JS API + static QScriptValue xmlVersion(QScriptContext *context, QScriptEngine *engine); + static QScriptValue xmlEncoding(QScriptContext *context, QScriptEngine *engine); + static QScriptValue xmlStandalone(QScriptContext *context, QScriptEngine *engine); + static QScriptValue documentElement(QScriptContext *context, QScriptEngine *engine); + + // C++ API + static QScriptValue prototype(QScriptEngine *); + static QScriptValue load(QScriptEngine *engine, const QByteArray &data); +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(Node) +Q_DECLARE_METATYPE(NodeList) +Q_DECLARE_METATYPE(NamedNodeMap) + +QT_BEGIN_NAMESPACE + +void NodeImpl::addref() +{ + A(document); +} + +void NodeImpl::release() +{ + D(document); +} + +QScriptValue Node::nodeName(QScriptContext *context, QScriptEngine *engine) +{ + Node node = qscriptvalue_cast(context->thisObject()); + if (node.isNull()) return engine->undefinedValue(); + + switch (node.d->type) { + case NodeImpl::Document: + return QScriptValue(QLatin1String("#document")); + case NodeImpl::CDATA: + return QScriptValue(QLatin1String("#cdata-section")); + case NodeImpl::Text: + return QScriptValue(QLatin1String("#text")); + default: + return QScriptValue(node.d->name); + } +} + +QScriptValue Node::nodeValue(QScriptContext *context, QScriptEngine *engine) +{ + Node node = qscriptvalue_cast(context->thisObject()); + if (node.isNull()) return engine->undefinedValue(); + + if (node.d->type == NodeImpl::Document || + node.d->type == NodeImpl::DocumentFragment || + node.d->type == NodeImpl::DocumentType || + node.d->type == NodeImpl::Element || + node.d->type == NodeImpl::Entity || + node.d->type == NodeImpl::EntityReference || + node.d->type == NodeImpl::Notation) + return engine->nullValue(); + + return QScriptValue(node.d->data); +} + +QScriptValue Node::nodeType(QScriptContext *context, QScriptEngine *engine) +{ + Node node = qscriptvalue_cast(context->thisObject()); + if (node.isNull()) return engine->undefinedValue(); + return QScriptValue(node.d->type); +} + +QScriptValue Node::parentNode(QScriptContext *context, QScriptEngine *engine) +{ + Node node = qscriptvalue_cast(context->thisObject()); + if (node.isNull()) return engine->undefinedValue(); + + if (node.d->parent) return Node::create(engine, node.d->parent); + else return engine->nullValue(); +} + +QScriptValue Node::childNodes(QScriptContext *context, QScriptEngine *engine) +{ + Node node = qscriptvalue_cast(context->thisObject()); + if (node.isNull()) return engine->undefinedValue(); + + return NodeList::create(engine, node.d); +} + +QScriptValue Node::firstChild(QScriptContext *context, QScriptEngine *engine) +{ + Node node = qscriptvalue_cast(context->thisObject()); + if (node.isNull()) return engine->undefinedValue(); + + if (node.d->children.isEmpty()) return engine->nullValue(); + else return Node::create(engine, node.d->children.first()); +} + +QScriptValue Node::lastChild(QScriptContext *context, QScriptEngine *engine) +{ + Node node = qscriptvalue_cast(context->thisObject()); + if (node.isNull()) return engine->undefinedValue(); + + if (node.d->children.isEmpty()) return engine->nullValue(); + else return Node::create(engine, node.d->children.last()); +} + +QScriptValue Node::previousSibling(QScriptContext *context, QScriptEngine *engine) +{ + Node node = qscriptvalue_cast(context->thisObject()); + if (node.isNull()) return engine->undefinedValue(); + + if (!node.d->parent) return engine->nullValue(); + + for (int ii = 0; ii < node.d->parent->children.count(); ++ii) { + if (node.d->parent->children.at(ii) == node.d) { + if (ii == 0) return engine->nullValue(); + else return Node::create(engine, node.d->parent->children.at(ii - 1)); + } + } + + return engine->nullValue(); +} + +QScriptValue Node::nextSibling(QScriptContext *context, QScriptEngine *engine) +{ + Node node = qscriptvalue_cast(context->thisObject()); + if (node.isNull()) return engine->undefinedValue(); + + if (!node.d->parent) return engine->nullValue(); + + for (int ii = 0; ii < node.d->parent->children.count(); ++ii) { + if (node.d->parent->children.at(ii) == node.d) { + if ((ii + 1) == node.d->parent->children.count()) return engine->nullValue(); + else return Node::create(engine, node.d->parent->children.at(ii + 1)); + } + } + + return engine->nullValue(); +} + +QScriptValue Node::attributes(QScriptContext *context, QScriptEngine *engine) +{ + Node node = qscriptvalue_cast(context->thisObject()); + if (node.isNull()) return engine->undefinedValue(); + + if (node.d->type != NodeImpl::Element) + return engine->nullValue(); + else + return NamedNodeMap::create(engine, node.d, &node.d->attributes); +} + +QScriptValue Node::prototype(QScriptEngine *engine) +{ + QScriptValue proto = engine->newObject(); + + proto.setProperty(QLatin1String("nodeName"), engine->newFunction(nodeName), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); + proto.setProperty(QLatin1String("nodeValue"), engine->newFunction(nodeValue), QScriptValue::ReadOnly | QScriptValue::PropertyGetter | QScriptValue::PropertySetter); + proto.setProperty(QLatin1String("nodeType"), engine->newFunction(nodeType), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); + proto.setProperty(QLatin1String("parentNode"), engine->newFunction(parentNode), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); + proto.setProperty(QLatin1String("childNodes"), engine->newFunction(childNodes), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); + proto.setProperty(QLatin1String("firstChild"), engine->newFunction(firstChild), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); + proto.setProperty(QLatin1String("lastChild"), engine->newFunction(lastChild), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); + proto.setProperty(QLatin1String("previousSibling"), engine->newFunction(previousSibling), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); + proto.setProperty(QLatin1String("nextSibling"), engine->newFunction(nextSibling), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); + proto.setProperty(QLatin1String("attributes"), engine->newFunction(attributes), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); + + return proto; +} + +QScriptValue Node::create(QScriptEngine *engine, NodeImpl *data) +{ + QScriptValue instance = engine->newObject(); + + switch (data->type) { + case NodeImpl::Attr: + instance.setPrototype(Attr::prototype(engine)); + break; + case NodeImpl::Comment: + case NodeImpl::Document: + case NodeImpl::DocumentFragment: + case NodeImpl::DocumentType: + case NodeImpl::Entity: + case NodeImpl::EntityReference: + case NodeImpl::Notation: + case NodeImpl::ProcessingInstruction: + return QScriptValue(); + case NodeImpl::CDATA: + instance.setPrototype(CDATA::prototype(engine)); + break; + case NodeImpl::Text: + instance.setPrototype(Text::prototype(engine)); + break; + case NodeImpl::Element: + instance.setPrototype(Element::prototype(engine)); + break; + } + + Node node; + node.d = data; + if (data) A(data); + + return engine->newVariant(instance, QVariant::fromValue(node)); +} + +QScriptValue Element::prototype(QScriptEngine *engine) +{ + QScriptValue proto = engine->newObject(); + proto.setPrototype(Node::prototype(engine)); + + proto.setProperty(QLatin1String("tagName"), engine->newFunction(nodeName), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); + + return proto; +} + +QScriptValue Attr::prototype(QScriptEngine *engine) +{ + QScriptValue proto = engine->newObject(); + proto.setPrototype(Node::prototype(engine)); + + proto.setProperty(QLatin1String("name"), engine->newFunction(name), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); + proto.setProperty(QLatin1String("value"), engine->newFunction(value), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); + proto.setProperty(QLatin1String("ownerElement"), engine->newFunction(ownerElement), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); + + return proto; +} + +QScriptValue Attr::name(QScriptContext *context, QScriptEngine *engine) +{ + Node node = qscriptvalue_cast(context->thisObject()); + if (node.isNull()) return engine->undefinedValue(); + + return QScriptValue(node.d->name); +} + +QScriptValue Attr::value(QScriptContext *context, QScriptEngine *engine) +{ + Node node = qscriptvalue_cast(context->thisObject()); + if (node.isNull()) return engine->undefinedValue(); + + return QScriptValue(node.d->data); +} + +QScriptValue Attr::ownerElement(QScriptContext *context, QScriptEngine *engine) +{ + Node node = qscriptvalue_cast(context->thisObject()); + if (node.isNull()) return engine->undefinedValue(); + + return Node::create(engine, node.d->parent); +} + +QScriptValue CharacterData::length(QScriptContext *context, QScriptEngine *engine) +{ + Node node = qscriptvalue_cast(context->thisObject()); + if (node.isNull()) return engine->undefinedValue(); + + return QScriptValue(node.d->data.length()); +} + +QScriptValue CharacterData::prototype(QScriptEngine *engine) +{ + QScriptValue proto = engine->newObject(); + proto.setPrototype(Node::prototype(engine)); + + proto.setProperty(QLatin1String("data"), engine->newFunction(nodeValue), QScriptValue::ReadOnly | QScriptValue::PropertyGetter | QScriptValue::PropertySetter); + proto.setProperty(QLatin1String("length"), engine->newFunction(length), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); + + return proto; +} + +QScriptValue Text::isElementContentWhitespace(QScriptContext *context, QScriptEngine *engine) +{ + Node node = qscriptvalue_cast(context->thisObject()); + if (node.isNull()) return engine->undefinedValue(); + + return node.d->data.trimmed().isEmpty(); +} + +QScriptValue Text::wholeText(QScriptContext *context, QScriptEngine *engine) +{ + Node node = qscriptvalue_cast(context->thisObject()); + if (node.isNull()) return engine->undefinedValue(); + + return node.d->data; +} + +QScriptValue Text::prototype(QScriptEngine *engine) +{ + QScriptValue proto = engine->newObject(); + proto.setPrototype(CharacterData::prototype(engine)); + + proto.setProperty(QLatin1String("isElementContentWhitespace"), engine->newFunction(isElementContentWhitespace), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); + proto.setProperty(QLatin1String("wholeText"), engine->newFunction(wholeText), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); + + return proto; +} + +QScriptValue CDATA::prototype(QScriptEngine *engine) +{ + QScriptValue proto = engine->newObject(); + proto.setPrototype(Text::prototype(engine)); + return proto; +} + +QScriptValue Document::prototype(QScriptEngine *engine) +{ + QScriptValue proto = engine->newObject(); + proto.setPrototype(Node::prototype(engine)); + + proto.setProperty(QLatin1String("xmlVersion"), engine->newFunction(xmlVersion), QScriptValue::ReadOnly | QScriptValue::PropertyGetter | QScriptValue::PropertySetter); + proto.setProperty(QLatin1String("xmlEncoding"), engine->newFunction(xmlEncoding), QScriptValue::ReadOnly | QScriptValue::PropertyGetter | QScriptValue::PropertySetter); + proto.setProperty(QLatin1String("xmlStandalone"), engine->newFunction(xmlStandalone), QScriptValue::ReadOnly | QScriptValue::PropertyGetter | QScriptValue::PropertySetter); + proto.setProperty(QLatin1String("documentElement"), engine->newFunction(documentElement), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); + + return proto; +} + +QScriptValue Document::load(QScriptEngine *engine, const QByteArray &data) +{ + Q_ASSERT(engine); + + DocumentImpl *document = 0; + QStack nodeStack; + + QXmlStreamReader reader(data); + + while (!reader.atEnd()) { + switch (reader.readNext()) { + case QXmlStreamReader::NoToken: + break; + case QXmlStreamReader::Invalid: + break; + case QXmlStreamReader::StartDocument: + Q_ASSERT(!document); + document = new DocumentImpl; + document->document = document; + document->version = reader.documentVersion().toString(); + document->encoding = reader.documentEncoding().toString(); + document->isStandalone = reader.isStandaloneDocument(); + break; + case QXmlStreamReader::EndDocument: + break; + case QXmlStreamReader::StartElement: + { + Q_ASSERT(document); + NodeImpl *node = new NodeImpl; + node->document = document; + node->namespaceUri = reader.namespaceUri().toString(); + node->name = reader.name().toString(); + if (nodeStack.isEmpty()) { + document->root = node; + } else { + node->parent = nodeStack.top(); + node->parent->children.append(node); + } + nodeStack.append(node); + + foreach (const QXmlStreamAttribute &a, reader.attributes()) { + NodeImpl *attr = new NodeImpl; + attr->document = document; + attr->type = NodeImpl::Attr; + attr->namespaceUri = a.namespaceUri().toString(); + attr->name = a.name().toString(); + attr->data = a.value().toString(); + attr->parent = node; + node->attributes.append(attr); + } + } + break; + case QXmlStreamReader::EndElement: + nodeStack.pop(); + break; + case QXmlStreamReader::Characters: + { + NodeImpl *node = new NodeImpl; + node->document = document; + node->type = reader.isCDATA()?NodeImpl::CDATA:NodeImpl::Text; + node->parent = nodeStack.top(); + node->parent->children.append(node); + node->data = reader.text().toString(); + } + break; + case QXmlStreamReader::Comment: + break; + case QXmlStreamReader::DTD: + break; + case QXmlStreamReader::EntityReference: + break; + case QXmlStreamReader::ProcessingInstruction: + break; + } + } + + if (!document || reader.hasError()) { + if (document) D(document); + return engine->nullValue(); + } + + QScriptValue instance = engine->newObject(); + instance.setPrototype(Document::prototype(engine)); + Node documentNode; + documentNode.d = document; + return engine->newVariant(instance, QVariant::fromValue(documentNode)); +} + +Node::Node() +: d(0) +{ +} + +Node::Node(const Node &o) +: d(o.d) +{ + if (d) A(d); +} + +Node::~Node() +{ + if (d) D(d); +} + +bool Node::isNull() const +{ + return d == 0; +} + +QScriptValue NamedNodeMap::length(QScriptContext *context, QScriptEngine *engine) +{ + NamedNodeMap map = qscriptvalue_cast(context->thisObject().data()); + if (map.isNull()) return engine->undefinedValue(); + + return QScriptValue(map.list->count()); +} + +QScriptValue NamedNodeMap::prototype(QScriptEngine *engine) +{ + QScriptValue proto = engine->newObject(); + + proto.setProperty(QLatin1String("length"), engine->newFunction(length), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); + + return proto; +} + +QScriptValue NamedNodeMap::create(QScriptEngine *engine, NodeImpl *data, QList *list) +{ + QScriptValue instance = engine->newObject(); + instance.setPrototype(NamedNodeMap::prototype(engine)); + + NamedNodeMap map; + map.d = data; + map.list = list; + if (data) A(data); + + instance.setData(engine->newVariant(QVariant::fromValue(map))); + + if (!QDeclarativeScriptEngine::get(engine)->namedNodeMapClass) + QDeclarativeScriptEngine::get(engine)->namedNodeMapClass= new NamedNodeMapClass(engine); + + instance.setScriptClass(QDeclarativeScriptEngine::get(engine)->namedNodeMapClass); + + return instance; +} + +NamedNodeMap::NamedNodeMap() +: d(0), list(0) +{ +} + +NamedNodeMap::NamedNodeMap(const NamedNodeMap &o) +: d(o.d), list(o.list) +{ + if (d) A(d); +} + +NamedNodeMap::~NamedNodeMap() +{ + if (d) D(d); +} + +bool NamedNodeMap::isNull() +{ + return d == 0; +} + +QScriptValue NodeList::length(QScriptContext *context, QScriptEngine *engine) +{ + NodeList list = qscriptvalue_cast(context->thisObject().data()); + if (list.isNull()) return engine->undefinedValue(); + + return QScriptValue(list.d->children.count()); +} + +QScriptValue NodeList::prototype(QScriptEngine *engine) +{ + QScriptValue proto = engine->newObject(); + + proto.setProperty(QLatin1String("length"), engine->newFunction(length), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); + + return proto; +} + +QScriptValue NodeList::create(QScriptEngine *engine, NodeImpl *data) +{ + QScriptValue instance = engine->newObject(); + instance.setPrototype(NodeList::prototype(engine)); + + NodeList list; + list.d = data; + if (data) A(data); + + instance.setData(engine->newVariant(QVariant::fromValue(list))); + + if (!QDeclarativeScriptEngine::get(engine)->nodeListClass) + QDeclarativeScriptEngine::get(engine)->nodeListClass= new NodeListClass(engine); + + instance.setScriptClass(QDeclarativeScriptEngine::get(engine)->nodeListClass); + + return instance; +} + +NodeList::NodeList() +: d(0) +{ +} + +NodeList::NodeList(const NodeList &o) +: d(o.d) +{ + if (d) A(d); +} + +NodeList::~NodeList() +{ + if (d) D(d); +} + +bool NodeList::isNull() +{ + return d == 0; +} + +NamedNodeMapClass::QueryFlags NamedNodeMapClass::queryProperty(const QScriptValue &object, const QScriptString &name, QueryFlags flags, uint *id) +{ + if (!(flags & HandlesReadAccess)) + return 0; + + NamedNodeMap map = qscriptvalue_cast(object.data()); + Q_ASSERT(!map.isNull()); + + bool ok = false; + QString nameString = name.toString(); + uint index = nameString.toUInt(&ok); + if (ok) { + if ((uint)map.list->count() <= index) + return 0; + + *id = index; + return HandlesReadAccess; + } else { + for (int ii = 0; ii < map.list->count(); ++ii) { + if (map.list->at(ii) && map.list->at(ii)->name == nameString) { + *id = ii; + return HandlesReadAccess; + } + } + } + + return 0; +} + +QScriptValue NamedNodeMapClass::property(const QScriptValue &object, const QScriptString &, uint id) +{ + NamedNodeMap map = qscriptvalue_cast(object.data()); + return Node::create(engine(), map.list->at(id)); +} + +NodeListClass::QueryFlags NodeListClass::queryProperty(const QScriptValue &object, const QScriptString &name, QueryFlags flags, uint *id) +{ + if (!(flags & HandlesReadAccess)) + return 0; + + bool ok = false; + uint index = name.toString().toUInt(&ok); + if (!ok) + return 0; + + NodeList list = qscriptvalue_cast(object.data()); + if (list.isNull() || (uint)list.d->children.count() <= index) + return 0; // ### I think we're meant to raise an exception + + *id = index; + return HandlesReadAccess; +} + +QScriptValue NodeListClass::property(const QScriptValue &object, const QScriptString &, uint id) +{ + NodeList list = qscriptvalue_cast(object.data()); + return Node::create(engine(), list.d->children.at(id)); +} + +QScriptValue Document::documentElement(QScriptContext *context, QScriptEngine *engine) +{ + Node document = qscriptvalue_cast(context->thisObject()); + if (document.isNull() || document.d->type != NodeImpl::Document) return engine->undefinedValue(); + + return Node::create(engine, static_cast(document.d)->root); +} + +QScriptValue Document::xmlStandalone(QScriptContext *context, QScriptEngine *engine) +{ + Node document = qscriptvalue_cast(context->thisObject()); + if (document.isNull() || document.d->type != NodeImpl::Document) return engine->undefinedValue(); + + return QScriptValue(static_cast(document.d)->isStandalone); +} + +QScriptValue Document::xmlVersion(QScriptContext *context, QScriptEngine *engine) +{ + Node document = qscriptvalue_cast(context->thisObject()); + if (document.isNull() || document.d->type != NodeImpl::Document) return engine->undefinedValue(); + + return QScriptValue(static_cast(document.d)->version); +} + +QScriptValue Document::xmlEncoding(QScriptContext *context, QScriptEngine *engine) +{ + Node document = qscriptvalue_cast(context->thisObject()); + if (document.isNull() || document.d->type != NodeImpl::Document) return engine->undefinedValue(); + + return QScriptValue(static_cast(document.d)->encoding); +} + +class QDeclarativeXMLHttpRequest : public QObject +{ +Q_OBJECT +public: + enum State { Unsent = 0, + Opened = 1, HeadersReceived = 2, + Loading = 3, Done = 4 }; + + QDeclarativeXMLHttpRequest(QNetworkAccessManager *manager); + virtual ~QDeclarativeXMLHttpRequest(); + + bool sendFlag() const; + bool errorFlag() const; + quint32 readyState() const; + int replyStatus() const; + QString replyStatusText() const; + + QScriptValue open(QScriptValue *me, const QString &, const QUrl &); + + void addHeader(const QString &, const QString &); + QString header(const QString &name); + QString headers(); + QScriptValue send(QScriptValue *me, const QByteArray &); + QScriptValue abort(QScriptValue *me); + + QString responseBody(); + const QByteArray & rawResponseBody() const; + bool receivedXml() const; +private slots: + void downloadProgress(qint64); + void error(QNetworkReply::NetworkError); + void finished(); + +private: + void requestFromUrl(const QUrl &url); + + State m_state; + bool m_errorFlag; + bool m_sendFlag; + QString m_method; + QUrl m_url; + QByteArray m_responseEntityBody; + QByteArray m_data; + int m_redirectCount; + + typedef QPair HeaderPair; + typedef QList HeadersList; + HeadersList m_headersList; + void fillHeadersList(); + + bool m_gotXml; + QByteArray m_mime; + QByteArray m_charset; + QTextCodec *m_textCodec; +#ifndef QT_NO_TEXTCODEC + QTextCodec* findTextCodec() const; +#endif + void readEncoding(); + + QScriptValue m_me; // Set to the data object while a send() is ongoing (to access the callback) + + QScriptValue dispatchCallback(QScriptValue *me); + void printError(const QScriptValue&); + + int m_status; + QString m_statusText; + QNetworkRequest m_request; + QDeclarativeGuard m_network; + void destroyNetwork(); + + QNetworkAccessManager *m_nam; + QNetworkAccessManager *networkAccessManager() { return m_nam; } +}; + +QDeclarativeXMLHttpRequest::QDeclarativeXMLHttpRequest(QNetworkAccessManager *manager) +: m_state(Unsent), m_errorFlag(false), m_sendFlag(false), + m_redirectCount(0), m_gotXml(false), m_textCodec(0), m_network(0), m_nam(manager) +{ +} + +QDeclarativeXMLHttpRequest::~QDeclarativeXMLHttpRequest() +{ + destroyNetwork(); +} + +bool QDeclarativeXMLHttpRequest::sendFlag() const +{ + return m_sendFlag; +} + +bool QDeclarativeXMLHttpRequest::errorFlag() const +{ + return m_errorFlag; +} + +quint32 QDeclarativeXMLHttpRequest::readyState() const +{ + return m_state; +} + +int QDeclarativeXMLHttpRequest::replyStatus() const +{ + return m_status; +} + +QString QDeclarativeXMLHttpRequest::replyStatusText() const +{ + return m_statusText; +} + +QScriptValue QDeclarativeXMLHttpRequest::open(QScriptValue *me, const QString &method, const QUrl &url) +{ + destroyNetwork(); + m_sendFlag = false; + m_errorFlag = false; + m_responseEntityBody = QByteArray(); + m_method = method; + m_url = url; + m_state = Opened; + return dispatchCallback(me); +} + +void QDeclarativeXMLHttpRequest::addHeader(const QString &name, const QString &value) +{ + QByteArray utfname = name.toUtf8(); + + if (m_request.hasRawHeader(utfname)) { + m_request.setRawHeader(utfname, m_request.rawHeader(utfname) + ',' + value.toUtf8()); + } else { + m_request.setRawHeader(utfname, value.toUtf8()); + } +} + +QString QDeclarativeXMLHttpRequest::header(const QString &name) +{ + QByteArray utfname = name.toLower().toUtf8(); + + foreach (const HeaderPair &header, m_headersList) { + if (header.first == utfname) + return QString::fromUtf8(header.second); + } + return QString(); +} + +QString QDeclarativeXMLHttpRequest::headers() +{ + QString ret; + + foreach (const HeaderPair &header, m_headersList) { + if (ret.length()) + ret.append(QLatin1String("\r\n")); + ret = ret % QString::fromUtf8(header.first) % QLatin1String(": ") + % QString::fromUtf8(header.second); + } + return ret; +} + +void QDeclarativeXMLHttpRequest::fillHeadersList() +{ + QList headerList = m_network->rawHeaderList(); + + m_headersList.clear(); + foreach (const QByteArray &header, headerList) { + HeaderPair pair (header.toLower(), m_network->rawHeader(header)); + if (pair.first == "set-cookie" || + pair.first == "set-cookie2") + continue; + + m_headersList << pair; + } +} + +void QDeclarativeXMLHttpRequest::requestFromUrl(const QUrl &url) +{ + QNetworkRequest request = m_request; + request.setUrl(url); + if(m_method == QLatin1String("POST") || + m_method == QLatin1String("PUT")) { + QVariant var = request.header(QNetworkRequest::ContentTypeHeader); + if (var.isValid()) { + QString str = var.toString(); + int charsetIdx = str.indexOf(QLatin1String("charset=")); + if (charsetIdx == -1) { + // No charset - append + if (!str.isEmpty()) str.append(QLatin1Char(';')); + str.append(QLatin1String("charset=UTF-8")); + } else { + charsetIdx += 8; + int n = 0; + int semiColon = str.indexOf(QLatin1Char(';'), charsetIdx); + if (semiColon == -1) { + n = str.length() - charsetIdx; + } else { + n = semiColon - charsetIdx; + } + + str.replace(charsetIdx, n, QLatin1String("UTF-8")); + } + request.setHeader(QNetworkRequest::ContentTypeHeader, str); + } else { + request.setHeader(QNetworkRequest::ContentTypeHeader, + QLatin1String("text/plain;charset=UTF-8")); + } + } + + if (xhrDump()) { + qWarning().nospace() << "XMLHttpRequest: " << qPrintable(m_method) << " " << qPrintable(url.toString()); + if (!m_data.isEmpty()) { + qWarning().nospace() << " " + << qPrintable(QString::fromUtf8(m_data)); + } + } + + if (m_method == QLatin1String("GET")) + m_network = networkAccessManager()->get(request); + else if (m_method == QLatin1String("HEAD")) + m_network = networkAccessManager()->head(request); + else if(m_method == QLatin1String("POST")) + m_network = networkAccessManager()->post(request, m_data); + else if(m_method == QLatin1String("PUT")) + m_network = networkAccessManager()->put(request, m_data); + + QObject::connect(m_network, SIGNAL(downloadProgress(qint64,qint64)), + this, SLOT(downloadProgress(qint64))); + QObject::connect(m_network, SIGNAL(error(QNetworkReply::NetworkError)), + this, SLOT(error(QNetworkReply::NetworkError))); + QObject::connect(m_network, SIGNAL(finished()), + this, SLOT(finished())); +} + +QScriptValue QDeclarativeXMLHttpRequest::send(QScriptValue *me, const QByteArray &data) +{ + m_errorFlag = false; + m_sendFlag = true; + m_redirectCount = 0; + m_data = data; + m_me = *me; + + requestFromUrl(m_url); + + return QScriptValue(); +} + +QScriptValue QDeclarativeXMLHttpRequest::abort(QScriptValue *me) +{ + destroyNetwork(); + m_responseEntityBody = QByteArray(); + m_errorFlag = true; + m_request = QNetworkRequest(); + + if (!(m_state == Unsent || + (m_state == Opened && !m_sendFlag) || + m_state == Done)) { + + m_state = Done; + m_sendFlag = false; + QScriptValue cbv = dispatchCallback(me); + if (cbv.isError()) return cbv; + } + + m_state = Unsent; + return QScriptValue(); +} + +void QDeclarativeXMLHttpRequest::downloadProgress(qint64 bytes) +{ + Q_UNUSED(bytes) + m_status = + m_network->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + m_statusText = + QString::fromUtf8(m_network->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toByteArray()); + + // ### We assume if this is called the headers are now available + if (m_state < HeadersReceived) { + m_state = HeadersReceived; + fillHeadersList (); + QScriptValue cbv = dispatchCallback(&m_me); + if (cbv.isError()) printError(cbv); + } + + bool wasEmpty = m_responseEntityBody.isEmpty(); + m_responseEntityBody.append(m_network->readAll()); + if (wasEmpty && !m_responseEntityBody.isEmpty()) { + m_state = Loading; + QScriptValue cbv = dispatchCallback(&m_me); + if (cbv.isError()) printError(cbv); + } +} + +void QDeclarativeXMLHttpRequest::error(QNetworkReply::NetworkError error) +{ + Q_UNUSED(error) + m_status = + m_network->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + m_statusText = + QString::fromUtf8(m_network->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toByteArray()); + + m_responseEntityBody = QByteArray(); + + m_request = QNetworkRequest(); + m_data.clear(); + destroyNetwork(); + + if (error == QNetworkReply::ContentAccessDenied || + error == QNetworkReply::ContentOperationNotPermittedError || + error == QNetworkReply::ContentNotFoundError || + error == QNetworkReply::AuthenticationRequiredError || + error == QNetworkReply::ContentReSendError) { + m_state = Loading; + QScriptValue cbv = dispatchCallback(&m_me); + if (cbv.isError()) printError(cbv); + } else { + m_errorFlag = true; + } + + m_state = Done; + QScriptValue cbv = dispatchCallback(&m_me); + if (cbv.isError()) printError(cbv); +} + +#define XMLHTTPREQUEST_MAXIMUM_REDIRECT_RECURSION 15 +void QDeclarativeXMLHttpRequest::finished() +{ + m_redirectCount++; + if (m_redirectCount < XMLHTTPREQUEST_MAXIMUM_REDIRECT_RECURSION) { + QVariant redirect = m_network->attribute(QNetworkRequest::RedirectionTargetAttribute); + if (redirect.isValid()) { + QUrl url = m_network->url().resolved(redirect.toUrl()); + destroyNetwork(); + requestFromUrl(url); + return; + } + } + + m_status = + m_network->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + m_statusText = + QString::fromUtf8(m_network->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toByteArray()); + + if (m_state < HeadersReceived) { + m_state = HeadersReceived; + fillHeadersList (); + QScriptValue cbv = dispatchCallback(&m_me); + if (cbv.isError()) printError(cbv); + } + m_responseEntityBody.append(m_network->readAll()); + readEncoding(); + + if (xhrDump()) { + qWarning().nospace() << "XMLHttpRequest: RESPONSE " << qPrintable(m_url.toString()); + if (!m_responseEntityBody.isEmpty()) { + qWarning().nospace() << " " + << qPrintable(QString::fromUtf8(m_responseEntityBody)); + } + } + + + m_data.clear(); + destroyNetwork(); + if (m_state < Loading) { + m_state = Loading; + QScriptValue cbv = dispatchCallback(&m_me); + if (cbv.isError()) printError(cbv); + } + m_state = Done; + QScriptValue cbv = dispatchCallback(&m_me); + if (cbv.isError()) printError(cbv); + + m_me = QScriptValue(); +} + + +void QDeclarativeXMLHttpRequest::readEncoding() +{ + foreach (const HeaderPair &header, m_headersList) { + if (header.first == "content-type") { + int separatorIdx = header.second.indexOf(';'); + if (separatorIdx == -1) { + m_mime == header.second; + } else { + m_mime = header.second.mid(0, separatorIdx); + int charsetIdx = header.second.indexOf("charset="); + if (charsetIdx != -1) { + charsetIdx += 8; + separatorIdx = header.second.indexOf(';', charsetIdx); + m_charset = header.second.mid(charsetIdx, separatorIdx >= 0 ? separatorIdx : header.second.length()); + } + } + break; + } + } + + if (m_mime.isEmpty() || m_mime == "text/xml" || m_mime == "application/xml" || m_mime.endsWith("+xml")) + m_gotXml = true; +} + +bool QDeclarativeXMLHttpRequest::receivedXml() const +{ + return m_gotXml; +} + + +#ifndef QT_NO_TEXTCODEC +QTextCodec* QDeclarativeXMLHttpRequest::findTextCodec() const +{ + QTextCodec *codec = 0; + + if (!m_charset.isEmpty()) + codec = QTextCodec::codecForName(m_charset); + + if (!codec && m_gotXml) { + QXmlStreamReader reader(m_responseEntityBody); + reader.readNext(); + codec = QTextCodec::codecForName(reader.documentEncoding().toString().toUtf8()); + } + + if (!codec && m_mime == "text/html") + codec = QTextCodec::codecForHtml(m_responseEntityBody, 0); + + if (!codec) + codec = QTextCodec::codecForUtfText(m_responseEntityBody, 0); + + if (!codec) + codec = QTextCodec::codecForName("UTF-8"); + return codec; +} +#endif + + +QString QDeclarativeXMLHttpRequest::responseBody() +{ +#ifndef QT_NO_TEXTCODEC + if (!m_textCodec) + m_textCodec = findTextCodec(); + if (m_textCodec) + return m_textCodec->toUnicode(m_responseEntityBody); +#endif + + return QString::fromUtf8(m_responseEntityBody); +} + +const QByteArray &QDeclarativeXMLHttpRequest::rawResponseBody() const +{ + return m_responseEntityBody; +} + +QScriptValue QDeclarativeXMLHttpRequest::dispatchCallback(QScriptValue *me) +{ + QScriptValue v = me->property(QLatin1String("callback")); + return v.call(); +} + +void QDeclarativeXMLHttpRequest::printError(const QScriptValue& sv) +{ + QDeclarativeError error; + QDeclarativeExpressionPrivate::exceptionToError(sv.engine(), error); + QDeclarativeEnginePrivate::warning(QDeclarativeEnginePrivate::get(sv.engine()), error); +} + +void QDeclarativeXMLHttpRequest::destroyNetwork() +{ + if (m_network) { + m_network->disconnect(); + m_network->deleteLater(); + m_network = 0; + } +} + +// XMLHttpRequest methods +static QScriptValue qmlxmlhttprequest_open(QScriptContext *context, QScriptEngine *engine) +{ + QScriptValue dataObject = context->thisObject().data(); + QDeclarativeXMLHttpRequest *request = qobject_cast(dataObject.toQObject()); + if (!request) + THROW_REFERENCE("Not an XMLHttpRequest object"); + + if (context->argumentCount() < 2 || context->argumentCount() > 5) + THROW_DOM(SYNTAX_ERR, "Incorrect argument count"); + + // Argument 0 - Method + QString method = context->argument(0).toString().toUpper(); + if (method != QLatin1String("GET") && + method != QLatin1String("PUT") && + method != QLatin1String("HEAD") && + method != QLatin1String("POST")) + THROW_DOM(SYNTAX_ERR, "Unsupported HTTP method type"); + + + // Argument 1 - URL + QUrl url = QUrl::fromEncoded(context->argument(1).toString().toUtf8()); + + if (url.isRelative()) { + url = QDeclarativeScriptEngine::get(engine)->resolvedUrl(context,url); + } + + // Argument 2 - async (optional) + if (context->argumentCount() > 2 && !context->argument(2).toBoolean()) + THROW_DOM(NOT_SUPPORTED_ERR, "Synchronous XMLHttpRequest calls are not supported"); + + + // Argument 3/4 - user/pass (optional) + QString username, password; + if (context->argumentCount() > 3) + username = context->argument(3).toString(); + if (context->argumentCount() > 4) + password = context->argument(4).toString(); + + + // Clear the fragment (if any) + url.setFragment(QString()); + // Set username/password + if (!username.isNull()) url.setUserName(username); + if (!password.isNull()) url.setPassword(password); + + return request->open(&dataObject, method, url); +} + +static QScriptValue qmlxmlhttprequest_setRequestHeader(QScriptContext *context, QScriptEngine *engine) +{ + QDeclarativeXMLHttpRequest *request = qobject_cast(context->thisObject().data().toQObject()); + if (!request) + THROW_REFERENCE("Not an XMLHttpRequest object"); + + if (context->argumentCount() != 2) + THROW_DOM(SYNTAX_ERR, "Incorrect argument count"); + + + if (request->readyState() != QDeclarativeXMLHttpRequest::Opened || + request->sendFlag()) + THROW_DOM(INVALID_STATE_ERR, "Invalid state"); + + + QString name = context->argument(0).toString(); + QString value = context->argument(1).toString(); + + // ### Check that name and value are well formed + + QString nameUpper = name.toUpper(); + if (nameUpper == QLatin1String("ACCEPT-CHARSET") || + nameUpper == QLatin1String("ACCEPT-ENCODING") || + nameUpper == QLatin1String("CONNECTION") || + nameUpper == QLatin1String("CONTENT-LENGTH") || + nameUpper == QLatin1String("COOKIE") || + nameUpper == QLatin1String("COOKIE2") || + nameUpper == QLatin1String("CONTENT-TRANSFER-ENCODING") || + nameUpper == QLatin1String("DATE") || + nameUpper == QLatin1String("EXPECT") || + nameUpper == QLatin1String("HOST") || + nameUpper == QLatin1String("KEEP-ALIVE") || + nameUpper == QLatin1String("REFERER") || + nameUpper == QLatin1String("TE") || + nameUpper == QLatin1String("TRAILER") || + nameUpper == QLatin1String("TRANSFER-ENCODING") || + nameUpper == QLatin1String("UPGRADE") || + nameUpper == QLatin1String("USER-AGENT") || + nameUpper == QLatin1String("VIA") || + nameUpper.startsWith(QLatin1String("PROXY-")) || + nameUpper.startsWith(QLatin1String("SEC-"))) + return engine->undefinedValue(); + + request->addHeader(nameUpper, value); + + return engine->undefinedValue(); +} + +static QScriptValue qmlxmlhttprequest_send(QScriptContext *context, QScriptEngine *) +{ + QScriptValue dataObject = context->thisObject().data(); + QDeclarativeXMLHttpRequest *request = qobject_cast(dataObject.toQObject()); + if (!request) + THROW_REFERENCE("Not an XMLHttpRequest object"); + + if (request->readyState() != QDeclarativeXMLHttpRequest::Opened) + THROW_DOM(INVALID_STATE_ERR, "Invalid state"); + + if (request->sendFlag()) + THROW_DOM(INVALID_STATE_ERR, "Invalid state"); + + QByteArray data; + if (context->argumentCount() > 0) + data = context->argument(0).toString().toUtf8(); + + return request->send(&dataObject, data); +} + +static QScriptValue qmlxmlhttprequest_abort(QScriptContext *context, QScriptEngine *) +{ + QScriptValue dataObject = context->thisObject().data(); + QDeclarativeXMLHttpRequest *request = qobject_cast(dataObject.toQObject()); + if (!request) + THROW_REFERENCE("Not an XMLHttpRequest object"); + + return request->abort(&dataObject); +} + +static QScriptValue qmlxmlhttprequest_getResponseHeader(QScriptContext *context, QScriptEngine *engine) +{ + Q_UNUSED(engine) + QDeclarativeXMLHttpRequest *request = qobject_cast(context->thisObject().data().toQObject()); + if (!request) + THROW_REFERENCE("Not an XMLHttpRequest object"); + + if (context->argumentCount() != 1) + THROW_DOM(SYNTAX_ERR, "Incorrect argument count"); + + if (request->readyState() != QDeclarativeXMLHttpRequest::Loading && + request->readyState() != QDeclarativeXMLHttpRequest::Done && + request->readyState() != QDeclarativeXMLHttpRequest::HeadersReceived) + THROW_DOM(INVALID_STATE_ERR, "Invalid state"); + + QString headerName = context->argument(0).toString(); + + return QScriptValue(request->header(headerName)); +} + +static QScriptValue qmlxmlhttprequest_getAllResponseHeaders(QScriptContext *context, QScriptEngine *engine) +{ + Q_UNUSED(engine) + QDeclarativeXMLHttpRequest *request = qobject_cast(context->thisObject().data().toQObject()); + if (!request) + THROW_REFERENCE("Not an XMLHttpRequest object"); + + if (context->argumentCount() != 0) + THROW_DOM(SYNTAX_ERR, "Incorrect argument count"); + + if (request->readyState() != QDeclarativeXMLHttpRequest::Loading && + request->readyState() != QDeclarativeXMLHttpRequest::Done && + request->readyState() != QDeclarativeXMLHttpRequest::HeadersReceived) + THROW_DOM(INVALID_STATE_ERR, "Invalid state"); + + return QScriptValue(request->headers()); +} + +// XMLHttpRequest properties +static QScriptValue qmlxmlhttprequest_readyState(QScriptContext *context, QScriptEngine *engine) +{ + Q_UNUSED(engine) + QDeclarativeXMLHttpRequest *request = qobject_cast(context->thisObject().data().toQObject()); + if (!request) + THROW_REFERENCE("Not an XMLHttpRequest object"); + + return QScriptValue(request->readyState()); +} + +static QScriptValue qmlxmlhttprequest_status(QScriptContext *context, QScriptEngine *engine) +{ + Q_UNUSED(engine) + QDeclarativeXMLHttpRequest *request = qobject_cast(context->thisObject().data().toQObject()); + if (!request) + THROW_REFERENCE("Not an XMLHttpRequest object"); + + if (request->readyState() == QDeclarativeXMLHttpRequest::Unsent || + request->readyState() == QDeclarativeXMLHttpRequest::Opened) + THROW_DOM(INVALID_STATE_ERR, "Invalid state"); + + if (request->errorFlag()) + return QScriptValue(0); + else + return QScriptValue(request->replyStatus()); +} + +static QScriptValue qmlxmlhttprequest_statusText(QScriptContext *context, QScriptEngine *engine) +{ + Q_UNUSED(engine) + QDeclarativeXMLHttpRequest *request = qobject_cast(context->thisObject().data().toQObject()); + if (!request) + THROW_REFERENCE("Not an XMLHttpRequest object"); + + if (request->readyState() == QDeclarativeXMLHttpRequest::Unsent || + request->readyState() == QDeclarativeXMLHttpRequest::Opened) + THROW_DOM(INVALID_STATE_ERR, "Invalid state"); + + if (request->errorFlag()) + return QScriptValue(0); + else + return QScriptValue(request->replyStatusText()); +} + +static QScriptValue qmlxmlhttprequest_responseText(QScriptContext *context, QScriptEngine *engine) +{ + Q_UNUSED(engine) + QDeclarativeXMLHttpRequest *request = qobject_cast(context->thisObject().data().toQObject()); + if (!request) + THROW_REFERENCE("Not an XMLHttpRequest object"); + + if (request->readyState() != QDeclarativeXMLHttpRequest::Loading && + request->readyState() != QDeclarativeXMLHttpRequest::Done) + return QScriptValue(QString()); + else + return QScriptValue(request->responseBody()); +} + +static QScriptValue qmlxmlhttprequest_responseXML(QScriptContext *context, QScriptEngine *engine) +{ + QDeclarativeXMLHttpRequest *request = qobject_cast(context->thisObject().data().toQObject()); + if (!request) + THROW_REFERENCE("Not an XMLHttpRequest object"); + + if (!request->receivedXml() || + (request->readyState() != QDeclarativeXMLHttpRequest::Loading && + request->readyState() != QDeclarativeXMLHttpRequest::Done)) + return engine->nullValue(); + else + return Document::load(engine, request->rawResponseBody()); +} + +static QScriptValue qmlxmlhttprequest_onreadystatechange(QScriptContext *context, QScriptEngine *engine) +{ + Q_UNUSED(engine); + QScriptValue dataObject = context->thisObject().data(); + QDeclarativeXMLHttpRequest *request = qobject_cast(dataObject.toQObject()); + if (!request) + THROW_REFERENCE("Not an XMLHttpRequest object"); + + if (context->argumentCount()) { + QScriptValue v = context->argument(0); + dataObject.setProperty(QLatin1String("callback"), v); + return v; + } else { + return dataObject.property(QLatin1String("callback")); + } +} + +// Constructor +static QScriptValue qmlxmlhttprequest_new(QScriptContext *context, QScriptEngine *engine) +{ + if (context->isCalledAsConstructor()) { + context->thisObject().setData(engine->newQObject(new QDeclarativeXMLHttpRequest(QDeclarativeScriptEngine::get(engine)->networkAccessManager()), QScriptEngine::ScriptOwnership)); + } + return engine->undefinedValue(); +} + +void qt_add_qmlxmlhttprequest(QScriptEngine *engine) +{ + QScriptValue prototype = engine->newObject(); + + // Methods + prototype.setProperty(QLatin1String("open"), engine->newFunction(qmlxmlhttprequest_open, 2)); + prototype.setProperty(QLatin1String("setRequestHeader"), engine->newFunction(qmlxmlhttprequest_setRequestHeader, 2)); + prototype.setProperty(QLatin1String("send"), engine->newFunction(qmlxmlhttprequest_send)); + prototype.setProperty(QLatin1String("abort"), engine->newFunction(qmlxmlhttprequest_abort)); + prototype.setProperty(QLatin1String("getResponseHeader"), engine->newFunction(qmlxmlhttprequest_getResponseHeader, 1)); + prototype.setProperty(QLatin1String("getAllResponseHeaders"), engine->newFunction(qmlxmlhttprequest_getAllResponseHeaders)); + + // Read-only properties + prototype.setProperty(QLatin1String("readyState"), engine->newFunction(qmlxmlhttprequest_readyState), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); + prototype.setProperty(QLatin1String("status"), engine->newFunction(qmlxmlhttprequest_status), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); + prototype.setProperty(QLatin1String("statusText"), engine->newFunction(qmlxmlhttprequest_statusText), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); + prototype.setProperty(QLatin1String("responseText"), engine->newFunction(qmlxmlhttprequest_responseText), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); + prototype.setProperty(QLatin1String("responseXML"), engine->newFunction(qmlxmlhttprequest_responseXML), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); + prototype.setProperty(QLatin1String("onreadystatechange"), engine->newFunction(qmlxmlhttprequest_onreadystatechange), QScriptValue::PropertyGetter | QScriptValue::PropertySetter); + + // State values + prototype.setProperty(QLatin1String("UNSENT"), 0, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); + prototype.setProperty(QLatin1String("OPENED"), 1, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); + prototype.setProperty(QLatin1String("HEADERS_RECEIVED"), 2, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); + prototype.setProperty(QLatin1String("LOADING"), 3, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); + prototype.setProperty(QLatin1String("DONE"), 4, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); + + // Constructor + QScriptValue constructor = engine->newFunction(qmlxmlhttprequest_new, prototype); + constructor.setProperty(QLatin1String("UNSENT"), 0, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); + constructor.setProperty(QLatin1String("OPENED"), 1, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); + constructor.setProperty(QLatin1String("HEADERS_RECEIVED"), 2, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); + constructor.setProperty(QLatin1String("LOADING"), 3, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); + constructor.setProperty(QLatin1String("DONE"), 4, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); + engine->globalObject().setProperty(QLatin1String("XMLHttpRequest"), constructor); + + // DOM Exception + QScriptValue domExceptionPrototype = engine->newObject(); + domExceptionPrototype.setProperty(QLatin1String("INDEX_SIZE_ERR"), INDEX_SIZE_ERR, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); + domExceptionPrototype.setProperty(QLatin1String("DOMSTRING_SIZE_ERR"), DOMSTRING_SIZE_ERR, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); + domExceptionPrototype.setProperty(QLatin1String("HIERARCHY_REQUEST_ERR"), HIERARCHY_REQUEST_ERR, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); + domExceptionPrototype.setProperty(QLatin1String("WRONG_DOCUMENT_ERR"), WRONG_DOCUMENT_ERR, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); + domExceptionPrototype.setProperty(QLatin1String("INVALID_CHARACTER_ERR"), INVALID_CHARACTER_ERR, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); + domExceptionPrototype.setProperty(QLatin1String("NO_DATA_ALLOWED_ERR"), NO_DATA_ALLOWED_ERR, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); + domExceptionPrototype.setProperty(QLatin1String("NO_MODIFICATION_ALLOWED_ERR"), NO_MODIFICATION_ALLOWED_ERR, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); + domExceptionPrototype.setProperty(QLatin1String("NOT_FOUND_ERR"), NOT_FOUND_ERR, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); + domExceptionPrototype.setProperty(QLatin1String("NOT_SUPPORTED_ERR"), NOT_SUPPORTED_ERR, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); + domExceptionPrototype.setProperty(QLatin1String("INUSE_ATTRIBUTE_ERR"), INUSE_ATTRIBUTE_ERR, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); + domExceptionPrototype.setProperty(QLatin1String("INVALID_STATE_ERR"), INVALID_STATE_ERR, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); + domExceptionPrototype.setProperty(QLatin1String("SYNTAX_ERR"), SYNTAX_ERR, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); + domExceptionPrototype.setProperty(QLatin1String("INVALID_MODIFICATION_ERR"), INVALID_MODIFICATION_ERR, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); + domExceptionPrototype.setProperty(QLatin1String("NAMESPACE_ERR"), NAMESPACE_ERR, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); + domExceptionPrototype.setProperty(QLatin1String("INVALID_ACCESS_ERR"), INVALID_ACCESS_ERR, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); + domExceptionPrototype.setProperty(QLatin1String("VALIDATION_ERR"), VALIDATION_ERR, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); + domExceptionPrototype.setProperty(QLatin1String("TYPE_MISMATCH_ERR"), TYPE_MISMATCH_ERR, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); + + engine->globalObject().setProperty(QLatin1String("DOMException"), domExceptionPrototype); +} + +QT_END_NAMESPACE + +#endif // QT_NO_XMLSTREAMREADER + +#include diff --git a/src/declarative/qml/qdeclarativexmlhttprequest_p.h b/src/declarative/qml/qdeclarativexmlhttprequest_p.h new file mode 100644 index 00000000..c7d3ebb6 --- /dev/null +++ b/src/declarative/qml/qdeclarativexmlhttprequest_p.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEXMLHTTPREQUEST_P_H +#define QDECLARATIVEXMLHTTPREQUEST_P_H + +#include +// +// 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 + +#ifndef QT_NO_XMLSTREAMREADER + +QT_BEGIN_NAMESPACE + +class QScriptEngine; +void qt_add_qmlxmlhttprequest(QScriptEngine *engine); + +QT_END_NAMESPACE + +#endif // QT_NO_XMLSTREAMREADER + +#endif // QDECLARATIVEXMLHTTPREQUEST_P_H + diff --git a/src/declarative/qml/qmetaobjectbuilder.cpp b/src/declarative/qml/qmetaobjectbuilder.cpp new file mode 100644 index 00000000..0648914f --- /dev/null +++ b/src/declarative/qml/qmetaobjectbuilder.cpp @@ -0,0 +1,2601 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qmetaobjectbuilder_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QMetaObjectBuilder + \internal + \brief The QMetaObjectBuilder class supports building QMetaObject objects at runtime. + +*/ + +/*! + \enum QMetaObjectBuilder::AddMember + This enum defines which members of QMetaObject should be copied by QMetaObjectBuilder::addMetaObject() + + \value ClassName Add the class name. + \value SuperClass Add the super class. + \value Methods Add methods that aren't signals or slots. + \value Signals Add signals. + \value Slots Add slots. + \value Constructors Add constructors. + \value Properties Add properties. + \value Enumerators Add enumerators. + \value ClassInfos Add items of class information. + \value RelatedMetaObjects Add related meta objects. + \value StaticMetacall Add the static metacall function. + \value PublicMethods Add public methods (ignored for signals). + \value ProtectedMethods Add protected methods (ignored for signals). + \value PrivateMethods All private methods (ignored for signals). + \value AllMembers Add all members. + \value AllPrimaryMembers Add everything except the class name, super class, and static metacall function. +*/ + +// copied from moc's generator.cpp +uint qvariant_nameToType(const char* name) +{ + if (!name) + return 0; + + if (strcmp(name, "QVariant") == 0) + return 0xffffffff; + if (strcmp(name, "QCString") == 0) + return QMetaType::QByteArray; + if (strcmp(name, "Q_LLONG") == 0) + return QMetaType::LongLong; + if (strcmp(name, "Q_ULLONG") == 0) + return QMetaType::ULongLong; + if (strcmp(name, "QIconSet") == 0) + return QMetaType::QIcon; + + uint tp = QMetaType::type(name); + return tp < QMetaType::User ? tp : 0; +} + +/* + Returns true if the type is a QVariant types. +*/ +bool isVariantType(const char* type) +{ + return qvariant_nameToType(type) != 0; +} + +// copied from qmetaobject_p.h +// do not touch without touching the moc as well +enum PropertyFlags { + Invalid = 0x00000000, + Readable = 0x00000001, + Writable = 0x00000002, + Resettable = 0x00000004, + EnumOrFlag = 0x00000008, + StdCppSet = 0x00000100, +// Override = 0x00000200, + Constant = 0x00000400, + Final = 0x00000800, + Designable = 0x00001000, + ResolveDesignable = 0x00002000, + Scriptable = 0x00004000, + ResolveScriptable = 0x00008000, + Stored = 0x00010000, + ResolveStored = 0x00020000, + Editable = 0x00040000, + ResolveEditable = 0x00080000, + User = 0x00100000, + ResolveUser = 0x00200000, + Notify = 0x00400000, + Revisioned = 0x00800000 +}; + +enum MethodFlags { + AccessPrivate = 0x00, + AccessProtected = 0x01, + AccessPublic = 0x02, + AccessMask = 0x03, //mask + + MethodMethod = 0x00, + MethodSignal = 0x04, + MethodSlot = 0x08, + MethodConstructor = 0x0c, + MethodTypeMask = 0x0c, + + MethodCompatibility = 0x10, + MethodCloned = 0x20, + MethodScriptable = 0x40, + MethodRevisioned = 0x80 +}; + +struct QMetaObjectPrivate +{ + int revision; + int className; + int classInfoCount, classInfoData; + int methodCount, methodData; + int propertyCount, propertyData; + int enumeratorCount, enumeratorData; + int constructorCount, constructorData; + int flags; +}; + +static inline const QMetaObjectPrivate *priv(const uint* data) +{ return reinterpret_cast(data); } +// end of copied lines from qmetaobject.cpp + +class QMetaMethodBuilderPrivate +{ +public: + QMetaMethodBuilderPrivate + (QMetaMethod::MethodType _methodType, + const QByteArray& _signature, + const QByteArray& _returnType = QByteArray(), + QMetaMethod::Access _access = QMetaMethod::Public) + : signature(QMetaObject::normalizedSignature(_signature.constData())), + returnType(QMetaObject::normalizedType(_returnType)), + attributes(((int)_access) | (((int)_methodType) << 2)) + { + } + + QByteArray signature; + QByteArray returnType; + QList parameterNames; + QByteArray tag; + int attributes; + + QMetaMethod::MethodType methodType() const + { + return (QMetaMethod::MethodType)((attributes & MethodTypeMask) >> 2); + } + + QMetaMethod::Access access() const + { + return (QMetaMethod::Access)(attributes & AccessMask); + } + + void setAccess(QMetaMethod::Access value) + { + attributes = ((attributes & ~AccessMask) | (int)value); + } +}; + +class QMetaPropertyBuilderPrivate +{ +public: + QMetaPropertyBuilderPrivate + (const QByteArray& _name, const QByteArray& _type, int notifierIdx=-1) + : name(_name), + type(QMetaObject::normalizedType(_type.constData())), + flags(Readable | Writable | Scriptable), notifySignal(-1) + { + if (notifierIdx >= 0) { + flags |= Notify; + notifySignal = notifierIdx; + } + } + + QByteArray name; + QByteArray type; + int flags; + int notifySignal; + + bool flag(int f) const + { + return ((flags & f) != 0); + } + + void setFlag(int f, bool value) + { + if (value) + flags |= f; + else + flags &= ~f; + } +}; + +class QMetaEnumBuilderPrivate +{ +public: + QMetaEnumBuilderPrivate(const QByteArray& _name) + : name(_name), isFlag(false) + { + } + + QByteArray name; + bool isFlag; + QList keys; + QList values; +}; + +class QMetaObjectBuilderPrivate +{ +public: + QMetaObjectBuilderPrivate() + : flags(0) + { + superClass = &QObject::staticMetaObject; + staticMetacallFunction = 0; + } + + QByteArray className; + const QMetaObject *superClass; + QMetaObjectBuilder::StaticMetacallFunction staticMetacallFunction; + QList methods; + QList constructors; + QList properties; + QList classInfoNames; + QList classInfoValues; + QList enumerators; +#ifdef Q_NO_DATA_RELOCATION + QList relatedMetaObjects; +#else + QList relatedMetaObjects; +#endif + int flags; +}; + +/*! + Constructs a new QMetaObjectBuilder. +*/ +QMetaObjectBuilder::QMetaObjectBuilder() +{ + d = new QMetaObjectBuilderPrivate(); +} + +/*! + Constructs a new QMetaObjectBuilder which is a copy of the + meta object information in \a prototype. Note: the super class + contents for \a prototype are not copied, only the immediate + class that is defined by \a prototype. + + The \a members parameter indicates which members of \a prototype + should be added. The default is AllMembers. + + \sa addMetaObject() +*/ +QMetaObjectBuilder::QMetaObjectBuilder + (const QMetaObject *prototype, QMetaObjectBuilder::AddMembers members) +{ + d = new QMetaObjectBuilderPrivate(); + addMetaObject(prototype, members); +} + +/*! + Destroys this meta object builder. +*/ +QMetaObjectBuilder::~QMetaObjectBuilder() +{ + delete d; +} + +/*! + Returns the name of the class being constructed by this + meta object builder. The default value is an empty QByteArray. + + \sa setClassName(), superClass() +*/ +QByteArray QMetaObjectBuilder::className() const +{ + return d->className; +} + +/*! + Sets the \a name of the class being constructed by this + meta object builder. + + \sa className(), setSuperClass() +*/ +void QMetaObjectBuilder::setClassName(const QByteArray& name) +{ + d->className = name; +} + +/*! + Returns the superclass meta object of the class being constructed + by this meta object builder. The default value is the meta object + for QObject. + + \sa setSuperClass(), className() +*/ +const QMetaObject *QMetaObjectBuilder::superClass() const +{ + return d->superClass; +} + +/*! + Sets the superclass meta object of the class being constructed + by this meta object builder to \a meta. The \a meta parameter + must not be null. + + \sa superClass(), setClassName() +*/ +void QMetaObjectBuilder::setSuperClass(const QMetaObject *meta) +{ + Q_ASSERT(meta); + d->superClass = meta; +} + +/*! + Returns the flags of the class being constructed by this meta object + builder. + + \sa setFlags() +*/ +QMetaObjectBuilder::MetaObjectFlags QMetaObjectBuilder::flags() const +{ + return (QMetaObjectBuilder::MetaObjectFlags)d->flags; +} + +/*! + Sets the \a flags of the class being constructed by this meta object + builder. + + \sa flags() +*/ +void QMetaObjectBuilder::setFlags(MetaObjectFlags flags) +{ + d->flags = flags; +} + +/*! + Returns the number of methods in this class, excluding the number + of methods in the base class. These include signals and slots + as well as normal member functions. + + \sa addMethod(), method(), removeMethod(), indexOfMethod() +*/ +int QMetaObjectBuilder::methodCount() const +{ + return d->methods.size(); +} + +/*! + Returns the number of constructors in this class. + + \sa addConstructor(), constructor(), removeConstructor(), indexOfConstructor() +*/ +int QMetaObjectBuilder::constructorCount() const +{ + return d->constructors.size(); +} + +/*! + Returns the number of properties in this class, excluding the number + of properties in the base class. + + \sa addProperty(), property(), removeProperty(), indexOfProperty() +*/ +int QMetaObjectBuilder::propertyCount() const +{ + return d->properties.size(); +} + +/*! + Returns the number of enumerators in this class, excluding the + number of enumerators in the base class. + + \sa addEnumerator(), enumerator(), removeEnumerator() + \sa indexOfEnumerator() +*/ +int QMetaObjectBuilder::enumeratorCount() const +{ + return d->enumerators.size(); +} + +/*! + Returns the number of items of class information in this class, + exclusing the number of items of class information in the base class. + + \sa addClassInfo(), classInfoName(), classInfoValue(), removeClassInfo() + \sa indexOfClassInfo() +*/ +int QMetaObjectBuilder::classInfoCount() const +{ + return d->classInfoNames.size(); +} + +/*! + Returns the number of related meta objects that are associated + with this class. + + Related meta objects are used when resolving the enumerated type + associated with a property, where the enumerated type is in a + different class from the property. + + \sa addRelatedMetaObject(), relatedMetaObject() + \sa removeRelatedMetaObject() +*/ +int QMetaObjectBuilder::relatedMetaObjectCount() const +{ + return d->relatedMetaObjects.size(); +} + +/*! + Adds a new public method to this class with the specified \a signature. + Returns an object that can be used to adjust the other attributes + of the method. The \a signature will be normalized before it is + added to the class. + + \sa method(), methodCount(), removeMethod(), indexOfMethod() +*/ +QMetaMethodBuilder QMetaObjectBuilder::addMethod(const QByteArray& signature) +{ + int index = d->methods.size(); + d->methods.append(QMetaMethodBuilderPrivate(QMetaMethod::Method, signature)); + return QMetaMethodBuilder(this, index); +} + +/*! + Adds a new public method to this class with the specified + \a signature and \a returnType. Returns an object that can be + used to adjust the other attributes of the method. The \a signature + and \a returnType will be normalized before they are added to + the class. If \a returnType is empty, then it indicates that + the method has \c{void} as its return type. + + \sa method(), methodCount(), removeMethod(), indexOfMethod() +*/ +QMetaMethodBuilder QMetaObjectBuilder::addMethod + (const QByteArray& signature, const QByteArray& returnType) +{ + int index = d->methods.size(); + d->methods.append(QMetaMethodBuilderPrivate + (QMetaMethod::Method, signature, returnType)); + return QMetaMethodBuilder(this, index); +} + +/*! + Adds a new public method to this class that has the same information as + \a prototype. This is used to clone the methods of an existing + QMetaObject. Returns an object that can be used to adjust the + attributes of the method. + + This function will detect if \a prototype is an ordinary method, + signal, slot, or constructor and act accordingly. + + \sa method(), methodCount(), removeMethod(), indexOfMethod() +*/ +QMetaMethodBuilder QMetaObjectBuilder::addMethod(const QMetaMethod& prototype) +{ + QMetaMethodBuilder method; + if (prototype.methodType() == QMetaMethod::Method) + method = addMethod(prototype.signature()); + else if (prototype.methodType() == QMetaMethod::Signal) + method = addSignal(prototype.signature()); + else if (prototype.methodType() == QMetaMethod::Slot) + method = addSlot(prototype.signature()); + else if (prototype.methodType() == QMetaMethod::Constructor) + method = addConstructor(prototype.signature()); + method.setReturnType(prototype.typeName()); + method.setParameterNames(prototype.parameterNames()); + method.setTag(prototype.tag()); + method.setAccess(prototype.access()); + method.setAttributes(prototype.attributes()); + return method; +} + +/*! + Adds a new public slot to this class with the specified \a signature. + Returns an object that can be used to adjust the other attributes + of the slot. The \a signature will be normalized before it is + added to the class. + + \sa addMethod(), addSignal(), indexOfSlot() +*/ +QMetaMethodBuilder QMetaObjectBuilder::addSlot(const QByteArray& signature) +{ + int index = d->methods.size(); + d->methods.append(QMetaMethodBuilderPrivate(QMetaMethod::Slot, signature)); + return QMetaMethodBuilder(this, index); +} + +/*! + Adds a new signal to this class with the specified \a signature. + Returns an object that can be used to adjust the other attributes + of the signal. The \a signature will be normalized before it is + added to the class. + + \sa addMethod(), addSlot(), indexOfSignal() +*/ +QMetaMethodBuilder QMetaObjectBuilder::addSignal(const QByteArray& signature) +{ + int index = d->methods.size(); + d->methods.append(QMetaMethodBuilderPrivate + (QMetaMethod::Signal, signature, QByteArray(), QMetaMethod::Protected)); + return QMetaMethodBuilder(this, index); +} + +/*! + Adds a new constructor to this class with the specified \a signature. + Returns an object that can be used to adjust the other attributes + of the constructor. The \a signature will be normalized before it is + added to the class. + + \sa constructor(), constructorCount(), removeConstructor() + \sa indexOfConstructor() +*/ +QMetaMethodBuilder QMetaObjectBuilder::addConstructor(const QByteArray& signature) +{ + int index = d->constructors.size(); + d->constructors.append(QMetaMethodBuilderPrivate(QMetaMethod::Constructor, signature)); + return QMetaMethodBuilder(this, -(index + 1)); +} + +/*! + Adds a new constructor to this class that has the same information as + \a prototype. This is used to clone the constructors of an existing + QMetaObject. Returns an object that can be used to adjust the + attributes of the constructor. + + This function requires that \a prototype be a constructor. + + \sa constructor(), constructorCount(), removeConstructor() + \sa indexOfConstructor() +*/ +QMetaMethodBuilder QMetaObjectBuilder::addConstructor(const QMetaMethod& prototype) +{ + Q_ASSERT(prototype.methodType() == QMetaMethod::Constructor); + QMetaMethodBuilder ctor = addConstructor(prototype.signature()); + ctor.setReturnType(prototype.typeName()); + ctor.setParameterNames(prototype.parameterNames()); + ctor.setTag(prototype.tag()); + ctor.setAccess(prototype.access()); + ctor.setAttributes(prototype.attributes()); + return ctor; +} + +/*! + Adds a new readable/writable property to this class with the + specified \a name and \a type. Returns an object that can be used + to adjust the other attributes of the property. The \a type will + be normalized before it is added to the class. \a notifierId will + be registered as the property's \e notify signal. + + \sa property(), propertyCount(), removeProperty(), indexOfProperty() +*/ +QMetaPropertyBuilder QMetaObjectBuilder::addProperty + (const QByteArray& name, const QByteArray& type, int notifierId) +{ + int index = d->properties.size(); + d->properties.append(QMetaPropertyBuilderPrivate(name, type, notifierId)); + return QMetaPropertyBuilder(this, index); +} + +/*! + Adds a new property to this class that has the same information as + \a prototype. This is used to clone the properties of an existing + QMetaObject. Returns an object that can be used to adjust the + attributes of the property. + + \sa property(), propertyCount(), removeProperty(), indexOfProperty() +*/ +QMetaPropertyBuilder QMetaObjectBuilder::addProperty(const QMetaProperty& prototype) +{ + QMetaPropertyBuilder property = addProperty(prototype.name(), prototype.typeName()); + property.setReadable(prototype.isReadable()); + property.setWritable(prototype.isWritable()); + property.setResettable(prototype.isResettable()); + property.setDesignable(prototype.isDesignable()); + property.setScriptable(prototype.isScriptable()); + property.setStored(prototype.isStored()); + property.setEditable(prototype.isEditable()); + property.setUser(prototype.isUser()); + property.setStdCppSet(prototype.hasStdCppSet()); + property.setEnumOrFlag(prototype.isEnumType()); + property.setConstant(prototype.isConstant()); + property.setFinal(prototype.isFinal()); + if (prototype.hasNotifySignal()) { + // Find an existing method for the notify signal, or add a new one. + QMetaMethod method = prototype.notifySignal(); + int index = indexOfMethod(method.signature()); + if (index == -1) + index = addMethod(method).index(); + d->properties[property._index].notifySignal = index; + d->properties[property._index].setFlag(Notify, true); + } + return property; +} + +/*! + Adds a new enumerator to this class with the specified + \a name. Returns an object that can be used to adjust + the other attributes of the enumerator. + + \sa enumerator(), enumeratorCount(), removeEnumerator(), + \sa indexOfEnumerator() +*/ +QMetaEnumBuilder QMetaObjectBuilder::addEnumerator(const QByteArray& name) +{ + int index = d->enumerators.size(); + d->enumerators.append(QMetaEnumBuilderPrivate(name)); + return QMetaEnumBuilder(this, index); +} + +/*! + Adds a new enumerator to this class that has the same information as + \a prototype. This is used to clone the enumerators of an existing + QMetaObject. Returns an object that can be used to adjust the + attributes of the enumerator. + + \sa enumerator(), enumeratorCount(), removeEnumerator(), + \sa indexOfEnumerator() +*/ +QMetaEnumBuilder QMetaObjectBuilder::addEnumerator(const QMetaEnum& prototype) +{ + QMetaEnumBuilder en = addEnumerator(prototype.name()); + en.setIsFlag(prototype.isFlag()); + int count = prototype.keyCount(); + for (int index = 0; index < count; ++index) + en.addKey(prototype.key(index), prototype.value(index)); + return en; +} + +/*! + Adds \a name and \a value as an item of class information to this class. + Returns the index of the new item of class information. + + \sa classInfoCount(), classInfoName(), classInfoValue(), removeClassInfo() + \sa indexOfClassInfo() +*/ +int QMetaObjectBuilder::addClassInfo(const QByteArray& name, const QByteArray& value) +{ + int index = d->classInfoNames.size(); + d->classInfoNames += name; + d->classInfoValues += value; + return index; +} + +/*! + Adds \a meta to this class as a related meta object. Returns + the index of the new related meta object entry. + + Related meta objects are used when resolving the enumerated type + associated with a property, where the enumerated type is in a + different class from the property. + + \sa relatedMetaObjectCount(), relatedMetaObject() + \sa removeRelatedMetaObject() +*/ +#ifdef Q_NO_DATA_RELOCATION +int QMetaObjectBuilder::addRelatedMetaObject(const QMetaObjectAccessor &meta) +#else +int QMetaObjectBuilder::addRelatedMetaObject(const QMetaObject *meta) +#endif +{ + Q_ASSERT(meta); + int index = d->relatedMetaObjects.size(); + d->relatedMetaObjects.append(meta); + return index; +} + +/*! + Adds the contents of \a prototype to this meta object builder. + This function is useful for cloning the contents of an existing QMetaObject. + + The \a members parameter indicates which members of \a prototype + should be added. The default is AllMembers. +*/ +void QMetaObjectBuilder::addMetaObject + (const QMetaObject *prototype, QMetaObjectBuilder::AddMembers members) +{ + Q_ASSERT(prototype); + int index; + + if ((members & ClassName) != 0) + d->className = prototype->className(); + + if ((members & SuperClass) != 0) + d->superClass = prototype->superClass(); + + if ((members & (Methods | Signals | Slots)) != 0) { + for (index = prototype->methodOffset(); index < prototype->methodCount(); ++index) { + QMetaMethod method = prototype->method(index); + if (method.methodType() != QMetaMethod::Signal) { + if (method.access() == QMetaMethod::Public && (members & PublicMethods) == 0) + continue; + if (method.access() == QMetaMethod::Private && (members & PrivateMethods) == 0) + continue; + if (method.access() == QMetaMethod::Protected && (members & ProtectedMethods) == 0) + continue; + } + if (method.methodType() == QMetaMethod::Method && (members & Methods) != 0) { + addMethod(method); + } else if (method.methodType() == QMetaMethod::Signal && + (members & Signals) != 0) { + addMethod(method); + } else if (method.methodType() == QMetaMethod::Slot && + (members & Slots) != 0) { + addMethod(method); + } + } + } + + if ((members & Constructors) != 0) { + for (index = 0; index < prototype->constructorCount(); ++index) + addConstructor(prototype->constructor(index)); + } + + if ((members & Properties) != 0) { + for (index = prototype->propertyOffset(); index < prototype->propertyCount(); ++index) + addProperty(prototype->property(index)); + } + + if ((members & Enumerators) != 0) { + for (index = prototype->enumeratorOffset(); index < prototype->enumeratorCount(); ++index) + addEnumerator(prototype->enumerator(index)); + } + + if ((members & ClassInfos) != 0) { + for (index = prototype->classInfoOffset(); index < prototype->classInfoCount(); ++index) { + QMetaClassInfo ci = prototype->classInfo(index); + addClassInfo(ci.name(), ci.value()); + } + } + + if ((members & RelatedMetaObjects) != 0) { +#ifdef Q_NO_DATA_RELOCATION + const QMetaObjectAccessor *objects = 0; +#else + const QMetaObject **objects; + if (priv(prototype->d.data)->revision < 2) { + objects = (const QMetaObject **)(prototype->d.extradata); + } else +#endif + { + const QMetaObjectExtraData *extra = (const QMetaObjectExtraData *)(prototype->d.extradata); + if (extra) + objects = extra->objects; + else + objects = 0; + } + if (objects) { + while (*objects != 0) { + addRelatedMetaObject(*objects); + ++objects; + } + } + } + + if ((members & StaticMetacall) != 0) { + if (priv(prototype->d.data)->revision >= 6) { + const QMetaObjectExtraData *extra = + (const QMetaObjectExtraData *)(prototype->d.extradata); + if (extra && extra->static_metacall) + setStaticMetacallFunction(extra->static_metacall); + } + } +} + +/*! + Returns the method at \a index in this class. + + \sa methodCount(), addMethod(), removeMethod(), indexOfMethod() +*/ +QMetaMethodBuilder QMetaObjectBuilder::method(int index) const +{ + if (index >= 0 && index < d->methods.size()) + return QMetaMethodBuilder(this, index); + else + return QMetaMethodBuilder(); +} + +/*! + Returns the constructor at \a index in this class. + + \sa methodCount(), addMethod(), removeMethod(), indexOfConstructor() +*/ +QMetaMethodBuilder QMetaObjectBuilder::constructor(int index) const +{ + if (index >= 0 && index < d->constructors.size()) + return QMetaMethodBuilder(this, -(index + 1)); + else + return QMetaMethodBuilder(); +} + +/*! + Returns the property at \a index in this class. + + \sa methodCount(), addMethod(), removeMethod(), indexOfProperty() +*/ +QMetaPropertyBuilder QMetaObjectBuilder::property(int index) const +{ + if (index >= 0 && index < d->properties.size()) + return QMetaPropertyBuilder(this, index); + else + return QMetaPropertyBuilder(); +} + +/*! + Returns the enumerator at \a index in this class. + + \sa enumeratorCount(), addEnumerator(), removeEnumerator() + \sa indexOfEnumerator() +*/ +QMetaEnumBuilder QMetaObjectBuilder::enumerator(int index) const +{ + if (index >= 0 && index < d->enumerators.size()) + return QMetaEnumBuilder(this, index); + else + return QMetaEnumBuilder(); +} + +/*! + Returns the related meta object at \a index in this class. + + Related meta objects are used when resolving the enumerated type + associated with a property, where the enumerated type is in a + different class from the property. + + \sa relatedMetaObjectCount(), addRelatedMetaObject() + \sa removeRelatedMetaObject() +*/ +const QMetaObject *QMetaObjectBuilder::relatedMetaObject(int index) const +{ + if (index >= 0 && index < d->relatedMetaObjects.size()) +#ifdef Q_NO_DATA_RELOCATION + return &((*(d->relatedMetaObjects[index]))()); +#else + return d->relatedMetaObjects[index]; +#endif + else + return 0; +} + +/*! + Returns the name of the item of class information at \a index + in this class. + + \sa classInfoCount(), addClassInfo(), classInfoValue(), removeClassInfo() + \sa indexOfClassInfo() +*/ +QByteArray QMetaObjectBuilder::classInfoName(int index) const +{ + if (index >= 0 && index < d->classInfoNames.size()) + return d->classInfoNames[index]; + else + return QByteArray(); +} + +/*! + Returns the value of the item of class information at \a index + in this class. + + \sa classInfoCount(), addClassInfo(), classInfoName(), removeClassInfo() + \sa indexOfClassInfo() +*/ +QByteArray QMetaObjectBuilder::classInfoValue(int index) const +{ + if (index >= 0 && index < d->classInfoValues.size()) + return d->classInfoValues[index]; + else + return QByteArray(); +} + +/*! + Removes the method at \a index from this class. The indices of + all following methods will be adjusted downwards by 1. If the + method is registered as a notify signal on a property, then the + notify signal will be removed from the property. + + \sa methodCount(), addMethod(), method(), indexOfMethod() +*/ +void QMetaObjectBuilder::removeMethod(int index) +{ + if (index >= 0 && index < d->methods.size()) { + d->methods.removeAt(index); + for (int prop = 0; prop < d->properties.size(); ++prop) { + // Adjust the indices of property notify signal references. + if (d->properties[prop].notifySignal == index) { + d->properties[prop].notifySignal = -1; + d->properties[prop].setFlag(Notify, false); + } else if (d->properties[prop].notifySignal > index) + (d->properties[prop].notifySignal)--; + } + } +} + +/*! + Removes the constructor at \a index from this class. The indices of + all following constructors will be adjusted downwards by 1. + + \sa constructorCount(), addConstructor(), constructor() + \sa indexOfConstructor() +*/ +void QMetaObjectBuilder::removeConstructor(int index) +{ + if (index >= 0 && index < d->constructors.size()) + d->constructors.removeAt(index); +} + +/*! + Removes the property at \a index from this class. The indices of + all following properties will be adjusted downwards by 1. + + \sa propertyCount(), addProperty(), property(), indexOfProperty() +*/ +void QMetaObjectBuilder::removeProperty(int index) +{ + if (index >= 0 && index < d->properties.size()) + d->properties.removeAt(index); +} + +/*! + Removes the enumerator at \a index from this class. The indices of + all following enumerators will be adjusted downwards by 1. + + \sa enumertorCount(), addEnumerator(), enumerator() + \sa indexOfEnumerator() +*/ +void QMetaObjectBuilder::removeEnumerator(int index) +{ + if (index >= 0 && index < d->enumerators.size()) + d->enumerators.removeAt(index); +} + +/*! + Removes the item of class information at \a index from this class. + The indices of all following items will be adjusted downwards by 1. + + \sa classInfoCount(), addClassInfo(), classInfoName(), classInfoValue() + \sa indexOfClassInfo() +*/ +void QMetaObjectBuilder::removeClassInfo(int index) +{ + if (index >= 0 && index < d->classInfoNames.size()) { + d->classInfoNames.removeAt(index); + d->classInfoValues.removeAt(index); + } +} + +/*! + Removes the related meta object at \a index from this class. + The indices of all following related meta objects will be adjusted + downwards by 1. + + Related meta objects are used when resolving the enumerated type + associated with a property, where the enumerated type is in a + different class from the property. + + \sa relatedMetaObjectCount(), addRelatedMetaObject() + \sa relatedMetaObject() +*/ +void QMetaObjectBuilder::removeRelatedMetaObject(int index) +{ + if (index >= 0 && index < d->relatedMetaObjects.size()) + d->relatedMetaObjects.removeAt(index); +} + +/*! + Finds a method with the specified \a signature and returns its index; + otherwise returns -1. The \a signature will be normalized by this method. + + \sa method(), methodCount(), addMethod(), removeMethod() +*/ +int QMetaObjectBuilder::indexOfMethod(const QByteArray& signature) +{ + QByteArray sig = QMetaObject::normalizedSignature(signature); + for (int index = 0; index < d->methods.size(); ++index) { + if (sig == d->methods[index].signature) + return index; + } + return -1; +} + +/*! + Finds a signal with the specified \a signature and returns its index; + otherwise returns -1. The \a signature will be normalized by this method. + + \sa indexOfMethod(), indexOfSlot() +*/ +int QMetaObjectBuilder::indexOfSignal(const QByteArray& signature) +{ + QByteArray sig = QMetaObject::normalizedSignature(signature); + for (int index = 0; index < d->methods.size(); ++index) { + if (sig == d->methods[index].signature && + d->methods[index].methodType() == QMetaMethod::Signal) + return index; + } + return -1; +} + +/*! + Finds a slot with the specified \a signature and returns its index; + otherwise returns -1. The \a signature will be normalized by this method. + + \sa indexOfMethod(), indexOfSignal() +*/ +int QMetaObjectBuilder::indexOfSlot(const QByteArray& signature) +{ + QByteArray sig = QMetaObject::normalizedSignature(signature); + for (int index = 0; index < d->methods.size(); ++index) { + if (sig == d->methods[index].signature && + d->methods[index].methodType() == QMetaMethod::Slot) + return index; + } + return -1; +} + +/*! + Finds a constructor with the specified \a signature and returns its index; + otherwise returns -1. The \a signature will be normalized by this method. + + \sa constructor(), constructorCount(), addConstructor(), removeConstructor() +*/ +int QMetaObjectBuilder::indexOfConstructor(const QByteArray& signature) +{ + QByteArray sig = QMetaObject::normalizedSignature(signature); + for (int index = 0; index < d->constructors.size(); ++index) { + if (sig == d->constructors[index].signature) + return index; + } + return -1; +} + +/*! + Finds a property with the specified \a name and returns its index; + otherwise returns -1. + + \sa property(), propertyCount(), addProperty(), removeProperty() +*/ +int QMetaObjectBuilder::indexOfProperty(const QByteArray& name) +{ + for (int index = 0; index < d->properties.size(); ++index) { + if (name == d->properties[index].name) + return index; + } + return -1; +} + +/*! + Finds an enumerator with the specified \a name and returns its index; + otherwise returns -1. + + \sa enumertor(), enumeratorCount(), addEnumerator(), removeEnumerator() +*/ +int QMetaObjectBuilder::indexOfEnumerator(const QByteArray& name) +{ + for (int index = 0; index < d->enumerators.size(); ++index) { + if (name == d->enumerators[index].name) + return index; + } + return -1; +} + +/*! + Finds an item of class information with the specified \a name and + returns its index; otherwise returns -1. + + \sa classInfoName(), classInfoValue(), classInfoCount(), addClassInfo() + \sa removeClassInfo() +*/ +int QMetaObjectBuilder::indexOfClassInfo(const QByteArray& name) +{ + for (int index = 0; index < d->classInfoNames.size(); ++index) { + if (name == d->classInfoNames[index]) + return index; + } + return -1; +} + +// Align on a specific type boundary. +#define ALIGN(size,type) \ + (size) = ((size) + sizeof(type) - 1) & ~(sizeof(type) - 1) + +// Build a string into a QMetaObject representation. Returns the +// position in the string table where the string was placed. +static int buildString + (char *buf, char *str, int *offset, const QByteArray& value, int empty) +{ + if (value.size() == 0 && empty >= 0) + return empty; + if (buf) { + memcpy(str + *offset, value.constData(), value.size()); + str[*offset + value.size()] = '\0'; + } + int posn = *offset; + *offset += value.size() + 1; + return posn; +} + +// Build the parameter array string for a method. +static QByteArray buildParameterNames + (const QByteArray& signature, const QList& parameterNames) +{ + // If the parameter name list is specified, then concatenate them. + if (!parameterNames.isEmpty()) { + QByteArray names; + bool first = true; + foreach (const QByteArray &name, parameterNames) { + if (first) + first = false; + else + names += (char)','; + names += name; + } + return names; + } + + // Count commas in the signature, excluding those inside template arguments. + int index = signature.indexOf('('); + if (index < 0) + return QByteArray(); + ++index; + if (index >= signature.size()) + return QByteArray(); + if (signature[index] == ')') + return QByteArray(); + int count = 1; + int brackets = 0; + while (index < signature.size() && signature[index] != ',') { + char ch = signature[index++]; + if (ch == '<') + ++brackets; + else if (ch == '>') + --brackets; + else if (ch == ',' && brackets <= 0) + ++count; + } + return QByteArray(count - 1, ','); +} + +// Build a QMetaObject in "buf" based on the information in "d". +// If "buf" is null, then return the number of bytes needed to +// build the QMetaObject. Returns -1 if the metaobject if +// relocatable is set, but the metaobject contains extradata. +static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, + bool relocatable) +{ + int size = 0; + int dataIndex; + int enumIndex; + int index; + bool hasNotifySignals = false; + + if (relocatable && + (d->relatedMetaObjects.size() > 0 || d->staticMetacallFunction)) + return -1; + + // Create the main QMetaObject structure at the start of the buffer. + QMetaObject *meta = reinterpret_cast(buf); + size += sizeof(QMetaObject); + ALIGN(size, int); + if (buf) { + if (!relocatable) meta->d.superdata = d->superClass; + meta->d.extradata = 0; + } + + // Populate the QMetaObjectPrivate structure. + QMetaObjectPrivate *pmeta + = reinterpret_cast(buf + size); + int pmetaSize = size; + dataIndex = 13; // Number of fields in the QMetaObjectPrivate. + for (index = 0; index < d->properties.size(); ++index) { + if (d->properties[index].notifySignal != -1) { + hasNotifySignals = true; + break; + } + } + if (buf) { + pmeta->revision = 3; + pmeta->flags = d->flags; + pmeta->className = 0; // Class name is always the first string. + + pmeta->classInfoCount = d->classInfoNames.size(); + pmeta->classInfoData = dataIndex; + dataIndex += 2 * d->classInfoNames.size(); + + pmeta->methodCount = d->methods.size(); + pmeta->methodData = dataIndex; + dataIndex += 5 * d->methods.size(); + + pmeta->propertyCount = d->properties.size(); + pmeta->propertyData = dataIndex; + dataIndex += 3 * d->properties.size(); + if (hasNotifySignals) + dataIndex += d->properties.size(); + + pmeta->enumeratorCount = d->enumerators.size(); + pmeta->enumeratorData = dataIndex; + dataIndex += 4 * d->enumerators.size(); + + pmeta->constructorCount = d->constructors.size(); + pmeta->constructorData = dataIndex; + dataIndex += 5 * d->constructors.size(); + } else { + dataIndex += 2 * d->classInfoNames.size(); + dataIndex += 5 * d->methods.size(); + dataIndex += 3 * d->properties.size(); + if (hasNotifySignals) + dataIndex += d->properties.size(); + dataIndex += 4 * d->enumerators.size(); + dataIndex += 5 * d->constructors.size(); + } + + // Allocate space for the enumerator key names and values. + enumIndex = dataIndex; + for (index = 0; index < d->enumerators.size(); ++index) { + QMetaEnumBuilderPrivate *enumerator = &(d->enumerators[index]); + dataIndex += 2 * enumerator->keys.size(); + } + + // Zero terminator at the end of the data offset table. + ++dataIndex; + + // Find the start of the data and string tables. + int *data = reinterpret_cast(pmeta); + size += dataIndex * sizeof(int); + char *str = reinterpret_cast(buf + size); + if (buf) { + if (relocatable) { + meta->d.stringdata = reinterpret_cast((quintptr)size); + meta->d.data = reinterpret_cast((quintptr)pmetaSize); + } else { + meta->d.stringdata = str; + meta->d.data = reinterpret_cast(data); + } + } + + // Reset the current data position to just past the QMetaObjectPrivate. + dataIndex = 13; + + // Add the class name to the string table. + int offset = 0; + buildString(buf, str, &offset, d->className, -1); + + // Add a common empty string, which is used to indicate "void" + // method returns, empty tag strings, etc. + int empty = buildString(buf, str, &offset, QByteArray(), -1); + + // Output the class infos, + for (index = 0; index < d->classInfoNames.size(); ++index) { + int name = buildString(buf, str, &offset, d->classInfoNames[index], empty); + int value = buildString(buf, str, &offset, d->classInfoValues[index], empty); + if (buf) { + data[dataIndex] = name; + data[dataIndex + 1] = value; + } + dataIndex += 2; + } + + // Output the methods in the class. + for (index = 0; index < d->methods.size(); ++index) { + QMetaMethodBuilderPrivate *method = &(d->methods[index]); + int sig = buildString(buf, str, &offset, method->signature, empty); + int params; + QByteArray names = buildParameterNames + (method->signature, method->parameterNames); + params = buildString(buf, str, &offset, names, empty); + int ret = buildString(buf, str, &offset, method->returnType, empty); + int tag = buildString(buf, str, &offset, method->tag, empty); + int attrs = method->attributes; + if (buf) { + data[dataIndex] = sig; + data[dataIndex + 1] = params; + data[dataIndex + 2] = ret; + data[dataIndex + 3] = tag; + data[dataIndex + 4] = attrs; + } + dataIndex += 5; + } + + // Output the properties in the class. + for (index = 0; index < d->properties.size(); ++index) { + QMetaPropertyBuilderPrivate *prop = &(d->properties[index]); + int name = buildString(buf, str, &offset, prop->name, empty); + int type = buildString(buf, str, &offset, prop->type, empty); + int flags = prop->flags; + + if (!isVariantType(prop->type)) { + flags |= EnumOrFlag; + } else { + flags |= qvariant_nameToType(prop->type) << 24; + } + + if (buf) { + data[dataIndex] = name; + data[dataIndex + 1] = type; + data[dataIndex + 2] = flags; + } + dataIndex += 3; + } + if (hasNotifySignals) { + for (index = 0; index < d->properties.size(); ++index) { + QMetaPropertyBuilderPrivate *prop = &(d->properties[index]); + if (buf) { + if (prop->notifySignal != -1) + data[dataIndex] = prop->notifySignal; + else + data[dataIndex] = 0; + } + ++dataIndex; + } + } + + // Output the enumerators in the class. + for (index = 0; index < d->enumerators.size(); ++index) { + QMetaEnumBuilderPrivate *enumerator = &(d->enumerators[index]); + int name = buildString(buf, str, &offset, enumerator->name, empty); + int isFlag = (int)(enumerator->isFlag); + int count = enumerator->keys.size(); + int enumOffset = enumIndex; + if (buf) { + data[dataIndex] = name; + data[dataIndex + 1] = isFlag; + data[dataIndex + 2] = count; + data[dataIndex + 3] = enumOffset; + } + for (int key = 0; key < count; ++key) { + int keyIndex = buildString(buf, str, &offset, enumerator->keys[key], empty); + if (buf) { + data[enumOffset++] = keyIndex; + data[enumOffset++] = enumerator->values[key]; + } + } + dataIndex += 4; + enumIndex += 2 * count; + } + + // Output the constructors in the class. + for (index = 0; index < d->constructors.size(); ++index) { + QMetaMethodBuilderPrivate *method = &(d->constructors[index]); + int sig = buildString(buf, str, &offset, method->signature, empty); + int params; + QByteArray names = buildParameterNames + (method->signature, method->parameterNames); + params = buildString(buf, str, &offset, names, empty); + int ret = buildString(buf, str, &offset, method->returnType, empty); + int tag = buildString(buf, str, &offset, method->tag, empty); + int attrs = method->attributes; + if (buf) { + data[dataIndex] = sig; + data[dataIndex + 1] = params; + data[dataIndex + 2] = ret; + data[dataIndex + 3] = tag; + data[dataIndex + 4] = attrs; + } + dataIndex += 5; + } + + // One more empty string to act as a terminator. + buildString(buf, str, &offset, QByteArray(), -1); + size += offset; + + // Output the zero terminator in the data array. + if (buf) + data[enumIndex] = 0; + + // Create the extradata block if we need one. + if (d->relatedMetaObjects.size() > 0 || d->staticMetacallFunction) { + ALIGN(size, QMetaObject **); + ALIGN(size, QMetaObjectBuilder::StaticMetacallFunction); + QMetaObjectExtraData *extra = + reinterpret_cast(buf + size); + size += sizeof(QMetaObjectExtraData); + ALIGN(size, QMetaObject *); +#ifdef Q_NO_DATA_RELOCATION + QMetaObjectAccessor *objects = + reinterpret_cast(buf + size); +#else + const QMetaObject **objects = + reinterpret_cast(buf + size); +#endif + if (buf) { + if (d->relatedMetaObjects.size() > 0) { + extra->objects = objects; + for (index = 0; index < d->relatedMetaObjects.size(); ++index) + objects[index] = d->relatedMetaObjects[index]; + objects[index] = 0; + } else { + extra->objects = 0; + } + extra->static_metacall = d->staticMetacallFunction; + meta->d.extradata = reinterpret_cast(extra); + } + if (d->relatedMetaObjects.size() > 0) + size += sizeof(QMetaObject *) * (d->relatedMetaObjects.size() + 1); + } + + // Align the final size and return it. + ALIGN(size, void *); + return size; +} + +/*! + Converts this meta object builder into a concrete QMetaObject. + The return value should be deallocated using qFree() once it + is no longer needed. + + The returned meta object is a snapshot of the state of the + QMetaObjectBuilder. Any further modifications to the QMetaObjectBuilder + will not be reflected in previous meta objects returned by + this method. +*/ +QMetaObject *QMetaObjectBuilder::toMetaObject() const +{ + int size = buildMetaObject(d, 0, false); + char *buf = reinterpret_cast(qMalloc(size)); + memset(buf, 0, size); + buildMetaObject(d, buf, false); + return reinterpret_cast(buf); +} + +/* + \internal + + Converts this meta object builder into relocatable data. This data can + be stored, copied and later passed to fromRelocatableData() to create a + concrete QMetaObject. + + The data is specific to the architecture on which it was created, but is not + specific to the process that created it. Not all meta object builder's can + be converted to data in this way. If \a ok is provided, it will be set to + true if the conversion succeeds, and false otherwise. If a + staticMetacallFunction() or any relatedMetaObject()'s are specified the + conversion to relocatable data will fail. +*/ +QByteArray QMetaObjectBuilder::toRelocatableData(bool *ok) const +{ + int size = buildMetaObject(d, 0, true); + if (size == -1) { + if (ok) *ok = false; + return QByteArray(); + } + + QByteArray data; + data.resize(size); + char *buf = data.data(); + memset(buf, 0, size); + buildMetaObject(d, buf, true); + if (ok) *ok = true; + return data; +} + +/* + \internal + + Sets the \a data returned from toRelocatableData() onto a concrete + QMetaObject instance, \a output. As the meta object's super class is not + saved in the relocatable data, it must be passed as \a superClass. +*/ +void QMetaObjectBuilder::fromRelocatableData(QMetaObject *output, + const QMetaObject *superclass, + const QByteArray &data) +{ + if (!output) + return; + + const char *buf = data.constData(); + const QMetaObject *dataMo = reinterpret_cast(buf); + + quintptr stringdataOffset = (quintptr)dataMo->d.stringdata; + quintptr dataOffset = (quintptr)dataMo->d.data; + + output->d.superdata = superclass; + output->d.stringdata = buf + stringdataOffset; + output->d.data = reinterpret_cast(buf + dataOffset); +} + +/*! + \typedef QMetaObjectBuilder::StaticMetacallFunction + + Typedef for static metacall functions. The three parameters are + the call type value, the constructor index, and the + array of parameters. +*/ + +/*! + Returns the static metacall function to use to construct objects + of this class. The default value is null. + + \sa setStaticMetacallFunction() +*/ +QMetaObjectBuilder::StaticMetacallFunction QMetaObjectBuilder::staticMetacallFunction() const +{ + return d->staticMetacallFunction; +} + +/*! + Sets the static metacall function to use to construct objects + of this class to \a value. The default value is null. + + \sa staticMetacallFunction() +*/ +void QMetaObjectBuilder::setStaticMetacallFunction + (QMetaObjectBuilder::StaticMetacallFunction value) +{ + d->staticMetacallFunction = value; +} + +#ifndef QT_NO_DATASTREAM + +/*! + Serializes the contents of the meta object builder onto \a stream. + + \sa deserialize() +*/ +void QMetaObjectBuilder::serialize(QDataStream& stream) const +{ + int index; + + // Write the class and super class names. + stream << d->className; + if (d->superClass) + stream << QByteArray(d->superClass->className()); + else + stream << QByteArray(); + + // Write the counts for each type of class member. + stream << d->classInfoNames.size(); + stream << d->methods.size(); + stream << d->properties.size(); + stream << d->enumerators.size(); + stream << d->constructors.size(); + stream << d->relatedMetaObjects.size(); + + // Write the items of class information. + for (index = 0; index < d->classInfoNames.size(); ++index) { + stream << d->classInfoNames[index]; + stream << d->classInfoValues[index]; + } + + // Write the methods. + for (index = 0; index < d->methods.size(); ++index) { + const QMetaMethodBuilderPrivate *method = &(d->methods[index]); + stream << method->signature; + stream << method->returnType; + stream << method->parameterNames; + stream << method->tag; + stream << method->attributes; + } + + // Write the properties. + for (index = 0; index < d->properties.size(); ++index) { + const QMetaPropertyBuilderPrivate *property = &(d->properties[index]); + stream << property->name; + stream << property->type; + stream << property->flags; + stream << property->notifySignal; + } + + // Write the enumerators. + for (index = 0; index < d->enumerators.size(); ++index) { + const QMetaEnumBuilderPrivate *enumerator = &(d->enumerators[index]); + stream << enumerator->name; + stream << enumerator->isFlag; + stream << enumerator->keys; + stream << enumerator->values; + } + + // Write the constructors. + for (index = 0; index < d->constructors.size(); ++index) { + const QMetaMethodBuilderPrivate *method = &(d->constructors[index]); + stream << method->signature; + stream << method->returnType; + stream << method->parameterNames; + stream << method->tag; + stream << method->attributes; + } + + // Write the related meta objects. +#ifdef Q_NO_DATA_RELOCATION + //### What do we do here? +#else + for (index = 0; index < d->relatedMetaObjects.size(); ++index) { + const QMetaObject *meta = d->relatedMetaObjects[index]; + stream << QByteArray(meta->className()); + } +#endif + + // Add an extra empty QByteArray for additional data in future versions. + // This should help maintain backwards compatibility, allowing older + // versions to read newer data. + stream << QByteArray(); +} + +// Resolve a class name using the name reference map. +static const QMetaObject *resolveClassName + (const QMap& references, + const QByteArray& name) +{ + if (name == QByteArray("QObject")) + return &QObject::staticMetaObject; + else + return references.value(name, 0); +} + +/*! + Deserializes a meta object builder from \a stream into + this meta object builder. + + The \a references parameter specifies a mapping from class names + to QMetaObject instances for resolving the super class name and + related meta objects in the object that is deserialized. + The meta object for QObject is implicitly added to \a references + and does not need to be supplied. + + The QDataStream::status() value on \a stream will be set to + QDataStream::ReadCorruptData if the input data is corrupt. + The status will be set to QDataStream::ReadPastEnd if the + input was exhausted before the full meta object was read. + + \sa serialize() +*/ +void QMetaObjectBuilder::deserialize + (QDataStream& stream, + const QMap& references) +{ + QByteArray name; + const QMetaObject *cl; + int index; + + // Clear all members in the builder to their default states. + d->className.clear(); + d->superClass = &QObject::staticMetaObject; + d->classInfoNames.clear(); + d->classInfoValues.clear(); + d->methods.clear(); + d->properties.clear(); + d->enumerators.clear(); + d->constructors.clear(); + d->relatedMetaObjects.clear(); + d->staticMetacallFunction = 0; + + // Read the class and super class names. + stream >> d->className; + stream >> name; + if (name.isEmpty()) { + d->superClass = 0; + } else if ((cl = resolveClassName(references, name)) != 0) { + d->superClass = cl; + } else { + stream.setStatus(QDataStream::ReadCorruptData); + return; + } + + // Read the counts for each type of class member. + int classInfoCount, methodCount, propertyCount; + int enumeratorCount, constructorCount, relatedMetaObjectCount; + stream >> classInfoCount; + stream >> methodCount; + stream >> propertyCount; + stream >> enumeratorCount; + stream >> constructorCount; + stream >> relatedMetaObjectCount; + if (classInfoCount < 0 || methodCount < 0 || + propertyCount < 0 || enumeratorCount < 0 || + constructorCount < 0 || relatedMetaObjectCount < 0) { + stream.setStatus(QDataStream::ReadCorruptData); + return; + } + + // Read the items of class information. + for (index = 0; index < classInfoCount; ++index) { + if (stream.status() != QDataStream::Ok) + return; + QByteArray value; + stream >> name; + stream >> value; + addClassInfo(name, value); + } + + // Read the member methods. + for (index = 0; index < methodCount; ++index) { + if (stream.status() != QDataStream::Ok) + return; + stream >> name; + addMethod(name); + QMetaMethodBuilderPrivate *method = &(d->methods[index]); + stream >> method->returnType; + stream >> method->parameterNames; + stream >> method->tag; + stream >> method->attributes; + if (method->methodType() == QMetaMethod::Constructor) { + // Cannot add a constructor in this set of methods. + stream.setStatus(QDataStream::ReadCorruptData); + return; + } + } + + // Read the properties. + for (index = 0; index < propertyCount; ++index) { + if (stream.status() != QDataStream::Ok) + return; + QByteArray type; + stream >> name; + stream >> type; + addProperty(name, type); + QMetaPropertyBuilderPrivate *property = &(d->properties[index]); + stream >> property->flags; + stream >> property->notifySignal; + if (property->notifySignal < -1 || + property->notifySignal >= d->methods.size()) { + // Notify signal method index is out of range. + stream.setStatus(QDataStream::ReadCorruptData); + return; + } + if (property->notifySignal >= 0 && + d->methods[property->notifySignal].methodType() != QMetaMethod::Signal) { + // Notify signal method index does not refer to a signal. + stream.setStatus(QDataStream::ReadCorruptData); + return; + } + } + + // Read the enumerators. + for (index = 0; index < enumeratorCount; ++index) { + if (stream.status() != QDataStream::Ok) + return; + stream >> name; + addEnumerator(name); + QMetaEnumBuilderPrivate *enumerator = &(d->enumerators[index]); + stream >> enumerator->isFlag; + stream >> enumerator->keys; + stream >> enumerator->values; + if (enumerator->keys.size() != enumerator->values.size()) { + // Mismatch between number of keys and number of values. + stream.setStatus(QDataStream::ReadCorruptData); + return; + } + } + + // Read the constructor methods. + for (index = 0; index < constructorCount; ++index) { + if (stream.status() != QDataStream::Ok) + return; + stream >> name; + addConstructor(name); + QMetaMethodBuilderPrivate *method = &(d->constructors[index]); + stream >> method->returnType; + stream >> method->parameterNames; + stream >> method->tag; + stream >> method->attributes; + if (method->methodType() != QMetaMethod::Constructor) { + // The type must be Constructor. + stream.setStatus(QDataStream::ReadCorruptData); + return; + } + } + + // Read the related meta objects. +#ifdef Q_NO_DATA_RELOCATION + //### What do we do here +#else + for (index = 0; index < relatedMetaObjectCount; ++index) { + if (stream.status() != QDataStream::Ok) + return; + stream >> name; + cl = resolveClassName(references, name); + if (!cl) { + stream.setStatus(QDataStream::ReadCorruptData); + return; + } + addRelatedMetaObject(cl); + } +#endif + + // Read the extra data block, which is reserved for future use. + stream >> name; +} + +#endif // !QT_NO_DATASTREAM + +/*! + \class QMetaMethodBuilder + \internal + \brief The QMetaMethodBuilder class enables modifications to a method definition on a meta object builder. +*/ + +QMetaMethodBuilderPrivate *QMetaMethodBuilder::d_func() const +{ + // Positive indices indicate methods, negative indices indicate constructors. + if (_mobj && _index >= 0 && _index < _mobj->d->methods.size()) + return &(_mobj->d->methods[_index]); + else if (_mobj && -_index >= 1 && -_index <= _mobj->d->constructors.size()) + return &(_mobj->d->constructors[(-_index) - 1]); + else + return 0; +} + +/*! + \fn QMetaMethodBuilder::QMetaMethodBuilder() + \internal +*/ + +/*! + Returns the index of this method within its QMetaObjectBuilder. +*/ +int QMetaMethodBuilder::index() const +{ + if (_index >= 0) + return _index; // Method, signal, or slot + else + return (-_index) - 1; // Constructor +} + +/*! + Returns the type of this method (signal, slot, method, or constructor). +*/ +QMetaMethod::MethodType QMetaMethodBuilder::methodType() const +{ + QMetaMethodBuilderPrivate *d = d_func(); + if (d) + return d->methodType(); + else + return QMetaMethod::Method; +} + +/*! + Returns the signature of this method. + + \sa parameterNames(), returnType() +*/ +QByteArray QMetaMethodBuilder::signature() const +{ + QMetaMethodBuilderPrivate *d = d_func(); + if (d) + return d->signature; + else + return QByteArray(); +} + +/*! + Returns the return type for this method; empty if the method's + return type is \c{void}. + + \sa setReturnType(), signature() +*/ +QByteArray QMetaMethodBuilder::returnType() const +{ + QMetaMethodBuilderPrivate *d = d_func(); + if (d) + return d->returnType; + else + return QByteArray(); +} + +/*! + Sets the return type for this method to \a value. If \a value + is empty, then the method's return type is \c{void}. The \a value + will be normalized before it is added to the method. + + \sa returnType(), signature() +*/ +void QMetaMethodBuilder::setReturnType(const QByteArray& value) +{ + QMetaMethodBuilderPrivate *d = d_func(); + if (d) + d->returnType = QMetaObject::normalizedType(value); +} + +/*! + Returns the list of parameter names for this method. + + \sa setParameterNames() +*/ +QList QMetaMethodBuilder::parameterNames() const +{ + QMetaMethodBuilderPrivate *d = d_func(); + if (d) + return d->parameterNames; + else + return QList(); +} + +/*! + Sets the list of parameter names for this method to \a value. + + \sa parameterNames() +*/ +void QMetaMethodBuilder::setParameterNames(const QList& value) +{ + QMetaMethodBuilderPrivate *d = d_func(); + if (d) + d->parameterNames = value; +} + +/*! + Returns the tag associated with this method. + + \sa setTag() +*/ +QByteArray QMetaMethodBuilder::tag() const +{ + QMetaMethodBuilderPrivate *d = d_func(); + if (d) + return d->tag; + else + return QByteArray(); +} + +/*! + Sets the tag associated with this method to \a value. + + \sa setTag() +*/ +void QMetaMethodBuilder::setTag(const QByteArray& value) +{ + QMetaMethodBuilderPrivate *d = d_func(); + if (d) + d->tag = value; +} + +/*! + Returns the access specification of this method (private, protected, + or public). The default value is QMetaMethod::Public for methods, + slots, and constructors. The default value is QMetaMethod::Protected + for signals. + + \sa setAccess() +*/ +QMetaMethod::Access QMetaMethodBuilder::access() const +{ + QMetaMethodBuilderPrivate *d = d_func(); + if (d) + return d->access(); + else + return QMetaMethod::Public; +} + +/*! + Sets the access specification of this method (private, protected, + or public) to \a value. If the method is a signal, this function + will be ignored. + + \sa access() +*/ +void QMetaMethodBuilder::setAccess(QMetaMethod::Access value) +{ + QMetaMethodBuilderPrivate *d = d_func(); + if (d && d->methodType() != QMetaMethod::Signal) + d->setAccess(value); +} + +/*! + Returns the additional attributes for this method. + + \sa setAttributes() +*/ +int QMetaMethodBuilder::attributes() const +{ + QMetaMethodBuilderPrivate *d = d_func(); + if (d) + return (d->attributes >> 4); + else + return 0; +} + +/*! + Sets the additional attributes for this method to \a value. + + \sa attributes() +*/ +void QMetaMethodBuilder::setAttributes(int value) +{ + QMetaMethodBuilderPrivate *d = d_func(); + if (d) + d->attributes = ((d->attributes & 0x0f) | (value << 4)); +} + +/*! + \class QMetaPropertyBuilder + \internal + \brief The QMetaPropertyBuilder class enables modifications to a property definition on a meta object builder. +*/ + +QMetaPropertyBuilderPrivate *QMetaPropertyBuilder::d_func() const +{ + if (_mobj && _index >= 0 && _index < _mobj->d->properties.size()) + return &(_mobj->d->properties[_index]); + else + return 0; +} + +/*! + \fn QMetaPropertyBuilder::QMetaPropertyBuilder() + \internal +*/ + +/*! + \fn int QMetaPropertyBuilder::index() const + + Returns the index of this property within its QMetaObjectBuilder. +*/ + +/*! + Returns the name associated with this property. + + \sa type() +*/ +QByteArray QMetaPropertyBuilder::name() const +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + return d->name; + else + return QByteArray(); +} + +/*! + Returns the type associated with this property. + + \sa name() +*/ +QByteArray QMetaPropertyBuilder::type() const +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + return d->type; + else + return QByteArray(); +} + +/*! + Returns true if this property has a notify signal; false otherwise. + + \sa notifySignal(), setNotifySignal(), removeNotifySignal() +*/ +bool QMetaPropertyBuilder::hasNotifySignal() const +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + return d->flag(Notify); + else + return false; +} + +/*! + Returns the notify signal associated with this property. + + \sa hasNotifySignal(), setNotifySignal(), removeNotifySignal() +*/ +QMetaMethodBuilder QMetaPropertyBuilder::notifySignal() const +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d && d->notifySignal >= 0) + return QMetaMethodBuilder(_mobj, d->notifySignal); + else + return QMetaMethodBuilder(); +} + +/*! + Sets the notify signal associated with this property to \a value. + + \sa hasNotifySignal(), notifySignal(), removeNotifySignal() +*/ +void QMetaPropertyBuilder::setNotifySignal(const QMetaMethodBuilder& value) +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) { + if (value._mobj) { + d->notifySignal = value._index; + d->setFlag(Notify, true); + } else { + d->notifySignal = -1; + d->setFlag(Notify, false); + } + } +} + +/*! + Removes the notify signal from this property. + + \sa hasNotifySignal(), notifySignal(), setNotifySignal() +*/ +void QMetaPropertyBuilder::removeNotifySignal() +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) { + d->notifySignal = -1; + d->setFlag(Notify, false); + } +} + +/*! + Returns true if this property is readable; otherwise returns false. + The default value is true. + + \sa setReadable(), isWritable() +*/ +bool QMetaPropertyBuilder::isReadable() const +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + return d->flag(Readable); + else + return false; +} + +/*! + Returns true if this property is writable; otherwise returns false. + The default value is true. + + \sa setWritable(), isReadable() +*/ +bool QMetaPropertyBuilder::isWritable() const +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + return d->flag(Writable); + else + return false; +} + +/*! + Returns true if this property can be reset to a default value; otherwise + returns false. The default value is false. + + \sa setResettable() +*/ +bool QMetaPropertyBuilder::isResettable() const +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + return d->flag(Resettable); + else + return false; +} + +/*! + Returns true if this property is designable; otherwise returns false. + This default value is false. + + \sa setDesignable(), isScriptable(), isStored() +*/ +bool QMetaPropertyBuilder::isDesignable() const +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + return d->flag(Designable); + else + return false; +} + +/*! + Returns true if the property is scriptable; otherwise returns false. + This default value is true. + + \sa setScriptable(), isDesignable(), isStored() +*/ +bool QMetaPropertyBuilder::isScriptable() const +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + return d->flag(Scriptable); + else + return false; +} + +/*! + Returns true if the property is stored; otherwise returns false. + This default value is false. + + \sa setStored(), isDesignable(), isScriptable() +*/ +bool QMetaPropertyBuilder::isStored() const +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + return d->flag(Stored); + else + return false; +} + +/*! + Returns true if the property is editable; otherwise returns false. + This default value is false. + + \sa setEditable(), isDesignable(), isScriptable(), isStored() +*/ +bool QMetaPropertyBuilder::isEditable() const +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + return d->flag(Editable); + else + return false; +} + +/*! + Returns true if this property is designated as the \c USER + property, i.e., the one that the user can edit or that is + significant in some other way. Otherwise it returns + false. This default value is false. + + \sa setUser(), isDesignable(), isScriptable() +*/ +bool QMetaPropertyBuilder::isUser() const +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + return d->flag(User); + else + return false; +} + +/*! + Returns true if the property has a C++ setter function that + follows Qt's standard "name" / "setName" pattern. Designer and uic + query hasStdCppSet() in order to avoid expensive + QObject::setProperty() calls. All properties in Qt [should] follow + this pattern. The default value is false. + + \sa setStdCppSet() +*/ +bool QMetaPropertyBuilder::hasStdCppSet() const +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + return d->flag(StdCppSet); + else + return false; +} + +/*! + Returns true if the property is an enumerator or flag type; + otherwise returns false. This default value is false. + + \sa setEnumOrFlag() +*/ +bool QMetaPropertyBuilder::isEnumOrFlag() const +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + return d->flag(EnumOrFlag); + else + return false; +} + +/*! + Returns true if the property is constant; otherwise returns false. + The default value is false. +*/ +bool QMetaPropertyBuilder::isConstant() const +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + return d->flag(Constant); + else + return false; +} + +/*! + Returns true if the property is final; otherwise returns false. + The default value is false. +*/ +bool QMetaPropertyBuilder::isFinal() const +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + return d->flag(Final); + else + return false; +} + +/*! + Sets this property to readable if \a value is true. + + \sa isReadable(), setWritable() +*/ +void QMetaPropertyBuilder::setReadable(bool value) +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + d->setFlag(Readable, value); +} + +/*! + Sets this property to writable if \a value is true. + + \sa isWritable(), setReadable() +*/ +void QMetaPropertyBuilder::setWritable(bool value) +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + d->setFlag(Writable, value); +} + +/*! + Sets this property to resettable if \a value is true. + + \sa isResettable() +*/ +void QMetaPropertyBuilder::setResettable(bool value) +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + d->setFlag(Resettable, value); +} + +/*! + Sets this property to designable if \a value is true. + + \sa isDesignable(), setScriptable(), setStored() +*/ +void QMetaPropertyBuilder::setDesignable(bool value) +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + d->setFlag(Designable, value); +} + +/*! + Sets this property to scriptable if \a value is true. + + \sa isScriptable(), setDesignable(), setStored() +*/ +void QMetaPropertyBuilder::setScriptable(bool value) +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + d->setFlag(Scriptable, value); +} + +/*! + Sets this property to storable if \a value is true. + + \sa isStored(), setDesignable(), setScriptable() +*/ +void QMetaPropertyBuilder::setStored(bool value) +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + d->setFlag(Stored, value); +} + +/*! + Sets this property to editable if \a value is true. + + \sa isEditable(), setDesignable(), setScriptable(), setStored() +*/ +void QMetaPropertyBuilder::setEditable(bool value) +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + d->setFlag(Editable, value); +} + +/*! + Sets the \c USER flag on this property to \a value. + + \sa isUser(), setDesignable(), setScriptable() +*/ +void QMetaPropertyBuilder::setUser(bool value) +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + d->setFlag(User, value); +} + +/*! + Sets the C++ setter flag on this property to \a value, which is + true if the property has a C++ setter function that follows Qt's + standard "name" / "setName" pattern. + + \sa hasStdCppSet() +*/ +void QMetaPropertyBuilder::setStdCppSet(bool value) +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + d->setFlag(StdCppSet, value); +} + +/*! + Sets this property to be of an enumerator or flag type if + \a value is true. + + \sa isEnumOrFlag() +*/ +void QMetaPropertyBuilder::setEnumOrFlag(bool value) +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + d->setFlag(EnumOrFlag, value); +} + +/*! + Sets the \c CONSTANT flag on this property to \a value. + + \sa isConstant() +*/ +void QMetaPropertyBuilder::setConstant(bool value) +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + d->setFlag(Constant, value); +} + +/*! + Sets the \c FINAL flag on this property to \a value. + + \sa isFinal() +*/ +void QMetaPropertyBuilder::setFinal(bool value) +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + d->setFlag(Final, value); +} + + +/*! + \class QMetaEnumBuilder + \internal + \brief The QMetaEnumBuilder class enables modifications to an enumerator definition on a meta object builder. +*/ + +QMetaEnumBuilderPrivate *QMetaEnumBuilder::d_func() const +{ + if (_mobj && _index >= 0 && _index < _mobj->d->enumerators.size()) + return &(_mobj->d->enumerators[_index]); + else + return 0; +} + +/*! + \fn QMetaEnumBuilder::QMetaEnumBuilder() + \internal +*/ + +/*! + \fn int QMetaEnumBuilder::index() const + + Returns the index of this enumerator within its QMetaObjectBuilder. +*/ + +/*! + Returns the name of the enumerator (without the scope). +*/ +QByteArray QMetaEnumBuilder::name() const +{ + QMetaEnumBuilderPrivate *d = d_func(); + if (d) + return d->name; + else + return QByteArray(); +} + +/*! + Returns true if this enumerator is used as a flag; otherwise returns + false. + + \sa setIsFlag() +*/ +bool QMetaEnumBuilder::isFlag() const +{ + QMetaEnumBuilderPrivate *d = d_func(); + if (d) + return d->isFlag; + else + return false; +} + +/*! + Sets this enumerator to be used as a flag if \a value is true. + + \sa isFlag() +*/ +void QMetaEnumBuilder::setIsFlag(bool value) +{ + QMetaEnumBuilderPrivate *d = d_func(); + if (d) + d->isFlag = value; +} + +/*! + Returns the number of keys. + + \sa key(), addKey() +*/ +int QMetaEnumBuilder::keyCount() const +{ + QMetaEnumBuilderPrivate *d = d_func(); + if (d) + return d->keys.size(); + else + return 0; +} + +/*! + Returns the key with the given \a index, or an empty QByteArray + if no such key exists. + + \sa keyCount(), addKey(), value() +*/ +QByteArray QMetaEnumBuilder::key(int index) const +{ + QMetaEnumBuilderPrivate *d = d_func(); + if (d && index >= 0 && index < d->keys.size()) + return d->keys[index]; + else + return QByteArray(); +} + +/*! + Returns the value with the given \a index; or returns -1 if there + is no such value. + + \sa keyCount(), addKey(), key() +*/ +int QMetaEnumBuilder::value(int index) const +{ + QMetaEnumBuilderPrivate *d = d_func(); + if (d && index >= 0 && index < d->keys.size()) + return d->values[index]; + else + return -1; +} + +/*! + Adds a new key called \a name to this enumerator, associated + with \a value. Returns the index of the new key. + + \sa keyCount(), key(), value(), removeKey() +*/ +int QMetaEnumBuilder::addKey(const QByteArray& name, int value) +{ + QMetaEnumBuilderPrivate *d = d_func(); + if (d) { + int index = d->keys.size(); + d->keys += name; + d->values += value; + return index; + } else { + return -1; + } +} + +/*! + Removes the key at \a index from this enumerator. + + \sa addKey() +*/ +void QMetaEnumBuilder::removeKey(int index) +{ + QMetaEnumBuilderPrivate *d = d_func(); + if (d && index >= 0 && index < d->keys.size()) { + d->keys.removeAt(index); + d->values.removeAt(index); + } +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qmetaobjectbuilder_p.h b/src/declarative/qml/qmetaobjectbuilder_p.h new file mode 100644 index 00000000..612d00fa --- /dev/null +++ b/src/declarative/qml/qmetaobjectbuilder_p.h @@ -0,0 +1,325 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMETAOBJECTBUILDER_H +#define QMETAOBJECTBUILDER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of moc. This header file may change from version to version without notice, +// or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QMetaObjectBuilderPrivate; +class QMetaMethodBuilder; +class QMetaMethodBuilderPrivate; +class QMetaPropertyBuilder; +class QMetaPropertyBuilderPrivate; +class QMetaEnumBuilder; +class QMetaEnumBuilderPrivate; + +class Q_DECLARATIVE_PRIVATE_EXPORT QMetaObjectBuilder +{ +public: + enum AddMember + { + ClassName = 0x00000001, + SuperClass = 0x00000002, + Methods = 0x00000004, + Signals = 0x00000008, + Slots = 0x00000010, + Constructors = 0x00000020, + Properties = 0x00000040, + Enumerators = 0x00000080, + ClassInfos = 0x00000100, + RelatedMetaObjects = 0x00000200, + StaticMetacall = 0x00000400, + PublicMethods = 0x00000800, + ProtectedMethods = 0x00001000, + PrivateMethods = 0x00002000, + AllMembers = 0x7FFFFFFF, + AllPrimaryMembers = 0x7FFFFBFC + }; + Q_DECLARE_FLAGS(AddMembers, AddMember) + + enum MetaObjectFlag { + DynamicMetaObject = 0x01 + }; + Q_DECLARE_FLAGS(MetaObjectFlags, MetaObjectFlag) + + QMetaObjectBuilder(); + explicit QMetaObjectBuilder(const QMetaObject *prototype, QMetaObjectBuilder::AddMembers members = AllMembers); + virtual ~QMetaObjectBuilder(); + + QByteArray className() const; + void setClassName(const QByteArray& name); + + const QMetaObject *superClass() const; + void setSuperClass(const QMetaObject *meta); + + MetaObjectFlags flags() const; + void setFlags(MetaObjectFlags); + + int methodCount() const; + int constructorCount() const; + int propertyCount() const; + int enumeratorCount() const; + int classInfoCount() const; + int relatedMetaObjectCount() const; + + QMetaMethodBuilder addMethod(const QByteArray& signature); + QMetaMethodBuilder addMethod(const QByteArray& signature, const QByteArray& returnType); + QMetaMethodBuilder addMethod(const QMetaMethod& prototype); + + QMetaMethodBuilder addSlot(const QByteArray& signature); + QMetaMethodBuilder addSignal(const QByteArray& signature); + + QMetaMethodBuilder addConstructor(const QByteArray& signature); + QMetaMethodBuilder addConstructor(const QMetaMethod& prototype); + + QMetaPropertyBuilder addProperty(const QByteArray& name, const QByteArray& type, int notifierId=-1); + QMetaPropertyBuilder addProperty(const QMetaProperty& prototype); + + QMetaEnumBuilder addEnumerator(const QByteArray& name); + QMetaEnumBuilder addEnumerator(const QMetaEnum& prototype); + + int addClassInfo(const QByteArray& name, const QByteArray& value); + +#ifdef Q_NO_DATA_RELOCATION + int addRelatedMetaObject(const QMetaObjectAccessor &meta); +#else + int addRelatedMetaObject(const QMetaObject *meta); +#endif + + void addMetaObject(const QMetaObject *prototype, QMetaObjectBuilder::AddMembers members = AllMembers); + + QMetaMethodBuilder method(int index) const; + QMetaMethodBuilder constructor(int index) const; + QMetaPropertyBuilder property(int index) const; + QMetaEnumBuilder enumerator(int index) const; + const QMetaObject *relatedMetaObject(int index) const; + + QByteArray classInfoName(int index) const; + QByteArray classInfoValue(int index) const; + + void removeMethod(int index); + void removeConstructor(int index); + void removeProperty(int index); + void removeEnumerator(int index); + void removeClassInfo(int index); + void removeRelatedMetaObject(int index); + + int indexOfMethod(const QByteArray& signature); + int indexOfSignal(const QByteArray& signature); + int indexOfSlot(const QByteArray& signature); + int indexOfConstructor(const QByteArray& signature); + int indexOfProperty(const QByteArray& name); + int indexOfEnumerator(const QByteArray& name); + int indexOfClassInfo(const QByteArray& name); + + typedef QMetaObjectExtraData::StaticMetacallFunction StaticMetacallFunction; + + QMetaObjectBuilder::StaticMetacallFunction staticMetacallFunction() const; + void setStaticMetacallFunction(QMetaObjectBuilder::StaticMetacallFunction value); + + QMetaObject *toMetaObject() const; + QByteArray toRelocatableData(bool * = 0) const; + static void fromRelocatableData(QMetaObject *, const QMetaObject *, const QByteArray &); + +#ifndef QT_NO_DATASTREAM + void serialize(QDataStream& stream) const; + void deserialize + (QDataStream& stream, + const QMap& references); +#endif + +private: + Q_DISABLE_COPY(QMetaObjectBuilder) + + QMetaObjectBuilderPrivate *d; + + friend class QMetaMethodBuilder; + friend class QMetaPropertyBuilder; + friend class QMetaEnumBuilder; +}; + +class Q_DECLARATIVE_PRIVATE_EXPORT QMetaMethodBuilder +{ +public: + QMetaMethodBuilder() : _mobj(0), _index(0) {} + + int index() const; + + QMetaMethod::MethodType methodType() const; + QByteArray signature() const; + + QByteArray returnType() const; + void setReturnType(const QByteArray& value); + + QList parameterNames() const; + void setParameterNames(const QList& value); + + QByteArray tag() const; + void setTag(const QByteArray& value); + + QMetaMethod::Access access() const; + void setAccess(QMetaMethod::Access value); + + int attributes() const; + void setAttributes(int value); + +private: + const QMetaObjectBuilder *_mobj; + int _index; + + friend class QMetaObjectBuilder; + friend class QMetaPropertyBuilder; + + QMetaMethodBuilder(const QMetaObjectBuilder *mobj, int index) + : _mobj(mobj), _index(index) {} + + QMetaMethodBuilderPrivate *d_func() const; +}; + +class Q_DECLARATIVE_PRIVATE_EXPORT QMetaPropertyBuilder +{ +public: + QMetaPropertyBuilder() : _mobj(0), _index(0) {} + + int index() const { return _index; } + + QByteArray name() const; + QByteArray type() const; + + bool hasNotifySignal() const; + QMetaMethodBuilder notifySignal() const; + void setNotifySignal(const QMetaMethodBuilder& value); + void removeNotifySignal(); + + bool isReadable() const; + bool isWritable() const; + bool isResettable() const; + bool isDesignable() const; + bool isScriptable() const; + bool isStored() const; + bool isEditable() const; + bool isUser() const; + bool hasStdCppSet() const; + bool isEnumOrFlag() const; + bool isConstant() const; + bool isFinal() const; + + void setReadable(bool value); + void setWritable(bool value); + void setResettable(bool value); + void setDesignable(bool value); + void setScriptable(bool value); + void setStored(bool value); + void setEditable(bool value); + void setUser(bool value); + void setStdCppSet(bool value); + void setEnumOrFlag(bool value); + void setConstant(bool value); + void setFinal(bool value); + +private: + const QMetaObjectBuilder *_mobj; + int _index; + + friend class QMetaObjectBuilder; + + QMetaPropertyBuilder(const QMetaObjectBuilder *mobj, int index) + : _mobj(mobj), _index(index) {} + + QMetaPropertyBuilderPrivate *d_func() const; +}; + +class Q_DECLARATIVE_PRIVATE_EXPORT QMetaEnumBuilder +{ +public: + QMetaEnumBuilder() : _mobj(0), _index(0) {} + + int index() const { return _index; } + + QByteArray name() const; + + bool isFlag() const; + void setIsFlag(bool value); + + int keyCount() const; + QByteArray key(int index) const; + int value(int index) const; + + int addKey(const QByteArray& name, int value); + void removeKey(int index); + +private: + const QMetaObjectBuilder *_mobj; + int _index; + + friend class QMetaObjectBuilder; + + QMetaEnumBuilder(const QMetaObjectBuilder *mobj, int index) + : _mobj(mobj), _index(index) {} + + QMetaEnumBuilderPrivate *d_func() const; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QMetaObjectBuilder::AddMembers) +Q_DECLARE_OPERATORS_FOR_FLAGS(QMetaObjectBuilder::MetaObjectFlags) + +QT_END_NAMESPACE + +#endif diff --git a/src/declarative/qml/qml.pri b/src/declarative/qml/qml.pri new file mode 100644 index 00000000..5d171bfe --- /dev/null +++ b/src/declarative/qml/qml.pri @@ -0,0 +1,139 @@ +INCLUDEPATH += $$PWD +SOURCES += \ + $$PWD/qdeclarativeparser.cpp \ + $$PWD/qdeclarativeinstruction.cpp \ + $$PWD/qdeclarativevmemetaobject.cpp \ + $$PWD/qdeclarativeengine.cpp \ + $$PWD/qdeclarativeexpression.cpp \ + $$PWD/qdeclarativebinding.cpp \ + $$PWD/qdeclarativeproperty.cpp \ + $$PWD/qdeclarativecomponent.cpp \ + $$PWD/qdeclarativecontext.cpp \ + $$PWD/qdeclarativeinclude.cpp \ + $$PWD/qdeclarativecustomparser.cpp \ + $$PWD/qdeclarativepropertyvaluesource.cpp \ + $$PWD/qdeclarativepropertyvalueinterceptor.cpp \ + $$PWD/qdeclarativeproxymetaobject.cpp \ + $$PWD/qdeclarativevme.cpp \ + $$PWD/qdeclarativecompiler.cpp \ + $$PWD/qdeclarativecompileddata.cpp \ + $$PWD/qdeclarativeboundsignal.cpp \ + $$PWD/qdeclarativedom.cpp \ + $$PWD/qdeclarativerefcount.cpp \ + $$PWD/qdeclarativemetatype.cpp \ + $$PWD/qdeclarativestringconverters.cpp \ + $$PWD/qdeclarativeparserstatus.cpp \ + $$PWD/qdeclarativetypeloader.cpp \ + $$PWD/qdeclarativeinfo.cpp \ + $$PWD/qdeclarativeerror.cpp \ + $$PWD/qdeclarativescriptparser.cpp \ + $$PWD/qdeclarativerewrite.cpp \ + $$PWD/qdeclarativevaluetype.cpp \ + $$PWD/qdeclarativecompiledbindings.cpp \ + $$PWD/qdeclarativefastproperties.cpp \ + $$PWD/qdeclarativexmlhttprequest.cpp \ + $$PWD/qdeclarativesqldatabase.cpp \ + $$PWD/qmetaobjectbuilder.cpp \ + $$PWD/qdeclarativewatcher.cpp \ + $$PWD/qdeclarativecleanup.cpp \ + $$PWD/qdeclarativepropertycache.cpp \ + $$PWD/qdeclarativenotifier.cpp \ + $$PWD/qdeclarativeintegercache.cpp \ + $$PWD/qdeclarativetypenotavailable.cpp \ + $$PWD/qdeclarativetypenamecache.cpp \ + $$PWD/qdeclarativescriptstring.cpp \ + $$PWD/qdeclarativeobjectscriptclass.cpp \ + $$PWD/qdeclarativecontextscriptclass.cpp \ + $$PWD/qdeclarativeglobalscriptclass.cpp \ + $$PWD/qdeclarativevaluetypescriptclass.cpp \ + $$PWD/qdeclarativetypenamescriptclass.cpp \ + $$PWD/qdeclarativelistscriptclass.cpp \ + $$PWD/qdeclarativeworkerscript.cpp \ + $$PWD/qdeclarativeimageprovider.cpp \ + $$PWD/qdeclarativenetworkaccessmanagerfactory.cpp \ + $$PWD/qdeclarativedirparser.cpp \ + $$PWD/qdeclarativeextensionplugin.cpp \ + $$PWD/qdeclarativeimport.cpp \ + $$PWD/qdeclarativelist.cpp \ + $$PWD/qperformancetimer.cpp + +HEADERS += \ + $$PWD/qdeclarativeparser_p.h \ + $$PWD/qdeclarativeglobal_p.h \ + $$PWD/qdeclarativeinstruction_p.h \ + $$PWD/qdeclarativevmemetaobject_p.h \ + $$PWD/qdeclarative.h \ + $$PWD/qdeclarativebinding_p.h \ + $$PWD/qdeclarativebinding_p_p.h \ + $$PWD/qdeclarativeproperty.h \ + $$PWD/qdeclarativecomponent.h \ + $$PWD/qdeclarativecomponent_p.h \ + $$PWD/qdeclarativecustomparser_p.h \ + $$PWD/qdeclarativecustomparser_p_p.h \ + $$PWD/qdeclarativepropertyvaluesource.h \ + $$PWD/qdeclarativepropertyvalueinterceptor.h \ + $$PWD/qdeclarativeboundsignal_p.h \ + $$PWD/qdeclarativeparserstatus.h \ + $$PWD/qdeclarativeproxymetaobject_p.h \ + $$PWD/qdeclarativevme_p.h \ + $$PWD/qdeclarativecompiler_p.h \ + $$PWD/qdeclarativeengine_p.h \ + $$PWD/qdeclarativeexpression_p.h \ + $$PWD/qdeclarativeprivate.h \ + $$PWD/qdeclarativedom_p.h \ + $$PWD/qdeclarativedom_p_p.h \ + $$PWD/qdeclarativerefcount_p.h \ + $$PWD/qdeclarativemetatype_p.h \ + $$PWD/qdeclarativeengine.h \ + $$PWD/qdeclarativecontext.h \ + $$PWD/qdeclarativeexpression.h \ + $$PWD/qdeclarativestringconverters_p.h \ + $$PWD/qdeclarativeinfo.h \ + $$PWD/qdeclarativeproperty_p.h \ + $$PWD/qdeclarativecontext_p.h \ + $$PWD/qdeclarativeinclude_p.h \ + $$PWD/qdeclarativetypeloader_p.h \ + $$PWD/qdeclarativelist.h \ + $$PWD/qdeclarativelist_p.h \ + $$PWD/qdeclarativedata_p.h \ + $$PWD/qdeclarativeerror.h \ + $$PWD/qdeclarativescriptparser_p.h \ + $$PWD/qdeclarativerewrite_p.h \ + $$PWD/qpodvector_p.h \ + $$PWD/qbitfield_p.h \ + $$PWD/qdeclarativevaluetype_p.h \ + $$PWD/qdeclarativecompiledbindings_p.h \ + $$PWD/qdeclarativefastproperties_p.h \ + $$PWD/qdeclarativexmlhttprequest_p.h \ + $$PWD/qdeclarativesqldatabase_p.h \ + $$PWD/qmetaobjectbuilder_p.h \ + $$PWD/qdeclarativewatcher_p.h \ + $$PWD/qdeclarativecleanup_p.h \ + $$PWD/qdeclarativepropertycache_p.h \ + $$PWD/qdeclarativenotifier_p.h \ + $$PWD/qdeclarativeintegercache_p.h \ + $$PWD/qdeclarativetypenotavailable_p.h \ + $$PWD/qdeclarativetypenamecache_p.h \ + $$PWD/qdeclarativescriptstring.h \ + $$PWD/qdeclarativeobjectscriptclass_p.h \ + $$PWD/qdeclarativecontextscriptclass_p.h \ + $$PWD/qdeclarativeglobalscriptclass_p.h \ + $$PWD/qdeclarativevaluetypescriptclass_p.h \ + $$PWD/qdeclarativetypenamescriptclass_p.h \ + $$PWD/qdeclarativelistscriptclass_p.h \ + $$PWD/qdeclarativeworkerscript_p.h \ + $$PWD/qdeclarativeguard_p.h \ + $$PWD/qdeclarativeimageprovider.h \ + $$PWD/qdeclarativenetworkaccessmanagerfactory.h \ + $$PWD/qdeclarativedirparser_p.h \ + $$PWD/qdeclarativeextensioninterface.h \ + $$PWD/qdeclarativeimport_p.h \ + $$PWD/qdeclarativeextensionplugin.h \ + $$PWD/qperformancetimer_p.h + +QT += sql +include(parser/parser.pri) +include(rewriter/rewriter.pri) + +# mirrors logic in corelib/kernel/kernel.pri +unix:!symbian: contains(QT_CONFIG, clock-gettime):include($$QT_SOURCE_TREE/config.tests/unix/clock-gettime/clock-gettime.pri) diff --git a/src/declarative/qml/qperformancetimer.cpp b/src/declarative/qml/qperformancetimer.cpp new file mode 100644 index 00000000..b67bee4b --- /dev/null +++ b/src/declarative/qml/qperformancetimer.cpp @@ -0,0 +1,227 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qperformancetimer_p.h" + +#if defined(Q_OS_MAC) +#include +#include +#include +#elif defined(Q_OS_SYMBIAN) +#include +#include +#include +#include +#elif defined(Q_OS_UNIX) +#include +#include +#include +#elif defined(Q_OS_WIN) +#include +#endif + +// mac/unix code heavily copied from QElapsedTimer + +QT_BEGIN_NAMESPACE + +////////////////////////////// Mac ////////////////////////////// +#if defined(Q_OS_MAC) + +static mach_timebase_info_data_t info = {0,0}; +static qint64 absoluteToNSecs(qint64 cpuTime) +{ + if (info.denom == 0) + mach_timebase_info(&info); + qint64 nsecs = cpuTime * info.numer / info.denom; + return nsecs; +} + +void QPerformanceTimer::start() +{ + t1 = mach_absolute_time(); +} + +qint64 QPerformanceTimer::elapsed() const +{ + uint64_t cpu_time = mach_absolute_time(); + return absoluteToNSecs(cpu_time - t1); +} + +////////////////////////////// Symbian ////////////////////////////// +#elif defined(Q_OS_SYMBIAN) + +static qint64 getTimeFromTick(quint64 elapsed) +{ + static TInt freq = 0; + if (!freq) + HAL::Get(HALData::EFastCounterFrequency, freq); + + return (elapsed * 1000000000) / freq; +} + +void QPerformanceTimer::start() +{ + t1 = User::FastCounter(); +} + +qint64 QPerformanceTimer::elapsed() const +{ + return getTimeFromTick(User::FastCounter() - t1); +} + + +////////////////////////////// Unix ////////////////////////////// +#elif defined(Q_OS_UNIX) + +#if defined(QT_NO_CLOCK_MONOTONIC) || defined(QT_BOOTSTRAPPED) +// turn off the monotonic clock +# ifdef _POSIX_MONOTONIC_CLOCK +# undef _POSIX_MONOTONIC_CLOCK +# endif +# define _POSIX_MONOTONIC_CLOCK -1 +#endif + +#if (_POSIX_MONOTONIC_CLOCK-0 != 0) +static const bool monotonicClockChecked = true; +static const bool monotonicClockAvailable = _POSIX_MONOTONIC_CLOCK > 0; +#else +static int monotonicClockChecked = false; +static int monotonicClockAvailable = false; +#endif + +#ifdef Q_CC_GNU +# define is_likely(x) __builtin_expect((x), 1) +#else +# define is_likely(x) (x) +#endif +#define load_acquire(x) ((volatile const int&)(x)) +#define store_release(x,v) ((volatile int&)(x) = (v)) + +static void unixCheckClockType() +{ +#if (_POSIX_MONOTONIC_CLOCK-0 == 0) + if (is_likely(load_acquire(monotonicClockChecked))) + return; + +# if defined(_SC_MONOTONIC_CLOCK) + // detect if the system support monotonic timers + long x = sysconf(_SC_MONOTONIC_CLOCK); + store_release(monotonicClockAvailable, x >= 200112L); +# endif + + store_release(monotonicClockChecked, true); +#endif +} + +static inline void do_gettime(qint64 *sec, qint64 *frac) +{ +#if (_POSIX_MONOTONIC_CLOCK-0 >= 0) + unixCheckClockType(); + if (is_likely(monotonicClockAvailable)) { + timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + *sec = ts.tv_sec; + *frac = ts.tv_nsec; + return; + } +#endif + *sec = 0; + *frac = 0; +} + +void QPerformanceTimer::start() +{ + do_gettime(&t1, &t2); +} + +qint64 QPerformanceTimer::elapsed() const +{ + qint64 sec, frac; + do_gettime(&sec, &frac); + sec = sec - t1; + frac = frac - t2; + + return sec * Q_INT64_C(1000000000) + frac; +} + +////////////////////////////// Windows ////////////////////////////// +#elif defined(Q_OS_WIN) + +static qint64 getTimeFromTick(quint64 elapsed) +{ + static LARGE_INTEGER freq; + if (!freq.QuadPart) + QueryPerformanceFrequency(&freq); + return 1000000000 * elapsed / freq.QuadPart; +} + +void QPerformanceTimer::start() +{ + LARGE_INTEGER li; + QueryPerformanceCounter(&li); + t1 = li.QuadPart; +} + +qint64 QPerformanceTimer::elapsed() const +{ + LARGE_INTEGER li; + QueryPerformanceCounter(&li); + return getTimeFromTick(li.QuadPart - t1); +} + +////////////////////////////// Default ////////////////////////////// +#else + +// default implementation (no hi-perf timer) does nothing +void QPerformanceTimer::start() +{ +} + +qint64 QPerformanceTimer::elapsed() const +{ + return 0; +} + +#endif + +QT_END_NAMESPACE + + diff --git a/src/declarative/qml/qperformancetimer_p.h b/src/declarative/qml/qperformancetimer_p.h new file mode 100644 index 00000000..b637cd70 --- /dev/null +++ b/src/declarative/qml/qperformancetimer_p.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPERFORMANCETIMER_P_H +#define QPERFORMANCETIMER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of moc. This header file may change from version to version without notice, +// or even be removed. +// +// We mean it. +// + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class Q_AUTOTEST_EXPORT QPerformanceTimer +{ +public: + void start(); + qint64 elapsed() const; + +private: + qint64 t1; + qint64 t2; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QPERFORMANCETIMER_P_H diff --git a/src/declarative/qml/qpodvector_p.h b/src/declarative/qml/qpodvector_p.h new file mode 100644 index 00000000..ee48e524 --- /dev/null +++ b/src/declarative/qml/qpodvector_p.h @@ -0,0 +1,173 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPODVECTOR_P_H +#define QPODVECTOR_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 +#include + +QT_BEGIN_NAMESPACE + +template +class QPODVector +{ +public: + QPODVector() + : m_count(0), m_capacity(0), m_data(0) {} + ~QPODVector() { if (m_data) ::free(m_data); } + + const T &at(int idx) const { + return m_data[idx]; + } + + T &operator[](int idx) { + return m_data[idx]; + } + + void clear() { + m_count = 0; + } + + void prepend(const T &v) { + insert(0, v); + } + + void append(const T &v) { + insert(m_count, v); + } + + void insert(int idx, const T &v) { + if (m_count == m_capacity) { + m_capacity += Increment; + m_data = (T *)q_check_ptr(realloc(m_data, m_capacity * sizeof(T))); + } + int moveCount = m_count - idx; + if (moveCount) + ::memmove(m_data + idx + 1, m_data + idx, moveCount * sizeof(T)); + m_count++; + m_data[idx] = v; + } + + void reserve(int count) { + if (count >= m_capacity) { + m_capacity = (count + (Increment-1)) & (0xFFFFFFFF - Increment + 1); + m_data = (T *)q_check_ptr(realloc(m_data, m_capacity * sizeof(T))); + } + } + + void insertBlank(int idx, int count) { + int newSize = m_count + count; + reserve(newSize); + int moveCount = m_count - idx; + if (moveCount) + ::memmove(m_data + idx + count, m_data + idx, + moveCount * sizeof(T)); + m_count = newSize; + } + + void remove(int idx, int count = 1) { + int moveCount = m_count - (idx + count); + if (moveCount) + ::memmove(m_data + idx, m_data + idx + count, + moveCount * sizeof(T)); + m_count -= count; + } + + void removeOne(const T &v) { + int idx = 0; + while (idx < m_count) { + if (m_data[idx] == v) { + remove(idx); + return; + } + ++idx; + } + } + + int find(const T &v) { + for (int idx = 0; idx < m_count; ++idx) + if (m_data[idx] == v) + return idx; + return -1; + } + + bool contains(const T &v) { + return find(v) != -1; + } + + int count() const { + return m_count; + } + + void copyAndClear(QPODVector &other) { + if (other.m_data) ::free(other.m_data); + other.m_count = m_count; + other.m_capacity = m_capacity; + other.m_data = m_data; + m_count = 0; + m_capacity = 0; + m_data = 0; + } + + QPODVector &operator<<(const T &v) { append(v); return *this; } +private: + QPODVector(const QPODVector &); + QPODVector &operator=(const QPODVector &); + int m_count; + int m_capacity; + T *m_data; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/declarative/qml/rewriter/rewriter.pri b/src/declarative/qml/rewriter/rewriter.pri new file mode 100644 index 00000000..a9fa1b57 --- /dev/null +++ b/src/declarative/qml/rewriter/rewriter.pri @@ -0,0 +1,4 @@ +INCLUDEPATH += $$PWD + +HEADERS += $$PWD/textwriter_p.h +SOURCES += $$PWD/textwriter.cpp diff --git a/src/declarative/qml/rewriter/textwriter.cpp b/src/declarative/qml/rewriter/textwriter.cpp new file mode 100644 index 00000000..6d07d3f9 --- /dev/null +++ b/src/declarative/qml/rewriter/textwriter.cpp @@ -0,0 +1,217 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/textwriter_p.h" + +QT_QML_BEGIN_NAMESPACE + +using namespace QDeclarativeJS; + +TextWriter::TextWriter() + :string(0), cursor(0) +{ +} + +static bool overlaps(int posA, int lengthA, int posB, int lengthB) { + return (posA < posB + lengthB && posA + lengthA > posB + lengthB) + || (posA < posB && posA + lengthA > posB); +} + +bool TextWriter::hasOverlap(int pos, int length) +{ + { + QListIterator i(replaceList); + while (i.hasNext()) { + const Replace &cmd = i.next(); + if (overlaps(pos, length, cmd.pos, cmd.length)) + return true; + } + } + { + QListIterator i(moveList); + while (i.hasNext()) { + const Move &cmd = i.next(); + if (overlaps(pos, length, cmd.pos, cmd.length)) + return true; + } + return false; + } +} + +bool TextWriter::hasMoveInto(int pos, int length) +{ + QListIterator i(moveList); + while (i.hasNext()) { + const Move &cmd = i.next(); + if (cmd.to >= pos && cmd.to < pos + length) + return true; + } + return false; +} + +void TextWriter::replace(int pos, int length, const QString &replacement) +{ + Q_ASSERT(!hasOverlap(pos, length)); + Q_ASSERT(!hasMoveInto(pos, length)); + + Replace cmd; + cmd.pos = pos; + cmd.length = length; + cmd.replacement = replacement; + replaceList += cmd; +} + +void TextWriter::move(int pos, int length, int to) +{ + Q_ASSERT(!hasOverlap(pos, length)); + + Move cmd; + cmd.pos = pos; + cmd.length = length; + cmd.to = to; + moveList += cmd; +} + +void TextWriter::doReplace(const Replace &replace) +{ + int diff = replace.replacement.size() - replace.length; + { + QMutableListIterator i(replaceList); + while (i.hasNext()) { + Replace &c = i.next(); + if (replace.pos < c.pos) + c.pos += diff; + else if (replace.pos + replace.length < c.pos + c.length) + c.length += diff; + } + } + { + QMutableListIterator i(moveList); + while (i.hasNext()) { + Move &c = i.next(); + if (replace.pos < c.pos) + c.pos += diff; + else if (replace.pos + replace.length < c.pos + c.length) + c.length += diff; + + if (replace.pos < c.to) + c.to += diff; + } + } + + if (string) { + string->replace(replace.pos, replace.length, replace.replacement); + } else if (cursor) { + cursor->setPosition(replace.pos); + cursor->setPosition(replace.pos + replace.length, QTextCursor::KeepAnchor); + cursor->insertText(replace.replacement); + } +} + +void TextWriter::doMove(const Move &move) +{ + QString text; + if (string) { + text = string->mid(move.pos, move.length); + } else if (cursor) { + cursor->setPosition(move.pos); + cursor->setPosition(move.pos + move.length, QTextCursor::KeepAnchor); + text = cursor->selectedText(); + } + + Replace cut; + cut.pos = move.pos; + cut.length = move.length; + Replace paste; + paste.pos = move.to; + paste.length = 0; + paste.replacement = text; + + replaceList.append(cut); + replaceList.append(paste); + + Replace cmd; + while (!replaceList.isEmpty()) { + cmd = replaceList.first(); + replaceList.removeFirst(); + doReplace(cmd); + } +} + +void TextWriter::write(QString *s) +{ + string = s; + write_helper(); + string = 0; +} + +void TextWriter::write(QTextCursor *textCursor) +{ + cursor = textCursor; + write_helper(); + cursor = 0; +} + +void TextWriter::write_helper() +{ + if (cursor) + cursor->beginEditBlock(); + { + Replace cmd; + while (!replaceList.isEmpty()) { + cmd = replaceList.first(); + replaceList.removeFirst(); + doReplace(cmd); + } + } + { + Move cmd; + while (!moveList.isEmpty()) { + cmd = moveList.first(); + moveList.removeFirst(); + doMove(cmd); + } + } + if (cursor) + cursor->endEditBlock(); +} + +QT_QML_END_NAMESPACE diff --git a/src/declarative/qml/rewriter/textwriter_p.h b/src/declarative/qml/rewriter/textwriter_p.h new file mode 100644 index 00000000..472539d5 --- /dev/null +++ b/src/declarative/qml/rewriter/textwriter_p.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TEXTWRITER_H +#define TEXTWRITER_H + +#include + +#include +#include +#include + +QT_BEGIN_HEADER +QT_QML_BEGIN_NAMESPACE + +namespace QDeclarativeJS { + +class TextWriter +{ + QString *string; + QTextCursor *cursor; + + struct Replace { + int pos; + int length; + QString replacement; + }; + + QList replaceList; + + struct Move { + int pos; + int length; + int to; + }; + + QList moveList; + + bool hasOverlap(int pos, int length); + bool hasMoveInto(int pos, int length); + + void doReplace(const Replace &replace); + void doMove(const Move &move); + + void write_helper(); + +public: + TextWriter(); + + void replace(int pos, int length, const QString &replacement); + void move(int pos, int length, int to); + + void write(QString *s); + void write(QTextCursor *textCursor); + +}; + +} // end of namespace QDeclarativeJS + +QT_QML_END_NAMESPACE +QT_END_HEADER + +#endif // TEXTWRITER_H diff --git a/src/declarative/util/qdeclarativeanimation.cpp b/src/declarative/util/qdeclarativeanimation.cpp new file mode 100644 index 00000000..496520e6 --- /dev/null +++ b/src/declarative/util/qdeclarativeanimation.cpp @@ -0,0 +1,2952 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativeanimation_p.h" +#include "private/qdeclarativeanimation_p_p.h" + +#include "private/qdeclarativebehavior_p.h" +#include "private/qdeclarativestateoperations_p.h" +#include "private/qdeclarativecontext_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmlclass Animation QDeclarativeAbstractAnimation + \ingroup qml-animation-transition + \since 4.7 + \brief The Animation element is the base of all QML animations. + + The Animation element cannot be used directly in a QML file. It exists + to provide a set of common properties and methods, available across all the + other animation types that inherit from it. Attempting to use the Animation + element directly will result in an error. +*/ + +QDeclarativeAbstractAnimation::QDeclarativeAbstractAnimation(QObject *parent) +: QObject(*(new QDeclarativeAbstractAnimationPrivate), parent) +{ +} + +QDeclarativeAbstractAnimation::~QDeclarativeAbstractAnimation() +{ +} + +QDeclarativeAbstractAnimation::QDeclarativeAbstractAnimation(QDeclarativeAbstractAnimationPrivate &dd, QObject *parent) +: QObject(dd, parent) +{ +} + +/*! + \qmlproperty bool Animation::running + This property holds whether the animation is currently running. + + The \c running property can be set to declaratively control whether or not + an animation is running. The following example will animate a rectangle + whenever the \l MouseArea is pressed. + + \code + Rectangle { + width: 100; height: 100 + NumberAnimation on x { + running: myMouse.pressed + from: 0; to: 100 + } + MouseArea { id: myMouse } + } + \endcode + + Likewise, the \c running property can be read to determine if the animation + is running. In the following example the text element will indicate whether + or not the animation is running. + + \code + NumberAnimation { id: myAnimation } + Text { text: myAnimation.running ? "Animation is running" : "Animation is not running" } + \endcode + + Animations can also be started and stopped imperatively from JavaScript + using the \c start() and \c stop() methods. + + By default, animations are not running. Though, when the animations are assigned to properties, + as property value sources using the \e on syntax, they are set to running by default. +*/ +bool QDeclarativeAbstractAnimation::isRunning() const +{ + Q_D(const QDeclarativeAbstractAnimation); + return d->running; +} + +// the behavior calls this function +void QDeclarativeAbstractAnimation::notifyRunningChanged(bool running) +{ + Q_D(QDeclarativeAbstractAnimation); + if (d->disableUserControl && d->running != running) { + d->running = running; + emit runningChanged(running); + } +} + +//commence is called to start an animation when it is used as a +//simple animation, and not as part of a transition +void QDeclarativeAbstractAnimationPrivate::commence() +{ + Q_Q(QDeclarativeAbstractAnimation); + + QDeclarativeStateActions actions; + QDeclarativeProperties properties; + q->transition(actions, properties, QDeclarativeAbstractAnimation::Forward); + + q->qtAnimation()->start(); + if (q->qtAnimation()->state() != QAbstractAnimation::Running) { + running = false; + emit q->completed(); + } +} + +QDeclarativeProperty QDeclarativeAbstractAnimationPrivate::createProperty(QObject *obj, const QString &str, QObject *infoObj) +{ + QDeclarativeProperty prop(obj, str, qmlContext(infoObj)); + if (!prop.isValid()) { + qmlInfo(infoObj) << QDeclarativeAbstractAnimation::tr("Cannot animate non-existent property \"%1\"").arg(str); + return QDeclarativeProperty(); + } else if (!prop.isWritable()) { + qmlInfo(infoObj) << QDeclarativeAbstractAnimation::tr("Cannot animate read-only property \"%1\"").arg(str); + return QDeclarativeProperty(); + } + return prop; +} + +void QDeclarativeAbstractAnimation::setRunning(bool r) +{ + Q_D(QDeclarativeAbstractAnimation); + if (!d->componentComplete) { + d->running = r; + if (r == false) + d->avoidPropertyValueSourceStart = true; + else if (!d->registered) { + d->registered = true; + QDeclarativeEnginePrivate *engPriv = QDeclarativeEnginePrivate::get(qmlEngine(this)); + engPriv->registerFinalizedParserStatusObject(this, this->metaObject()->indexOfSlot("componentFinalized()")); + } + return; + } + + if (d->running == r) + return; + + if (d->group || d->disableUserControl) { + qmlInfo(this) << "setRunning() cannot be used on non-root animation nodes."; + return; + } + + d->running = r; + if (d->running) { + bool supressStart = false; + if (d->alwaysRunToEnd && d->loopCount != 1 + && qtAnimation()->state() == QAbstractAnimation::Running) { + //we've restarted before the final loop finished; restore proper loop count + if (d->loopCount == -1) + qtAnimation()->setLoopCount(d->loopCount); + else + qtAnimation()->setLoopCount(qtAnimation()->currentLoop() + d->loopCount); + supressStart = true; //we want the animation to continue, rather than restart + } + + if (!d->connectedTimeLine) { + QObject::connect(qtAnimation(), SIGNAL(finished()), + this, SLOT(timelineComplete())); + d->connectedTimeLine = true; + } + if (!supressStart) + d->commence(); + emit started(); + } else { + if (d->alwaysRunToEnd) { + if (d->loopCount != 1) + qtAnimation()->setLoopCount(qtAnimation()->currentLoop()+1); //finish the current loop + } else + qtAnimation()->stop(); + + emit completed(); + } + + emit runningChanged(d->running); +} + +/*! + \qmlproperty bool Animation::paused + This property holds whether the animation is currently paused. + + The \c paused property can be set to declaratively control whether or not + an animation is paused. + + Animations can also be paused and resumed imperatively from JavaScript + using the \c pause() and \c resume() methods. + + By default, animations are not paused. +*/ +bool QDeclarativeAbstractAnimation::isPaused() const +{ + Q_D(const QDeclarativeAbstractAnimation); + return d->paused; +} + +void QDeclarativeAbstractAnimation::setPaused(bool p) +{ + Q_D(QDeclarativeAbstractAnimation); + if (d->paused == p) + return; + + if (d->group || d->disableUserControl) { + qmlInfo(this) << "setPaused() cannot be used on non-root animation nodes."; + return; + } + + d->paused = p; + if (d->paused) + qtAnimation()->pause(); + else + qtAnimation()->resume(); + + emit pausedChanged(d->paused); +} + +void QDeclarativeAbstractAnimation::classBegin() +{ + Q_D(QDeclarativeAbstractAnimation); + d->componentComplete = false; +} + +void QDeclarativeAbstractAnimation::componentComplete() +{ + Q_D(QDeclarativeAbstractAnimation); + d->componentComplete = true; +} + +void QDeclarativeAbstractAnimation::componentFinalized() +{ + Q_D(QDeclarativeAbstractAnimation); + if (d->running) { + d->running = false; + setRunning(true); + } +} + +/*! + \qmlproperty bool Animation::alwaysRunToEnd + This property holds whether the animation should run to completion when it is stopped. + + If this true the animation will complete its current iteration when it + is stopped - either by setting the \c running property to false, or by + calling the \c stop() method. The \c complete() method is not effected + by this value. + + This behavior is most useful when the \c repeat property is set, as the + animation will finish playing normally but not restart. + + By default, the alwaysRunToEnd property is not set. + + \note alwaysRunToEnd has no effect on animations in a Transition. +*/ +bool QDeclarativeAbstractAnimation::alwaysRunToEnd() const +{ + Q_D(const QDeclarativeAbstractAnimation); + return d->alwaysRunToEnd; +} + +void QDeclarativeAbstractAnimation::setAlwaysRunToEnd(bool f) +{ + Q_D(QDeclarativeAbstractAnimation); + if (d->alwaysRunToEnd == f) + return; + + d->alwaysRunToEnd = f; + emit alwaysRunToEndChanged(f); +} + +/*! + \qmlproperty int Animation::loops + This property holds the number of times the animation should play. + + By default, \c loops is 1: the animation will play through once and then stop. + + If set to Animation.Infinite, the animation will continuously repeat until it is explicitly + stopped - either by setting the \c running property to false, or by calling + the \c stop() method. + + In the following example, the rectangle will spin indefinitely. + + \code + Rectangle { + width: 100; height: 100; color: "green" + RotationAnimation on rotation { + loops: Animation.Infinite + from: 0 + to: 360 + } + } + \endcode +*/ +int QDeclarativeAbstractAnimation::loops() const +{ + Q_D(const QDeclarativeAbstractAnimation); + return d->loopCount; +} + +void QDeclarativeAbstractAnimation::setLoops(int loops) +{ + Q_D(QDeclarativeAbstractAnimation); + if (loops < 0) + loops = -1; + + if (loops == d->loopCount) + return; + + d->loopCount = loops; + qtAnimation()->setLoopCount(loops); + emit loopCountChanged(loops); +} + + +int QDeclarativeAbstractAnimation::currentTime() +{ + return qtAnimation()->currentLoopTime(); +} + +void QDeclarativeAbstractAnimation::setCurrentTime(int time) +{ + qtAnimation()->setCurrentTime(time); +} + +QDeclarativeAnimationGroup *QDeclarativeAbstractAnimation::group() const +{ + Q_D(const QDeclarativeAbstractAnimation); + return d->group; +} + +void QDeclarativeAbstractAnimation::setGroup(QDeclarativeAnimationGroup *g) +{ + Q_D(QDeclarativeAbstractAnimation); + if (d->group == g) + return; + if (d->group) + static_cast(d->group->d_func())->animations.removeAll(this); + + d->group = g; + + if (d->group && !static_cast(d->group->d_func())->animations.contains(this)) + static_cast(d->group->d_func())->animations.append(this); + + //if (g) //if removed from a group, then the group should no longer be the parent + setParent(g); +} + +/*! + \qmlmethod Animation::start() + \brief Starts the animation. + + If the animation is already running, calling this method has no effect. The + \c running property will be true following a call to \c start(). +*/ +void QDeclarativeAbstractAnimation::start() +{ + setRunning(true); +} + +/*! + \qmlmethod Animation::pause() + \brief Pauses the animation. + + If the animation is already paused, calling this method has no effect. The + \c paused property will be true following a call to \c pause(). +*/ +void QDeclarativeAbstractAnimation::pause() +{ + setPaused(true); +} + +/*! + \qmlmethod Animation::resume() + \brief Resumes a paused animation. + + If the animation is not paused, calling this method has no effect. The + \c paused property will be false following a call to \c resume(). +*/ +void QDeclarativeAbstractAnimation::resume() +{ + setPaused(false); +} + +/*! + \qmlmethod Animation::stop() + \brief Stops the animation. + + If the animation is not running, calling this method has no effect. The + \c running property will be false following a call to \c stop(). + + Normally \c stop() stops the animation immediately, and the animation has + no further influence on property values. In this example animation + \code + Rectangle { + NumberAnimation on x { from: 0; to: 100; duration: 500 } + } + \endcode + was stopped at time 250ms, the \c x property will have a value of 50. + + However, if the \c alwaysRunToEnd property is set, the animation will + continue running until it completes and then stop. The \c running property + will still become false immediately. +*/ +void QDeclarativeAbstractAnimation::stop() +{ + setRunning(false); +} + +/*! + \qmlmethod Animation::restart() + \brief Restarts the animation. + + This is a convenience method, and is equivalent to calling \c stop() and + then \c start(). +*/ +void QDeclarativeAbstractAnimation::restart() +{ + stop(); + start(); +} + +/*! + \qmlmethod Animation::complete() + \brief Stops the animation, jumping to the final property values. + + If the animation is not running, calling this method has no effect. The + \c running property will be false following a call to \c complete(). + + Unlike \c stop(), \c complete() immediately fast-forwards the animation to + its end. In the following example, + \code + Rectangle { + NumberAnimation on x { from: 0; to: 100; duration: 500 } + } + \endcode + calling \c stop() at time 250ms will result in the \c x property having + a value of 50, while calling \c complete() will set the \c x property to + 100, exactly as though the animation had played the whole way through. +*/ +void QDeclarativeAbstractAnimation::complete() +{ + if (isRunning()) { + qtAnimation()->setCurrentTime(qtAnimation()->duration()); + } +} + +void QDeclarativeAbstractAnimation::setTarget(const QDeclarativeProperty &p) +{ + Q_D(QDeclarativeAbstractAnimation); + d->defaultProperty = p; + + if (!d->avoidPropertyValueSourceStart) + setRunning(true); +} + +/* + we rely on setTarget only being called when used as a value source + so this function allows us to do the same thing as setTarget without + that assumption +*/ +void QDeclarativeAbstractAnimation::setDefaultTarget(const QDeclarativeProperty &p) +{ + Q_D(QDeclarativeAbstractAnimation); + d->defaultProperty = p; +} + +/* + don't allow start/stop/pause/resume to be manually invoked, + because something else (like a Behavior) already has control + over the animation. +*/ +void QDeclarativeAbstractAnimation::setDisableUserControl() +{ + Q_D(QDeclarativeAbstractAnimation); + d->disableUserControl = true; +} + +void QDeclarativeAbstractAnimation::transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) +{ + Q_UNUSED(actions); + Q_UNUSED(modified); + Q_UNUSED(direction); +} + +void QDeclarativeAbstractAnimation::timelineComplete() +{ + Q_D(QDeclarativeAbstractAnimation); + setRunning(false); + if (d->alwaysRunToEnd && d->loopCount != 1) { + //restore the proper loopCount for the next run + qtAnimation()->setLoopCount(d->loopCount); + } +} + +/*! + \qmlclass PauseAnimation QDeclarativePauseAnimation + \ingroup qml-animation-transition + \since 4.7 + \inherits Animation + \brief The PauseAnimation element provides a pause for an animation. + + When used in a SequentialAnimation, PauseAnimation is a step when + nothing happens, for a specified duration. + + A 500ms animation sequence, with a 100ms pause between two animations: + \code + SequentialAnimation { + NumberAnimation { ... duration: 200 } + PauseAnimation { duration: 100 } + NumberAnimation { ... duration: 200 } + } + \endcode + + \sa {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example} +*/ +QDeclarativePauseAnimation::QDeclarativePauseAnimation(QObject *parent) +: QDeclarativeAbstractAnimation(*(new QDeclarativePauseAnimationPrivate), parent) +{ + Q_D(QDeclarativePauseAnimation); + d->init(); +} + +QDeclarativePauseAnimation::~QDeclarativePauseAnimation() +{ +} + +void QDeclarativePauseAnimationPrivate::init() +{ + Q_Q(QDeclarativePauseAnimation); + pa = new QPauseAnimation; + QDeclarative_setParent_noEvent(pa, q); +} + +/*! + \qmlproperty int PauseAnimation::duration + This property holds the duration of the pause in milliseconds + + The default value is 250. +*/ +int QDeclarativePauseAnimation::duration() const +{ + Q_D(const QDeclarativePauseAnimation); + return d->pa->duration(); +} + +void QDeclarativePauseAnimation::setDuration(int duration) +{ + if (duration < 0) { + qmlInfo(this) << tr("Cannot set a duration of < 0"); + return; + } + + Q_D(QDeclarativePauseAnimation); + if (d->pa->duration() == duration) + return; + d->pa->setDuration(duration); + emit durationChanged(duration); +} + +QAbstractAnimation *QDeclarativePauseAnimation::qtAnimation() +{ + Q_D(QDeclarativePauseAnimation); + return d->pa; +} + +/*! + \qmlclass ColorAnimation QDeclarativeColorAnimation + \ingroup qml-animation-transition + \since 4.7 + \inherits PropertyAnimation + \brief The ColorAnimation element animates changes in color values. + + ColorAnimation is a specialized PropertyAnimation that defines an + animation to be applied when a color value changes. + + Here is a ColorAnimation applied to the \c color property of a \l Rectangle + as a property value source. It animates the \c color property's value from + its current value to a value of "red", over 1000 milliseconds: + + \snippet doc/src/snippets/declarative/coloranimation.qml 0 + + Like any other animation element, a ColorAnimation can be applied in a + number of ways, including transitions, behaviors and property value + sources. The \l {QML Animation and Transitions} documentation shows a + variety of methods for creating animations. + + For convenience, when a ColorAnimation is used in a \l Transition, it will + animate any \c color properties that have been modified during the state + change. If a \l{PropertyAnimation::}{property} or + \l{PropertyAnimation::}{properties} are explicitly set for the animation, + then those are used instead. + + \sa {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example} +*/ +QDeclarativeColorAnimation::QDeclarativeColorAnimation(QObject *parent) +: QDeclarativePropertyAnimation(parent) +{ + Q_D(QDeclarativePropertyAnimation); + d->interpolatorType = QMetaType::QColor; + d->interpolator = QVariantAnimationPrivate::getInterpolator(d->interpolatorType); + d->defaultToInterpolatorType = true; +} + +QDeclarativeColorAnimation::~QDeclarativeColorAnimation() +{ +} + +/*! + \qmlproperty color ColorAnimation::from + This property holds the color value at which the animation should begin. + + For example, the following animation is not applied until a color value + has reached "#c0c0c0": + + \qml + Item { + states: [ + // States are defined here... + ] + + transition: Transition { + NumberAnimation { from: "#c0c0c0"; duration: 2000 } + } + } + \endqml + + If the ColorAnimation is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the starting state of the + \l Transition, or the current value of the property at the moment the + \l Behavior is triggered. + + \sa {QML Animation and Transitions} +*/ +QColor QDeclarativeColorAnimation::from() const +{ + Q_D(const QDeclarativePropertyAnimation); + return d->from.value(); +} + +void QDeclarativeColorAnimation::setFrom(const QColor &f) +{ + QDeclarativePropertyAnimation::setFrom(f); +} + +/*! + \qmlproperty color ColorAnimation::to + + This property holds the color value at which the animation should end. + + If the ColorAnimation is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the end state of the + \l Transition, or the value of the property change that triggered the + \l Behavior. + + \sa {QML Animation and Transitions} +*/ +QColor QDeclarativeColorAnimation::to() const +{ + Q_D(const QDeclarativePropertyAnimation); + return d->to.value(); +} + +void QDeclarativeColorAnimation::setTo(const QColor &t) +{ + QDeclarativePropertyAnimation::setTo(t); +} + + + +/*! + \qmlclass ScriptAction QDeclarativeScriptAction + \ingroup qml-animation-transition + \since 4.7 + \inherits Animation + \brief The ScriptAction element allows scripts to be run during an animation. + + ScriptAction can be used to run a script at a specific point in an animation. + + \qml + SequentialAnimation { + NumberAnimation { + // ... + } + ScriptAction { script: doSomething(); } + NumberAnimation { + // ... + } + } + \endqml + + When used as part of a Transition, you can also target a specific + StateChangeScript to run using the \c scriptName property. + + \snippet doc/src/snippets/declarative/states/statechangescript.qml state and transition + + \sa StateChangeScript +*/ +QDeclarativeScriptAction::QDeclarativeScriptAction(QObject *parent) + :QDeclarativeAbstractAnimation(*(new QDeclarativeScriptActionPrivate), parent) +{ + Q_D(QDeclarativeScriptAction); + d->init(); +} + +QDeclarativeScriptAction::~QDeclarativeScriptAction() +{ +} + +void QDeclarativeScriptActionPrivate::init() +{ + Q_Q(QDeclarativeScriptAction); + rsa = new QActionAnimation(&proxy); + QDeclarative_setParent_noEvent(rsa, q); +} + +/*! + \qmlproperty script ScriptAction::script + This property holds the script to run. +*/ +QDeclarativeScriptString QDeclarativeScriptAction::script() const +{ + Q_D(const QDeclarativeScriptAction); + return d->script; +} + +void QDeclarativeScriptAction::setScript(const QDeclarativeScriptString &script) +{ + Q_D(QDeclarativeScriptAction); + d->script = script; +} + +/*! + \qmlproperty string ScriptAction::scriptName + This property holds the the name of the StateChangeScript to run. + + This property is only valid when ScriptAction is used as part of a transition. + If both script and scriptName are set, scriptName will be used. + + \note When using scriptName in a reversible transition, the script will only + be run when the transition is being run forwards. +*/ +QString QDeclarativeScriptAction::stateChangeScriptName() const +{ + Q_D(const QDeclarativeScriptAction); + return d->name; +} + +void QDeclarativeScriptAction::setStateChangeScriptName(const QString &name) +{ + Q_D(QDeclarativeScriptAction); + d->name = name; +} + +void QDeclarativeScriptActionPrivate::execute() +{ + Q_Q(QDeclarativeScriptAction); + if (hasRunScriptScript && reversing) + return; + + QDeclarativeScriptString scriptStr = hasRunScriptScript ? runScriptScript : script; + + const QString &str = scriptStr.script(); + if (!str.isEmpty()) { + QDeclarativeExpression expr(scriptStr.context(), scriptStr.scopeObject(), str); + QDeclarativeData *ddata = QDeclarativeData::get(q); + if (ddata && ddata->outerContext && !ddata->outerContext->url.isEmpty()) + expr.setSourceLocation(ddata->outerContext->url.toString(), ddata->lineNumber); + expr.evaluate(); + if (expr.hasError()) + qmlInfo(q) << expr.error(); + } +} + +void QDeclarativeScriptAction::transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) +{ + Q_D(QDeclarativeScriptAction); + Q_UNUSED(modified); + + d->hasRunScriptScript = false; + d->reversing = (direction == Backward); + for (int ii = 0; ii < actions.count(); ++ii) { + QDeclarativeAction &action = actions[ii]; + + if (action.event && action.event->typeName() == QLatin1String("StateChangeScript") + && static_cast(action.event)->name() == d->name) { + d->runScriptScript = static_cast(action.event)->script(); + d->hasRunScriptScript = true; + action.actionDone = true; + break; //only match one (names should be unique) + } + } +} + +QAbstractAnimation *QDeclarativeScriptAction::qtAnimation() +{ + Q_D(QDeclarativeScriptAction); + return d->rsa; +} + + + +/*! + \qmlclass PropertyAction QDeclarativePropertyAction + \ingroup qml-animation-transition + \since 4.7 + \inherits Animation + \brief The PropertyAction element allows immediate property changes during animation. + + PropertyAction is used to specify an immediate property change during an + animation. The property change is not animated. + + It is useful for setting non-animated property values during an animation. + + For example, here is a SequentialAnimation that sets the image's + \l {Image::}{smooth} property to \c true, animates the width of the image, + then sets \l {Image::}{smooth} back to \c false: + + \snippet doc/src/snippets/declarative/propertyaction.qml standalone + + PropertyAction is also useful for setting the exact point at which a property + change should occur during a \l Transition. For example, if PropertyChanges + was used in a \l State to rotate an item around a particular + \l {Item::}{transformOrigin}, it might be implemented like this: + + \snippet doc/src/snippets/declarative/propertyaction.qml transition + + However, with this code, the \c transformOrigin is not set until \e after + the animation, as a \l State is taken to define the values at the \e end of + a transition. The animation would rotate at the default \c transformOrigin, + then jump to \c Item.BottomRight. To fix this, insert a PropertyAction + before the RotationAnimation begins: + + \snippet doc/src/snippets/declarative/propertyaction-sequential.qml sequential + + This immediately sets the \c transformOrigin property to the value defined + in the end state of the \l Transition (i.e. the value defined in the + PropertyAction object) so that the rotation animation begins with the + correct transform origin. + + \sa {QML Animation and Transitions}, QtDeclarative +*/ +QDeclarativePropertyAction::QDeclarativePropertyAction(QObject *parent) +: QDeclarativeAbstractAnimation(*(new QDeclarativePropertyActionPrivate), parent) +{ + Q_D(QDeclarativePropertyAction); + d->init(); +} + +QDeclarativePropertyAction::~QDeclarativePropertyAction() +{ +} + +void QDeclarativePropertyActionPrivate::init() +{ + Q_Q(QDeclarativePropertyAction); + spa = new QActionAnimation; + QDeclarative_setParent_noEvent(spa, q); +} + +QObject *QDeclarativePropertyAction::target() const +{ + Q_D(const QDeclarativePropertyAction); + return d->target; +} + +void QDeclarativePropertyAction::setTarget(QObject *o) +{ + Q_D(QDeclarativePropertyAction); + if (d->target == o) + return; + d->target = o; + emit targetChanged(); +} + +QString QDeclarativePropertyAction::property() const +{ + Q_D(const QDeclarativePropertyAction); + return d->propertyName; +} + +void QDeclarativePropertyAction::setProperty(const QString &n) +{ + Q_D(QDeclarativePropertyAction); + if (d->propertyName == n) + return; + d->propertyName = n; + emit propertyChanged(); +} + +/*! + \qmlproperty Object PropertyAction::target + \qmlproperty list PropertyAction::targets + \qmlproperty string PropertyAction::property + \qmlproperty string PropertyAction::properties + + These properties determine the items and their properties that are + affected by this action. + + The details of how these properties are interpreted in different situations + is covered in the \l{PropertyAnimation::properties}{corresponding} PropertyAnimation + documentation. + + \sa exclude +*/ +QString QDeclarativePropertyAction::properties() const +{ + Q_D(const QDeclarativePropertyAction); + return d->properties; +} + +void QDeclarativePropertyAction::setProperties(const QString &p) +{ + Q_D(QDeclarativePropertyAction); + if (d->properties == p) + return; + d->properties = p; + emit propertiesChanged(p); +} + +QDeclarativeListProperty QDeclarativePropertyAction::targets() +{ + Q_D(QDeclarativePropertyAction); + return QDeclarativeListProperty(this, d->targets); +} + +/*! + \qmlproperty list PropertyAction::exclude + This property holds the objects that should not be affected by this action. + + \sa targets +*/ +QDeclarativeListProperty QDeclarativePropertyAction::exclude() +{ + Q_D(QDeclarativePropertyAction); + return QDeclarativeListProperty(this, d->exclude); +} + +/*! + \qmlproperty any PropertyAction::value + This property holds the value to be set on the property. + + If the PropertyAction is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the end state of the + \l Transition, or the value of the property change that triggered the + \l Behavior. +*/ +QVariant QDeclarativePropertyAction::value() const +{ + Q_D(const QDeclarativePropertyAction); + return d->value; +} + +void QDeclarativePropertyAction::setValue(const QVariant &v) +{ + Q_D(QDeclarativePropertyAction); + if (d->value.isNull || d->value != v) { + d->value = v; + emit valueChanged(v); + } +} + +QAbstractAnimation *QDeclarativePropertyAction::qtAnimation() +{ + Q_D(QDeclarativePropertyAction); + return d->spa; +} + +void QDeclarativePropertyAction::transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) +{ + Q_D(QDeclarativePropertyAction); + Q_UNUSED(direction); + + struct QDeclarativeSetPropertyAnimationAction : public QAbstractAnimationAction + { + QDeclarativeStateActions actions; + virtual void doAction() + { + for (int ii = 0; ii < actions.count(); ++ii) { + const QDeclarativeAction &action = actions.at(ii); + QDeclarativePropertyPrivate::write(action.property, action.toValue, QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding); + } + } + }; + + QStringList props = d->properties.isEmpty() ? QStringList() : d->properties.split(QLatin1Char(',')); + for (int ii = 0; ii < props.count(); ++ii) + props[ii] = props.at(ii).trimmed(); + if (!d->propertyName.isEmpty()) + props << d->propertyName; + + QList targets = d->targets; + if (d->target) + targets.append(d->target); + + bool hasSelectors = !props.isEmpty() || !targets.isEmpty() || !d->exclude.isEmpty(); + + if (d->defaultProperty.isValid() && !hasSelectors) { + props << d->defaultProperty.name(); + targets << d->defaultProperty.object(); + } + + QDeclarativeSetPropertyAnimationAction *data = new QDeclarativeSetPropertyAnimationAction; + + bool hasExplicit = false; + //an explicit animation has been specified + if (d->value.isValid()) { + for (int i = 0; i < props.count(); ++i) { + for (int j = 0; j < targets.count(); ++j) { + QDeclarativeAction myAction; + myAction.property = d->createProperty(targets.at(j), props.at(i), this); + if (myAction.property.isValid()) { + myAction.toValue = d->value; + QDeclarativePropertyAnimationPrivate::convertVariant(myAction.toValue, myAction.property.propertyType()); + data->actions << myAction; + hasExplicit = true; + for (int ii = 0; ii < actions.count(); ++ii) { + QDeclarativeAction &action = actions[ii]; + if (action.property.object() == myAction.property.object() && + myAction.property.name() == action.property.name()) { + modified << action.property; + break; //### any chance there could be multiples? + } + } + } + } + } + } + + if (!hasExplicit) + for (int ii = 0; ii < actions.count(); ++ii) { + QDeclarativeAction &action = actions[ii]; + + QObject *obj = action.property.object(); + QString propertyName = action.property.name(); + QObject *sObj = action.specifiedObject; + QString sPropertyName = action.specifiedProperty; + bool same = (obj == sObj); + + if ((targets.isEmpty() || targets.contains(obj) || (!same && targets.contains(sObj))) && + (!d->exclude.contains(obj)) && (same || (!d->exclude.contains(sObj))) && + (props.contains(propertyName) || (!same && props.contains(sPropertyName)))) { + QDeclarativeAction myAction = action; + + if (d->value.isValid()) + myAction.toValue = d->value; + QDeclarativePropertyAnimationPrivate::convertVariant(myAction.toValue, myAction.property.propertyType()); + + modified << action.property; + data->actions << myAction; + action.fromValue = myAction.toValue; + } + } + + if (data->actions.count()) { + d->spa->setAnimAction(data, QAbstractAnimation::DeleteWhenStopped); + } else { + delete data; + } +} + +/*! + \qmlclass NumberAnimation QDeclarativeNumberAnimation + \ingroup qml-animation-transition + \since 4.7 + \inherits PropertyAnimation + \brief The NumberAnimation element animates changes in qreal-type values. + + NumberAnimation is a specialized PropertyAnimation that defines an + animation to be applied when a numerical value changes. + + Here is a NumberAnimation applied to the \c x property of a \l Rectangle + as a property value source. It animates the \c x value from its current + value to a value of 50, over 1000 milliseconds: + + \snippet doc/src/snippets/declarative/numberanimation.qml 0 + + Like any other animation element, a NumberAnimation can be applied in a + number of ways, including transitions, behaviors and property value + sources. The \l {QML Animation and Transitions} documentation shows a + variety of methods for creating animations. + + Note that NumberAnimation may not animate smoothly if there are irregular + changes in the number value that it is tracking. If this is the case, use + SmoothedAnimation instead. + + \sa {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example} +*/ +QDeclarativeNumberAnimation::QDeclarativeNumberAnimation(QObject *parent) +: QDeclarativePropertyAnimation(parent) +{ + init(); +} + +QDeclarativeNumberAnimation::QDeclarativeNumberAnimation(QDeclarativePropertyAnimationPrivate &dd, QObject *parent) +: QDeclarativePropertyAnimation(dd, parent) +{ + init(); +} + +QDeclarativeNumberAnimation::~QDeclarativeNumberAnimation() +{ +} + +void QDeclarativeNumberAnimation::init() +{ + Q_D(QDeclarativePropertyAnimation); + d->interpolatorType = QMetaType::QReal; + d->interpolator = QVariantAnimationPrivate::getInterpolator(d->interpolatorType); +} + +/*! + \qmlproperty real NumberAnimation::from + This property holds the starting value for the animation. + + For example, the following animation is not applied until the \c x value + has reached 100: + + \qml + Item { + states: [ + // ... + ] + + transition: Transition { + NumberAnimation { properties: "x"; from: 100; duration: 200 } + } + } + \endqml + + If the NumberAnimation is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the starting state of the + \l Transition, or the current value of the property at the moment the + \l Behavior is triggered. + + \sa {QML Animation and Transitions} +*/ + +qreal QDeclarativeNumberAnimation::from() const +{ + Q_D(const QDeclarativePropertyAnimation); + return d->from.toReal(); +} + +void QDeclarativeNumberAnimation::setFrom(qreal f) +{ + QDeclarativePropertyAnimation::setFrom(f); +} + +/*! + \qmlproperty real NumberAnimation::to + This property holds the end value for the animation. + + If the NumberAnimation is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the end state of the + \l Transition, or the value of the property change that triggered the + \l Behavior. + + \sa {QML Animation and Transitions} +*/ +qreal QDeclarativeNumberAnimation::to() const +{ + Q_D(const QDeclarativePropertyAnimation); + return d->to.toReal(); +} + +void QDeclarativeNumberAnimation::setTo(qreal t) +{ + QDeclarativePropertyAnimation::setTo(t); +} + + + +/*! + \qmlclass Vector3dAnimation QDeclarativeVector3dAnimation + \ingroup qml-animation-transition + \since 4.7 + \inherits PropertyAnimation + \brief The Vector3dAnimation element animates changes in QVector3d values. + + Vector3dAnimation is a specialized PropertyAnimation that defines an + animation to be applied when a Vector3d value changes. + + Like any other animation element, a Vector3dAnimation can be applied in a + number of ways, including transitions, behaviors and property value + sources. The \l {QML Animation and Transitions} documentation shows a + variety of methods for creating animations. + + \sa {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example} +*/ +QDeclarativeVector3dAnimation::QDeclarativeVector3dAnimation(QObject *parent) +: QDeclarativePropertyAnimation(parent) +{ + Q_D(QDeclarativePropertyAnimation); + d->interpolatorType = QMetaType::QVector3D; + d->interpolator = QVariantAnimationPrivate::getInterpolator(d->interpolatorType); + d->defaultToInterpolatorType = true; +} + +QDeclarativeVector3dAnimation::~QDeclarativeVector3dAnimation() +{ +} + +/*! + \qmlproperty real Vector3dAnimation::from + This property holds the starting value for the animation. + + If the Vector3dAnimation is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the starting state of the + \l Transition, or the current value of the property at the moment the + \l Behavior is triggered. + + \sa {QML Animation and Transitions} +*/ +QVector3D QDeclarativeVector3dAnimation::from() const +{ + Q_D(const QDeclarativePropertyAnimation); + return d->from.value(); +} + +void QDeclarativeVector3dAnimation::setFrom(QVector3D f) +{ + QDeclarativePropertyAnimation::setFrom(f); +} + +/*! + \qmlproperty real Vector3dAnimation::to + This property holds the end value for the animation. + + If the Vector3dAnimation is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the end state of the + \l Transition, or the value of the property change that triggered the + \l Behavior. + + \sa {QML Animation and Transitions} +*/ +QVector3D QDeclarativeVector3dAnimation::to() const +{ + Q_D(const QDeclarativePropertyAnimation); + return d->to.value(); +} + +void QDeclarativeVector3dAnimation::setTo(QVector3D t) +{ + QDeclarativePropertyAnimation::setTo(t); +} + + + +/*! + \qmlclass RotationAnimation QDeclarativeRotationAnimation + \ingroup qml-animation-transition + \since 4.7 + \inherits PropertyAnimation + \brief The RotationAnimation element animates changes in rotation values. + + RotationAnimation is a specialized PropertyAnimation that gives control + over the direction of rotation during an animation. + + By default, it rotates in the direction + of the numerical change; a rotation from 0 to 240 will rotate 240 degrees + clockwise, while a rotation from 240 to 0 will rotate 240 degrees + counterclockwise. The \l direction property can be set to specify the + direction in which the rotation should occur. + + In the following example we use RotationAnimation to animate the rotation + between states via the shortest path: + + \snippet doc/src/snippets/declarative/rotationanimation.qml 0 + + Notice the RotationAnimation did not need to set a \l target + value. As a convenience, when used in a transition, RotationAnimation will rotate all + properties named "rotation" or "angle". You can override this by providing + your own properties via \l {PropertyAnimation::properties}{properties} or + \l {PropertyAnimation::property}{property}. + + Also, note the \l Rectangle will be rotated around its default + \l {Item::}{transformOrigin} (which is \c Item.Center). To use a different + transform origin, set the origin in the PropertyChanges object and apply + the change at the start of the animation using PropertyAction. See the + PropertyAction documentation for more details. + + Like any other animation element, a RotationAnimation can be applied in a + number of ways, including transitions, behaviors and property value + sources. The \l {QML Animation and Transitions} documentation shows a + variety of methods for creating animations. + + \sa {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example} +*/ +QVariant _q_interpolateShortestRotation(qreal &f, qreal &t, qreal progress) +{ + qreal newt = t; + qreal diff = t-f; + while(diff > 180.0){ + newt -= 360.0; + diff -= 360.0; + } + while(diff < -180.0){ + newt += 360.0; + diff += 360.0; + } + return QVariant(f + (newt - f) * progress); +} + +QVariant _q_interpolateClockwiseRotation(qreal &f, qreal &t, qreal progress) +{ + qreal newt = t; + qreal diff = t-f; + while(diff < 0.0){ + newt += 360.0; + diff += 360.0; + } + return QVariant(f + (newt - f) * progress); +} + +QVariant _q_interpolateCounterclockwiseRotation(qreal &f, qreal &t, qreal progress) +{ + qreal newt = t; + qreal diff = t-f; + while(diff > 0.0){ + newt -= 360.0; + diff -= 360.0; + } + return QVariant(f + (newt - f) * progress); +} + +QDeclarativeRotationAnimation::QDeclarativeRotationAnimation(QObject *parent) +: QDeclarativePropertyAnimation(*(new QDeclarativeRotationAnimationPrivate), parent) +{ + Q_D(QDeclarativeRotationAnimation); + d->interpolatorType = QMetaType::QReal; + d->interpolator = QVariantAnimationPrivate::getInterpolator(d->interpolatorType); + d->defaultProperties = QLatin1String("rotation,angle"); +} + +QDeclarativeRotationAnimation::~QDeclarativeRotationAnimation() +{ +} + +/*! + \qmlproperty real RotationAnimation::from + This property holds the starting value for the animation. + + For example, the following animation is not applied until the \c angle value + has reached 100: + + \qml + Item { + states: [ + // ... + ] + + transition: Transition { + RotationAnimation { properties: "angle"; from: 100; duration: 2000 } + } + } + \endqml + + If the RotationAnimation is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the starting state of the + \l Transition, or the current value of the property at the moment the + \l Behavior is triggered. + + \sa {QML Animation and Transitions} +*/ +qreal QDeclarativeRotationAnimation::from() const +{ + Q_D(const QDeclarativeRotationAnimation); + return d->from.toReal(); +} + +void QDeclarativeRotationAnimation::setFrom(qreal f) +{ + QDeclarativePropertyAnimation::setFrom(f); +} + +/*! + \qmlproperty real RotationAnimation::to + This property holds the end value for the animation.. + + If the RotationAnimation is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the end state of the + \l Transition, or the value of the property change that triggered the + \l Behavior. + + \sa {QML Animation and Transitions} +*/ +qreal QDeclarativeRotationAnimation::to() const +{ + Q_D(const QDeclarativeRotationAnimation); + return d->to.toReal(); +} + +void QDeclarativeRotationAnimation::setTo(qreal t) +{ + QDeclarativePropertyAnimation::setTo(t); +} + +/*! + \qmlproperty enumeration RotationAnimation::direction + This property holds the direction of the rotation. + + Possible values are: + + \list + \o RotationAnimation.Numerical (default) - Rotate by linearly interpolating between the two numbers. + A rotation from 10 to 350 will rotate 340 degrees clockwise. + \o RotationAnimation.Clockwise - Rotate clockwise between the two values + \o RotationAnimation.Counterclockwise - Rotate counterclockwise between the two values + \o RotationAnimation.Shortest - Rotate in the direction that produces the shortest animation path. + A rotation from 10 to 350 will rotate 20 degrees counterclockwise. + \endlist +*/ +QDeclarativeRotationAnimation::RotationDirection QDeclarativeRotationAnimation::direction() const +{ + Q_D(const QDeclarativeRotationAnimation); + return d->direction; +} + +void QDeclarativeRotationAnimation::setDirection(QDeclarativeRotationAnimation::RotationDirection direction) +{ + Q_D(QDeclarativeRotationAnimation); + if (d->direction == direction) + return; + + d->direction = direction; + switch(d->direction) { + case Clockwise: + d->interpolator = reinterpret_cast(&_q_interpolateClockwiseRotation); + break; + case Counterclockwise: + d->interpolator = reinterpret_cast(&_q_interpolateCounterclockwiseRotation); + break; + case Shortest: + d->interpolator = reinterpret_cast(&_q_interpolateShortestRotation); + break; + default: + d->interpolator = QVariantAnimationPrivate::getInterpolator(d->interpolatorType); + break; + } + + emit directionChanged(); +} + + + +QDeclarativeAnimationGroup::QDeclarativeAnimationGroup(QObject *parent) +: QDeclarativeAbstractAnimation(*(new QDeclarativeAnimationGroupPrivate), parent) +{ +} + +QDeclarativeAnimationGroup::QDeclarativeAnimationGroup(QDeclarativeAnimationGroupPrivate &dd, QObject *parent) + : QDeclarativeAbstractAnimation(dd, parent) +{ +} + +void QDeclarativeAnimationGroupPrivate::append_animation(QDeclarativeListProperty *list, QDeclarativeAbstractAnimation *a) +{ + QDeclarativeAnimationGroup *q = qobject_cast(list->object); + if (q) { + a->setGroup(q); + // This is an optimization for the parenting that already occurs via addAnimation + QDeclarative_setParent_noEvent(a->qtAnimation(), q->d_func()->ag); + q->d_func()->ag->addAnimation(a->qtAnimation()); + } +} + +void QDeclarativeAnimationGroupPrivate::clear_animation(QDeclarativeListProperty *list) +{ + QDeclarativeAnimationGroup *q = qobject_cast(list->object); + if (q) { + while (q->d_func()->animations.count()) { + QDeclarativeAbstractAnimation *firstAnim = q->d_func()->animations.at(0); + QDeclarative_setParent_noEvent(firstAnim->qtAnimation(), 0); + q->d_func()->ag->removeAnimation(firstAnim->qtAnimation()); + firstAnim->setGroup(0); + } + } +} + +QDeclarativeAnimationGroup::~QDeclarativeAnimationGroup() +{ +} + +QDeclarativeListProperty QDeclarativeAnimationGroup::animations() +{ + Q_D(QDeclarativeAnimationGroup); + QDeclarativeListProperty list(this, d->animations); + list.append = &QDeclarativeAnimationGroupPrivate::append_animation; + list.clear = &QDeclarativeAnimationGroupPrivate::clear_animation; + return list; +} + +/*! + \qmlclass SequentialAnimation QDeclarativeSequentialAnimation + \ingroup qml-animation-transition + \since 4.7 + \inherits Animation + \brief The SequentialAnimation element allows animations to be run sequentially. + + The SequentialAnimation and ParallelAnimation elements allow multiple + animations to be run together. Animations defined in a SequentialAnimation + are run one after the other, while animations defined in a ParallelAnimation + are run at the same time. + + The following example runs two number animations in a sequence. The \l Rectangle + animates to a \c x position of 50, then to a \c y position of 50. + + \snippet doc/src/snippets/declarative/sequentialanimation.qml 0 + + Animations defined within a \l Transition are automatically run in parallel, + so SequentialAnimation can be used to enclose the animations in a \l Transition + if this is the preferred behavior. + + Like any other animation element, a SequentialAnimation can be applied in a + number of ways, including transitions, behaviors and property value + sources. The \l {QML Animation and Transitions} documentation shows a + variety of methods for creating animations. + + \note Once an animation has been grouped into a SequentialAnimation or + ParallelAnimation, it cannot be individually started and stopped; the + SequentialAnimation or ParallelAnimation must be started and stopped as a group. + + \sa ParallelAnimation, {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example} +*/ + +QDeclarativeSequentialAnimation::QDeclarativeSequentialAnimation(QObject *parent) : + QDeclarativeAnimationGroup(parent) +{ + Q_D(QDeclarativeAnimationGroup); + d->ag = new QSequentialAnimationGroup; + QDeclarative_setParent_noEvent(d->ag, this); +} + +QDeclarativeSequentialAnimation::~QDeclarativeSequentialAnimation() +{ +} + +QAbstractAnimation *QDeclarativeSequentialAnimation::qtAnimation() +{ + Q_D(QDeclarativeAnimationGroup); + return d->ag; +} + +void QDeclarativeSequentialAnimation::transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) +{ + Q_D(QDeclarativeAnimationGroup); + + int inc = 1; + int from = 0; + if (direction == Backward) { + inc = -1; + from = d->animations.count() - 1; + } + + bool valid = d->defaultProperty.isValid(); + for (int ii = from; ii < d->animations.count() && ii >= 0; ii += inc) { + if (valid) + d->animations.at(ii)->setDefaultTarget(d->defaultProperty); + d->animations.at(ii)->transition(actions, modified, direction); + } +} + + + +/*! + \qmlclass ParallelAnimation QDeclarativeParallelAnimation + \ingroup qml-animation-transition + \since 4.7 + \inherits Animation + \brief The ParallelAnimation element allows animations to be run in parallel. + + The SequentialAnimation and ParallelAnimation elements allow multiple + animations to be run together. Animations defined in a SequentialAnimation + are run one after the other, while animations defined in a ParallelAnimation + are run at the same time. + + The following animation runs two number animations in parallel. The \l Rectangle + moves to (50,50) by animating its \c x and \c y properties at the same time. + + \snippet doc/src/snippets/declarative/parallelanimation.qml 0 + + Like any other animation element, a ParallelAnimation can be applied in a + number of ways, including transitions, behaviors and property value + sources. The \l {QML Animation and Transitions} documentation shows a + variety of methods for creating animations. + + \note Once an animation has been grouped into a SequentialAnimation or + ParallelAnimation, it cannot be individually started and stopped; the + SequentialAnimation or ParallelAnimation must be started and stopped as a group. + + \sa SequentialAnimation, {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example} +*/ +QDeclarativeParallelAnimation::QDeclarativeParallelAnimation(QObject *parent) : + QDeclarativeAnimationGroup(parent) +{ + Q_D(QDeclarativeAnimationGroup); + d->ag = new QParallelAnimationGroup; + QDeclarative_setParent_noEvent(d->ag, this); +} + +QDeclarativeParallelAnimation::~QDeclarativeParallelAnimation() +{ +} + +QAbstractAnimation *QDeclarativeParallelAnimation::qtAnimation() +{ + Q_D(QDeclarativeAnimationGroup); + return d->ag; +} + +void QDeclarativeParallelAnimation::transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) +{ + Q_D(QDeclarativeAnimationGroup); + bool valid = d->defaultProperty.isValid(); + for (int ii = 0; ii < d->animations.count(); ++ii) { + if (valid) + d->animations.at(ii)->setDefaultTarget(d->defaultProperty); + d->animations.at(ii)->transition(actions, modified, direction); + } +} + + + +//convert a variant from string type to another animatable type +void QDeclarativePropertyAnimationPrivate::convertVariant(QVariant &variant, int type) +{ + if (variant.userType() != QVariant::String) { + variant.convert((QVariant::Type)type); + return; + } + + switch (type) { + case QVariant::Rect: { + variant.setValue(QDeclarativeStringConverters::rectFFromString(variant.toString()).toRect()); + break; + } + case QVariant::RectF: { + variant.setValue(QDeclarativeStringConverters::rectFFromString(variant.toString())); + break; + } + case QVariant::Point: { + variant.setValue(QDeclarativeStringConverters::pointFFromString(variant.toString()).toPoint()); + break; + } + case QVariant::PointF: { + variant.setValue(QDeclarativeStringConverters::pointFFromString(variant.toString())); + break; + } + case QVariant::Size: { + variant.setValue(QDeclarativeStringConverters::sizeFFromString(variant.toString()).toSize()); + break; + } + case QVariant::SizeF: { + variant.setValue(QDeclarativeStringConverters::sizeFFromString(variant.toString())); + break; + } + case QVariant::Color: { + variant.setValue(QDeclarativeStringConverters::colorFromString(variant.toString())); + break; + } + case QVariant::Vector3D: { + variant.setValue(QDeclarativeStringConverters::vector3DFromString(variant.toString())); + break; + } + default: + if (QDeclarativeValueTypeFactory::isValueType((uint)type)) { + variant.convert((QVariant::Type)type); + } else { + QDeclarativeMetaType::StringConverter converter = QDeclarativeMetaType::customStringConverter(type); + if (converter) + variant = converter(variant.toString()); + } + break; + } +} + +/*! + \qmlclass PropertyAnimation QDeclarativePropertyAnimation + \ingroup qml-animation-transition + \since 4.7 + \inherits Animation + \brief The PropertyAnimation element animates changes in property values. + + PropertyAnimation provides a way to animate changes to a property's value. + + It can be used to define animations in a number of ways: + + \list + \o In a \l Transition + + For example, to animate any objects that have changed their \c x or \c y properties + as a result of a state change, using an \c InOutQuad easing curve: + + \snippet doc/src/snippets/declarative/propertyanimation.qml transition + + + \o In a \l Behavior + + For example, to animate all changes to a rectangle's \c x property: + + \snippet doc/src/snippets/declarative/propertyanimation.qml behavior + + + \o As a property value source + + For example, to repeatedly animate the rectangle's \c x property: + + \snippet doc/src/snippets/declarative/propertyanimation.qml propertyvaluesource + + + \o In a signal handler + + For example, to fade out \c theObject when clicked: + \qml + MouseArea { + anchors.fill: theObject + onClicked: PropertyAnimation { target: theObject; property: "opacity"; to: 0 } + } + \endqml + + \o Standalone + + For example, to animate \c rect's \c width property over 500ms, from its current width to 30: + + \snippet doc/src/snippets/declarative/propertyanimation.qml standalone + + \endlist + + Depending on how the animation is used, the set of properties normally used will be + different. For more information see the individual property documentation, as well + as the \l{QML Animation and Transitions} introduction. + + Note that PropertyAnimation inherits the abstract \l Animation element. + This includes additional properties and methods for controlling the animation. + + \sa {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example} +*/ + +QDeclarativePropertyAnimation::QDeclarativePropertyAnimation(QObject *parent) +: QDeclarativeAbstractAnimation(*(new QDeclarativePropertyAnimationPrivate), parent) +{ + Q_D(QDeclarativePropertyAnimation); + d->init(); +} + +QDeclarativePropertyAnimation::QDeclarativePropertyAnimation(QDeclarativePropertyAnimationPrivate &dd, QObject *parent) +: QDeclarativeAbstractAnimation(dd, parent) +{ + Q_D(QDeclarativePropertyAnimation); + d->init(); +} + +QDeclarativePropertyAnimation::~QDeclarativePropertyAnimation() +{ +} + +void QDeclarativePropertyAnimationPrivate::init() +{ + Q_Q(QDeclarativePropertyAnimation); + va = new QDeclarativeBulkValueAnimator; + QDeclarative_setParent_noEvent(va, q); +} + +/*! + \qmlproperty int PropertyAnimation::duration + This property holds the duration of the animation, in milliseconds. + + The default value is 250. +*/ +int QDeclarativePropertyAnimation::duration() const +{ + Q_D(const QDeclarativePropertyAnimation); + return d->va->duration(); +} + +void QDeclarativePropertyAnimation::setDuration(int duration) +{ + if (duration < 0) { + qmlInfo(this) << tr("Cannot set a duration of < 0"); + return; + } + + Q_D(QDeclarativePropertyAnimation); + if (d->va->duration() == duration) + return; + d->va->setDuration(duration); + emit durationChanged(duration); +} + +/*! + \qmlproperty real PropertyAnimation::from + This property holds the starting value for the animation. + + If the PropertyAnimation is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the starting state of the + \l Transition, or the current value of the property at the moment the + \l Behavior is triggered. + + \sa {QML Animation and Transitions} +*/ +QVariant QDeclarativePropertyAnimation::from() const +{ + Q_D(const QDeclarativePropertyAnimation); + return d->from; +} + +void QDeclarativePropertyAnimation::setFrom(const QVariant &f) +{ + Q_D(QDeclarativePropertyAnimation); + if (d->fromIsDefined && f == d->from) + return; + d->from = f; + d->fromIsDefined = f.isValid(); + emit fromChanged(f); +} + +/*! + \qmlproperty real PropertyAnimation::to + This property holds the end value for the animation. + + If the PropertyAnimation is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the end state of the + \l Transition, or the value of the property change that triggered the + \l Behavior. + + \sa {QML Animation and Transitions} +*/ +QVariant QDeclarativePropertyAnimation::to() const +{ + Q_D(const QDeclarativePropertyAnimation); + return d->to; +} + +void QDeclarativePropertyAnimation::setTo(const QVariant &t) +{ + Q_D(QDeclarativePropertyAnimation); + if (d->toIsDefined && t == d->to) + return; + d->to = t; + d->toIsDefined = t.isValid(); + emit toChanged(t); +} + +/*! + \qmlproperty enumeration PropertyAnimation::easing.type + \qmlproperty real PropertyAnimation::easing.amplitude + \qmlproperty real PropertyAnimation::easing.overshoot + \qmlproperty real PropertyAnimation::easing.period + \brief the easing curve used for the animation. + + To specify an easing curve you need to specify at least the type. For some curves you can also specify + amplitude, period and/or overshoot (more details provided after the table). The default easing curve is + \c Easing.Linear. + + \qml + PropertyAnimation { properties: "y"; easing.type: Easing.InOutElastic; easing.amplitude: 2.0; easing.period: 1.5 } + \endqml + + Available types are: + + \table + \row + \o \c Easing.Linear + \o Easing curve for a linear (t) function: velocity is constant. + \o \inlineimage qeasingcurve-linear.png + \row + \o \c Easing.InQuad + \o Easing curve for a quadratic (t^2) function: accelerating from zero velocity. + \o \inlineimage qeasingcurve-inquad.png + \row + \o \c Easing.OutQuad + \o Easing curve for a quadratic (t^2) function: decelerating to zero velocity. + \o \inlineimage qeasingcurve-outquad.png + \row + \o \c Easing.InOutQuad + \o Easing curve for a quadratic (t^2) function: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutquad.png + \row + \o \c Easing.OutInQuad + \o Easing curve for a quadratic (t^2) function: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outinquad.png + \row + \o \c Easing.InCubic + \o Easing curve for a cubic (t^3) function: accelerating from zero velocity. + \o \inlineimage qeasingcurve-incubic.png + \row + \o \c Easing.OutCubic + \o Easing curve for a cubic (t^3) function: decelerating from zero velocity. + \o \inlineimage qeasingcurve-outcubic.png + \row + \o \c Easing.InOutCubic + \o Easing curve for a cubic (t^3) function: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutcubic.png + \row + \o \c Easing.OutInCubic + \o Easing curve for a cubic (t^3) function: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outincubic.png + \row + \o \c Easing.InQuart + \o Easing curve for a quartic (t^4) function: accelerating from zero velocity. + \o \inlineimage qeasingcurve-inquart.png + \row + \o \c Easing.OutQuart + \o Easing curve for a quartic (t^4) function: decelerating from zero velocity. + \o \inlineimage qeasingcurve-outquart.png + \row + \o \c Easing.InOutQuart + \o Easing curve for a quartic (t^4) function: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutquart.png + \row + \o \c Easing.OutInQuart + \o Easing curve for a quartic (t^4) function: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outinquart.png + \row + \o \c Easing.InQuint + \o Easing curve for a quintic (t^5) function: accelerating from zero velocity. + \o \inlineimage qeasingcurve-inquint.png + \row + \o \c Easing.OutQuint + \o Easing curve for a quintic (t^5) function: decelerating from zero velocity. + \o \inlineimage qeasingcurve-outquint.png + \row + \o \c Easing.InOutQuint + \o Easing curve for a quintic (t^5) function: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutquint.png + \row + \o \c Easing.OutInQuint + \o Easing curve for a quintic (t^5) function: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outinquint.png + \row + \o \c Easing.InSine + \o Easing curve for a sinusoidal (sin(t)) function: accelerating from zero velocity. + \o \inlineimage qeasingcurve-insine.png + \row + \o \c Easing.OutSine + \o Easing curve for a sinusoidal (sin(t)) function: decelerating from zero velocity. + \o \inlineimage qeasingcurve-outsine.png + \row + \o \c Easing.InOutSine + \o Easing curve for a sinusoidal (sin(t)) function: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutsine.png + \row + \o \c Easing.OutInSine + \o Easing curve for a sinusoidal (sin(t)) function: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outinsine.png + \row + \o \c Easing.InExpo + \o Easing curve for an exponential (2^t) function: accelerating from zero velocity. + \o \inlineimage qeasingcurve-inexpo.png + \row + \o \c Easing.OutExpo + \o Easing curve for an exponential (2^t) function: decelerating from zero velocity. + \o \inlineimage qeasingcurve-outexpo.png + \row + \o \c Easing.InOutExpo + \o Easing curve for an exponential (2^t) function: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutexpo.png + \row + \o \c Easing.OutInExpo + \o Easing curve for an exponential (2^t) function: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outinexpo.png + \row + \o \c Easing.InCirc + \o Easing curve for a circular (sqrt(1-t^2)) function: accelerating from zero velocity. + \o \inlineimage qeasingcurve-incirc.png + \row + \o \c Easing.OutCirc + \o Easing curve for a circular (sqrt(1-t^2)) function: decelerating from zero velocity. + \o \inlineimage qeasingcurve-outcirc.png + \row + \o \c Easing.InOutCirc + \o Easing curve for a circular (sqrt(1-t^2)) function: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutcirc.png + \row + \o \c Easing.OutInCirc + \o Easing curve for a circular (sqrt(1-t^2)) function: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outincirc.png + \row + \o \c Easing.InElastic + \o Easing curve for an elastic (exponentially decaying sine wave) function: accelerating from zero velocity. + \br The peak amplitude can be set with the \e amplitude parameter, and the period of decay by the \e period parameter. + \o \inlineimage qeasingcurve-inelastic.png + \row + \o \c Easing.OutElastic + \o Easing curve for an elastic (exponentially decaying sine wave) function: decelerating from zero velocity. + \br The peak amplitude can be set with the \e amplitude parameter, and the period of decay by the \e period parameter. + \o \inlineimage qeasingcurve-outelastic.png + \row + \o \c Easing.InOutElastic + \o Easing curve for an elastic (exponentially decaying sine wave) function: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutelastic.png + \row + \o \c Easing.OutInElastic + \o Easing curve for an elastic (exponentially decaying sine wave) function: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outinelastic.png + \row + \o \c Easing.InBack + \o Easing curve for a back (overshooting cubic function: (s+1)*t^3 - s*t^2) easing in: accelerating from zero velocity. + \o \inlineimage qeasingcurve-inback.png + \row + \o \c Easing.OutBack + \o Easing curve for a back (overshooting cubic function: (s+1)*t^3 - s*t^2) easing out: decelerating to zero velocity. + \o \inlineimage qeasingcurve-outback.png + \row + \o \c Easing.InOutBack + \o Easing curve for a back (overshooting cubic function: (s+1)*t^3 - s*t^2) easing in/out: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutback.png + \row + \o \c Easing.OutInBack + \o Easing curve for a back (overshooting cubic easing: (s+1)*t^3 - s*t^2) easing out/in: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outinback.png + \row + \o \c Easing.InBounce + \o Easing curve for a bounce (exponentially decaying parabolic bounce) function: accelerating from zero velocity. + \o \inlineimage qeasingcurve-inbounce.png + \row + \o \c Easing.OutBounce + \o Easing curve for a bounce (exponentially decaying parabolic bounce) function: decelerating from zero velocity. + \o \inlineimage qeasingcurve-outbounce.png + \row + \o \c Easing.InOutBounce + \o Easing curve for a bounce (exponentially decaying parabolic bounce) function easing in/out: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutbounce.png + \row + \o \c Easing.OutInBounce + \o Easing curve for a bounce (exponentially decaying parabolic bounce) function easing out/in: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outinbounce.png + \endtable + + \c easing.amplitude is only applicable for bounce and elastic curves (curves of type + \c Easing.InBounce, \c Easing.OutBounce, \c Easing.InOutBounce, \c Easing.OutInBounce, \c Easing.InElastic, + \c Easing.OutElastic, \c Easing.InOutElastic or \c Easing.OutInElastic). + + \c easing.overshoot is only applicable if \c easing.type is: \c Easing.InBack, \c Easing.OutBack, + \c Easing.InOutBack or \c Easing.OutInBack. + + \c easing.period is only applicable if easing.type is: \c Easing.InElastic, \c Easing.OutElastic, + \c Easing.InOutElastic or \c Easing.OutInElastic. + + See the \l {declarative/animation/easing}{easing} example for a demonstration of + the different easing settings. +*/ +QEasingCurve QDeclarativePropertyAnimation::easing() const +{ + Q_D(const QDeclarativePropertyAnimation); + return d->va->easingCurve(); +} + +void QDeclarativePropertyAnimation::setEasing(const QEasingCurve &e) +{ + Q_D(QDeclarativePropertyAnimation); + if (d->va->easingCurve() == e) + return; + + d->va->setEasingCurve(e); + emit easingChanged(e); +} + +QObject *QDeclarativePropertyAnimation::target() const +{ + Q_D(const QDeclarativePropertyAnimation); + return d->target; +} + +void QDeclarativePropertyAnimation::setTarget(QObject *o) +{ + Q_D(QDeclarativePropertyAnimation); + if (d->target == o) + return; + d->target = o; + emit targetChanged(); +} + +QString QDeclarativePropertyAnimation::property() const +{ + Q_D(const QDeclarativePropertyAnimation); + return d->propertyName; +} + +void QDeclarativePropertyAnimation::setProperty(const QString &n) +{ + Q_D(QDeclarativePropertyAnimation); + if (d->propertyName == n) + return; + d->propertyName = n; + emit propertyChanged(); +} + +QString QDeclarativePropertyAnimation::properties() const +{ + Q_D(const QDeclarativePropertyAnimation); + return d->properties; +} + +void QDeclarativePropertyAnimation::setProperties(const QString &prop) +{ + Q_D(QDeclarativePropertyAnimation); + if (d->properties == prop) + return; + + d->properties = prop; + emit propertiesChanged(prop); +} + +/*! + \qmlproperty string PropertyAnimation::properties + \qmlproperty list PropertyAnimation::targets + \qmlproperty string PropertyAnimation::property + \qmlproperty Object PropertyAnimation::target + + These properties are used as a set to determine which properties should be animated. + The singular and plural forms are functionally identical, e.g. + \qml + NumberAnimation { target: theItem; property: "x"; to: 500 } + \endqml + has the same meaning as + \qml + NumberAnimation { targets: theItem; properties: "x"; to: 500 } + \endqml + The singular forms are slightly optimized, so if you do have only a single target/property + to animate you should try to use them. + + The \c targets property allows multiple targets to be set. For example, this animates the + \c x property of both \c itemA and \c itemB: + + \qml + NumberAnimation { targets: [itemA, itemB]; properties: "x"; to: 500 } + \endqml + + In many cases these properties do not need to be explicitly specified, as they can be + inferred from the animation framework: + + \table 80% + \row + \o Value Source / Behavior + \o When an animation is used as a value source or in a Behavior, the default target and property + name to be animated can both be inferred. + \qml + Rectangle { + id: theRect + width: 100; height: 100 + color: Qt.rgba(0,0,1) + NumberAnimation on x { to: 500; loops: Animation.Infinite } //animate theRect's x property + Behavior on y { NumberAnimation {} } //animate theRect's y property + } + \endqml + \row + \o Transition + \o When used in a transition, a property animation is assumed to match \e all targets + but \e no properties. In practice, that means you need to specify at least the properties + in order for the animation to do anything. + \qml + Rectangle { + id: theRect + width: 100; height: 100 + color: Qt.rgba(0,0,1) + Item { id: uselessItem } + states: State { + name: "state1" + PropertyChanges { target: theRect; x: 200; y: 200; z: 4 } + PropertyChanges { target: uselessItem; x: 10; y: 10; z: 2 } + } + transitions: Transition { + //animate both theRect's and uselessItem's x and y to their final values + NumberAnimation { properties: "x,y" } + + //animate theRect's z to its final value + NumberAnimation { target: theRect; property: "z" } + } + } + \endqml + \row + \o Standalone + \o When an animation is used standalone, both the target and property need to be + explicitly specified. + \qml + Rectangle { + id: theRect + width: 100; height: 100 + color: Qt.rgba(0,0,1) + //need to explicitly specify target and property + NumberAnimation { id: theAnim; target: theRect; property: "x"; to: 500 } + MouseArea { + anchors.fill: parent + onClicked: theAnim.start() + } + } + \endqml + \endtable + + As seen in the above example, properties is specified as a comma-separated string of property names to animate. + + \sa exclude, {QML Animation and Transitions} +*/ +QDeclarativeListProperty QDeclarativePropertyAnimation::targets() +{ + Q_D(QDeclarativePropertyAnimation); + return QDeclarativeListProperty(this, d->targets); +} + +/*! + \qmlproperty list PropertyAnimation::exclude + This property holds the items not to be affected by this animation. + \sa PropertyAnimation::targets +*/ +QDeclarativeListProperty QDeclarativePropertyAnimation::exclude() +{ + Q_D(QDeclarativePropertyAnimation); + return QDeclarativeListProperty(this, d->exclude); +} + +QAbstractAnimation *QDeclarativePropertyAnimation::qtAnimation() +{ + Q_D(QDeclarativePropertyAnimation); + return d->va; +} + +void QDeclarativeAnimationPropertyUpdater::setValue(qreal v) +{ + bool deleted = false; + wasDeleted = &deleted; + if (reverse) //QVariantAnimation sends us 1->0 when reversed, but we are expecting 0->1 + v = 1 - v; + for (int ii = 0; ii < actions.count(); ++ii) { + QDeclarativeAction &action = actions[ii]; + + if (v == 1.) + QDeclarativePropertyPrivate::write(action.property, action.toValue, QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding); + else { + if (!fromSourced && !fromDefined) { + action.fromValue = action.property.read(); + if (interpolatorType) + QDeclarativePropertyAnimationPrivate::convertVariant(action.fromValue, interpolatorType); + } + if (!interpolatorType) { + int propType = action.property.propertyType(); + if (!prevInterpolatorType || prevInterpolatorType != propType) { + prevInterpolatorType = propType; + interpolator = QVariantAnimationPrivate::getInterpolator(prevInterpolatorType); + } + } + if (interpolator) + QDeclarativePropertyPrivate::write(action.property, interpolator(action.fromValue.constData(), action.toValue.constData(), v), QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding); + } + if (deleted) + return; + } + wasDeleted = 0; + fromSourced = true; +} + +void QDeclarativePropertyAnimation::transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) +{ + Q_D(QDeclarativePropertyAnimation); + + QStringList props = d->properties.isEmpty() ? QStringList() : d->properties.split(QLatin1Char(',')); + for (int ii = 0; ii < props.count(); ++ii) + props[ii] = props.at(ii).trimmed(); + if (!d->propertyName.isEmpty()) + props << d->propertyName; + + QList targets = d->targets; + if (d->target) + targets.append(d->target); + + bool hasSelectors = !props.isEmpty() || !targets.isEmpty() || !d->exclude.isEmpty(); + bool useType = (props.isEmpty() && d->defaultToInterpolatorType) ? true : false; + + if (d->defaultProperty.isValid() && !hasSelectors) { + props << d->defaultProperty.name(); + targets << d->defaultProperty.object(); + } + + if (props.isEmpty() && !d->defaultProperties.isEmpty()) { + props << d->defaultProperties.split(QLatin1Char(',')); + } + + QDeclarativeAnimationPropertyUpdater *data = new QDeclarativeAnimationPropertyUpdater; + data->interpolatorType = d->interpolatorType; + data->interpolator = d->interpolator; + data->reverse = direction == Backward ? true : false; + data->fromSourced = false; + data->fromDefined = d->fromIsDefined; + + bool hasExplicit = false; + //an explicit animation has been specified + if (d->toIsDefined) { + for (int i = 0; i < props.count(); ++i) { + for (int j = 0; j < targets.count(); ++j) { + QDeclarativeAction myAction; + myAction.property = d->createProperty(targets.at(j), props.at(i), this); + if (myAction.property.isValid()) { + if (d->fromIsDefined) { + myAction.fromValue = d->from; + d->convertVariant(myAction.fromValue, d->interpolatorType ? d->interpolatorType : myAction.property.propertyType()); + } + myAction.toValue = d->to; + d->convertVariant(myAction.toValue, d->interpolatorType ? d->interpolatorType : myAction.property.propertyType()); + data->actions << myAction; + hasExplicit = true; + for (int ii = 0; ii < actions.count(); ++ii) { + QDeclarativeAction &action = actions[ii]; + if (action.property.object() == myAction.property.object() && + myAction.property.name() == action.property.name()) { + modified << action.property; + break; //### any chance there could be multiples? + } + } + } + } + } + } + + if (!hasExplicit) + for (int ii = 0; ii < actions.count(); ++ii) { + QDeclarativeAction &action = actions[ii]; + + QObject *obj = action.property.object(); + QString propertyName = action.property.name(); + QObject *sObj = action.specifiedObject; + QString sPropertyName = action.specifiedProperty; + bool same = (obj == sObj); + + if ((targets.isEmpty() || targets.contains(obj) || (!same && targets.contains(sObj))) && + (!d->exclude.contains(obj)) && (same || (!d->exclude.contains(sObj))) && + (props.contains(propertyName) || (!same && props.contains(sPropertyName)) + || (useType && action.property.propertyType() == d->interpolatorType))) { + QDeclarativeAction myAction = action; + + if (d->fromIsDefined) + myAction.fromValue = d->from; + else + myAction.fromValue = QVariant(); + if (d->toIsDefined) + myAction.toValue = d->to; + + d->convertVariant(myAction.fromValue, d->interpolatorType ? d->interpolatorType : myAction.property.propertyType()); + d->convertVariant(myAction.toValue, d->interpolatorType ? d->interpolatorType : myAction.property.propertyType()); + + modified << action.property; + + data->actions << myAction; + action.fromValue = myAction.toValue; + } + } + + if (data->actions.count()) { + if (!d->rangeIsSet) { + d->va->setStartValue(qreal(0)); + d->va->setEndValue(qreal(1)); + d->rangeIsSet = true; + } + d->va->setAnimValue(data, QAbstractAnimation::DeleteWhenStopped); + d->va->setFromSourcedValue(&data->fromSourced); + d->actions = &data->actions; + } else { + delete data; + d->va->setFromSourcedValue(0); //clear previous data + d->va->setAnimValue(0, QAbstractAnimation::DeleteWhenStopped); //clear previous data + d->actions = 0; + } +} + +/*! + \qmlclass ParentAnimation QDeclarativeParentAnimation + \ingroup qml-animation-transition + \since 4.7 + \inherits Animation + \brief The ParentAnimation element animates changes in parent values. + + ParentAnimation is used to animate a parent change for an \l Item. + + For example, the following ParentChange changes \c blueRect to become + a child of \c redRect when it is clicked. The inclusion of the + ParentAnimation, which defines a NumberAnimation to be applied during + the transition, ensures the item animates smoothly as it moves to + its new parent: + + \snippet doc/src/snippets/declarative/parentanimation.qml 0 + + A ParentAnimation can contain any number of animations. These animations will + be run in parallel; to run them sequentially, define them within a + SequentialAnimation. + + In some cases, such as when reparenting between items with clipping enabled, it is useful + to animate the parent change via another item that does not have clipping + enabled. Such an item can be set using the \l via property. + + For convenience, when a ParentAnimation is used in a \l Transition, it will + animate any ParentChange that has occurred during the state change. + This can be overridden by setting a specific target item using the + \l target property. + + Like any other animation element, a ParentAnimation can be applied in a + number of ways, including transitions, behaviors and property value + sources. The \l {QML Animation and Transitions} documentation shows a + variety of methods for creating animations. + + \sa {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example} +*/ +QDeclarativeParentAnimation::QDeclarativeParentAnimation(QObject *parent) + : QDeclarativeAnimationGroup(*(new QDeclarativeParentAnimationPrivate), parent) +{ + Q_D(QDeclarativeParentAnimation); + d->topLevelGroup = new QSequentialAnimationGroup; + QDeclarative_setParent_noEvent(d->topLevelGroup, this); + + d->startAction = new QActionAnimation; + QDeclarative_setParent_noEvent(d->startAction, d->topLevelGroup); + d->topLevelGroup->addAnimation(d->startAction); + + d->ag = new QParallelAnimationGroup; + QDeclarative_setParent_noEvent(d->ag, d->topLevelGroup); + d->topLevelGroup->addAnimation(d->ag); + + d->endAction = new QActionAnimation; + QDeclarative_setParent_noEvent(d->endAction, d->topLevelGroup); + d->topLevelGroup->addAnimation(d->endAction); +} + +QDeclarativeParentAnimation::~QDeclarativeParentAnimation() +{ +} + +/*! + \qmlproperty Item ParentAnimation::target + The item to reparent. + + When used in a transition, if no target is specified, all + ParentChange occurrences are animated by the ParentAnimation. +*/ +QDeclarativeItem *QDeclarativeParentAnimation::target() const +{ + Q_D(const QDeclarativeParentAnimation); + return d->target; +} + +void QDeclarativeParentAnimation::setTarget(QDeclarativeItem *target) +{ + Q_D(QDeclarativeParentAnimation); + if (target == d->target) + return; + + d->target = target; + emit targetChanged(); +} + +/*! + \qmlproperty Item ParentAnimation::newParent + The new parent to animate to. + + If the ParentAnimation is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the end state of the + \l Transition, or the value of the property change that triggered the + \l Behavior. +*/ +QDeclarativeItem *QDeclarativeParentAnimation::newParent() const +{ + Q_D(const QDeclarativeParentAnimation); + return d->newParent; +} + +void QDeclarativeParentAnimation::setNewParent(QDeclarativeItem *newParent) +{ + Q_D(QDeclarativeParentAnimation); + if (newParent == d->newParent) + return; + + d->newParent = newParent; + emit newParentChanged(); +} + +/*! + \qmlproperty Item ParentAnimation::via + The item to reparent via. This provides a way to do an unclipped animation + when both the old parent and new parent are clipped. + + \qml + ParentAnimation { + target: myItem + via: topLevelItem + // ... + } + \endqml +*/ +QDeclarativeItem *QDeclarativeParentAnimation::via() const +{ + Q_D(const QDeclarativeParentAnimation); + return d->via; +} + +void QDeclarativeParentAnimation::setVia(QDeclarativeItem *via) +{ + Q_D(QDeclarativeParentAnimation); + if (via == d->via) + return; + + d->via = via; + emit viaChanged(); +} + +//### mirrors same-named function in QDeclarativeItem +QPointF QDeclarativeParentAnimationPrivate::computeTransformOrigin(QDeclarativeItem::TransformOrigin origin, qreal width, qreal height) const +{ + switch(origin) { + default: + case QDeclarativeItem::TopLeft: + return QPointF(0, 0); + case QDeclarativeItem::Top: + return QPointF(width / 2., 0); + case QDeclarativeItem::TopRight: + return QPointF(width, 0); + case QDeclarativeItem::Left: + return QPointF(0, height / 2.); + case QDeclarativeItem::Center: + return QPointF(width / 2., height / 2.); + case QDeclarativeItem::Right: + return QPointF(width, height / 2.); + case QDeclarativeItem::BottomLeft: + return QPointF(0, height); + case QDeclarativeItem::Bottom: + return QPointF(width / 2., height); + case QDeclarativeItem::BottomRight: + return QPointF(width, height); + } +} + +void QDeclarativeParentAnimation::transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) +{ + Q_D(QDeclarativeParentAnimation); + + struct QDeclarativeParentAnimationData : public QAbstractAnimationAction + { + QDeclarativeParentAnimationData() {} + ~QDeclarativeParentAnimationData() { qDeleteAll(pc); } + + QDeclarativeStateActions actions; + //### reverse should probably apply on a per-action basis + bool reverse; + QList pc; + virtual void doAction() + { + for (int ii = 0; ii < actions.count(); ++ii) { + const QDeclarativeAction &action = actions.at(ii); + if (reverse) + action.event->reverse(); + else + action.event->execute(); + } + } + }; + + QDeclarativeParentAnimationData *data = new QDeclarativeParentAnimationData; + QDeclarativeParentAnimationData *viaData = new QDeclarativeParentAnimationData; + + bool hasExplicit = false; + if (d->target && d->newParent) { + data->reverse = false; + QDeclarativeAction myAction; + QDeclarativeParentChange *pc = new QDeclarativeParentChange; + pc->setObject(d->target); + pc->setParent(d->newParent); + myAction.event = pc; + data->pc << pc; + data->actions << myAction; + hasExplicit = true; + if (d->via) { + viaData->reverse = false; + QDeclarativeAction myVAction; + QDeclarativeParentChange *vpc = new QDeclarativeParentChange; + vpc->setObject(d->target); + vpc->setParent(d->via); + myVAction.event = vpc; + viaData->pc << vpc; + viaData->actions << myVAction; + } + //### once actions have concept of modified, + // loop to match appropriate ParentChanges and mark as modified + } + + if (!hasExplicit) + for (int i = 0; i < actions.size(); ++i) { + QDeclarativeAction &action = actions[i]; + if (action.event && action.event->typeName() == QLatin1String("ParentChange") + && (!d->target || static_cast(action.event)->object() == d->target)) { + + QDeclarativeParentChange *pc = static_cast(action.event); + QDeclarativeAction myAction = action; + data->reverse = action.reverseEvent; + + //### this logic differs from PropertyAnimation + // (probably a result of modified vs. done) + if (d->newParent) { + QDeclarativeParentChange *epc = new QDeclarativeParentChange; + epc->setObject(static_cast(action.event)->object()); + epc->setParent(d->newParent); + myAction.event = epc; + data->pc << epc; + data->actions << myAction; + pc = epc; + } else { + action.actionDone = true; + data->actions << myAction; + } + + if (d->via) { + viaData->reverse = false; + QDeclarativeAction myAction; + QDeclarativeParentChange *vpc = new QDeclarativeParentChange; + vpc->setObject(pc->object()); + vpc->setParent(d->via); + myAction.event = vpc; + viaData->pc << vpc; + viaData->actions << myAction; + QDeclarativeAction dummyAction; + QDeclarativeAction &xAction = pc->xIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction; + QDeclarativeAction &yAction = pc->yIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction; + QDeclarativeAction &sAction = pc->scaleIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction; + QDeclarativeAction &rAction = pc->rotationIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction; + QDeclarativeItem *target = pc->object(); + QDeclarativeItem *targetParent = action.reverseEvent ? pc->originalParent() : pc->parent(); + + //### this mirrors the logic in QDeclarativeParentChange. + bool ok; + const QTransform &transform = targetParent->itemTransform(d->via, &ok); + if (transform.type() >= QTransform::TxShear || !ok) { + qmlInfo(this) << QDeclarativeParentAnimation::tr("Unable to preserve appearance under complex transform"); + ok = false; + } + + qreal scale = 1; + qreal rotation = 0; + bool isRotate = (transform.type() == QTransform::TxRotate) || (transform.m11() < 0); + if (ok && !isRotate) { + if (transform.m11() == transform.m22()) + scale = transform.m11(); + else { + qmlInfo(this) << QDeclarativeParentAnimation::tr("Unable to preserve appearance under non-uniform scale"); + ok = false; + } + } else if (ok && isRotate) { + if (transform.m11() == transform.m22()) + scale = qSqrt(transform.m11()*transform.m11() + transform.m12()*transform.m12()); + else { + qmlInfo(this) << QDeclarativeParentAnimation::tr("Unable to preserve appearance under non-uniform scale"); + ok = false; + } + + if (scale != 0) + rotation = atan2(transform.m12()/scale, transform.m11()/scale) * 180/qreal(M_PI); + else { + qmlInfo(this) << QDeclarativeParentAnimation::tr("Unable to preserve appearance under scale of 0"); + ok = false; + } + } + + const QPointF &point = transform.map(QPointF(xAction.toValue.toReal(),yAction.toValue.toReal())); + qreal x = point.x(); + qreal y = point.y(); + if (ok && target->transformOrigin() != QDeclarativeItem::TopLeft) { + qreal w = target->width(); + qreal h = target->height(); + if (pc->widthIsSet() && i < actions.size() - 1) + w = actions[++i].toValue.toReal(); + if (pc->heightIsSet() && i < actions.size() - 1) + h = actions[++i].toValue.toReal(); + const QPointF &transformOrigin + = d->computeTransformOrigin(target->transformOrigin(), w,h); + qreal tempxt = transformOrigin.x(); + qreal tempyt = transformOrigin.y(); + QTransform t; + t.translate(-tempxt, -tempyt); + t.rotate(rotation); + t.scale(scale, scale); + t.translate(tempxt, tempyt); + const QPointF &offset = t.map(QPointF(0,0)); + x += offset.x(); + y += offset.y(); + } + + if (ok) { + //qDebug() << x << y << rotation << scale; + xAction.toValue = x; + yAction.toValue = y; + sAction.toValue = sAction.toValue.toReal() * scale; + rAction.toValue = rAction.toValue.toReal() + rotation; + } + } + } + } + + if (data->actions.count()) { + if (direction == QDeclarativeAbstractAnimation::Forward) { + d->startAction->setAnimAction(d->via ? viaData : data, QActionAnimation::DeleteWhenStopped); + d->endAction->setAnimAction(d->via ? data : 0, QActionAnimation::DeleteWhenStopped); + } else { + d->endAction->setAnimAction(d->via ? viaData : data, QActionAnimation::DeleteWhenStopped); + d->startAction->setAnimAction(d->via ? data : 0, QActionAnimation::DeleteWhenStopped); + } + if (!d->via) + delete viaData; + } else { + delete data; + delete viaData; + } + + //take care of any child animations + bool valid = d->defaultProperty.isValid(); + for (int ii = 0; ii < d->animations.count(); ++ii) { + if (valid) + d->animations.at(ii)->setDefaultTarget(d->defaultProperty); + d->animations.at(ii)->transition(actions, modified, direction); + } + +} + +QAbstractAnimation *QDeclarativeParentAnimation::qtAnimation() +{ + Q_D(QDeclarativeParentAnimation); + return d->topLevelGroup; +} + +/*! + \qmlclass AnchorAnimation QDeclarativeAnchorAnimation + \ingroup qml-animation-transition + \since 4.7 + \inherits Animation + \brief The AnchorAnimation element animates changes in anchor values. + + AnchorAnimation is used to animate an anchor change. + + In the following snippet we animate the addition of a right anchor to a \l Rectangle: + + \snippet doc/src/snippets/declarative/anchoranimation.qml 0 + + For convenience, when an AnchorAnimation is used in a \l Transition, it will + animate any AnchorChanges that have occurred during the state change. + This can be overridden by setting a specific target item using the + \l target property. + + Like any other animation element, an AnchorAnimation can be applied in a + number of ways, including transitions, behaviors and property value + sources. The \l {QML Animation and Transitions} documentation shows a + variety of methods for creating animations. + + \sa {QML Animation and Transitions}, AnchorChanges +*/ + +QDeclarativeAnchorAnimation::QDeclarativeAnchorAnimation(QObject *parent) +: QDeclarativeAbstractAnimation(*(new QDeclarativeAnchorAnimationPrivate), parent) +{ + Q_D(QDeclarativeAnchorAnimation); + d->va = new QDeclarativeBulkValueAnimator; + QDeclarative_setParent_noEvent(d->va, this); +} + +QDeclarativeAnchorAnimation::~QDeclarativeAnchorAnimation() +{ +} + +QAbstractAnimation *QDeclarativeAnchorAnimation::qtAnimation() +{ + Q_D(QDeclarativeAnchorAnimation); + return d->va; +} + +/*! + \qmlproperty list AnchorAnimation::targets + The items to reanchor. + + If no targets are specified all AnchorChanges will be + animated by the AnchorAnimation. +*/ +QDeclarativeListProperty QDeclarativeAnchorAnimation::targets() +{ + Q_D(QDeclarativeAnchorAnimation); + return QDeclarativeListProperty(this, d->targets); +} + +/*! + \qmlproperty int AnchorAnimation::duration + This property holds the duration of the animation, in milliseconds. + + The default value is 250. +*/ +int QDeclarativeAnchorAnimation::duration() const +{ + Q_D(const QDeclarativeAnchorAnimation); + return d->va->duration(); +} + +void QDeclarativeAnchorAnimation::setDuration(int duration) +{ + if (duration < 0) { + qmlInfo(this) << tr("Cannot set a duration of < 0"); + return; + } + + Q_D(QDeclarativeAnchorAnimation); + if (d->va->duration() == duration) + return; + d->va->setDuration(duration); + emit durationChanged(duration); +} + +/*! + \qmlproperty enumeration AnchorAnimation::easing.type + \qmlproperty real AnchorAnimation::easing.amplitude + \qmlproperty real AnchorAnimation::easing.overshoot + \qmlproperty real AnchorAnimation::easing.period + \brief the easing curve used for the animation. + + To specify an easing curve you need to specify at least the type. For some curves you can also specify + amplitude, period and/or overshoot. The default easing curve is + Linear. + + \qml + AnchorAnimation { easing.type: Easing.InOutQuad } + \endqml + + See the \l{PropertyAnimation::easing.type} documentation for information + about the different types of easing curves. +*/ + +QEasingCurve QDeclarativeAnchorAnimation::easing() const +{ + Q_D(const QDeclarativeAnchorAnimation); + return d->va->easingCurve(); +} + +void QDeclarativeAnchorAnimation::setEasing(const QEasingCurve &e) +{ + Q_D(QDeclarativeAnchorAnimation); + if (d->va->easingCurve() == e) + return; + + d->va->setEasingCurve(e); + emit easingChanged(e); +} + +void QDeclarativeAnchorAnimation::transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) +{ + Q_UNUSED(modified); + Q_D(QDeclarativeAnchorAnimation); + QDeclarativeAnimationPropertyUpdater *data = new QDeclarativeAnimationPropertyUpdater; + data->interpolatorType = QMetaType::QReal; + data->interpolator = d->interpolator; + + data->reverse = direction == Backward ? true : false; + data->fromSourced = false; + data->fromDefined = false; + + for (int ii = 0; ii < actions.count(); ++ii) { + QDeclarativeAction &action = actions[ii]; + if (action.event && action.event->typeName() == QLatin1String("AnchorChanges") + && (d->targets.isEmpty() || d->targets.contains(static_cast(action.event)->object()))) { + data->actions << static_cast(action.event)->additionalActions(); + } + } + + if (data->actions.count()) { + if (!d->rangeIsSet) { + d->va->setStartValue(qreal(0)); + d->va->setEndValue(qreal(1)); + d->rangeIsSet = true; + } + d->va->setAnimValue(data, QAbstractAnimation::DeleteWhenStopped); + d->va->setFromSourcedValue(&data->fromSourced); + } else { + delete data; + } +} + +QDeclarativeScriptActionPrivate::QDeclarativeScriptActionPrivate() + : QDeclarativeAbstractAnimationPrivate(), hasRunScriptScript(false), reversing(false), proxy(this), rsa(0) {} + + +QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativeanimation_p.h b/src/declarative/util/qdeclarativeanimation_p.h new file mode 100644 index 00000000..e80df259 --- /dev/null +++ b/src/declarative/util/qdeclarativeanimation_p.h @@ -0,0 +1,528 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEANIMATION_H +#define QDECLARATIVEANIMATION_H + +#include "private/qdeclarativetransition_p.h" +#include "private/qdeclarativestate_p.h" +#include + +#include +#include +#include + +#include +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeAbstractAnimationPrivate; +class QDeclarativeAnimationGroup; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeAbstractAnimation : public QObject, public QDeclarativePropertyValueSource, public QDeclarativeParserStatus +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeAbstractAnimation) + + Q_INTERFACES(QDeclarativeParserStatus) + Q_INTERFACES(QDeclarativePropertyValueSource) + Q_ENUMS(Loops) + Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged) + Q_PROPERTY(bool paused READ isPaused WRITE setPaused NOTIFY pausedChanged) + Q_PROPERTY(bool alwaysRunToEnd READ alwaysRunToEnd WRITE setAlwaysRunToEnd NOTIFY alwaysRunToEndChanged) + Q_PROPERTY(int loops READ loops WRITE setLoops NOTIFY loopCountChanged) + Q_CLASSINFO("DefaultMethod", "start()") + +public: + QDeclarativeAbstractAnimation(QObject *parent=0); + virtual ~QDeclarativeAbstractAnimation(); + + enum Loops { Infinite = -2 }; + + bool isRunning() const; + void setRunning(bool); + bool isPaused() const; + void setPaused(bool); + bool alwaysRunToEnd() const; + void setAlwaysRunToEnd(bool); + + int loops() const; + void setLoops(int); + + int currentTime(); + void setCurrentTime(int); + + QDeclarativeAnimationGroup *group() const; + void setGroup(QDeclarativeAnimationGroup *); + + void setDefaultTarget(const QDeclarativeProperty &); + void setDisableUserControl(); + + void classBegin(); + void componentComplete(); + +Q_SIGNALS: + void started(); + void completed(); + void runningChanged(bool); + void pausedChanged(bool); + void alwaysRunToEndChanged(bool); + void loopCountChanged(int); + +public Q_SLOTS: + void restart(); + void start(); + void pause(); + void resume(); + void stop(); + void complete(); + +protected: + QDeclarativeAbstractAnimation(QDeclarativeAbstractAnimationPrivate &dd, QObject *parent); + +public: + enum TransitionDirection { Forward, Backward }; + virtual void transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation() = 0; + +private Q_SLOTS: + void timelineComplete(); + void componentFinalized(); +private: + virtual void setTarget(const QDeclarativeProperty &); + void notifyRunningChanged(bool running); + friend class QDeclarativeBehavior; + + +}; + +class QDeclarativePauseAnimationPrivate; +class Q_AUTOTEST_EXPORT QDeclarativePauseAnimation : public QDeclarativeAbstractAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativePauseAnimation) + + Q_PROPERTY(int duration READ duration WRITE setDuration NOTIFY durationChanged) + +public: + QDeclarativePauseAnimation(QObject *parent=0); + virtual ~QDeclarativePauseAnimation(); + + int duration() const; + void setDuration(int); + +Q_SIGNALS: + void durationChanged(int); + +protected: + virtual QAbstractAnimation *qtAnimation(); +}; + +class QDeclarativeScriptActionPrivate; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeScriptAction : public QDeclarativeAbstractAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeScriptAction) + + Q_PROPERTY(QDeclarativeScriptString script READ script WRITE setScript) + Q_PROPERTY(QString scriptName READ stateChangeScriptName WRITE setStateChangeScriptName) + +public: + QDeclarativeScriptAction(QObject *parent=0); + virtual ~QDeclarativeScriptAction(); + + QDeclarativeScriptString script() const; + void setScript(const QDeclarativeScriptString &); + + QString stateChangeScriptName() const; + void setStateChangeScriptName(const QString &); + +protected: + virtual void transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation(); +}; + +class QDeclarativePropertyActionPrivate; +class QDeclarativePropertyAction : public QDeclarativeAbstractAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativePropertyAction) + + Q_PROPERTY(QObject *target READ target WRITE setTarget NOTIFY targetChanged) + Q_PROPERTY(QString property READ property WRITE setProperty NOTIFY propertyChanged) + Q_PROPERTY(QString properties READ properties WRITE setProperties NOTIFY propertiesChanged) + Q_PROPERTY(QDeclarativeListProperty targets READ targets) + Q_PROPERTY(QDeclarativeListProperty exclude READ exclude) + Q_PROPERTY(QVariant value READ value WRITE setValue NOTIFY valueChanged) + +public: + QDeclarativePropertyAction(QObject *parent=0); + virtual ~QDeclarativePropertyAction(); + + QObject *target() const; + void setTarget(QObject *); + + QString property() const; + void setProperty(const QString &); + + QString properties() const; + void setProperties(const QString &); + + QDeclarativeListProperty targets(); + QDeclarativeListProperty exclude(); + + QVariant value() const; + void setValue(const QVariant &); + +Q_SIGNALS: + void valueChanged(const QVariant &); + void propertiesChanged(const QString &); + void targetChanged(); + void propertyChanged(); + +protected: + virtual void transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation(); +}; + +class QDeclarativeItem; +class QDeclarativePropertyAnimationPrivate; +class Q_AUTOTEST_EXPORT QDeclarativePropertyAnimation : public QDeclarativeAbstractAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativePropertyAnimation) + + Q_PROPERTY(int duration READ duration WRITE setDuration NOTIFY durationChanged) + Q_PROPERTY(QVariant from READ from WRITE setFrom NOTIFY fromChanged) + Q_PROPERTY(QVariant to READ to WRITE setTo NOTIFY toChanged) + Q_PROPERTY(QEasingCurve easing READ easing WRITE setEasing NOTIFY easingChanged) + Q_PROPERTY(QObject *target READ target WRITE setTarget NOTIFY targetChanged) + Q_PROPERTY(QString property READ property WRITE setProperty NOTIFY propertyChanged) + Q_PROPERTY(QString properties READ properties WRITE setProperties NOTIFY propertiesChanged) + Q_PROPERTY(QDeclarativeListProperty targets READ targets) + Q_PROPERTY(QDeclarativeListProperty exclude READ exclude) + +public: + QDeclarativePropertyAnimation(QObject *parent=0); + virtual ~QDeclarativePropertyAnimation(); + + virtual int duration() const; + virtual void setDuration(int); + + QVariant from() const; + void setFrom(const QVariant &); + + QVariant to() const; + void setTo(const QVariant &); + + QEasingCurve easing() const; + void setEasing(const QEasingCurve &); + + QObject *target() const; + void setTarget(QObject *); + + QString property() const; + void setProperty(const QString &); + + QString properties() const; + void setProperties(const QString &); + + QDeclarativeListProperty targets(); + QDeclarativeListProperty exclude(); + +protected: + QDeclarativePropertyAnimation(QDeclarativePropertyAnimationPrivate &dd, QObject *parent); + virtual void transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation(); + +Q_SIGNALS: + void durationChanged(int); + void fromChanged(QVariant); + void toChanged(QVariant); + void easingChanged(const QEasingCurve &); + void propertiesChanged(const QString &); + void targetChanged(); + void propertyChanged(); +}; + +class Q_AUTOTEST_EXPORT QDeclarativeColorAnimation : public QDeclarativePropertyAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativePropertyAnimation) + Q_PROPERTY(QColor from READ from WRITE setFrom) + Q_PROPERTY(QColor to READ to WRITE setTo) + +public: + QDeclarativeColorAnimation(QObject *parent=0); + virtual ~QDeclarativeColorAnimation(); + + QColor from() const; + void setFrom(const QColor &); + + QColor to() const; + void setTo(const QColor &); +}; + +class Q_AUTOTEST_EXPORT QDeclarativeNumberAnimation : public QDeclarativePropertyAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativePropertyAnimation) + + Q_PROPERTY(qreal from READ from WRITE setFrom) + Q_PROPERTY(qreal to READ to WRITE setTo) + +public: + QDeclarativeNumberAnimation(QObject *parent=0); + virtual ~QDeclarativeNumberAnimation(); + + qreal from() const; + void setFrom(qreal); + + qreal to() const; + void setTo(qreal); + +protected: + QDeclarativeNumberAnimation(QDeclarativePropertyAnimationPrivate &dd, QObject *parent); + +private: + void init(); +}; + +class Q_AUTOTEST_EXPORT QDeclarativeVector3dAnimation : public QDeclarativePropertyAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativePropertyAnimation) + + Q_PROPERTY(QVector3D from READ from WRITE setFrom) + Q_PROPERTY(QVector3D to READ to WRITE setTo) + +public: + QDeclarativeVector3dAnimation(QObject *parent=0); + virtual ~QDeclarativeVector3dAnimation(); + + QVector3D from() const; + void setFrom(QVector3D); + + QVector3D to() const; + void setTo(QVector3D); +}; + +class QDeclarativeRotationAnimationPrivate; +class Q_AUTOTEST_EXPORT QDeclarativeRotationAnimation : public QDeclarativePropertyAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeRotationAnimation) + Q_ENUMS(RotationDirection) + + Q_PROPERTY(qreal from READ from WRITE setFrom) + Q_PROPERTY(qreal to READ to WRITE setTo) + Q_PROPERTY(RotationDirection direction READ direction WRITE setDirection NOTIFY directionChanged) + +public: + QDeclarativeRotationAnimation(QObject *parent=0); + virtual ~QDeclarativeRotationAnimation(); + + qreal from() const; + void setFrom(qreal); + + qreal to() const; + void setTo(qreal); + + enum RotationDirection { Numerical, Shortest, Clockwise, Counterclockwise }; + RotationDirection direction() const; + void setDirection(RotationDirection direction); + +Q_SIGNALS: + void directionChanged(); +}; + +class QDeclarativeAnimationGroupPrivate; +class Q_AUTOTEST_EXPORT QDeclarativeAnimationGroup : public QDeclarativeAbstractAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeAnimationGroup) + + Q_CLASSINFO("DefaultProperty", "animations") + Q_PROPERTY(QDeclarativeListProperty animations READ animations) + +public: + QDeclarativeAnimationGroup(QObject *parent); + virtual ~QDeclarativeAnimationGroup(); + + QDeclarativeListProperty animations(); + friend class QDeclarativeAbstractAnimation; + +protected: + QDeclarativeAnimationGroup(QDeclarativeAnimationGroupPrivate &dd, QObject *parent); +}; + +class QDeclarativeSequentialAnimation : public QDeclarativeAnimationGroup +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeAnimationGroup) + +public: + QDeclarativeSequentialAnimation(QObject *parent=0); + virtual ~QDeclarativeSequentialAnimation(); + +protected: + virtual void transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation(); +}; + +class QDeclarativeParallelAnimation : public QDeclarativeAnimationGroup +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeAnimationGroup) + +public: + QDeclarativeParallelAnimation(QObject *parent=0); + virtual ~QDeclarativeParallelAnimation(); + +protected: + virtual void transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation(); +}; + +class QDeclarativeParentAnimationPrivate; +class QDeclarativeParentAnimation : public QDeclarativeAnimationGroup +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeParentAnimation) + + Q_PROPERTY(QDeclarativeItem *target READ target WRITE setTarget NOTIFY targetChanged) + Q_PROPERTY(QDeclarativeItem *newParent READ newParent WRITE setNewParent NOTIFY newParentChanged) + Q_PROPERTY(QDeclarativeItem *via READ via WRITE setVia NOTIFY viaChanged) + +public: + QDeclarativeParentAnimation(QObject *parent=0); + virtual ~QDeclarativeParentAnimation(); + + QDeclarativeItem *target() const; + void setTarget(QDeclarativeItem *); + + QDeclarativeItem *newParent() const; + void setNewParent(QDeclarativeItem *); + + QDeclarativeItem *via() const; + void setVia(QDeclarativeItem *); + +Q_SIGNALS: + void targetChanged(); + void newParentChanged(); + void viaChanged(); + +protected: + virtual void transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation(); +}; + +class QDeclarativeAnchorAnimationPrivate; +class QDeclarativeAnchorAnimation : public QDeclarativeAbstractAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeAnchorAnimation) + Q_PROPERTY(QDeclarativeListProperty targets READ targets) + Q_PROPERTY(int duration READ duration WRITE setDuration NOTIFY durationChanged) + Q_PROPERTY(QEasingCurve easing READ easing WRITE setEasing NOTIFY easingChanged) + +public: + QDeclarativeAnchorAnimation(QObject *parent=0); + virtual ~QDeclarativeAnchorAnimation(); + + QDeclarativeListProperty targets(); + + int duration() const; + void setDuration(int); + + QEasingCurve easing() const; + void setEasing(const QEasingCurve &); + +Q_SIGNALS: + void durationChanged(int); + void easingChanged(const QEasingCurve&); + +protected: + virtual void transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeAbstractAnimation) +QML_DECLARE_TYPE(QDeclarativePauseAnimation) +QML_DECLARE_TYPE(QDeclarativeScriptAction) +QML_DECLARE_TYPE(QDeclarativePropertyAction) +QML_DECLARE_TYPE(QDeclarativePropertyAnimation) +QML_DECLARE_TYPE(QDeclarativeColorAnimation) +QML_DECLARE_TYPE(QDeclarativeNumberAnimation) +QML_DECLARE_TYPE(QDeclarativeSequentialAnimation) +QML_DECLARE_TYPE(QDeclarativeParallelAnimation) +QML_DECLARE_TYPE(QDeclarativeVector3dAnimation) +QML_DECLARE_TYPE(QDeclarativeRotationAnimation) +QML_DECLARE_TYPE(QDeclarativeParentAnimation) +QML_DECLARE_TYPE(QDeclarativeAnchorAnimation) + +QT_END_HEADER + +#endif // QDECLARATIVEANIMATION_H diff --git a/src/declarative/util/qdeclarativeanimation_p_p.h b/src/declarative/util/qdeclarativeanimation_p_p.h new file mode 100644 index 00000000..24e10884 --- /dev/null +++ b/src/declarative/util/qdeclarativeanimation_p_p.h @@ -0,0 +1,397 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEANIMATION_P_H +#define QDECLARATIVEANIMATION_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 "private/qdeclarativeanimation_p.h" + +#include "private/qdeclarativenullablevalue_p_p.h" +#include "private/qdeclarativetimeline_p_p.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +//interface for classes that provide animation actions for QActionAnimation +class QAbstractAnimationAction +{ +public: + virtual ~QAbstractAnimationAction() {} + virtual void doAction() = 0; +}; + +//templated animation action +//allows us to specify an action that calls a function of a class. +//(so that class doesn't have to inherit QDeclarativeAbstractAnimationAction) +template +class QAnimationActionProxy : public QAbstractAnimationAction +{ +public: + QAnimationActionProxy(T *p) : m_p(p) {} + virtual void doAction() { (m_p->*method)(); } + +private: + T *m_p; +}; + +//performs an action of type QAbstractAnimationAction +class Q_AUTOTEST_EXPORT QActionAnimation : public QAbstractAnimation +{ + Q_OBJECT +public: + QActionAnimation(QObject *parent = 0) : QAbstractAnimation(parent), animAction(0), policy(KeepWhenStopped) {} + QActionAnimation(QAbstractAnimationAction *action, QObject *parent = 0) + : QAbstractAnimation(parent), animAction(action), policy(KeepWhenStopped) {} + ~QActionAnimation() { if (policy == DeleteWhenStopped) { delete animAction; animAction = 0; } } + virtual int duration() const { return 0; } + void setAnimAction(QAbstractAnimationAction *action, DeletionPolicy p) + { + if (state() == Running) + stop(); + if (policy == DeleteWhenStopped) + delete animAction; + animAction = action; + policy = p; + } +protected: + virtual void updateCurrentTime(int) {} + + virtual void updateState(State newState, State /*oldState*/) + { + if (newState == Running) { + if (animAction) { + animAction->doAction(); + if (state() == Stopped && policy == DeleteWhenStopped) { + delete animAction; + animAction = 0; + } + } + } + } + +private: + QAbstractAnimationAction *animAction; + DeletionPolicy policy; +}; + +class QDeclarativeBulkValueUpdater +{ +public: + virtual ~QDeclarativeBulkValueUpdater() {} + virtual void setValue(qreal value) = 0; +}; + +//animates QDeclarativeBulkValueUpdater (assumes start and end values will be reals or compatible) +class Q_AUTOTEST_EXPORT QDeclarativeBulkValueAnimator : public QVariantAnimation +{ + Q_OBJECT +public: + QDeclarativeBulkValueAnimator(QObject *parent = 0) : QVariantAnimation(parent), animValue(0), fromSourced(0), policy(KeepWhenStopped) {} + ~QDeclarativeBulkValueAnimator() { if (policy == DeleteWhenStopped) { delete animValue; animValue = 0; } } + void setAnimValue(QDeclarativeBulkValueUpdater *value, DeletionPolicy p) + { + if (state() == Running) + stop(); + if (policy == DeleteWhenStopped) + delete animValue; + animValue = value; + policy = p; + } + void setFromSourcedValue(bool *value) + { + fromSourced = value; + } +protected: + virtual void updateCurrentValue(const QVariant &value) + { + if (state() == QAbstractAnimation::Stopped) + return; + + if (animValue) + animValue->setValue(value.toReal()); + } + virtual void updateState(State newState, State oldState) + { + QVariantAnimation::updateState(newState, oldState); + if (newState == Running) { + //check for new from every loop + if (fromSourced) + *fromSourced = false; + } + } + +private: + QDeclarativeBulkValueUpdater *animValue; + bool *fromSourced; + DeletionPolicy policy; +}; + +//an animation that just gives a tick +template +class QTickAnimationProxy : public QAbstractAnimation +{ + //Q_OBJECT //doesn't work with templating +public: + QTickAnimationProxy(T *p, QObject *parent = 0) : QAbstractAnimation(parent), m_p(p) {} + virtual int duration() const { return -1; } +protected: + virtual void updateCurrentTime(int msec) { (m_p->*method)(msec); } + +private: + T *m_p; +}; + +class QDeclarativeAbstractAnimationPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeAbstractAnimation) +public: + QDeclarativeAbstractAnimationPrivate() + : running(false), paused(false), alwaysRunToEnd(false), + connectedTimeLine(false), componentComplete(true), + avoidPropertyValueSourceStart(false), disableUserControl(false), + registered(false), loopCount(1), group(0) {} + + bool running:1; + bool paused:1; + bool alwaysRunToEnd:1; + bool connectedTimeLine:1; + bool componentComplete:1; + bool avoidPropertyValueSourceStart:1; + bool disableUserControl:1; + bool registered:1; + + int loopCount; + + void commence(); + + QDeclarativeProperty defaultProperty; + + QDeclarativeAnimationGroup *group; + + static QDeclarativeProperty createProperty(QObject *obj, const QString &str, QObject *infoObj); +}; + +class QDeclarativePauseAnimationPrivate : public QDeclarativeAbstractAnimationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativePauseAnimation) +public: + QDeclarativePauseAnimationPrivate() + : QDeclarativeAbstractAnimationPrivate(), pa(0) {} + + void init(); + + QPauseAnimation *pa; +}; + +class QDeclarativeScriptActionPrivate : public QDeclarativeAbstractAnimationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeScriptAction) +public: + QDeclarativeScriptActionPrivate(); + + void init(); + + QDeclarativeScriptString script; + QString name; + QDeclarativeScriptString runScriptScript; + bool hasRunScriptScript; + bool reversing; + + void execute(); + + QAnimationActionProxy proxy; + QActionAnimation *rsa; +}; + +class QDeclarativePropertyActionPrivate : public QDeclarativeAbstractAnimationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativePropertyAction) +public: + QDeclarativePropertyActionPrivate() + : QDeclarativeAbstractAnimationPrivate(), target(0), spa(0) {} + + void init(); + + QObject *target; + QString propertyName; + QString properties; + QList targets; + QList exclude; + + QDeclarativeNullableValue value; + + QActionAnimation *spa; +}; + +class QDeclarativeAnimationGroupPrivate : public QDeclarativeAbstractAnimationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeAnimationGroup) +public: + QDeclarativeAnimationGroupPrivate() + : QDeclarativeAbstractAnimationPrivate(), ag(0) {} + + static void append_animation(QDeclarativeListProperty *list, QDeclarativeAbstractAnimation *role); + static void clear_animation(QDeclarativeListProperty *list); + QList animations; + QAnimationGroup *ag; +}; + +class QDeclarativePropertyAnimationPrivate : public QDeclarativeAbstractAnimationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativePropertyAnimation) +public: + QDeclarativePropertyAnimationPrivate() + : QDeclarativeAbstractAnimationPrivate(), target(0), fromSourced(false), fromIsDefined(false), toIsDefined(false), + rangeIsSet(false), defaultToInterpolatorType(0), interpolatorType(0), interpolator(0), va(0), actions(0) {} + + void init(); + + QVariant from; + QVariant to; + + QObject *target; + QString propertyName; + QString properties; + QList targets; + QList exclude; + QString defaultProperties; + + bool fromSourced; + bool fromIsDefined:1; + bool toIsDefined:1; + bool rangeIsSet:1; + bool defaultToInterpolatorType:1; + int interpolatorType; + QVariantAnimation::Interpolator interpolator; + + QDeclarativeBulkValueAnimator *va; + + // for animations that don't use the QDeclarativeBulkValueAnimator + QDeclarativeStateActions *actions; + + static QVariant interpolateVariant(const QVariant &from, const QVariant &to, qreal progress); + static void convertVariant(QVariant &variant, int type); +}; + +class QDeclarativeRotationAnimationPrivate : public QDeclarativePropertyAnimationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeRotationAnimation) +public: + QDeclarativeRotationAnimationPrivate() : direction(QDeclarativeRotationAnimation::Numerical) {} + + QDeclarativeRotationAnimation::RotationDirection direction; +}; + +class QDeclarativeParentAnimationPrivate : public QDeclarativeAnimationGroupPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeParentAnimation) +public: + QDeclarativeParentAnimationPrivate() + : QDeclarativeAnimationGroupPrivate(), target(0), newParent(0), + via(0), topLevelGroup(0), startAction(0), endAction(0) {} + + QDeclarativeItem *target; + QDeclarativeItem *newParent; + QDeclarativeItem *via; + + QSequentialAnimationGroup *topLevelGroup; + QActionAnimation *startAction; + QActionAnimation *endAction; + + QPointF computeTransformOrigin(QDeclarativeItem::TransformOrigin origin, qreal width, qreal height) const; +}; + +class QDeclarativeAnchorAnimationPrivate : public QDeclarativeAbstractAnimationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeAnchorAnimation) +public: + QDeclarativeAnchorAnimationPrivate() : rangeIsSet(false), va(0), + interpolator(QVariantAnimationPrivate::getInterpolator(QMetaType::QReal)) {} + + bool rangeIsSet; + QDeclarativeBulkValueAnimator *va; + QVariantAnimation::Interpolator interpolator; + QList targets; +}; + +class Q_AUTOTEST_EXPORT QDeclarativeAnimationPropertyUpdater : public QDeclarativeBulkValueUpdater +{ +public: + QDeclarativeStateActions actions; + int interpolatorType; //for Number/ColorAnimation + int prevInterpolatorType; //for generic + QVariantAnimation::Interpolator interpolator; + bool reverse; + bool fromSourced; + bool fromDefined; + bool *wasDeleted; + QDeclarativeAnimationPropertyUpdater() : prevInterpolatorType(0), wasDeleted(0) {} + ~QDeclarativeAnimationPropertyUpdater() { if (wasDeleted) *wasDeleted = true; } + void setValue(qreal v); +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVEANIMATION_P_H diff --git a/src/declarative/util/qdeclarativeapplication.cpp b/src/declarative/util/qdeclarativeapplication.cpp new file mode 100644 index 00000000..af80aede --- /dev/null +++ b/src/declarative/util/qdeclarativeapplication.cpp @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativeapplication_p.h" +#include +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeApplicationPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeApplication) +public: + QDeclarativeApplicationPrivate() : active(QApplication::activeWindow() != 0), + layoutDirection(QApplication::layoutDirection()) {} + bool active; + Qt::LayoutDirection layoutDirection; +}; + +/* + This object and its properties are documented as part of the Qt object, + in qdeclarativengine.cpp +*/ + +QDeclarativeApplication::QDeclarativeApplication(QObject *parent) : QObject(*new QDeclarativeApplicationPrivate(), parent) +{ + if (qApp) + qApp->installEventFilter(this); +} + +QDeclarativeApplication::~QDeclarativeApplication() +{ +} + +bool QDeclarativeApplication::active() const +{ + Q_D(const QDeclarativeApplication); + return d->active; +} + +Qt::LayoutDirection QDeclarativeApplication::layoutDirection() const +{ + Q_D(const QDeclarativeApplication); + return d->layoutDirection; +} + +bool QDeclarativeApplication::eventFilter(QObject *obj, QEvent *event) +{ + Q_UNUSED(obj) + Q_D(QDeclarativeApplication); + if (event->type() == QEvent::ApplicationActivate + || event->type() == QEvent::ApplicationDeactivate) { + bool active = d->active; + if (event->type() == QEvent::ApplicationActivate) + active = true; + else if (event->type() == QEvent::ApplicationDeactivate) + active = false; + + if (d->active != active) { + d->active = active; + emit activeChanged(); + } + } + if (event->type() == QEvent::LayoutDirectionChange) { + Qt::LayoutDirection direction = QApplication::layoutDirection(); + if (d->layoutDirection != direction) { + d->layoutDirection = direction; + emit layoutDirectionChanged(); + } + } + return false; +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativeapplication_p.h b/src/declarative/util/qdeclarativeapplication_p.h new file mode 100644 index 00000000..70852ed8 --- /dev/null +++ b/src/declarative/util/qdeclarativeapplication_p.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEAPPLICATION_P_H +#define QDECLARATIVEAPPLICATION_P_H + +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeApplicationPrivate; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeApplication : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool active READ active NOTIFY activeChanged) + Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection NOTIFY layoutDirectionChanged) + +public: + explicit QDeclarativeApplication(QObject *parent = 0); + virtual ~QDeclarativeApplication(); + bool active() const; + Qt::LayoutDirection layoutDirection() const; + +protected: + bool eventFilter(QObject *obj, QEvent *event); + +Q_SIGNALS: + void activeChanged(); + void layoutDirectionChanged(); + +private: + Q_DISABLE_COPY(QDeclarativeApplication) + Q_DECLARE_PRIVATE(QDeclarativeApplication) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeApplication) + +QT_END_HEADER + +#endif // QDECLARATIVEAPPLICATION_P_H diff --git a/src/declarative/util/qdeclarativebehavior.cpp b/src/declarative/util/qdeclarativebehavior.cpp new file mode 100644 index 00000000..8921bee6 --- /dev/null +++ b/src/declarative/util/qdeclarativebehavior.cpp @@ -0,0 +1,230 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativebehavior_p.h" + +#include "private/qdeclarativeanimation_p.h" +#include "private/qdeclarativetransition_p.h" + +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeBehaviorPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeBehavior) +public: + QDeclarativeBehaviorPrivate() : animation(0), enabled(true), finalized(false) + , blockRunningChanged(false) {} + + QDeclarativeProperty property; + QVariant currentValue; + QVariant targetValue; + QDeclarativeGuard animation; + bool enabled; + bool finalized; + bool blockRunningChanged; +}; + +/*! + \qmlclass Behavior QDeclarativeBehavior + \ingroup qml-animation-transition + \since 4.7 + \brief The Behavior element allows you to specify a default animation for a property change. + + A Behavior defines the default animation to be applied whenever a + particular property value changes. + + For example, the following Behavior defines a NumberAnimation to be run + whenever the \l Rectangle's \c width value changes. When the MouseArea + is clicked, the \c width is changed, triggering the behavior's animation: + + \snippet doc/src/snippets/declarative/behavior.qml 0 + + Note that a property cannot have more than one assigned Behavior. To provide + multiple animations within a Behavior, use ParallelAnimation or + SequentialAnimation. + + If a \l{QML States}{state change} has a \l Transition that matches the same property as a + Behavior, the \l Transition animation overrides the Behavior for that + state change. For general advice on using Behaviors to animate state changes, see + \l{Using QML Behaviors with States}. + + \sa {QML Animation and Transitions}, {declarative/animation/behaviors}{Behavior example}, QtDeclarative +*/ + + +QDeclarativeBehavior::QDeclarativeBehavior(QObject *parent) + : QObject(*(new QDeclarativeBehaviorPrivate), parent) +{ +} + +QDeclarativeBehavior::~QDeclarativeBehavior() +{ +} + +/*! + \qmlproperty Animation Behavior::animation + \default + + This property holds the animation to run when the behavior is triggered. +*/ + +QDeclarativeAbstractAnimation *QDeclarativeBehavior::animation() +{ + Q_D(QDeclarativeBehavior); + return d->animation; +} + +void QDeclarativeBehavior::setAnimation(QDeclarativeAbstractAnimation *animation) +{ + Q_D(QDeclarativeBehavior); + if (d->animation) { + qmlInfo(this) << tr("Cannot change the animation assigned to a Behavior."); + return; + } + + d->animation = animation; + if (d->animation) { + d->animation->setDefaultTarget(d->property); + d->animation->setDisableUserControl(); + connect(d->animation->qtAnimation(), + SIGNAL(stateChanged(QAbstractAnimation::State,QAbstractAnimation::State)), + this, + SLOT(qtAnimationStateChanged(QAbstractAnimation::State,QAbstractAnimation::State))); + } +} + + +void QDeclarativeBehavior::qtAnimationStateChanged(QAbstractAnimation::State newState,QAbstractAnimation::State) +{ + Q_D(QDeclarativeBehavior); + if (!d->blockRunningChanged) + d->animation->notifyRunningChanged(newState == QAbstractAnimation::Running); +} + + +/*! + \qmlproperty bool Behavior::enabled + + This property holds whether the behavior will be triggered when the tracked + property changes value. + + By default a Behavior is enabled. +*/ + +bool QDeclarativeBehavior::enabled() const +{ + Q_D(const QDeclarativeBehavior); + return d->enabled; +} + +void QDeclarativeBehavior::setEnabled(bool enabled) +{ + Q_D(QDeclarativeBehavior); + if (d->enabled == enabled) + return; + d->enabled = enabled; + emit enabledChanged(); +} + +void QDeclarativeBehavior::write(const QVariant &value) +{ + Q_D(QDeclarativeBehavior); + qmlExecuteDeferred(this); + if (!d->animation || !d->enabled || !d->finalized) { + QDeclarativePropertyPrivate::write(d->property, value, QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding); + d->targetValue = value; + return; + } + + if (d->animation->isRunning() && value == d->targetValue) + return; + + d->currentValue = d->property.read(); + d->targetValue = value; + + if (d->animation->qtAnimation()->duration() != -1 + && d->animation->qtAnimation()->state() != QAbstractAnimation::Stopped) { + d->blockRunningChanged = true; + d->animation->qtAnimation()->stop(); + } + + QDeclarativeStateOperation::ActionList actions; + QDeclarativeAction action; + action.property = d->property; + action.fromValue = d->currentValue; + action.toValue = value; + actions << action; + + QList after; + d->animation->transition(actions, after, QDeclarativeAbstractAnimation::Forward); + d->animation->qtAnimation()->start(); + d->blockRunningChanged = false; + if (!after.contains(d->property)) + QDeclarativePropertyPrivate::write(d->property, value, QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding); +} + +void QDeclarativeBehavior::setTarget(const QDeclarativeProperty &property) +{ + Q_D(QDeclarativeBehavior); + d->property = property; + d->currentValue = property.read(); + if (d->animation) + d->animation->setDefaultTarget(property); + + QDeclarativeEnginePrivate *engPriv = QDeclarativeEnginePrivate::get(qmlEngine(this)); + engPriv->registerFinalizedParserStatusObject(this, this->metaObject()->indexOfSlot("componentFinalized()")); +} + +void QDeclarativeBehavior::componentFinalized() +{ + Q_D(QDeclarativeBehavior); + d->finalized = true; +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativebehavior_p.h b/src/declarative/util/qdeclarativebehavior_p.h new file mode 100644 index 00000000..d9294678 --- /dev/null +++ b/src/declarative/util/qdeclarativebehavior_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEBEHAVIOR_H +#define QDECLARATIVEBEHAVIOR_H + +#include "private/qdeclarativestate_p.h" + +#include +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeAbstractAnimation; +class QDeclarativeBehaviorPrivate; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeBehavior : public QObject, public QDeclarativePropertyValueInterceptor +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeBehavior) + + Q_INTERFACES(QDeclarativePropertyValueInterceptor) + Q_CLASSINFO("DefaultProperty", "animation") + Q_PROPERTY(QDeclarativeAbstractAnimation *animation READ animation WRITE setAnimation) + Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) + Q_CLASSINFO("DeferredPropertyNames", "animation") + +public: + QDeclarativeBehavior(QObject *parent=0); + ~QDeclarativeBehavior(); + + virtual void setTarget(const QDeclarativeProperty &); + virtual void write(const QVariant &value); + + QDeclarativeAbstractAnimation *animation(); + void setAnimation(QDeclarativeAbstractAnimation *); + + bool enabled() const; + void setEnabled(bool enabled); + +Q_SIGNALS: + void enabledChanged(); + +private Q_SLOTS: + void componentFinalized(); + void qtAnimationStateChanged(QAbstractAnimation::State,QAbstractAnimation::State); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeBehavior) + +QT_END_HEADER + +#endif // QDECLARATIVEBEHAVIOR_H diff --git a/src/declarative/util/qdeclarativebind.cpp b/src/declarative/util/qdeclarativebind.cpp new file mode 100644 index 00000000..bd077a81 --- /dev/null +++ b/src/declarative/util/qdeclarativebind.cpp @@ -0,0 +1,214 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativebind_p.h" + +#include "private/qdeclarativenullablevalue_p_p.h" +#include "private/qdeclarativeguard_p.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeBindPrivate : public QObjectPrivate +{ +public: + QDeclarativeBindPrivate() : when(true), componentComplete(true), obj(0) {} + + bool when : 1; + bool componentComplete : 1; + QDeclarativeGuard obj; + QString prop; + QDeclarativeNullableValue value; +}; + + +/*! + \qmlclass Binding QDeclarativeBind + \ingroup qml-working-with-data + \since 4.7 + \brief The Binding element allows arbitrary property bindings to be created. + + Sometimes it is necessary to bind to a property of an object that wasn't + directly instantiated by QML - generally a property of a class exported + to QML by C++. In these cases, regular property binding doesn't work. Binding + allows you to bind any value to any property. + + For example, imagine a C++ application that maps an "app.enteredText" + property into QML. You could use Binding to update the enteredText property + like this. + \code + TextEdit { id: myTextField; text: "Please type here..." } + Binding { target: app; property: "enteredText"; value: myTextField.text } + \endcode + Whenever the text in the TextEdit is updated, the C++ property will be + updated also. + + If the binding target or binding property is changed, the bound value is + immediately pushed onto the new target. + + \sa QtDeclarative +*/ +QDeclarativeBind::QDeclarativeBind(QObject *parent) + : QObject(*(new QDeclarativeBindPrivate), parent) +{ +} + +QDeclarativeBind::~QDeclarativeBind() +{ +} + +/*! + \qmlproperty bool Binding::when + + This property holds when the binding is active. + This should be set to an expression that evaluates to true when you want the binding to be active. + + \code + Binding { + target: contactName; property: 'text' + value: name; when: list.ListView.isCurrentItem + } + \endcode +*/ +bool QDeclarativeBind::when() const +{ + Q_D(const QDeclarativeBind); + return d->when; +} + +void QDeclarativeBind::setWhen(bool v) +{ + Q_D(QDeclarativeBind); + d->when = v; + eval(); +} + +/*! + \qmlproperty Object Binding::target + + The object to be updated. +*/ +QObject *QDeclarativeBind::object() +{ + Q_D(const QDeclarativeBind); + return d->obj; +} + +void QDeclarativeBind::setObject(QObject *obj) +{ + Q_D(QDeclarativeBind); + d->obj = obj; + eval(); +} + +/*! + \qmlproperty string Binding::property + + The property to be updated. +*/ +QString QDeclarativeBind::property() const +{ + Q_D(const QDeclarativeBind); + return d->prop; +} + +void QDeclarativeBind::setProperty(const QString &p) +{ + Q_D(QDeclarativeBind); + d->prop = p; + eval(); +} + +/*! + \qmlproperty any Binding::value + + The value to be set on the target object and property. This can be a + constant (which isn't very useful), or a bound expression. +*/ +QVariant QDeclarativeBind::value() const +{ + Q_D(const QDeclarativeBind); + return d->value.value; +} + +void QDeclarativeBind::setValue(const QVariant &v) +{ + Q_D(QDeclarativeBind); + d->value.value = v; + d->value.isNull = false; + eval(); +} + +void QDeclarativeBind::classBegin() +{ + Q_D(QDeclarativeBind); + d->componentComplete = false; +} + +void QDeclarativeBind::componentComplete() +{ + Q_D(QDeclarativeBind); + d->componentComplete = true; + eval(); +} + +void QDeclarativeBind::eval() +{ + Q_D(QDeclarativeBind); + if (!d->obj || d->value.isNull || !d->when || !d->componentComplete) + return; + + QDeclarativeProperty prop(d->obj, d->prop); + prop.write(d->value.value); +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativebind_p.h b/src/declarative/util/qdeclarativebind_p.h new file mode 100644 index 00000000..eb607a71 --- /dev/null +++ b/src/declarative/util/qdeclarativebind_p.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEBIND_H +#define QDECLARATIVEBIND_H + +#include + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeBindPrivate; +class Q_AUTOTEST_EXPORT QDeclarativeBind : public QObject, public QDeclarativeParserStatus +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeBind) + Q_INTERFACES(QDeclarativeParserStatus) + Q_PROPERTY(QObject *target READ object WRITE setObject) + Q_PROPERTY(QString property READ property WRITE setProperty) + Q_PROPERTY(QVariant value READ value WRITE setValue) + Q_PROPERTY(bool when READ when WRITE setWhen) + +public: + QDeclarativeBind(QObject *parent=0); + ~QDeclarativeBind(); + + bool when() const; + void setWhen(bool); + + QObject *object(); + void setObject(QObject *); + + QString property() const; + void setProperty(const QString &); + + QVariant value() const; + void setValue(const QVariant &); + +protected: + virtual void classBegin(); + virtual void componentComplete(); + +private: + void eval(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeBind) + +QT_END_HEADER + +#endif diff --git a/src/declarative/util/qdeclarativeconnections.cpp b/src/declarative/util/qdeclarativeconnections.cpp new file mode 100644 index 00000000..7c9c9336 --- /dev/null +++ b/src/declarative/util/qdeclarativeconnections.cpp @@ -0,0 +1,289 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativeconnections_p.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeConnectionsPrivate : public QObjectPrivate +{ +public: + QDeclarativeConnectionsPrivate() : target(0), targetSet(false), ignoreUnknownSignals(false), componentcomplete(true) {} + + QList boundsignals; + QObject *target; + + bool targetSet; + bool ignoreUnknownSignals; + bool componentcomplete; + + QByteArray data; +}; + +/*! + \qmlclass Connections QDeclarativeConnections + \ingroup qml-utility-elements + \since 4.7 + \brief A Connections element describes generalized connections to signals. + + A Connections object creates a connection to a QML signal. + + When connecting to signals in QML, the usual way is to create an + "on" handler that reacts when a signal is received, like this: + + \qml + MouseArea { + onClicked: { foo(parameters) } + } + \endqml + + However, it is not possible to connect to a signal in this way in some + cases, such as when: + + \list + \i Multiple connections to the same signal are required + \i Creating connections outside the scope of the signal sender + \i Connecting to targets not defined in QML + \endlist + + When any of these are needed, the Connections element can be used instead. + + For example, the above code can be changed to use a Connections object, + like this: + + \qml + MouseArea { + Connections { + onClicked: foo(parameters) + } + } + \endqml + + More generally, the Connections object can be a child of some object other than + the sender of the signal: + + \qml + MouseArea { + id: area + } + // ... + \endqml + \qml + Connections { + target: area + onClicked: foo(parameters) + } + \endqml + + \sa QtDeclarative +*/ +QDeclarativeConnections::QDeclarativeConnections(QObject *parent) : + QObject(*(new QDeclarativeConnectionsPrivate), parent) +{ +} + +QDeclarativeConnections::~QDeclarativeConnections() +{ +} + +/*! + \qmlproperty Object Connections::target + This property holds the object that sends the signal. + + If this property is not set, the \c target defaults to the parent of the Connection. + + If set to null, no connection is made and any signal handlers are ignored + until the target is not null. +*/ +QObject *QDeclarativeConnections::target() const +{ + Q_D(const QDeclarativeConnections); + return d->targetSet ? d->target : parent(); +} + +void QDeclarativeConnections::setTarget(QObject *obj) +{ + Q_D(QDeclarativeConnections); + d->targetSet = true; // even if setting to 0, it is *set* + if (d->target == obj) + return; + foreach (QDeclarativeBoundSignal *s, d->boundsignals) { + // It is possible that target is being changed due to one of our signal + // handlers -> use deleteLater(). + if (s->isEvaluating()) + s->deleteLater(); + else + delete s; + } + d->boundsignals.clear(); + d->target = obj; + connectSignals(); + emit targetChanged(); +} + +/*! + \qmlproperty bool Connections::ignoreUnknownSignals + + Normally, a connection to a non-existent signal produces runtime errors. + + If this property is set to \c true, such errors are ignored. + This is useful if you intend to connect to different types of objects, handling + a different set of signals for each object. +*/ +bool QDeclarativeConnections::ignoreUnknownSignals() const +{ + Q_D(const QDeclarativeConnections); + return d->ignoreUnknownSignals; +} + +void QDeclarativeConnections::setIgnoreUnknownSignals(bool ignore) +{ + Q_D(QDeclarativeConnections); + d->ignoreUnknownSignals = ignore; +} + + + +QByteArray +QDeclarativeConnectionsParser::compile(const QList &props) +{ + QByteArray rv; + QDataStream ds(&rv, QIODevice::WriteOnly); + + for(int ii = 0; ii < props.count(); ++ii) + { + QString propName = QString::fromUtf8(props.at(ii).name()); + if (!propName.startsWith(QLatin1String("on")) || !propName.at(2).isUpper()) { + error(props.at(ii), QDeclarativeConnections::tr("Cannot assign to non-existent property \"%1\"").arg(propName)); + return QByteArray(); + } + + QList values = props.at(ii).assignedValues(); + + for (int i = 0; i < values.count(); ++i) { + const QVariant &value = values.at(i); + + if (value.userType() == qMetaTypeId()) { + error(props.at(ii), QDeclarativeConnections::tr("Connections: nested objects not allowed")); + return QByteArray(); + } else if (value.userType() == qMetaTypeId()) { + error(props.at(ii), QDeclarativeConnections::tr("Connections: syntax error")); + return QByteArray(); + } else { + QDeclarativeParser::Variant v = qvariant_cast(value); + if (v.isScript()) { + ds << propName; + ds << v.asScript(); + } else { + error(props.at(ii), QDeclarativeConnections::tr("Connections: script expected")); + return QByteArray(); + } + } + } + } + + return rv; +} + +void QDeclarativeConnectionsParser::setCustomData(QObject *object, + const QByteArray &data) +{ + QDeclarativeConnectionsPrivate *p = + static_cast(QObjectPrivate::get(object)); + p->data = data; +} + + +void QDeclarativeConnections::connectSignals() +{ + Q_D(QDeclarativeConnections); + if (!d->componentcomplete || (d->targetSet && !target())) + return; + + QDataStream ds(d->data); + while (!ds.atEnd()) { + QString propName; + ds >> propName; + QString script; + ds >> script; + QDeclarativeProperty prop(target(), propName); + if (prop.isValid() && (prop.type() & QDeclarativeProperty::SignalProperty)) { + QDeclarativeBoundSignal *signal = + new QDeclarativeBoundSignal(target(), prop.method(), this); + QDeclarativeExpression *expression = new QDeclarativeExpression(qmlContext(this), 0, script); + QDeclarativeData *ddata = QDeclarativeData::get(this); + if (ddata && ddata->outerContext && !ddata->outerContext->url.isEmpty()) + expression->setSourceLocation(ddata->outerContext->url.toString(), ddata->lineNumber); + signal->setExpression(expression); + d->boundsignals += signal; + } else { + if (!d->ignoreUnknownSignals) + qmlInfo(this) << tr("Cannot assign to non-existent property \"%1\"").arg(propName); + } + } +} + +void QDeclarativeConnections::classBegin() +{ + Q_D(QDeclarativeConnections); + d->componentcomplete=false; +} + +void QDeclarativeConnections::componentComplete() +{ + Q_D(QDeclarativeConnections); + d->componentcomplete=true; + connectSignals(); +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativeconnections_p.h b/src/declarative/util/qdeclarativeconnections_p.h new file mode 100644 index 00000000..2cb34f2f --- /dev/null +++ b/src/declarative/util/qdeclarativeconnections_p.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVECONNECTIONS_H +#define QDECLARATIVECONNECTIONS_H + +#include +#include +#include + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeBoundSignal; +class QDeclarativeContext; +class QDeclarativeConnectionsPrivate; +class Q_AUTOTEST_EXPORT QDeclarativeConnections : public QObject, public QDeclarativeParserStatus +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeConnections) + + Q_INTERFACES(QDeclarativeParserStatus) + Q_PROPERTY(QObject *target READ target WRITE setTarget NOTIFY targetChanged) + Q_PROPERTY(bool ignoreUnknownSignals READ ignoreUnknownSignals WRITE setIgnoreUnknownSignals) + +public: + QDeclarativeConnections(QObject *parent=0); + ~QDeclarativeConnections(); + + QObject *target() const; + void setTarget(QObject *); + + bool ignoreUnknownSignals() const; + void setIgnoreUnknownSignals(bool ignore); + +Q_SIGNALS: + void targetChanged(); + +private: + void connectSignals(); + void classBegin(); + void componentComplete(); +}; + +class QDeclarativeConnectionsParser : public QDeclarativeCustomParser +{ +public: + virtual QByteArray compile(const QList &); + virtual void setCustomData(QObject *, const QByteArray &); +}; + + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeConnections) + +QT_END_HEADER + +#endif diff --git a/src/declarative/util/qdeclarativefontloader.cpp b/src/declarative/util/qdeclarativefontloader.cpp new file mode 100644 index 00000000..d1fd49a1 --- /dev/null +++ b/src/declarative/util/qdeclarativefontloader.cpp @@ -0,0 +1,338 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativefontloader_p.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +#define FONTLOADER_MAXIMUM_REDIRECT_RECURSION 16 + +class QDeclarativeFontObject : public QObject +{ +Q_OBJECT + +public: + QDeclarativeFontObject(int _id); + + void download(const QUrl &url, QNetworkAccessManager *manager); + +Q_SIGNALS: + void fontDownloaded(const QString&, QDeclarativeFontLoader::Status); + +private Q_SLOTS: + void replyFinished(); + +public: + int id; + +private: + QNetworkReply *reply; + int redirectCount; + + Q_DISABLE_COPY(QDeclarativeFontObject) +}; + +QDeclarativeFontObject::QDeclarativeFontObject(int _id = -1) + : QObject(0), id(_id), reply(0), redirectCount(0) {} + + +void QDeclarativeFontObject::download(const QUrl &url, QNetworkAccessManager *manager) +{ + QNetworkRequest req(url); + req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); + reply = manager->get(req); + QObject::connect(reply, SIGNAL(finished()), this, SLOT(replyFinished())); +} + +void QDeclarativeFontObject::replyFinished() +{ + if (reply) { + redirectCount++; + if (redirectCount < FONTLOADER_MAXIMUM_REDIRECT_RECURSION) { + QVariant redirect = reply->attribute(QNetworkRequest::RedirectionTargetAttribute); + if (redirect.isValid()) { + QUrl url = reply->url().resolved(redirect.toUrl()); + QNetworkAccessManager *manager = reply->manager(); + reply->deleteLater(); + reply = 0; + download(url, manager); + return; + } + } + redirectCount = 0; + + if (!reply->error()) { + id = QFontDatabase::addApplicationFontFromData(reply->readAll()); + if (id != -1) + emit fontDownloaded(QFontDatabase::applicationFontFamilies(id).at(0), QDeclarativeFontLoader::Ready); + else + emit fontDownloaded(QString(), QDeclarativeFontLoader::Error); + } else { + emit fontDownloaded(QString(), QDeclarativeFontLoader::Error); + } + reply->deleteLater(); + reply = 0; + } +} + + +class QDeclarativeFontLoaderPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeFontLoader) + +public: + QDeclarativeFontLoaderPrivate() : status(QDeclarativeFontLoader::Null) {} + + QUrl url; + QString name; + QDeclarativeFontLoader::Status status; + static QHash fonts; +}; + +QHash QDeclarativeFontLoaderPrivate::fonts; + +/*! + \qmlclass FontLoader QDeclarativeFontLoader + \ingroup qml-utility-elements + \since 4.7 + \brief The FontLoader element allows fonts to be loaded by name or URL. + + The FontLoader element is used to load fonts by name or URL. + + The \l status indicates when the font has been loaded, which is useful + for fonts loaded from remote sources. + + For example: + \qml + import QtQuick 1.0 + + Column { + FontLoader { id: fixedFont; name: "Courier" } + FontLoader { id: webFont; source: "http://www.mysite.com/myfont.ttf" } + + Text { text: "Fixed-size font"; font.family: fixedFont.name } + Text { text: "Fancy font"; font.family: webFont.name } + } + \endqml + + \sa {declarative/text/fonts}{Fonts example} +*/ +QDeclarativeFontLoader::QDeclarativeFontLoader(QObject *parent) + : QObject(*(new QDeclarativeFontLoaderPrivate), parent) +{ +} + +QDeclarativeFontLoader::~QDeclarativeFontLoader() +{ +} + +/*! + \qmlproperty url FontLoader::source + The url of the font to load. +*/ +QUrl QDeclarativeFontLoader::source() const +{ + Q_D(const QDeclarativeFontLoader); + return d->url; +} + +void QDeclarativeFontLoader::setSource(const QUrl &url) +{ + Q_D(QDeclarativeFontLoader); + if (url == d->url) + return; + d->url = qmlContext(this)->resolvedUrl(url); + emit sourceChanged(); + +#ifndef QT_NO_LOCALFILE_OPTIMIZED_QML + QString localFile = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(d->url); + if (!localFile.isEmpty()) { + if (!d->fonts.contains(d->url)) { + int id = QFontDatabase::addApplicationFont(localFile); + if (id != -1) { + updateFontInfo(QFontDatabase::applicationFontFamilies(id).at(0), Ready); + QDeclarativeFontObject *fo = new QDeclarativeFontObject(id); + d->fonts[d->url] = fo; + } else { + updateFontInfo(QString(), Error); + } + } else { + updateFontInfo(QFontDatabase::applicationFontFamilies(d->fonts[d->url]->id).at(0), Ready); + } + } else +#endif + { + if (!d->fonts.contains(d->url)) { + QDeclarativeFontObject *fo = new QDeclarativeFontObject; + d->fonts[d->url] = fo; + fo->download(d->url, qmlEngine(this)->networkAccessManager()); + d->status = Loading; + emit statusChanged(); + QObject::connect(fo, SIGNAL(fontDownloaded(QString,QDeclarativeFontLoader::Status)), + this, SLOT(updateFontInfo(QString,QDeclarativeFontLoader::Status))); + } else { + QDeclarativeFontObject *fo = d->fonts[d->url]; + if (fo->id == -1) { + d->status = Loading; + emit statusChanged(); + QObject::connect(fo, SIGNAL(fontDownloaded(QString,QDeclarativeFontLoader::Status)), + this, SLOT(updateFontInfo(QString,QDeclarativeFontLoader::Status))); + } + else + updateFontInfo(QFontDatabase::applicationFontFamilies(fo->id).at(0), Ready); + } + } +} + +void QDeclarativeFontLoader::updateFontInfo(const QString& name, QDeclarativeFontLoader::Status status) +{ + Q_D(QDeclarativeFontLoader); + + if (name != d->name) { + d->name = name; + emit nameChanged(); + } + if (status != d->status) { + if (status == Error) + qmlInfo(this) << "Cannot load font: \"" << d->url.toString() << "\""; + d->status = status; + emit statusChanged(); + } +} + +/*! + \qmlproperty string FontLoader::name + + This property holds the name of the font family. + It is set automatically when a font is loaded using the \c url property. + + Use this to set the \c font.family property of a \c Text item. + + Example: + \qml + Item { + width: 200; height: 50 + + FontLoader { + id: webFont + source: "http://www.mysite.com/myfont.ttf" + } + Text { + text: "Fancy font" + font.family: webFont.name + } + } + \endqml +*/ +QString QDeclarativeFontLoader::name() const +{ + Q_D(const QDeclarativeFontLoader); + return d->name; +} + +void QDeclarativeFontLoader::setName(const QString &name) +{ + Q_D(QDeclarativeFontLoader); + if (d->name == name) + return; + d->name = name; + emit nameChanged(); + d->status = Ready; + emit statusChanged(); +} + +/*! + \qmlproperty enumeration FontLoader::status + + This property holds the status of font loading. It can be one of: + \list + \o FontLoader.Null - no font has been set + \o FontLoader.Ready - the font has been loaded + \o FontLoader.Loading - the font is currently being loaded + \o FontLoader.Error - an error occurred while loading the font + \endlist + + Use this status to provide an update or respond to the status change in some way. + For example, you could: + + \list + \o Trigger a state change: + \qml + State { name: 'loaded'; when: loader.status == FontLoader.Ready } + \endqml + + \o Implement an \c onStatusChanged signal handler: + \qml + FontLoader { + id: loader + onStatusChanged: if (loader.status == FontLoader.Ready) console.log('Loaded') + } + \endqml + + \o Bind to the status value: + \qml + Text { text: loader.status == FontLoader.Ready ? 'Loaded' : 'Not loaded' } + \endqml + \endlist +*/ +QDeclarativeFontLoader::Status QDeclarativeFontLoader::status() const +{ + Q_D(const QDeclarativeFontLoader); + return d->status; +} + +QT_END_NAMESPACE + +#include diff --git a/src/declarative/util/qdeclarativefontloader_p.h b/src/declarative/util/qdeclarativefontloader_p.h new file mode 100644 index 00000000..23927224 --- /dev/null +++ b/src/declarative/util/qdeclarativefontloader_p.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEFONTLOADER_H +#define QDECLARATIVEFONTLOADER_H + +#include + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeFontLoaderPrivate; +class Q_AUTOTEST_EXPORT QDeclarativeFontLoader : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeFontLoader) + Q_ENUMS(Status) + + Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_PROPERTY(Status status READ status NOTIFY statusChanged) + +public: + enum Status { Null = 0, Ready, Loading, Error }; + + QDeclarativeFontLoader(QObject *parent = 0); + ~QDeclarativeFontLoader(); + + QUrl source() const; + void setSource(const QUrl &url); + + QString name() const; + void setName(const QString &name); + + Status status() const; + +private Q_SLOTS: + void updateFontInfo(const QString&, QDeclarativeFontLoader::Status); + +Q_SIGNALS: + void sourceChanged(); + void nameChanged(); + void statusChanged(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeFontLoader) + +QT_END_HEADER + +#endif // QDECLARATIVEFONTLOADER_H + diff --git a/src/declarative/util/qdeclarativelistaccessor.cpp b/src/declarative/util/qdeclarativelistaccessor.cpp new file mode 100644 index 00000000..5a0e2fde --- /dev/null +++ b/src/declarative/util/qdeclarativelistaccessor.cpp @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativelistaccessor_p.h" + +#include + +#include +#include + +// ### Remove me +#include + +QT_BEGIN_NAMESPACE + +QDeclarativeListAccessor::QDeclarativeListAccessor() +: m_type(Invalid) +{ +} + +QDeclarativeListAccessor::~QDeclarativeListAccessor() +{ +} + +QVariant QDeclarativeListAccessor::list() const +{ + return d; +} + +void QDeclarativeListAccessor::setList(const QVariant &v, QDeclarativeEngine *engine) +{ + d = v; + + QDeclarativeEnginePrivate *enginePrivate = engine?QDeclarativeEnginePrivate::get(engine):0; + + if (!d.isValid()) { + m_type = Invalid; + } else if (d.userType() == QVariant::StringList) { + m_type = StringList; + } else if (d.userType() == QMetaType::QVariantList) { + m_type = VariantList; + } else if (d.canConvert(QVariant::Int)) { + m_type = Integer; + } else if ((!enginePrivate && QDeclarativeMetaType::isQObject(d.userType())) || + (enginePrivate && enginePrivate->isQObject(d.userType()))) { + QObject *data = enginePrivate?enginePrivate->toQObject(v):QDeclarativeMetaType::toQObject(v); + d = QVariant::fromValue(data); + m_type = Instance; + } else if (d.userType() == qMetaTypeId()) { + m_type = ListProperty; + } else { + m_type = Instance; + } +} + +int QDeclarativeListAccessor::count() const +{ + switch(m_type) { + case StringList: + return qvariant_cast(d).count(); + case VariantList: + return qvariant_cast(d).count(); + case ListProperty: + return ((QDeclarativeListReference *)d.constData())->count(); + case Instance: + return 1; + case Integer: + return d.toInt(); + default: + case Invalid: + return 0; + } +} + +QVariant QDeclarativeListAccessor::at(int idx) const +{ + Q_ASSERT(idx >= 0 && idx < count()); + switch(m_type) { + case StringList: + return QVariant::fromValue(qvariant_cast(d).at(idx)); + case VariantList: + return qvariant_cast(d).at(idx); + case ListProperty: + return QVariant::fromValue(((QDeclarativeListReference *)d.constData())->at(idx)); + case Instance: + return d; + case Integer: + return QVariant(idx); + default: + case Invalid: + return QVariant(); + } +} + +bool QDeclarativeListAccessor::isValid() const +{ + return m_type != Invalid; +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativelistaccessor_p.h b/src/declarative/util/qdeclarativelistaccessor_p.h new file mode 100644 index 00000000..8686a9d2 --- /dev/null +++ b/src/declarative/util/qdeclarativelistaccessor_p.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVELISTACCESSOR_H +#define QDECLARATIVELISTACCESSOR_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeEngine; +class Q_AUTOTEST_EXPORT QDeclarativeListAccessor +{ +public: + QDeclarativeListAccessor(); + ~QDeclarativeListAccessor(); + + QVariant list() const; + void setList(const QVariant &, QDeclarativeEngine * = 0); + + bool isValid() const; + + int count() const; + QVariant at(int) const; + + enum Type { Invalid, StringList, VariantList, ListProperty, Instance, Integer }; + Type type() const { return m_type; } + +private: + Type m_type; + QVariant d; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVELISTACCESSOR_H diff --git a/src/declarative/util/qdeclarativelistmodel.cpp b/src/declarative/util/qdeclarativelistmodel.cpp new file mode 100644 index 00000000..b87f9144 --- /dev/null +++ b/src/declarative/util/qdeclarativelistmodel.cpp @@ -0,0 +1,1628 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativelistmodel_p_p.h" +#include "private/qdeclarativelistmodelworkeragent_p.h" +#include "private/qdeclarativeopenmetaobject_p.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +Q_DECLARE_METATYPE(QListModelInterface *) + +QT_BEGIN_NAMESPACE + +template +void qdeclarativelistmodel_move(int from, int to, int n, T *items) +{ + if (n == 1) { + items->move(from, to); + } else { + T replaced; + int i=0; + typename T::ConstIterator it=items->begin(); it += from+n; + for (; ibegin(); it += from; + for (; ibegin(); t += from; + for (; f != replaced.end(); ++f, ++t) + *t = *f; + } +} + +QDeclarativeListModelParser::ListInstruction *QDeclarativeListModelParser::ListModelData::instructions() const +{ + return (QDeclarativeListModelParser::ListInstruction *)((char *)this + sizeof(ListModelData)); +} + +/*! + \qmlclass ListModel QDeclarativeListModel + \ingroup qml-working-with-data + \since 4.7 + \brief The ListModel element defines a free-form list data source. + + The ListModel is a simple container of ListElement definitions, each containing data roles. + The contents can be defined dynamically, or explicitly in QML. + + The number of elements in the model can be obtained from its \l count property. + A number of familiar methods are also provided to manipulate the contents of the + model, including append(), insert(), move(), remove() and set(). These methods + accept dictionaries as their arguments; these are translated to ListElement objects + by the model. + + Elements can be manipulated via the model using the setProperty() method, which + allows the roles of the specified element to be set and changed. + + \section1 Example Usage + + The following example shows a ListModel containing three elements, with the roles + "name" and "cost". + + \div {class="float-right"} + \inlineimage listmodel.png + \enddiv + + \snippet doc/src/snippets/declarative/listmodel.qml 0 + + \clearfloat + Roles (properties) in each element must begin with a lower-case letter and + should be common to all elements in a model. The ListElement documentation + provides more guidelines for how elements should be defined. + + Since the example model contains an \c id property, it can be referenced + by views, such as the ListView in this example: + + \snippet doc/src/snippets/declarative/listmodel-simple.qml 0 + \dots 8 + \snippet doc/src/snippets/declarative/listmodel-simple.qml 1 + + It is possible for roles to contain list data. In the following example we + create a list of fruit attributes: + + \snippet doc/src/snippets/declarative/listmodel-nested.qml model + + The delegate displays all the fruit attributes: + + \div {class="float-right"} + \inlineimage listmodel-nested.png + \enddiv + + \snippet doc/src/snippets/declarative/listmodel-nested.qml delegate + + \clearfloat + \section1 Modifying List Models + + The content of a ListModel may be created and modified using the clear(), + append(), set(), insert() and setProperty() methods. For example: + + \snippet doc/src/snippets/declarative/listmodel-modify.qml delegate + + Note that when creating content dynamically the set of available properties + cannot be changed once set. Whatever properties are first added to the model + are the only permitted properties in the model. + + \section1 Using Threaded List Models with WorkerScript + + ListModel can be used together with WorkerScript access a list model + from multiple threads. This is useful if list modifications are + synchronous and take some time: the list operations can be moved to a + different thread to avoid blocking of the main GUI thread. + + Here is an example that uses WorkerScript to periodically append the + current time to a list model: + + \snippet examples/declarative/threading/threadedlistmodel/timedisplay.qml 0 + + The included file, \tt dataloader.js, looks like this: + + \snippet examples/declarative/threading/threadedlistmodel/dataloader.js 0 + + The timer in the main example sends messages to the worker script by calling + \l WorkerScript::sendMessage(). When this message is received, + \l{WorkerScript::onMessage}{WorkerScript.onMessage()} is invoked in \c dataloader.js, + which appends the current time to the list model. + + Note the call to sync() from the \l{WorkerScript::onMessage}{WorkerScript.onMessage()} + handler. You must call sync() or else the changes made to the list from the external + thread will not be reflected in the list model in the main thread. + + \section1 Restrictions + + If a list model is to be accessed from a WorkerScript, it cannot + contain list-type data. So, the following model cannot be used from a WorkerScript + because of the list contained in the "attributes" property: + + \code + ListModel { + id: fruitModel + ListElement { + name: "Apple" + cost: 2.45 + attributes: [ + ListElement { description: "Core" }, + ListElement { description: "Deciduous" } + ] + } + } + \endcode + + In addition, the WorkerScript cannot add list-type data to the model. + + \sa {qmlmodels}{Data Models}, {declarative/threading/threadedlistmodel}{Threaded ListModel example}, QtDeclarative +*/ + + +/* + A ListModel internally uses either a NestedListModel or FlatListModel. + + A NestedListModel can contain lists of ListElements (which + when retrieved from get() is accessible as a list model within the list + model) whereas a FlatListModel cannot. + + ListModel uses a NestedListModel to begin with, and if the model is later + used from a WorkerScript, it changes to use a FlatListModel instead. This + is because ModelNode (which abstracts the nested list model data) needs + access to the declarative engine and script engine, which cannot be + safely used from outside of the main thread. +*/ + +QDeclarativeListModel::QDeclarativeListModel(QObject *parent) +: QListModelInterface(parent), m_agent(0), m_nested(new NestedListModel(this)), m_flat(0) +{ +} + +QDeclarativeListModel::QDeclarativeListModel(const QDeclarativeListModel *orig, QDeclarativeListModelWorkerAgent *parent) +: QListModelInterface(parent), m_agent(0), m_nested(0), m_flat(0) +{ + m_flat = new FlatListModel(this); + m_flat->m_parentAgent = parent; + + if (orig->m_flat) { + m_flat->m_roles = orig->m_flat->m_roles; + m_flat->m_strings = orig->m_flat->m_strings; + m_flat->m_values = orig->m_flat->m_values; + + m_flat->m_nodeData.reserve(m_flat->m_values.count()); + for (int i=0; im_values.count(); i++) + m_flat->m_nodeData << 0; + } +} + +QDeclarativeListModel::~QDeclarativeListModel() +{ + if (m_agent) + m_agent->release(); + + delete m_nested; + delete m_flat; +} + +bool QDeclarativeListModel::flatten() +{ + if (m_flat) + return true; + + QList roles = m_nested->roles(); + + QList > values; + bool hasNested = false; + for (int i=0; icount(); i++) { + values.append(m_nested->data(i, roles, &hasNested)); + if (hasNested) + return false; + } + + FlatListModel *flat = new FlatListModel(this); + flat->m_values = values; + + for (int i=0; itoString(roles[i]); + flat->m_roles.insert(roles[i], s); + flat->m_strings.insert(s, roles[i]); + } + + flat->m_nodeData.reserve(flat->m_values.count()); + for (int i=0; im_values.count(); i++) + flat->m_nodeData << 0; + + m_flat = flat; + delete m_nested; + m_nested = 0; + return true; +} + +bool QDeclarativeListModel::inWorkerThread() const +{ + return m_flat && m_flat->m_parentAgent; +} + +QDeclarativeListModelWorkerAgent *QDeclarativeListModel::agent() +{ + if (m_agent) + return m_agent; + + if (!flatten()) { + qmlInfo(this) << "List contains list-type data and cannot be used from a worker script"; + return 0; + } + + m_agent = new QDeclarativeListModelWorkerAgent(this); + return m_agent; +} + +QList QDeclarativeListModel::roles() const +{ + return m_flat ? m_flat->roles() : m_nested->roles(); +} + +QString QDeclarativeListModel::toString(int role) const +{ + return m_flat ? m_flat->toString(role) : m_nested->toString(role); +} + +QVariant QDeclarativeListModel::data(int index, int role) const +{ + if (index >= count() || index < 0) + return QVariant(); + + return m_flat ? m_flat->data(index, role) : m_nested->data(index, role); +} + +/*! + \qmlproperty int ListModel::count + The number of data entries in the model. +*/ +int QDeclarativeListModel::count() const +{ + return m_flat ? m_flat->count() : m_nested->count(); +} + +/*! + \qmlmethod ListModel::clear() + + Deletes all content from the model. + + \sa append() remove() +*/ +void QDeclarativeListModel::clear() +{ + int cleared = count(); + if (m_flat) + m_flat->clear(); + else + m_nested->clear(); + + if (!inWorkerThread()) { + emit itemsRemoved(0, cleared); + emit countChanged(); + } +} + +QDeclarativeListModel *ModelNode::model(const NestedListModel *model) +{ + if (!modelCache) { + modelCache = new QDeclarativeListModel; + QDeclarativeEngine::setContextForObject(modelCache,QDeclarativeEngine::contextForObject(model->m_listModel)); + modelCache->m_nested->_root = this; // ListModel defaults to nestable model + + for (int i=0; i(values.at(i)); + if (subNode) + subNode->m_model = modelCache->m_nested; + } + } + return modelCache; +} + +ModelObject *ModelNode::object(const NestedListModel *model) +{ + if (!objectCache) { + objectCache = new ModelObject(this, + const_cast(model), + QDeclarativeEnginePrivate::getScriptEngine(qmlEngine(model->m_listModel))); + QHash::iterator it; + for (it = properties.begin(); it != properties.end(); ++it) { + objectCache->setValue(it.key().toUtf8(), model->valueForNode(*it)); + } + objectCache->setNodeUpdatesEnabled(true); + } + return objectCache; +} + +/*! + \qmlmethod ListModel::remove(int index) + + Deletes the content at \a index from the model. + + \sa clear() +*/ +void QDeclarativeListModel::remove(int index) +{ + if (index < 0 || index >= count()) { + qmlInfo(this) << tr("remove: index %1 out of range").arg(index); + return; + } + + if (m_flat) + m_flat->remove(index); + else + m_nested->remove(index); + + if (!inWorkerThread()) { + emit itemsRemoved(index, 1); + emit countChanged(); + } +} + +/*! + \qmlmethod ListModel::insert(int index, jsobject dict) + + Adds a new item to the list model at position \a index, with the + values in \a dict. + + \code + fruitModel.insert(2, {"cost": 5.95, "name":"Pizza"}) + \endcode + + The \a index must be to an existing item in the list, or one past + the end of the list (equivalent to append). + + \sa set() append() +*/ +void QDeclarativeListModel::insert(int index, const QScriptValue& valuemap) +{ + if (!valuemap.isObject() || valuemap.isArray()) { + qmlInfo(this) << tr("insert: value is not an object"); + return; + } + + if (index < 0 || index > count()) { + qmlInfo(this) << tr("insert: index %1 out of range").arg(index); + return; + } + + bool ok = m_flat ? m_flat->insert(index, valuemap) : m_nested->insert(index, valuemap); + if (ok && !inWorkerThread()) { + emit itemsInserted(index, 1); + emit countChanged(); + } +} + +/*! + \qmlmethod ListModel::move(int from, int to, int n) + + Moves \a n items \a from one position \a to another. + + The from and to ranges must exist; for example, to move the first 3 items + to the end of the list: + + \code + fruitModel.move(0, fruitModel.count - 3, 3) + \endcode + + \sa append() +*/ +void QDeclarativeListModel::move(int from, int to, int n) +{ + if (n==0 || from==to) + return; + if (!canMove(from, to, n)) { + qmlInfo(this) << tr("move: out of range"); + return; + } + + int origfrom = from; + int origto = to; + int orign = n; + if (from > to) { + // Only move forwards - flip if backwards moving + int tfrom = from; + int tto = to; + from = tto; + to = tto+n; + n = tfrom-tto; + } + + if (m_flat) + m_flat->move(from, to, n); + else + m_nested->move(from, to, n); + + if (!inWorkerThread()) + emit itemsMoved(origfrom, origto, orign); +} + +/*! + \qmlmethod ListModel::append(jsobject dict) + + Adds a new item to the end of the list model, with the + values in \a dict. + + \code + fruitModel.append({"cost": 5.95, "name":"Pizza"}) + \endcode + + \sa set() remove() +*/ +void QDeclarativeListModel::append(const QScriptValue& valuemap) +{ + if (!valuemap.isObject() || valuemap.isArray()) { + qmlInfo(this) << tr("append: value is not an object"); + return; + } + + insert(count(), valuemap); +} + +/*! + \qmlmethod object ListModel::get(int index) + + Returns the item at \a index in the list model. This allows the item + data to be accessed or modified from JavaScript: + + \code + Component.onCompleted: { + fruitModel.append({"cost": 5.95, "name":"Jackfruit"}); + console.log(fruitModel.get(0).cost); + fruitModel.get(0).cost = 10.95; + } + \endcode + + The \a index must be an element in the list. + + Note that properties of the returned object that are themselves objects + will also be models, and this get() method is used to access elements: + + \code + fruitModel.append(..., "attributes": + [{"name":"spikes","value":"7mm"}, + {"name":"color","value":"green"}]); + fruitModel.get(0).attributes.get(1).value; // == "green" + \endcode + + \warning The returned object is not guaranteed to remain valid. It + should not be used in \l{Property Binding}{property bindings}. + + \sa append() +*/ +QScriptValue QDeclarativeListModel::get(int index) const +{ + // the internal flat/nested class checks for bad index + return m_flat ? m_flat->get(index) : m_nested->get(index); +} + +/*! + \qmlmethod ListModel::set(int index, jsobject dict) + + Changes the item at \a index in the list model with the + values in \a dict. Properties not appearing in \a dict + are left unchanged. + + \code + fruitModel.set(3, {"cost": 5.95, "name":"Pizza"}) + \endcode + + If \a index is equal to count() then a new item is appended to the + list. Otherwise, \a index must be an element in the list. + + \sa append() +*/ +void QDeclarativeListModel::set(int index, const QScriptValue& valuemap) +{ + QList roles; + set(index, valuemap, &roles); + if (!roles.isEmpty() && !inWorkerThread()) + emit itemsChanged(index, 1, roles); +} + +void QDeclarativeListModel::set(int index, const QScriptValue& valuemap, QList *roles) +{ + if (!valuemap.isObject() || valuemap.isArray()) { + qmlInfo(this) << tr("set: value is not an object"); + return; + } + if (index > count() || index < 0) { + qmlInfo(this) << tr("set: index %1 out of range").arg(index); + return; + } + + if (index == count()) { + append(valuemap); + } else { + if (m_flat) + m_flat->set(index, valuemap, roles); + else + m_nested->set(index, valuemap, roles); + } +} + +/*! + \qmlmethod ListModel::setProperty(int index, string property, variant value) + + Changes the \a property of the item at \a index in the list model to \a value. + + \code + fruitModel.setProperty(3, "cost", 5.95) + \endcode + + The \a index must be an element in the list. + + \sa append() +*/ +void QDeclarativeListModel::setProperty(int index, const QString& property, const QVariant& value) +{ + QList roles; + setProperty(index, property, value, &roles); + if (!roles.isEmpty() && !inWorkerThread()) + emit itemsChanged(index, 1, roles); +} + +void QDeclarativeListModel::setProperty(int index, const QString& property, const QVariant& value, QList *roles) +{ + if (count() == 0 || index >= count() || index < 0) { + qmlInfo(this) << tr("set: index %1 out of range").arg(index); + return; + } + + if (m_flat) + m_flat->setProperty(index, property, value, roles); + else + m_nested->setProperty(index, property, value, roles); +} + +/*! + \qmlmethod ListModel::sync() + + Writes any unsaved changes to the list model after it has been modified + from a worker script. +*/ +void QDeclarativeListModel::sync() +{ + // This is just a dummy method to make it look like sync() exists in + // ListModel (and not just QDeclarativeListModelWorkerAgent) and to let + // us document sync(). + qmlInfo(this) << "List sync() can only be called from a WorkerScript"; +} + +bool QDeclarativeListModelParser::compileProperty(const QDeclarativeCustomParserProperty &prop, QList &instr, QByteArray &data) +{ + QList values = prop.assignedValues(); + for(int ii = 0; ii < values.count(); ++ii) { + const QVariant &value = values.at(ii); + + if(value.userType() == qMetaTypeId()) { + QDeclarativeCustomParserNode node = + qvariant_cast(value); + + if (node.name() != listElementTypeName) { + const QMetaObject *mo = resolveType(node.name()); + if (mo != &QDeclarativeListElement::staticMetaObject) { + error(node, QDeclarativeListModel::tr("ListElement: cannot contain nested elements")); + return false; + } + listElementTypeName = node.name(); // cache right name for next time + } + + { + ListInstruction li; + li.type = ListInstruction::Push; + li.dataIdx = -1; + instr << li; + } + + QList props = node.properties(); + for(int jj = 0; jj < props.count(); ++jj) { + const QDeclarativeCustomParserProperty &nodeProp = props.at(jj); + if (nodeProp.name().isEmpty()) { + error(nodeProp, QDeclarativeListModel::tr("ListElement: cannot contain nested elements")); + return false; + } + if (nodeProp.name() == "id") { + error(nodeProp, QDeclarativeListModel::tr("ListElement: cannot use reserved \"id\" property")); + return false; + } + + ListInstruction li; + int ref = data.count(); + data.append(nodeProp.name()); + data.append('\0'); + li.type = ListInstruction::Set; + li.dataIdx = ref; + instr << li; + + if(!compileProperty(nodeProp, instr, data)) + return false; + + li.type = ListInstruction::Pop; + li.dataIdx = -1; + instr << li; + } + + { + ListInstruction li; + li.type = ListInstruction::Pop; + li.dataIdx = -1; + instr << li; + } + + } else { + + QDeclarativeParser::Variant variant = + qvariant_cast(value); + + int ref = data.count(); + + QByteArray d; + d += char(variant.type()); // type tag + if (variant.isString()) { + d += variant.asString().toUtf8(); + } else if (variant.isNumber()) { + d += QByteArray::number(variant.asNumber(),'g',20); + } else if (variant.isBoolean()) { + d += char(variant.asBoolean()); + } else if (variant.isScript()) { + if (definesEmptyList(variant.asScript())) { + d[0] = char(QDeclarativeParser::Variant::Invalid); // marks empty list + } else { + QByteArray script = variant.asScript().toUtf8(); + int v = evaluateEnum(script); + if (v<0) { + if (script.startsWith("QT_TR_NOOP(\"") && script.endsWith("\")")) { + d[0] = char(QDeclarativeParser::Variant::String); + d += script.mid(12,script.length()-14); + } else { + error(prop, QDeclarativeListModel::tr("ListElement: cannot use script for property value")); + return false; + } + } else { + d[0] = char(QDeclarativeParser::Variant::Number); + d += QByteArray::number(v); + } + } + } + d.append('\0'); + data.append(d); + + ListInstruction li; + li.type = ListInstruction::Value; + li.dataIdx = ref; + instr << li; + } + } + + return true; +} + +QByteArray QDeclarativeListModelParser::compile(const QList &customProps) +{ + QList instr; + QByteArray data; + listElementTypeName = QByteArray(); // unknown + + for(int ii = 0; ii < customProps.count(); ++ii) { + const QDeclarativeCustomParserProperty &prop = customProps.at(ii); + if(!prop.name().isEmpty()) { // isn't default property + error(prop, QDeclarativeListModel::tr("ListModel: undefined property '%1'").arg(QString::fromUtf8(prop.name()))); + return QByteArray(); + } + + if(!compileProperty(prop, instr, data)) { + return QByteArray(); + } + } + + int size = sizeof(ListModelData) + + instr.count() * sizeof(ListInstruction) + + data.count(); + + QByteArray rv; + rv.resize(size); + + ListModelData *lmd = (ListModelData *)rv.data(); + lmd->dataOffset = sizeof(ListModelData) + + instr.count() * sizeof(ListInstruction); + lmd->instrCount = instr.count(); + for (int ii = 0; ii < instr.count(); ++ii) + lmd->instructions()[ii] = instr.at(ii); + ::memcpy(rv.data() + lmd->dataOffset, data.constData(), data.count()); + + return rv; +} + +void QDeclarativeListModelParser::setCustomData(QObject *obj, const QByteArray &d) +{ + QDeclarativeListModel *rv = static_cast(obj); + + ModelNode *root = new ModelNode(rv->m_nested); + rv->m_nested->m_ownsRoot = true; + rv->m_nested->_root = root; + QStack nodes; + nodes << root; + + bool processingSet = false; + + const ListModelData *lmd = (const ListModelData *)d.constData(); + const char *data = ((const char *)lmd) + lmd->dataOffset; + + for (int ii = 0; ii < lmd->instrCount; ++ii) { + const ListInstruction &instr = lmd->instructions()[ii]; + + switch(instr.type) { + case ListInstruction::Push: + { + ModelNode *n = nodes.top(); + ModelNode *n2 = new ModelNode(rv->m_nested); + n->values << QVariant::fromValue(n2); + nodes.push(n2); + if (processingSet) + n->isArray = true; + } + break; + + case ListInstruction::Pop: + nodes.pop(); + break; + + case ListInstruction::Value: + { + ModelNode *n = nodes.top(); + switch (QDeclarativeParser::Variant::Type(data[instr.dataIdx])) { + case QDeclarativeParser::Variant::Invalid: + n->isArray = true; + break; + case QDeclarativeParser::Variant::Boolean: + n->values.append(bool(data[1 + instr.dataIdx])); + break; + case QDeclarativeParser::Variant::Number: + n->values.append(QByteArray(data + 1 + instr.dataIdx).toDouble()); + break; + case QDeclarativeParser::Variant::String: + n->values.append(QString::fromUtf8(data + 1 + instr.dataIdx)); + break; + default: + Q_ASSERT("Format error in ListInstruction"); + } + + processingSet = false; + } + break; + + case ListInstruction::Set: + { + ModelNode *n = nodes.top(); + ModelNode *n2 = new ModelNode(rv->m_nested); + n->properties.insert(QString::fromUtf8(data + instr.dataIdx), n2); + nodes.push(n2); + processingSet = true; + } + break; + } + } + + ModelNode *rootNode = rv->m_nested->_root; + for (int i=0; ivalues.count(); ++i) { + ModelNode *node = qvariant_cast(rootNode->values[i]); + node->listIndex = i; + node->updateListIndexes(); + } +} + +bool QDeclarativeListModelParser::definesEmptyList(const QString &s) +{ + if (s.startsWith(QLatin1Char('[')) && s.endsWith(QLatin1Char(']'))) { + for (int i=1; i= 0 && index < m_values.count()); + if (m_values[index].contains(role)) + return m_values[index][role]; + return QVariant(); +} + +QList FlatListModel::roles() const +{ + return m_roles.keys(); +} + +QString FlatListModel::toString(int role) const +{ + if (m_roles.contains(role)) + return m_roles[role]; + return QString(); +} + +int FlatListModel::count() const +{ + return m_values.count(); +} + +void FlatListModel::clear() +{ + m_values.clear(); + + qDeleteAll(m_nodeData); + m_nodeData.clear(); +} + +void FlatListModel::remove(int index) +{ + m_values.removeAt(index); + removedNode(index); +} + +bool FlatListModel::insert(int index, const QScriptValue &value) +{ + Q_ASSERT(index >= 0 && index <= m_values.count()); + + QHash row; + if (!addValue(value, &row, 0)) + return false; + + m_values.insert(index, row); + insertedNode(index); + + return true; +} + +QScriptValue FlatListModel::get(int index) const +{ + QScriptEngine *scriptEngine = m_scriptEngine ? m_scriptEngine : QDeclarativeEnginePrivate::getScriptEngine(qmlEngine(m_listModel)); + + if (!scriptEngine) + return 0; + + if (index < 0 || index >= m_values.count()) + return scriptEngine->undefinedValue(); + + FlatListModel *that = const_cast(this); + if (!m_scriptClass) + that->m_scriptClass = new FlatListScriptClass(that, scriptEngine); + + FlatNodeData *data = m_nodeData.value(index); + if (!data) { + data = new FlatNodeData(index); + that->m_nodeData.replace(index, data); + } + + return QScriptDeclarativeClass::newObject(scriptEngine, m_scriptClass, new FlatNodeObjectData(data)); +} + +void FlatListModel::set(int index, const QScriptValue &value, QList *roles) +{ + Q_ASSERT(index >= 0 && index < m_values.count()); + + QHash row = m_values[index]; + if (addValue(value, &row, roles)) + m_values[index] = row; +} + +void FlatListModel::setProperty(int index, const QString& property, const QVariant& value, QList *roles) +{ + Q_ASSERT(index >= 0 && index < m_values.count()); + + QHash::Iterator iter = m_strings.find(property); + int role; + if (iter == m_strings.end()) { + role = m_roles.count(); + m_roles.insert(role, property); + m_strings.insert(property, role); + } else { + role = iter.value(); + } + + if (m_values[index][role] != value) { + roles->append(role); + m_values[index][role] = value; + } +} + +void FlatListModel::move(int from, int to, int n) +{ + qdeclarativelistmodel_move > >(from, to, n, &m_values); + moveNodes(from, to, n); +} + +bool FlatListModel::addValue(const QScriptValue &value, QHash *row, QList *roles) +{ + QScriptValueIterator it(value); + while (it.hasNext()) { + it.next(); + QScriptValue value = it.value(); + if (!value.isVariant() && !value.isRegExp() && !value.isDate() && value.isObject()) { + qmlInfo(m_listModel) << "Cannot add list-type data when modifying or after modification from a worker script"; + return false; + } + + QString name = it.name(); + QVariant v = it.value().toVariant(); + + QHash::Iterator iter = m_strings.find(name); + if (iter == m_strings.end()) { + int role = m_roles.count(); + m_roles.insert(role, name); + iter = m_strings.insert(name, role); + if (roles) + roles->append(role); + } else { + int role = iter.value(); + if (roles && row->contains(role) && row->value(role) != v) + roles->append(role); + } + row->insert(*iter, v); + } + return true; +} + +void FlatListModel::insertedNode(int index) +{ + if (index >= 0 && index <= m_values.count()) { + m_nodeData.insert(index, 0); + + for (int i=index + 1; iindex = i; + } + } +} + +void FlatListModel::removedNode(int index) +{ + if (index >= 0 && index < m_nodeData.count()) { + delete m_nodeData.takeAt(index); + + for (int i=index; iindex = i; + } + } +} + +void FlatListModel::moveNodes(int from, int to, int n) +{ + if (!m_listModel->canMove(from, to, n)) + return; + + qdeclarativelistmodel_move >(from, to, n, &m_nodeData); + + for (int i=from; iindex = i; + } +} + + + +FlatNodeData::~FlatNodeData() +{ + for (QSet::Iterator iter = objects.begin(); iter != objects.end(); ++iter) { + FlatNodeObjectData *data = *iter; + data->nodeData = 0; + } +} + +void FlatNodeData::addData(FlatNodeObjectData *data) +{ + objects.insert(data); +} + +void FlatNodeData::removeData(FlatNodeObjectData *data) +{ + objects.remove(data); +} + + +FlatListScriptClass::FlatListScriptClass(FlatListModel *model, QScriptEngine *seng) + : QScriptDeclarativeClass(seng), + m_model(model) +{ +} + +QScriptDeclarativeClass::Value FlatListScriptClass::property(Object *obj, const Identifier &name) +{ + FlatNodeObjectData *objData = static_cast(obj); + if (!objData->nodeData) // item at this index has been deleted + return QScriptDeclarativeClass::Value(engine(), engine()->undefinedValue()); + + int index = objData->nodeData->index; + QString propName = toString(name); + int role = m_model->m_strings.value(propName, -1); + + if (role >= 0 && index >=0 ) { + const QHash &row = m_model->m_values[index]; + QScriptValue sv = engine()->toScriptValue(row[role]); + return QScriptDeclarativeClass::Value(engine(), sv); + } + + return QScriptDeclarativeClass::Value(engine(), engine()->undefinedValue()); +} + +void FlatListScriptClass::setProperty(Object *obj, const Identifier &name, const QScriptValue &value) +{ + if (!value.isVariant() && !value.isRegExp() && !value.isDate() && value.isObject()) { + qmlInfo(m_model->m_listModel) << "Cannot add list-type data when modifying or after modification from a worker script"; + return; + } + + FlatNodeObjectData *objData = static_cast(obj); + if (!objData->nodeData) // item at this index has been deleted + return; + + int index = objData->nodeData->index; + QString propName = toString(name); + + int role = m_model->m_strings.value(propName, -1); + if (role >= 0 && index >= 0) { + QHash &row = m_model->m_values[index]; + row[role] = value.toVariant(); + + QList roles; + roles << role; + if (m_model->m_parentAgent) { + // This is the list in the worker thread, so tell the agent to + // emit itemsChanged() later + m_model->m_parentAgent->changedData(index, 1, roles); + } else { + // This is the list in the main thread, so emit itemsChanged() + emit m_model->m_listModel->itemsChanged(index, 1, roles); + } + } +} + +QScriptClass::QueryFlags FlatListScriptClass::queryProperty(Object *, const Identifier &, QScriptClass::QueryFlags) +{ + return (QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess); +} + +bool FlatListScriptClass::compare(Object *obj1, Object *obj2) +{ + FlatNodeObjectData *data1 = static_cast(obj1); + FlatNodeObjectData *data2 = static_cast(obj2); + + if (!data1->nodeData || !data2->nodeData) + return false; + + return data1->nodeData->index == data2->nodeData->index; +} + + + +NestedListModel::NestedListModel(QDeclarativeListModel *base) + : _root(0), m_ownsRoot(false), m_listModel(base), _rolesOk(false) +{ +} + +NestedListModel::~NestedListModel() +{ + if (m_ownsRoot) + delete _root; +} + +QVariant NestedListModel::valueForNode(ModelNode *node, bool *hasNested) const +{ + QObject *rv = 0; + if (hasNested) + *hasNested = false; + + if (node->isArray) { + // List + rv = node->model(this); + if (hasNested) + *hasNested = true; + } else { + if (!node->properties.isEmpty()) { + // Object + rv = node->object(this); + } else if (node->values.count() == 0) { + // Invalid + return QVariant(); + } else if (node->values.count() == 1) { + // Value + QVariant &var = node->values[0]; + ModelNode *valueNode = qvariant_cast(var); + if (valueNode) { + if (!valueNode->properties.isEmpty()) + rv = valueNode->object(this); + else + rv = valueNode->model(this); + } else { + return var; + } + } + } + + if (rv) { + return QVariant::fromValue(rv); + } else { + return QVariant(); + } +} + +QHash NestedListModel::data(int index, const QList &roles, bool *hasNested) const +{ + Q_ASSERT(_root && index >= 0 && index < _root->values.count()); + checkRoles(); + QHash rv; + + ModelNode *node = qvariant_cast(_root->values.at(index)); + if (!node) + return rv; + + for (int ii = 0; ii < roles.count(); ++ii) { + const QString &roleString = roleStrings.at(roles.at(ii)); + + QHash::ConstIterator iter = node->properties.find(roleString); + if (iter != node->properties.end()) { + ModelNode *row = *iter; + rv.insert(roles.at(ii), valueForNode(row, hasNested)); + } + } + + return rv; +} + +QVariant NestedListModel::data(int index, int role) const +{ + Q_ASSERT(_root && index >= 0 && index < _root->values.count()); + checkRoles(); + QVariant rv; + if (roleStrings.count() < role) + return rv; + + ModelNode *node = qvariant_cast(_root->values.at(index)); + if (!node) + return rv; + + const QString &roleString = roleStrings.at(role); + + QHash::ConstIterator iter = node->properties.find(roleString); + if (iter != node->properties.end()) { + ModelNode *row = *iter; + rv = valueForNode(row); + } + + return rv; +} + +int NestedListModel::count() const +{ + if (!_root) return 0; + return _root->values.count(); +} + +void NestedListModel::clear() +{ + if (_root) + _root->clear(); +} + +void NestedListModel::remove(int index) +{ + if (!_root) + return; + ModelNode *node = qvariant_cast(_root->values.at(index)); + _root->values.removeAt(index); + if (node) + delete node; +} + +bool NestedListModel::insert(int index, const QScriptValue& valuemap) +{ + if (!_root) { + _root = new ModelNode(this); + m_ownsRoot = true; + } + + ModelNode *mn = new ModelNode(this); + mn->listIndex = index; + mn->setObjectValue(valuemap); + _root->values.insert(index,QVariant::fromValue(mn)); + return true; +} + +void NestedListModel::move(int from, int to, int n) +{ + if (!_root) + return; + qdeclarativelistmodel_move(from, to, n, &_root->values); +} + +QScriptValue NestedListModel::get(int index) const +{ + QDeclarativeEngine *eng = qmlEngine(m_listModel); + if (!eng) + return 0; + + if (index < 0 || index >= count()) { + QScriptEngine *seng = QDeclarativeEnginePrivate::getScriptEngine(eng); + if (seng) + return seng->undefinedValue(); + return 0; + } + + ModelNode *node = qvariant_cast(_root->values.at(index)); + if (!node) + return 0; + + return QDeclarativeEnginePrivate::qmlScriptObject(node->object(this), eng); +} + +void NestedListModel::set(int index, const QScriptValue& valuemap, QList *roles) +{ + Q_ASSERT(index >=0 && index < count()); + + ModelNode *node = qvariant_cast(_root->values.at(index)); + bool emitItemsChanged = node->setObjectValue(valuemap); + if (!emitItemsChanged) + return; + + QScriptValueIterator it(valuemap); + while (it.hasNext()) { + it.next(); + int r = roleStrings.indexOf(it.name()); + if (r < 0) { + r = roleStrings.count(); + roleStrings << it.name(); + } + roles->append(r); + } +} + +void NestedListModel::setProperty(int index, const QString& property, const QVariant& value, QList *roles) +{ + Q_ASSERT(index >=0 && index < count()); + + ModelNode *node = qvariant_cast(_root->values.at(index)); + bool emitItemsChanged = node->setProperty(property, value); + if (!emitItemsChanged) + return; + + int r = roleStrings.indexOf(property); + if (r < 0) { + r = roleStrings.count(); + roleStrings << property; + } + roles->append(r); +} + +void NestedListModel::checkRoles() const +{ + if (_rolesOk || !_root) + return; + + for (int i = 0; i<_root->values.count(); ++i) { + ModelNode *node = qvariant_cast(_root->values.at(i)); + if (node) { + foreach (const QString &role, node->properties.keys()) { + if (!roleStrings.contains(role)) + roleStrings.append(role); + } + } + } + + _rolesOk = true; +} + +QList NestedListModel::roles() const +{ + checkRoles(); + QList rv; + for (int ii = 0; ii < roleStrings.count(); ++ii) + rv << ii; + return rv; +} + +QString NestedListModel::toString(int role) const +{ + checkRoles(); + if (role < roleStrings.count()) + return roleStrings.at(role); + else + return QString(); +} + + +ModelNode::ModelNode(NestedListModel *model) +: modelCache(0), objectCache(0), isArray(false), m_model(model), listIndex(-1) +{ +} + +ModelNode::~ModelNode() +{ + clear(); + if (modelCache) { modelCache->m_nested->_root = 0/* ==this */; delete modelCache; modelCache = 0; } + if (objectCache) { delete objectCache; objectCache = 0; } +} + +void ModelNode::clear() +{ + ModelNode *node; + for (int ii = 0; ii < values.count(); ++ii) { + node = qvariant_cast(values.at(ii)); + if (node) { delete node; node = 0; } + } + values.clear(); + + qDeleteAll(properties.values()); + properties.clear(); +} + +bool ModelNode::setObjectValue(const QScriptValue& valuemap, bool writeToCache) +{ + bool emitItemsChanged = false; + + QScriptValueIterator it(valuemap); + while (it.hasNext()) { + it.next(); + ModelNode *prev = properties.value(it.name()); + ModelNode *value = new ModelNode(m_model); + QScriptValue v = it.value(); + + if (v.isArray()) { + value->isArray = true; + value->setListValue(v); + if (writeToCache && objectCache) + objectCache->setValue(it.name().toUtf8(), QVariant::fromValue(value->model(m_model))); + emitItemsChanged = true; // for now, too inefficient to check whether list and sublists have changed + } else { + value->values << v.toVariant(); + if (writeToCache && objectCache) + objectCache->setValue(it.name().toUtf8(), value->values.last()); + if (!emitItemsChanged && prev && prev->values.count() == 1 + && prev->values[0] != value->values.last()) { + emitItemsChanged = true; + } + } + if (properties.contains(it.name())) + delete properties[it.name()]; + properties.insert(it.name(), value); + } + return emitItemsChanged; +} + +void ModelNode::setListValue(const QScriptValue& valuelist) { + values.clear(); + int size = valuelist.property(QLatin1String("length")).toInt32(); + for (int i=0; iisArray = true; + value->setListValue(v); + } else if (v.isObject()) { + value->listIndex = i; + value->setObjectValue(v); + } else { + value->listIndex = i; + value->values << v.toVariant(); + } + values.append(QVariant::fromValue(value)); + } +} + +bool ModelNode::setProperty(const QString& prop, const QVariant& val) { + QHash::const_iterator it = properties.find(prop); + bool emitItemsChanged = false; + if (it != properties.end()) { + if (val != (*it)->values[0]) + emitItemsChanged = true; + (*it)->values[0] = val; + } else { + ModelNode *n = new ModelNode(m_model); + n->values << val; + properties.insert(prop,n); + } + if (objectCache) + objectCache->setValue(prop.toUtf8(), val); + return emitItemsChanged; +} + +void ModelNode::updateListIndexes() +{ + for (QHash::ConstIterator iter = properties.begin(); iter != properties.end(); ++iter) { + ModelNode *node = iter.value(); + if (node->isArray) { + for (int i=0; ivalues.count(); ++i) { + ModelNode *subNode = qvariant_cast(node->values.at(i)); + if (subNode) + subNode->listIndex = i; + } + } + node->updateListIndexes(); + } +} + +/* + Need to call this to emit itemsChanged() for modifications outside of set() + and setProperty(), i.e. if an item returned from get() is modified +*/ +void ModelNode::changedProperty(const QString &name) const +{ + if (listIndex < 0) + return; + + m_model->checkRoles(); + QList roles; + int role = m_model->roleStrings.indexOf(name); + if (role < 0) + roles = m_model->roles(); + else + roles << role; + emit m_model->m_listModel->itemsChanged(listIndex, 1, roles); +} + +void ModelNode::dump(ModelNode *node, int ind) +{ + QByteArray indentBa(ind * 4, ' '); + const char *indent = indentBa.constData(); + + for (int ii = 0; ii < node->values.count(); ++ii) { + ModelNode *subNode = qvariant_cast(node->values.at(ii)); + if (subNode) { + qWarning().nospace() << indent << "Sub-node " << ii; + dump(subNode, ind + 1); + } else { + qWarning().nospace() << indent << "Sub-node " << ii << ": " << node->values.at(ii).toString(); + } + } + + for (QHash::ConstIterator iter = node->properties.begin(); iter != node->properties.end(); ++iter) { + qWarning().nospace() << indent << "Property " << iter.key() << ':'; + dump(iter.value(), ind + 1); + } +} + +ModelObject::ModelObject(ModelNode *node, NestedListModel *model, QScriptEngine *seng) + : m_model(model), + m_node(node), + m_meta(new ModelNodeMetaObject(seng, this)) +{ +} + +void ModelObject::setValue(const QByteArray &name, const QVariant &val) +{ + m_meta->setValue(name, val); + //setProperty(name.constData(), val); +} + +void ModelObject::setNodeUpdatesEnabled(bool enable) +{ + m_meta->m_enabled = enable; +} + + +ModelNodeMetaObject::ModelNodeMetaObject(QScriptEngine *seng, ModelObject *object) + : QDeclarativeOpenMetaObject(object), + m_enabled(false), + m_seng(seng), + m_obj(object) +{ +} + +void ModelNodeMetaObject::propertyWritten(int index) +{ + if (!m_enabled) + return; + + QString propName = QString::fromUtf8(name(index)); + QVariant value = operator[](index); + + QScriptValue sv = m_seng->newObject(); + sv.setProperty(propName, m_seng->newVariant(value)); + bool changed = m_obj->m_node->setObjectValue(sv, false); + if (changed) + m_obj->m_node->changedProperty(propName); +} + + +QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativelistmodel_p.h b/src/declarative/util/qdeclarativelistmodel_p.h new file mode 100644 index 00000000..716837f0 --- /dev/null +++ b/src/declarative/util/qdeclarativelistmodel_p.h @@ -0,0 +1,158 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVELISTMODEL_H +#define QDECLARATIVELISTMODEL_H + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class FlatListModel; +class NestedListModel; +class QDeclarativeListModelWorkerAgent; +struct ModelNode; +class FlatListScriptClass; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeListModel : public QListModelInterface +{ + Q_OBJECT + Q_PROPERTY(int count READ count NOTIFY countChanged) + +public: + QDeclarativeListModel(QObject *parent=0); + ~QDeclarativeListModel(); + + virtual QList roles() const; + virtual QString toString(int role) const; + virtual int count() const; + virtual QVariant data(int index, int role) const; + + Q_INVOKABLE void clear(); + Q_INVOKABLE void remove(int index); + Q_INVOKABLE void append(const QScriptValue&); + Q_INVOKABLE void insert(int index, const QScriptValue&); + Q_INVOKABLE QScriptValue get(int index) const; + Q_INVOKABLE void set(int index, const QScriptValue&); + Q_INVOKABLE void setProperty(int index, const QString& property, const QVariant& value); + Q_INVOKABLE void move(int from, int to, int count); + Q_INVOKABLE void sync(); + + QDeclarativeListModelWorkerAgent *agent(); + +Q_SIGNALS: + void countChanged(); + +private: + friend class QDeclarativeListModelParser; + friend class QDeclarativeListModelWorkerAgent; + friend class FlatListModel; + friend class FlatListScriptClass; + friend struct ModelNode; + + // Constructs a flat list model for a worker agent + QDeclarativeListModel(const QDeclarativeListModel *orig, QDeclarativeListModelWorkerAgent *parent); + + void set(int index, const QScriptValue&, QList *roles); + void setProperty(int index, const QString& property, const QVariant& value, QList *roles); + + bool flatten(); + bool inWorkerThread() const; + + inline bool canMove(int from, int to, int n) const { return !(from+n > count() || to+n > count() || from < 0 || to < 0 || n < 0); } + + QDeclarativeListModelWorkerAgent *m_agent; + NestedListModel *m_nested; + FlatListModel *m_flat; +}; + +// ### FIXME +class QDeclarativeListElement : public QObject +{ +Q_OBJECT +}; + +class QDeclarativeListModelParser : public QDeclarativeCustomParser +{ +public: + QByteArray compile(const QList &); + void setCustomData(QObject *, const QByteArray &); + +private: + struct ListInstruction + { + enum { Push, Pop, Value, Set } type; + int dataIdx; + }; + struct ListModelData + { + int dataOffset; + int instrCount; + ListInstruction *instructions() const; + }; + bool compileProperty(const QDeclarativeCustomParserProperty &prop, QList &instr, QByteArray &data); + + bool definesEmptyList(const QString &); + + QByteArray listElementTypeName; +}; + + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeListModel) +QML_DECLARE_TYPE(QDeclarativeListElement) + +QT_END_HEADER + +#endif // QDECLARATIVELISTMODEL_H diff --git a/src/declarative/util/qdeclarativelistmodel_p_p.h b/src/declarative/util/qdeclarativelistmodel_p_p.h new file mode 100644 index 00000000..c5a897c3 --- /dev/null +++ b/src/declarative/util/qdeclarativelistmodel_p_p.h @@ -0,0 +1,281 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVELISTMODEL_P_P_H +#define QDECLARATIVELISTMODEL_P_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 "private/qdeclarativelistmodel_p.h" +#include "private/qdeclarativeengine_p.h" +#include "private/qdeclarativeopenmetaobject_p.h" +#include "qdeclarative.h" + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeOpenMetaObject; +class QScriptEngine; +class QDeclarativeListModelWorkerAgent; +struct ModelNode; +class FlatListScriptClass; +class FlatNodeData; + +class FlatListModel +{ +public: + FlatListModel(QDeclarativeListModel *base); + ~FlatListModel(); + + QVariant data(int index, int role) const; + + QList roles() const; + QString toString(int role) const; + + int count() const; + void clear(); + void remove(int index); + bool insert(int index, const QScriptValue&); + QScriptValue get(int index) const; + void set(int index, const QScriptValue&, QList *roles); + void setProperty(int index, const QString& property, const QVariant& value, QList *roles); + void move(int from, int to, int count); + +private: + friend class QDeclarativeListModelWorkerAgent; + friend class QDeclarativeListModel; + friend class FlatListScriptClass; + friend class FlatNodeData; + + bool addValue(const QScriptValue &value, QHash *row, QList *roles); + void insertedNode(int index); + void removedNode(int index); + void moveNodes(int from, int to, int n); + + QScriptEngine *m_scriptEngine; + QHash m_roles; + QHash m_strings; + QList > m_values; + QDeclarativeListModel *m_listModel; + + FlatListScriptClass *m_scriptClass; + QList m_nodeData; + QDeclarativeListModelWorkerAgent *m_parentAgent; +}; + + +/* + Created when get() is called on a FlatListModel. This allows changes to the + object returned by get() to be tracked, and passed onto the model. +*/ +class FlatListScriptClass : public QScriptDeclarativeClass +{ +public: + FlatListScriptClass(FlatListModel *model, QScriptEngine *seng); + + Value property(Object *, const Identifier &); + void setProperty(Object *, const Identifier &name, const QScriptValue &); + QScriptClass::QueryFlags queryProperty(Object *, const Identifier &, QScriptClass::QueryFlags flags); + bool compare(Object *, Object *); + +private: + FlatListModel *m_model; +}; + +/* + FlatNodeData and FlatNodeObjectData allow objects returned by get() to still + point to the correct list index if move(), insert() or remove() are called. +*/ +struct FlatNodeObjectData; +class FlatNodeData +{ +public: + FlatNodeData(int i) + : index(i) {} + + ~FlatNodeData(); + + void addData(FlatNodeObjectData *data); + void removeData(FlatNodeObjectData *data); + + int index; + +private: + QSet objects; +}; + +struct FlatNodeObjectData : public QScriptDeclarativeClass::Object +{ + FlatNodeObjectData(FlatNodeData *data) : nodeData(data) { + nodeData->addData(this); + } + + ~FlatNodeObjectData() { + if (nodeData) + nodeData->removeData(this); + } + + FlatNodeData *nodeData; +}; + + + +class NestedListModel +{ +public: + NestedListModel(QDeclarativeListModel *base); + ~NestedListModel(); + + QHash data(int index, const QList &roles, bool *hasNested = 0) const; + QVariant data(int index, int role) const; + + QList roles() const; + QString toString(int role) const; + + int count() const; + void clear(); + void remove(int index); + bool insert(int index, const QScriptValue&); + QScriptValue get(int index) const; + void set(int index, const QScriptValue&, QList *roles); + void setProperty(int index, const QString& property, const QVariant& value, QList *roles); + void move(int from, int to, int count); + + QVariant valueForNode(ModelNode *, bool *hasNested = 0) const; + void checkRoles() const; + + ModelNode *_root; + bool m_ownsRoot; + QDeclarativeListModel *m_listModel; + +private: + friend struct ModelNode; + mutable QStringList roleStrings; + mutable bool _rolesOk; +}; + + +class ModelNodeMetaObject; +class ModelObject : public QObject +{ + Q_OBJECT +public: + ModelObject(ModelNode *node, NestedListModel *model, QScriptEngine *seng); + void setValue(const QByteArray &name, const QVariant &val); + void setNodeUpdatesEnabled(bool enable); + + NestedListModel *m_model; + ModelNode *m_node; + +private: + ModelNodeMetaObject *m_meta; +}; + +class ModelNodeMetaObject : public QDeclarativeOpenMetaObject +{ +public: + ModelNodeMetaObject(QScriptEngine *seng, ModelObject *object); + + bool m_enabled; + +protected: + void propertyWritten(int index); + +private: + QScriptEngine *m_seng; + ModelObject *m_obj; +}; + + +/* + A ModelNode is created for each item in a NestedListModel. +*/ +struct ModelNode +{ + ModelNode(NestedListModel *model); + ~ModelNode(); + + QList values; + QHash properties; + + void clear(); + + QDeclarativeListModel *model(const NestedListModel *model); + ModelObject *object(const NestedListModel *model); + + bool setObjectValue(const QScriptValue& valuemap, bool writeToCache = true); + void setListValue(const QScriptValue& valuelist); + bool setProperty(const QString& prop, const QVariant& val); + void changedProperty(const QString &name) const; + void updateListIndexes(); + static void dump(ModelNode *node, int ind); + + QDeclarativeListModel *modelCache; + ModelObject *objectCache; + bool isArray; + + NestedListModel *m_model; + int listIndex; // only used for top-level nodes within a list +}; + + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(ModelNode *) + +QT_END_HEADER + +#endif // QDECLARATIVELISTMODEL_P_P_H + diff --git a/src/declarative/util/qdeclarativelistmodelworkeragent.cpp b/src/declarative/util/qdeclarativelistmodelworkeragent.cpp new file mode 100644 index 00000000..5422eb64 --- /dev/null +++ b/src/declarative/util/qdeclarativelistmodelworkeragent.cpp @@ -0,0 +1,278 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativelistmodelworkeragent_p.h" +#include "private/qdeclarativelistmodel_p_p.h" +#include "private/qdeclarativedata_p.h" +#include "private/qdeclarativeengine_p.h" +#include "qdeclarativeinfo.h" + +#include +#include +#include + + +QT_BEGIN_NAMESPACE + + +void QDeclarativeListModelWorkerAgent::Data::clearChange() +{ + changes.clear(); +} + +void QDeclarativeListModelWorkerAgent::Data::insertChange(int index, int count) +{ + Change c = { Change::Inserted, index, count, 0, QList() }; + changes << c; +} + +void QDeclarativeListModelWorkerAgent::Data::removeChange(int index, int count) +{ + Change c = { Change::Removed, index, count, 0, QList() }; + changes << c; +} + +void QDeclarativeListModelWorkerAgent::Data::moveChange(int index, int count, int to) +{ + Change c = { Change::Moved, index, count, to, QList() }; + changes << c; +} + +void QDeclarativeListModelWorkerAgent::Data::changedChange(int index, int count, const QList &roles) +{ + Change c = { Change::Changed, index, count, 0, roles }; + changes << c; +} + +QDeclarativeListModelWorkerAgent::QDeclarativeListModelWorkerAgent(QDeclarativeListModel *model) + : m_engine(0), + m_ref(1), + m_orig(model), + m_copy(new QDeclarativeListModel(model, this)) +{ +} + +QDeclarativeListModelWorkerAgent::~QDeclarativeListModelWorkerAgent() +{ +} + +void QDeclarativeListModelWorkerAgent::setScriptEngine(QScriptEngine *eng) +{ + m_engine = eng; + if (m_copy->m_flat) + m_copy->m_flat->m_scriptEngine = eng; +} + +QScriptEngine *QDeclarativeListModelWorkerAgent::scriptEngine() const +{ + return m_engine; +} + +void QDeclarativeListModelWorkerAgent::addref() +{ + m_ref.ref(); +} + +void QDeclarativeListModelWorkerAgent::release() +{ + bool del = !m_ref.deref(); + + if (del) + delete this; +} + +int QDeclarativeListModelWorkerAgent::count() const +{ + return m_copy->count(); +} + +void QDeclarativeListModelWorkerAgent::clear() +{ + data.clearChange(); + data.removeChange(0, m_copy->count()); + m_copy->clear(); +} + +void QDeclarativeListModelWorkerAgent::remove(int index) +{ + int count = m_copy->count(); + m_copy->remove(index); + + if (m_copy->count() != count) + data.removeChange(index, 1); +} + +void QDeclarativeListModelWorkerAgent::append(const QScriptValue &value) +{ + int count = m_copy->count(); + m_copy->append(value); + + if (m_copy->count() != count) + data.insertChange(m_copy->count() - 1, 1); +} + +void QDeclarativeListModelWorkerAgent::insert(int index, const QScriptValue &value) +{ + int count = m_copy->count(); + m_copy->insert(index, value); + + if (m_copy->count() != count) + data.insertChange(index, 1); +} + +QScriptValue QDeclarativeListModelWorkerAgent::get(int index) const +{ + return m_copy->get(index); +} + +void QDeclarativeListModelWorkerAgent::set(int index, const QScriptValue &value) +{ + QList roles; + m_copy->set(index, value, &roles); + if (!roles.isEmpty()) + data.changedChange(index, 1, roles); +} + +void QDeclarativeListModelWorkerAgent::setProperty(int index, const QString& property, const QVariant& value) +{ + QList roles; + m_copy->setProperty(index, property, value, &roles); + if (!roles.isEmpty()) + data.changedChange(index, 1, roles); +} + +void QDeclarativeListModelWorkerAgent::move(int from, int to, int count) +{ + m_copy->move(from, to, count); + data.moveChange(from, to, count); +} + +void QDeclarativeListModelWorkerAgent::sync() +{ + Sync *s = new Sync; + s->data = data; + s->list = m_copy; + data.changes.clear(); + + mutex.lock(); + QCoreApplication::postEvent(this, s); + syncDone.wait(&mutex); + mutex.unlock(); +} + +void QDeclarativeListModelWorkerAgent::changedData(int index, int count, const QList &roles) +{ + data.changedChange(index, count, roles); +} + +bool QDeclarativeListModelWorkerAgent::event(QEvent *e) +{ + if (e->type() == QEvent::User) { + QMutexLocker locker(&mutex); + Sync *s = static_cast(e); + + const QList &changes = s->data.changes; + + if (m_copy) { + bool cc = m_orig->count() != s->list->count(); + + FlatListModel *orig = m_orig->m_flat; + FlatListModel *copy = s->list->m_flat; + if (!orig || !copy) { + syncDone.wakeAll(); + return QObject::event(e); + } + + orig->m_roles = copy->m_roles; + orig->m_strings = copy->m_strings; + orig->m_values = copy->m_values; + + // update the orig->m_nodeData list + for (int ii = 0; ii < changes.count(); ++ii) { + const Change &change = changes.at(ii); + switch (change.type) { + case Change::Inserted: + orig->insertedNode(change.index); + break; + case Change::Removed: + orig->removedNode(change.index); + break; + case Change::Moved: + orig->moveNodes(change.index, change.to, change.count); + break; + case Change::Changed: + break; + } + } + + syncDone.wakeAll(); + locker.unlock(); + + for (int ii = 0; ii < changes.count(); ++ii) { + const Change &change = changes.at(ii); + switch (change.type) { + case Change::Inserted: + emit m_orig->itemsInserted(change.index, change.count); + break; + case Change::Removed: + emit m_orig->itemsRemoved(change.index, change.count); + break; + case Change::Moved: + emit m_orig->itemsMoved(change.index, change.to, change.count); + break; + case Change::Changed: + emit m_orig->itemsChanged(change.index, change.count, change.roles); + break; + } + } + + if (cc) + emit m_orig->countChanged(); + } else { + syncDone.wakeAll(); + } + } + + return QObject::event(e); +} + +QT_END_NAMESPACE + diff --git a/src/declarative/util/qdeclarativelistmodelworkeragent_p.h b/src/declarative/util/qdeclarativelistmodelworkeragent_p.h new file mode 100644 index 00000000..3c1181d8 --- /dev/null +++ b/src/declarative/util/qdeclarativelistmodelworkeragent_p.h @@ -0,0 +1,163 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVELISTMODELWORKERAGENT_P_H +#define QDECLARATIVELISTMODELWORKERAGENT_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 "qdeclarative.h" + +#include +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeListModel; +class FlatListScriptClass; + +class QDeclarativeListModelWorkerAgent : public QObject +{ + Q_OBJECT + Q_PROPERTY(int count READ count) + +public: + QDeclarativeListModelWorkerAgent(QDeclarativeListModel *); + ~QDeclarativeListModelWorkerAgent(); + + void setScriptEngine(QScriptEngine *eng); + QScriptEngine *scriptEngine() const; + + void addref(); + void release(); + + int count() const; + + Q_INVOKABLE void clear(); + Q_INVOKABLE void remove(int index); + Q_INVOKABLE void append(const QScriptValue &); + Q_INVOKABLE void insert(int index, const QScriptValue&); + Q_INVOKABLE QScriptValue get(int index) const; + Q_INVOKABLE void set(int index, const QScriptValue &); + Q_INVOKABLE void setProperty(int index, const QString& property, const QVariant& value); + Q_INVOKABLE void move(int from, int to, int count); + Q_INVOKABLE void sync(); + + struct VariantRef + { + VariantRef() : a(0) {} + VariantRef(const VariantRef &r) : a(r.a) { if (a) a->addref(); } + VariantRef(QDeclarativeListModelWorkerAgent *_a) : a(_a) { if (a) a->addref(); } + ~VariantRef() { if (a) a->release(); } + + VariantRef &operator=(const VariantRef &o) { + if (o.a) o.a->addref(); + if (a) a->release(); a = o.a; + return *this; + } + + QDeclarativeListModelWorkerAgent *a; + }; +protected: + virtual bool event(QEvent *); + +private: + friend class QDeclarativeWorkerScriptEnginePrivate; + friend class FlatListScriptClass; + QScriptEngine *m_engine; + + struct Change { + enum { Inserted, Removed, Moved, Changed } type; + int index; // Inserted/Removed/Moved/Changed + int count; // Inserted/Removed/Moved/Changed + int to; // Moved + QList roles; + }; + + struct Data { + QList changes; + + void clearChange(); + void insertChange(int index, int count); + void removeChange(int index, int count); + void moveChange(int index, int count, int to); + void changedChange(int index, int count, const QList &roles); + }; + Data data; + + struct Sync : public QEvent { + Sync() : QEvent(QEvent::User) {} + Data data; + QDeclarativeListModel *list; + }; + + void changedData(int index, int count, const QList &roles); + + QAtomicInt m_ref; + QDeclarativeListModel *m_orig; + QDeclarativeListModel *m_copy; + QMutex mutex; + QWaitCondition syncDone; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QDeclarativeListModelWorkerAgent::VariantRef) + +QT_END_HEADER + +#endif // QDECLARATIVEWORKERSCRIPT_P_H + diff --git a/src/declarative/util/qdeclarativenullablevalue_p_p.h b/src/declarative/util/qdeclarativenullablevalue_p_p.h new file mode 100644 index 00000000..5d04f53b --- /dev/null +++ b/src/declarative/util/qdeclarativenullablevalue_p_p.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVENULLABLEVALUE_P_H +#define QDECLARATIVENULLABLEVALUE_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_BEGIN_NAMESPACE + +template +struct QDeclarativeNullableValue +{ + QDeclarativeNullableValue() + : isNull(true), value(T()) {} + QDeclarativeNullableValue(const QDeclarativeNullableValue &o) + : isNull(o.isNull), value(o.value) {} + QDeclarativeNullableValue(const T &t) + : isNull(false), value(t) {} + QDeclarativeNullableValue &operator=(const T &t) + { isNull = false; value = t; return *this; } + QDeclarativeNullableValue &operator=(const QDeclarativeNullableValue &o) + { isNull = o.isNull; value = o.value; return *this; } + operator T() const { return value; } + + void invalidate() { isNull = true; } + bool isValid() const { return !isNull; } + bool isNull; + T value; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVENULLABLEVALUE_P_H diff --git a/src/declarative/util/qdeclarativeopenmetaobject.cpp b/src/declarative/util/qdeclarativeopenmetaobject.cpp new file mode 100644 index 00000000..6290bf93 --- /dev/null +++ b/src/declarative/util/qdeclarativeopenmetaobject.cpp @@ -0,0 +1,380 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativeopenmetaobject_p.h" +#include "private/qdeclarativepropertycache_p.h" +#include "private/qdeclarativedata_p.h" +#include +#include + +QT_BEGIN_NAMESPACE + + +class QDeclarativeOpenMetaObjectTypePrivate +{ +public: + QDeclarativeOpenMetaObjectTypePrivate() : mem(0), cache(0), engine(0) {} + + void init(const QMetaObject *metaObj); + + int propertyOffset; + int signalOffset; + QHash names; + QMetaObjectBuilder mob; + QMetaObject *mem; + QDeclarativePropertyCache *cache; + QDeclarativeEngine *engine; + QSet referers; +}; + +QDeclarativeOpenMetaObjectType::QDeclarativeOpenMetaObjectType(const QMetaObject *base, QDeclarativeEngine *engine) + : d(new QDeclarativeOpenMetaObjectTypePrivate) +{ + d->engine = engine; + d->init(base); +} + +QDeclarativeOpenMetaObjectType::~QDeclarativeOpenMetaObjectType() +{ + if (d->mem) + qFree(d->mem); + if (d->cache) + d->cache->release(); + delete d; +} + + +int QDeclarativeOpenMetaObjectType::propertyOffset() const +{ + return d->propertyOffset; +} + +int QDeclarativeOpenMetaObjectType::signalOffset() const +{ + return d->signalOffset; +} + +int QDeclarativeOpenMetaObjectType::createProperty(const QByteArray &name) +{ + int id = d->mob.propertyCount(); + d->mob.addSignal("__" + QByteArray::number(id) + "()"); + QMetaPropertyBuilder build = d->mob.addProperty(name, "QVariant", id); + propertyCreated(id, build); + qFree(d->mem); + d->mem = d->mob.toMetaObject(); + d->names.insert(name, id); + QSet::iterator it = d->referers.begin(); + while (it != d->referers.end()) { + QDeclarativeOpenMetaObject *omo = *it; + *static_cast(omo) = *d->mem; + if (d->cache) + d->cache->update(d->engine, omo); + ++it; + } + + return d->propertyOffset + id; +} + +void QDeclarativeOpenMetaObjectType::propertyCreated(int id, QMetaPropertyBuilder &builder) +{ + if (d->referers.count()) + (*d->referers.begin())->propertyCreated(id, builder); +} + +void QDeclarativeOpenMetaObjectTypePrivate::init(const QMetaObject *metaObj) +{ + if (!mem) { + mob.setSuperClass(metaObj); + mob.setClassName(metaObj->className()); + mob.setFlags(QMetaObjectBuilder::DynamicMetaObject); + + mem = mob.toMetaObject(); + + propertyOffset = mem->propertyOffset(); + signalOffset = mem->methodOffset(); + } +} + +//---------------------------------------------------------------------------- + +class QDeclarativeOpenMetaObjectPrivate +{ +public: + QDeclarativeOpenMetaObjectPrivate(QDeclarativeOpenMetaObject *_q) + : q(_q), parent(0), type(0), cacheProperties(false) {} + + inline QVariant &getData(int idx) { + while (data.count() <= idx) + data << QPair(QVariant(), false); + QPair &prop = data[idx]; + if (!prop.second) { + prop.first = q->initialValue(idx); + prop.second = true; + } + return prop.first; + } + + inline void writeData(int idx, const QVariant &value) { + while (data.count() <= idx) + data << QPair(QVariant(), false); + QPair &prop = data[idx]; + prop.first = value; + prop.second = true; + } + + inline bool hasData(int idx) const { + if (idx >= data.count()) + return false; + return data[idx].second; + } + + bool autoCreate; + QDeclarativeOpenMetaObject *q; + QAbstractDynamicMetaObject *parent; + QList > data; + QObject *object; + QDeclarativeOpenMetaObjectType *type; + bool cacheProperties; +}; + +QDeclarativeOpenMetaObject::QDeclarativeOpenMetaObject(QObject *obj, bool automatic) +: d(new QDeclarativeOpenMetaObjectPrivate(this)) +{ + d->autoCreate = automatic; + d->object = obj; + + d->type = new QDeclarativeOpenMetaObjectType(obj->metaObject(), 0); + d->type->d->referers.insert(this); + + QObjectPrivate *op = QObjectPrivate::get(obj); + d->parent = static_cast(op->metaObject); + *static_cast(this) = *d->type->d->mem; + op->metaObject = this; +} + +QDeclarativeOpenMetaObject::QDeclarativeOpenMetaObject(QObject *obj, QDeclarativeOpenMetaObjectType *type, bool automatic) +: d(new QDeclarativeOpenMetaObjectPrivate(this)) +{ + d->autoCreate = automatic; + d->object = obj; + + d->type = type; + d->type->addref(); + d->type->d->referers.insert(this); + + QObjectPrivate *op = QObjectPrivate::get(obj); + d->parent = static_cast(op->metaObject); + *static_cast(this) = *d->type->d->mem; + op->metaObject = this; +} + +QDeclarativeOpenMetaObject::~QDeclarativeOpenMetaObject() +{ + if (d->parent) + delete d->parent; + d->type->d->referers.remove(this); + d->type->release(); + delete d; +} + +QDeclarativeOpenMetaObjectType *QDeclarativeOpenMetaObject::type() const +{ + return d->type; +} + +int QDeclarativeOpenMetaObject::metaCall(QMetaObject::Call c, int id, void **a) +{ + if (( c == QMetaObject::ReadProperty || c == QMetaObject::WriteProperty) + && id >= d->type->d->propertyOffset) { + int propId = id - d->type->d->propertyOffset; + if (c == QMetaObject::ReadProperty) { + propertyRead(propId); + *reinterpret_cast(a[0]) = d->getData(propId); + } else if (c == QMetaObject::WriteProperty) { + if (propId <= d->data.count() || d->data[propId].first != *reinterpret_cast(a[0])) { + propertyWrite(propId); + d->writeData(propId, *reinterpret_cast(a[0])); + propertyWritten(propId); + activate(d->object, d->type->d->signalOffset + propId, 0); + } + } + return -1; + } else { + if (d->parent) + return d->parent->metaCall(c, id, a); + else + return d->object->qt_metacall(c, id, a); + } +} + +QAbstractDynamicMetaObject *QDeclarativeOpenMetaObject::parent() const +{ + return d->parent; +} + +QVariant QDeclarativeOpenMetaObject::value(int id) const +{ + return d->getData(id); +} + +void QDeclarativeOpenMetaObject::setValue(int id, const QVariant &value) +{ + d->writeData(id, value); + activate(d->object, id + d->type->d->signalOffset, 0); +} + +QVariant QDeclarativeOpenMetaObject::value(const QByteArray &name) const +{ + QHash::ConstIterator iter = d->type->d->names.find(name); + if (iter == d->type->d->names.end()) + return QVariant(); + + return d->getData(*iter); +} + +QVariant &QDeclarativeOpenMetaObject::operator[](const QByteArray &name) +{ + QHash::ConstIterator iter = d->type->d->names.find(name); + Q_ASSERT(iter != d->type->d->names.end()); + + return d->getData(*iter); +} + +QVariant &QDeclarativeOpenMetaObject::operator[](int id) +{ + return d->getData(id); +} + +void QDeclarativeOpenMetaObject::setValue(const QByteArray &name, const QVariant &val) +{ + QHash::ConstIterator iter = d->type->d->names.find(name); + + int id = -1; + if (iter == d->type->d->names.end()) { + id = createProperty(name.constData(), "") - d->type->d->propertyOffset; + } else { + id = *iter; + } + + if (id >= 0) { + QVariant &dataVal = d->getData(id); + if (dataVal == val) + return; + + dataVal = val; + activate(d->object, id + d->type->d->signalOffset, 0); + } +} + +// returns true if this value has been initialized by a call to either value() or setValue() +bool QDeclarativeOpenMetaObject::hasValue(int id) const +{ + return d->hasData(id); +} + +void QDeclarativeOpenMetaObject::setCached(bool c) +{ + if (c == d->cacheProperties || !d->type->d->engine) + return; + + d->cacheProperties = c; + + QDeclarativeData *qmldata = QDeclarativeData::get(d->object, true); + if (d->cacheProperties) { + if (!d->type->d->cache) + d->type->d->cache = new QDeclarativePropertyCache(d->type->d->engine, this); + qmldata->propertyCache = d->type->d->cache; + d->type->d->cache->addref(); + } else { + if (d->type->d->cache) + d->type->d->cache->release(); + qmldata->propertyCache = 0; + } +} + + +int QDeclarativeOpenMetaObject::createProperty(const char *name, const char *) +{ + if (d->autoCreate) + return d->type->createProperty(name); + else + return -1; +} + +void QDeclarativeOpenMetaObject::propertyRead(int) +{ +} + +void QDeclarativeOpenMetaObject::propertyWrite(int) +{ +} + +void QDeclarativeOpenMetaObject::propertyWritten(int) +{ +} + +void QDeclarativeOpenMetaObject::propertyCreated(int, QMetaPropertyBuilder &) +{ +} + +QVariant QDeclarativeOpenMetaObject::initialValue(int) +{ + return QVariant(); +} + +int QDeclarativeOpenMetaObject::count() const +{ + return d->type->d->names.count(); +} + +QByteArray QDeclarativeOpenMetaObject::name(int idx) const +{ + Q_ASSERT(idx >= 0 && idx < d->type->d->names.count()); + + return d->type->d->mob.property(idx).name(); +} + +QObject *QDeclarativeOpenMetaObject::object() const +{ + return d->object; +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativeopenmetaobject_p.h b/src/declarative/util/qdeclarativeopenmetaobject_p.h new file mode 100644 index 00000000..74f27bbb --- /dev/null +++ b/src/declarative/util/qdeclarativeopenmetaobject_p.h @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEOPENMETAOBJECT_H +#define QDECLARATIVEOPENMETAOBJECT_H + +#include +#include + +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeEngine; +class QMetaPropertyBuilder; +class QDeclarativeOpenMetaObjectTypePrivate; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeOpenMetaObjectType : public QDeclarativeRefCount +{ +public: + QDeclarativeOpenMetaObjectType(const QMetaObject *base, QDeclarativeEngine *engine); + ~QDeclarativeOpenMetaObjectType(); + + int createProperty(const QByteArray &name); + + int propertyOffset() const; + int signalOffset() const; + +protected: + virtual void propertyCreated(int, QMetaPropertyBuilder &); + +private: + QDeclarativeOpenMetaObjectTypePrivate *d; + friend class QDeclarativeOpenMetaObject; + friend class QDeclarativeOpenMetaObjectPrivate; +}; + +class QDeclarativeOpenMetaObjectPrivate; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeOpenMetaObject : public QAbstractDynamicMetaObject +{ +public: + QDeclarativeOpenMetaObject(QObject *, bool = true); + QDeclarativeOpenMetaObject(QObject *, QDeclarativeOpenMetaObjectType *, bool = true); + ~QDeclarativeOpenMetaObject(); + + QVariant value(const QByteArray &) const; + void setValue(const QByteArray &, const QVariant &); + QVariant value(int) const; + void setValue(int, const QVariant &); + QVariant &operator[](const QByteArray &); + QVariant &operator[](int); + bool hasValue(int) const; + + int count() const; + QByteArray name(int) const; + + QObject *object() const; + virtual QVariant initialValue(int); + + // Be careful - once setCached(true) is called createProperty() is no + // longer automatically called for new properties. + void setCached(bool); + + QDeclarativeOpenMetaObjectType *type() const; + +protected: + virtual int metaCall(QMetaObject::Call _c, int _id, void **_a); + virtual int createProperty(const char *, const char *); + + virtual void propertyRead(int); + virtual void propertyWrite(int); + virtual void propertyWritten(int); + virtual void propertyCreated(int, QMetaPropertyBuilder &); + + QAbstractDynamicMetaObject *parent() const; + +private: + QDeclarativeOpenMetaObjectPrivate *d; + friend class QDeclarativeOpenMetaObjectType; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEOPENMETAOBJECT_H diff --git a/src/declarative/util/qdeclarativepackage.cpp b/src/declarative/util/qdeclarativepackage.cpp new file mode 100644 index 00000000..f138bef9 --- /dev/null +++ b/src/declarative/util/qdeclarativepackage.cpp @@ -0,0 +1,201 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativepackage_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmlclass Package QDeclarativePackage + \ingroup qml-working-with-data + \brief Package provides a collection of named items. + + The Package class is used in conjunction with + VisualDataModel to enable delegates with a shared context + to be provided to multiple views. + + Any item within a Package may be assigned a name via the + \l{Package::name}{Package.name} attached property. + + The example below creates a Package containing two named items; + \e list and \e grid. The third element in the package (the \l Rectangle) is parented to whichever + delegate it should appear in. This allows an item to move + between views. + + \snippet examples/declarative/modelviews/package/Delegate.qml 0 + + These named items are used as the delegates by the two views who + reference the special \l{VisualDataModel::parts} property to select + a model which provides the chosen delegate. + + \snippet examples/declarative/modelviews/package/view.qml 0 + + \sa {declarative/modelviews/package}{Package example}, {demos/declarative/photoviewer}{Photo Viewer demo}, QtDeclarative +*/ + +/*! + \qmlattachedproperty string Package::name + This attached property holds the name of an item within a Package. +*/ + + +class QDeclarativePackagePrivate : public QObjectPrivate +{ +public: + QDeclarativePackagePrivate() {} + + struct DataGuard : public QDeclarativeGuard + { + DataGuard(QObject *obj, QList *l) : list(l) { (QDeclarativeGuard&)*this = obj; } + QList *list; + void objectDestroyed(QObject *) { + // we assume priv will always be destroyed after objectDestroyed calls + list->removeOne(*this); + } + }; + + QList dataList; + static void data_append(QDeclarativeListProperty *prop, QObject *o) { + QList *list = static_cast *>(prop->data); + list->append(DataGuard(o, list)); + } + static void data_clear(QDeclarativeListProperty *prop) { + QList *list = static_cast *>(prop->data); + list->clear(); + } + static QObject *data_at(QDeclarativeListProperty *prop, int index) { + QList *list = static_cast *>(prop->data); + return list->at(index); + } + static int data_count(QDeclarativeListProperty *prop) { + QList *list = static_cast *>(prop->data); + return list->count(); + } +}; + +QHash QDeclarativePackageAttached::attached; + +QDeclarativePackageAttached::QDeclarativePackageAttached(QObject *parent) +: QObject(parent) +{ + attached.insert(parent, this); +} + +QDeclarativePackageAttached::~QDeclarativePackageAttached() +{ + attached.remove(parent()); +} + +QString QDeclarativePackageAttached::name() const +{ + return _name; +} + +void QDeclarativePackageAttached::setName(const QString &n) +{ + _name = n; +} + +QDeclarativePackage::QDeclarativePackage(QObject *parent) + : QObject(*(new QDeclarativePackagePrivate), parent) +{ +} + +QDeclarativePackage::~QDeclarativePackage() +{ + Q_D(QDeclarativePackage); + for (int ii = 0; ii < d->dataList.count(); ++ii) { + QObject *obj = d->dataList.at(ii); + obj->setParent(this); + } +} + +QDeclarativeListProperty QDeclarativePackage::data() +{ + Q_D(QDeclarativePackage); + return QDeclarativeListProperty(this, &d->dataList, QDeclarativePackagePrivate::data_append, + QDeclarativePackagePrivate::data_count, + QDeclarativePackagePrivate::data_at, + QDeclarativePackagePrivate::data_clear); +} + +bool QDeclarativePackage::hasPart(const QString &name) +{ + Q_D(QDeclarativePackage); + for (int ii = 0; ii < d->dataList.count(); ++ii) { + QObject *obj = d->dataList.at(ii); + QDeclarativePackageAttached *a = QDeclarativePackageAttached::attached.value(obj); + if (a && a->name() == name) + return true; + } + return false; +} + +QObject *QDeclarativePackage::part(const QString &name) +{ + Q_D(QDeclarativePackage); + if (name.isEmpty() && !d->dataList.isEmpty()) + return d->dataList.at(0); + + for (int ii = 0; ii < d->dataList.count(); ++ii) { + QObject *obj = d->dataList.at(ii); + QDeclarativePackageAttached *a = QDeclarativePackageAttached::attached.value(obj); + if (a && a->name() == name) + return obj; + } + + if (name == QLatin1String("default") && !d->dataList.isEmpty()) + return d->dataList.at(0); + + return 0; +} + +QDeclarativePackageAttached *QDeclarativePackage::qmlAttachedProperties(QObject *o) +{ + return new QDeclarativePackageAttached(o); +} + + + +QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativepackage_p.h b/src/declarative/util/qdeclarativepackage_p.h new file mode 100644 index 00000000..27efbe2c --- /dev/null +++ b/src/declarative/util/qdeclarativepackage_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPACKAGE_H +#define QDECLARATIVEPACKAGE_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativePackagePrivate; +class QDeclarativePackageAttached; +class Q_AUTOTEST_EXPORT QDeclarativePackage : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativePackage) + + Q_CLASSINFO("DefaultProperty", "data") + Q_PROPERTY(QDeclarativeListProperty data READ data SCRIPTABLE false) + +public: + QDeclarativePackage(QObject *parent=0); + virtual ~QDeclarativePackage(); + + QDeclarativeListProperty data(); + + QObject *part(const QString & = QString()); + bool hasPart(const QString &); + + static QDeclarativePackageAttached *qmlAttachedProperties(QObject *); +}; + +class QDeclarativePackageAttached : public QObject +{ +Q_OBJECT +Q_PROPERTY(QString name READ name WRITE setName) +public: + QDeclarativePackageAttached(QObject *parent); + virtual ~QDeclarativePackageAttached(); + + QString name() const; + void setName(const QString &n); + + static QHash attached; +private: + QString _name; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativePackage) +QML_DECLARE_TYPEINFO(QDeclarativePackage, QML_HAS_ATTACHED_PROPERTIES) + +QT_END_HEADER + +#endif // QDECLARATIVEPACKAGE_H diff --git a/src/declarative/util/qdeclarativepixmapcache.cpp b/src/declarative/util/qdeclarativepixmapcache.cpp new file mode 100644 index 00000000..03f878f9 --- /dev/null +++ b/src/declarative/util/qdeclarativepixmapcache.cpp @@ -0,0 +1,1119 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativepixmapcache_p.h" +#include "qdeclarativenetworkaccessmanagerfactory.h" +#include "qdeclarativeimageprovider.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IMAGEREQUEST_MAX_REQUEST_COUNT 8 +#define IMAGEREQUEST_MAX_REDIRECT_RECURSION 16 +#define CACHE_EXPIRE_TIME 30 +#define CACHE_REMOVAL_FRACTION 4 + +QT_BEGIN_NAMESPACE + +// The cache limit describes the maximum "junk" in the cache. +// These are the same defaults as QPixmapCache +#if defined(Q_WS_QWS) || defined(Q_WS_WINCE) +static int cache_limit = 2048 * 1024; // 2048 KB cache limit for embedded +#else +static int cache_limit = 10240 * 1024; // 10 MB cache limit for desktop +#endif + +class QDeclarativePixmapReader; +class QDeclarativePixmapData; +class QDeclarativePixmapReply : public QObject +{ + Q_OBJECT +public: + enum ReadError { NoError, Loading, Decoding }; + + QDeclarativePixmapReply(QDeclarativePixmapData *); + ~QDeclarativePixmapReply(); + + QDeclarativePixmapData *data; + QDeclarativeEngine *engineForReader; // always access reader inside readerMutex. + QSize requestSize; + QUrl url; + + bool loading; + int redirectCount; + + class Event : public QEvent { + public: + Event(ReadError, const QString &, const QSize &, const QImage &); + + ReadError error; + QString errorString; + QSize implicitSize; + QImage image; + }; + void postReply(ReadError, const QString &, const QSize &, const QImage &); + + +Q_SIGNALS: + void finished(); + void downloadProgress(qint64, qint64); + +protected: + bool event(QEvent *event); + +private: + Q_DISABLE_COPY(QDeclarativePixmapReply) + +public: + static int finishedIndex; + static int downloadProgressIndex; +}; + +class QDeclarativePixmapReaderThreadObject : public QObject { + Q_OBJECT +public: + QDeclarativePixmapReaderThreadObject(QDeclarativePixmapReader *); + void processJobs(); + virtual bool event(QEvent *e); +private slots: + void networkRequestDone(); +private: + QDeclarativePixmapReader *reader; +}; + +class QDeclarativePixmapData; +class QDeclarativePixmapReader : public QThread +{ + Q_OBJECT +public: + QDeclarativePixmapReader(QDeclarativeEngine *eng); + ~QDeclarativePixmapReader(); + + QDeclarativePixmapReply *getImage(QDeclarativePixmapData *); + void cancel(QDeclarativePixmapReply *rep); + + static QDeclarativePixmapReader *instance(QDeclarativeEngine *engine); + static QDeclarativePixmapReader *existingInstance(QDeclarativeEngine *engine); + +protected: + void run(); + +private: + friend class QDeclarativePixmapReaderThreadObject; + void processJobs(); + void processJob(QDeclarativePixmapReply *, const QUrl &, const QSize &); + void networkRequestDone(QNetworkReply *); + + QList jobs; + QList cancelled; + QDeclarativeEngine *engine; + QObject *eventLoopQuitHack; + + QMutex mutex; + QDeclarativePixmapReaderThreadObject *threadObject; + QWaitCondition waitCondition; + + QNetworkAccessManager *networkAccessManager(); + QNetworkAccessManager *accessManager; + + QHash replies; + + static int replyDownloadProgress; + static int replyFinished; + static int downloadProgress; + static int threadNetworkRequestDone; + static QHash readers; +public: + static QMutex readerMutex; +}; + +class QDeclarativePixmapData +{ +public: + QDeclarativePixmapData(const QUrl &u, const QSize &s, const QString &e) + : refCount(1), inCache(false), pixmapStatus(QDeclarativePixmap::Error), + url(u), errorString(e), requestSize(s), reply(0), prevUnreferenced(0), + prevUnreferencedPtr(0), nextUnreferenced(0) + { + } + + QDeclarativePixmapData(const QUrl &u, const QSize &r) + : refCount(1), inCache(false), pixmapStatus(QDeclarativePixmap::Loading), + url(u), requestSize(r), reply(0), prevUnreferenced(0), prevUnreferencedPtr(0), + nextUnreferenced(0) + { + } + + QDeclarativePixmapData(const QUrl &u, const QPixmap &p, const QSize &s, const QSize &r) + : refCount(1), inCache(false), privatePixmap(false), pixmapStatus(QDeclarativePixmap::Ready), + url(u), pixmap(p), implicitSize(s), requestSize(r), reply(0), prevUnreferenced(0), + prevUnreferencedPtr(0), nextUnreferenced(0) + { + } + + QDeclarativePixmapData(const QPixmap &p) + : refCount(1), inCache(false), privatePixmap(true), pixmapStatus(QDeclarativePixmap::Ready), + pixmap(p), implicitSize(p.size()), requestSize(p.size()), reply(0), prevUnreferenced(0), + prevUnreferencedPtr(0), nextUnreferenced(0) + { + } + + int cost() const; + void addref(); + void release(); + void addToCache(); + void removeFromCache(); + + uint refCount; + + bool inCache:1; + bool privatePixmap:1; + + QDeclarativePixmap::Status pixmapStatus; + QUrl url; + QString errorString; + QPixmap pixmap; + QSize implicitSize; + QSize requestSize; + + QDeclarativePixmapReply *reply; + + QDeclarativePixmapData *prevUnreferenced; + QDeclarativePixmapData**prevUnreferencedPtr; + QDeclarativePixmapData *nextUnreferenced; +}; + +int QDeclarativePixmapReply::finishedIndex = -1; +int QDeclarativePixmapReply::downloadProgressIndex = -1; + +// XXX +QHash QDeclarativePixmapReader::readers; +QMutex QDeclarativePixmapReader::readerMutex; + +int QDeclarativePixmapReader::replyDownloadProgress = -1; +int QDeclarativePixmapReader::replyFinished = -1; +int QDeclarativePixmapReader::downloadProgress = -1; +int QDeclarativePixmapReader::threadNetworkRequestDone = -1; + + +void QDeclarativePixmapReply::postReply(ReadError error, const QString &errorString, + const QSize &implicitSize, const QImage &image) +{ + loading = false; + QCoreApplication::postEvent(this, new Event(error, errorString, implicitSize, image)); +} + +QDeclarativePixmapReply::Event::Event(ReadError e, const QString &s, const QSize &iSize, const QImage &i) +: QEvent(QEvent::User), error(e), errorString(s), implicitSize(iSize), image(i) +{ +} + +QNetworkAccessManager *QDeclarativePixmapReader::networkAccessManager() +{ + if (!accessManager) { + Q_ASSERT(threadObject); + accessManager = QDeclarativeEnginePrivate::get(engine)->createNetworkAccessManager(threadObject); + } + return accessManager; +} + +static bool readImage(const QUrl& url, QIODevice *dev, QImage *image, QString *errorString, QSize *impsize, + const QSize &requestSize) +{ + QImageReader imgio(dev); + + bool force_scale = false; + if (url.path().endsWith(QLatin1String(".svg"),Qt::CaseInsensitive)) { + imgio.setFormat("svg"); // QSvgPlugin::capabilities bug QTBUG-9053 + force_scale = true; + } + + bool scaled = false; + if (requestSize.width() > 0 || requestSize.height() > 0) { + QSize s = imgio.size(); + if (requestSize.width() && (force_scale || requestSize.width() < s.width())) { + if (requestSize.height() <= 0) + s.setHeight(s.height()*requestSize.width()/s.width()); + s.setWidth(requestSize.width()); scaled = true; + } + if (requestSize.height() && (force_scale || requestSize.height() < s.height())) { + if (requestSize.width() <= 0) + s.setWidth(s.width()*requestSize.height()/s.height()); + s.setHeight(requestSize.height()); scaled = true; + } + if (scaled) { imgio.setScaledSize(s); } + } + + if (impsize) + *impsize = imgio.size(); + + if (imgio.read(image)) { + if (impsize && impsize->width() < 0) + *impsize = image->size(); + return true; + } else { + if (errorString) + *errorString = QDeclarativePixmap::tr("Error decoding: %1: %2").arg(url.toString()) + .arg(imgio.errorString()); + return false; + } +} + +QDeclarativePixmapReader::QDeclarativePixmapReader(QDeclarativeEngine *eng) +: QThread(eng), engine(eng), threadObject(0), accessManager(0) +{ + eventLoopQuitHack = new QObject; + eventLoopQuitHack->moveToThread(this); + connect(eventLoopQuitHack, SIGNAL(destroyed(QObject*)), SLOT(quit()), Qt::DirectConnection); + start(QThread::IdlePriority); +} + +QDeclarativePixmapReader::~QDeclarativePixmapReader() +{ + readerMutex.lock(); + readers.remove(engine); + readerMutex.unlock(); + + mutex.lock(); + // manually cancel all outstanding jobs. + foreach (QDeclarativePixmapReply *reply, jobs) { + delete reply; + } + jobs.clear(); + QList activeJobs = replies.values(); + foreach (QDeclarativePixmapReply *reply, activeJobs) { + if (reply->loading) { + cancelled.append(reply); + reply->data = 0; + } + } + if (threadObject) threadObject->processJobs(); + mutex.unlock(); + + eventLoopQuitHack->deleteLater(); + wait(); +} + +void QDeclarativePixmapReader::networkRequestDone(QNetworkReply *reply) +{ + QDeclarativePixmapReply *job = replies.take(reply); + + if (job) { + job->redirectCount++; + if (job->redirectCount < IMAGEREQUEST_MAX_REDIRECT_RECURSION) { + QVariant redirect = reply->attribute(QNetworkRequest::RedirectionTargetAttribute); + if (redirect.isValid()) { + QUrl url = reply->url().resolved(redirect.toUrl()); + QNetworkRequest req(url); + req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); + + reply->deleteLater(); + reply = networkAccessManager()->get(req); + + QMetaObject::connect(reply, replyDownloadProgress, job, downloadProgress); + QMetaObject::connect(reply, replyFinished, threadObject, threadNetworkRequestDone); + + replies.insert(reply, job); + return; + } + } + + QImage image; + QDeclarativePixmapReply::ReadError error = QDeclarativePixmapReply::NoError; + QString errorString; + QSize readSize; + if (reply->error()) { + error = QDeclarativePixmapReply::Loading; + errorString = reply->errorString(); + } else { + QByteArray all = reply->readAll(); + QBuffer buff(&all); + buff.open(QIODevice::ReadOnly); + if (!readImage(reply->url(), &buff, &image, &errorString, &readSize, job->requestSize)) { + error = QDeclarativePixmapReply::Decoding; + } + } + // send completion event to the QDeclarativePixmapReply + mutex.lock(); + if (!cancelled.contains(job)) job->postReply(error, errorString, readSize, image); + mutex.unlock(); + } + reply->deleteLater(); + + // kick off event loop again incase we have dropped below max request count + threadObject->processJobs(); +} + +QDeclarativePixmapReaderThreadObject::QDeclarativePixmapReaderThreadObject(QDeclarativePixmapReader *i) +: reader(i) +{ +} + +void QDeclarativePixmapReaderThreadObject::processJobs() +{ + QCoreApplication::postEvent(this, new QEvent(QEvent::User)); +} + +bool QDeclarativePixmapReaderThreadObject::event(QEvent *e) +{ + if (e->type() == QEvent::User) { + reader->processJobs(); + return true; + } else { + return QObject::event(e); + } +} + +void QDeclarativePixmapReaderThreadObject::networkRequestDone() +{ + QNetworkReply *reply = static_cast(sender()); + reader->networkRequestDone(reply); +} + +void QDeclarativePixmapReader::processJobs() +{ + QMutexLocker locker(&mutex); + + while (true) { + if (cancelled.isEmpty() && (jobs.isEmpty() || replies.count() >= IMAGEREQUEST_MAX_REQUEST_COUNT)) + return; // Nothing else to do + + // Clean cancelled jobs + if (cancelled.count()) { + for (int i = 0; i < cancelled.count(); ++i) { + QDeclarativePixmapReply *job = cancelled.at(i); + QNetworkReply *reply = replies.key(job, 0); + if (reply && reply->isRunning()) { + // cancel any jobs already started + replies.remove(reply); + reply->close(); + } + // deleteLater, since not owned by this thread + job->deleteLater(); + } + cancelled.clear(); + } + + if (!jobs.isEmpty() && replies.count() < IMAGEREQUEST_MAX_REQUEST_COUNT) { + QDeclarativePixmapReply *runningJob = jobs.takeLast(); + runningJob->loading = true; + QUrl url = runningJob->url; + QSize requestSize = runningJob->requestSize; + locker.unlock(); + processJob(runningJob, url, requestSize); + locker.relock(); + } + } +} + +void QDeclarativePixmapReader::processJob(QDeclarativePixmapReply *runningJob, const QUrl &url, + const QSize &requestSize) +{ + // fetch + if (url.scheme() == QLatin1String("image")) { + // Use QmlImageProvider + QSize readSize; + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); + QImage image = ep->getImageFromProvider(url, &readSize, requestSize); + + QDeclarativePixmapReply::ReadError errorCode = QDeclarativePixmapReply::NoError; + QString errorStr; + if (image.isNull()) { + errorCode = QDeclarativePixmapReply::Loading; + errorStr = QDeclarativePixmap::tr("Failed to get image from provider: %1").arg(url.toString()); + } + mutex.lock(); + if (!cancelled.contains(runningJob)) runningJob->postReply(errorCode, errorStr, readSize, image); + mutex.unlock(); + } else { + QString lf = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url); + if (!lf.isEmpty()) { + // Image is local - load/decode immediately + QImage image; + QDeclarativePixmapReply::ReadError errorCode = QDeclarativePixmapReply::NoError; + QString errorStr; + QFile f(lf); + QSize readSize; + if (f.open(QIODevice::ReadOnly)) { + if (!readImage(url, &f, &image, &errorStr, &readSize, requestSize)) + errorCode = QDeclarativePixmapReply::Loading; + } else { + errorStr = QDeclarativePixmap::tr("Cannot open: %1").arg(url.toString()); + errorCode = QDeclarativePixmapReply::Loading; + } + mutex.lock(); + if (!cancelled.contains(runningJob)) runningJob->postReply(errorCode, errorStr, readSize, image); + mutex.unlock(); + } else { + // Network resource + QNetworkRequest req(url); + req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); + QNetworkReply *reply = networkAccessManager()->get(req); + QMetaObject::connect(reply, replyDownloadProgress, runningJob, downloadProgress); + QMetaObject::connect(reply, replyFinished, threadObject, threadNetworkRequestDone); + replies.insert(reply, runningJob); + } + } +} + +QDeclarativePixmapReader *QDeclarativePixmapReader::instance(QDeclarativeEngine *engine) +{ + // XXX NOTE: must be called within readerMutex locking. + QDeclarativePixmapReader *reader = readers.value(engine); + if (!reader) { + reader = new QDeclarativePixmapReader(engine); + readers.insert(engine, reader); + } + + return reader; +} + +QDeclarativePixmapReader *QDeclarativePixmapReader::existingInstance(QDeclarativeEngine *engine) +{ + // XXX NOTE: must be called within readerMutex locking. + return readers.value(engine, 0); +} + +QDeclarativePixmapReply *QDeclarativePixmapReader::getImage(QDeclarativePixmapData *data) +{ + mutex.lock(); + QDeclarativePixmapReply *reply = new QDeclarativePixmapReply(data); + reply->engineForReader = engine; + jobs.append(reply); + // XXX + if (threadObject) threadObject->processJobs(); + mutex.unlock(); + return reply; +} + +void QDeclarativePixmapReader::cancel(QDeclarativePixmapReply *reply) +{ + mutex.lock(); + if (reply->loading) { + cancelled.append(reply); + reply->data = 0; + // XXX + if (threadObject) threadObject->processJobs(); + } else { + jobs.removeAll(reply); + delete reply; + } + mutex.unlock(); +} + +void QDeclarativePixmapReader::run() +{ + if (replyDownloadProgress == -1) { + const QMetaObject *nr = &QNetworkReply::staticMetaObject; + const QMetaObject *pr = &QDeclarativePixmapReply::staticMetaObject; + const QMetaObject *ir = &QDeclarativePixmapReaderThreadObject::staticMetaObject; + replyDownloadProgress = nr->indexOfSignal("downloadProgress(qint64,qint64)"); + replyFinished = nr->indexOfSignal("finished()"); + downloadProgress = pr->indexOfSignal("downloadProgress(qint64,qint64)"); + threadNetworkRequestDone = ir->indexOfSlot("networkRequestDone()"); + } + + mutex.lock(); + threadObject = new QDeclarativePixmapReaderThreadObject(this); + mutex.unlock(); + + processJobs(); + exec(); + + delete threadObject; + threadObject = 0; +} + +class QDeclarativePixmapKey +{ +public: + const QUrl *url; + const QSize *size; +}; + +inline bool operator==(const QDeclarativePixmapKey &lhs, const QDeclarativePixmapKey &rhs) +{ + return *lhs.size == *rhs.size && *lhs.url == *rhs.url; +} + +inline uint qHash(const QDeclarativePixmapKey &key) +{ + return qHash(*key.url) ^ key.size->width() ^ key.size->height(); +} + +class QDeclarativePixmapStore : public QObject +{ + Q_OBJECT +public: + QDeclarativePixmapStore(); + + void unreferencePixmap(QDeclarativePixmapData *); + void referencePixmap(QDeclarativePixmapData *); + void flushCache(); + +protected: + virtual void timerEvent(QTimerEvent *); + +public: + QHash m_cache; + +private: + void shrinkCache(int remove); + + QDeclarativePixmapData *m_unreferencedPixmaps; + QDeclarativePixmapData *m_lastUnreferencedPixmap; + + int m_unreferencedCost; + int m_timerId; +}; +Q_GLOBAL_STATIC(QDeclarativePixmapStore, pixmapStore); + +QDeclarativePixmapStore::QDeclarativePixmapStore() +: m_unreferencedPixmaps(0), m_lastUnreferencedPixmap(0), m_unreferencedCost(0), m_timerId(-1) +{ +} + +void QDeclarativePixmapStore::unreferencePixmap(QDeclarativePixmapData *data) +{ + Q_ASSERT(data->prevUnreferenced == 0); + Q_ASSERT(data->prevUnreferencedPtr == 0); + Q_ASSERT(data->nextUnreferenced == 0); + + data->nextUnreferenced = m_unreferencedPixmaps; + data->prevUnreferencedPtr = &m_unreferencedPixmaps; + + m_unreferencedPixmaps = data; + if (m_unreferencedPixmaps->nextUnreferenced) { + m_unreferencedPixmaps->nextUnreferenced->prevUnreferenced = m_unreferencedPixmaps; + m_unreferencedPixmaps->nextUnreferenced->prevUnreferencedPtr = &m_unreferencedPixmaps->nextUnreferenced; + } + + if (!m_lastUnreferencedPixmap) + m_lastUnreferencedPixmap = data; + + m_unreferencedCost += data->cost(); + + shrinkCache(-1); // Shrink the cache incase it has become larger than cache_limit + + if (m_timerId == -1 && m_unreferencedPixmaps) + m_timerId = startTimer(CACHE_EXPIRE_TIME * 1000); +} + +void QDeclarativePixmapStore::referencePixmap(QDeclarativePixmapData *data) +{ + Q_ASSERT(data->prevUnreferencedPtr); + + *data->prevUnreferencedPtr = data->nextUnreferenced; + if (data->nextUnreferenced) { + data->nextUnreferenced->prevUnreferencedPtr = data->prevUnreferencedPtr; + data->nextUnreferenced->prevUnreferenced = data->prevUnreferenced; + } + if (m_lastUnreferencedPixmap == data) + m_lastUnreferencedPixmap = data->prevUnreferenced; + + data->nextUnreferenced = 0; + data->prevUnreferencedPtr = 0; + data->prevUnreferenced = 0; + + m_unreferencedCost -= data->cost(); +} + +void QDeclarativePixmapStore::shrinkCache(int remove) +{ + while ((remove > 0 || m_unreferencedCost > cache_limit) && m_lastUnreferencedPixmap) { + QDeclarativePixmapData *data = m_lastUnreferencedPixmap; + Q_ASSERT(data->nextUnreferenced == 0); + + *data->prevUnreferencedPtr = 0; + m_lastUnreferencedPixmap = data->prevUnreferenced; + data->prevUnreferencedPtr = 0; + data->prevUnreferenced = 0; + + remove -= data->cost(); + m_unreferencedCost -= data->cost(); + data->removeFromCache(); + delete data; + } +} + +void QDeclarativePixmapStore::timerEvent(QTimerEvent *) +{ + int removalCost = m_unreferencedCost / CACHE_REMOVAL_FRACTION; + + shrinkCache(removalCost); + + if (m_unreferencedPixmaps == 0) { + killTimer(m_timerId); + m_timerId = -1; + } +} + +/* + Remove all unreferenced pixmaps from the cache. +*/ +void QDeclarativePixmapStore::flushCache() +{ + shrinkCache(m_unreferencedCost); +} + +QDeclarativePixmapReply::QDeclarativePixmapReply(QDeclarativePixmapData *d) +: data(d), engineForReader(0), requestSize(d->requestSize), url(d->url), loading(false), redirectCount(0) +{ + if (finishedIndex == -1) { + finishedIndex = QDeclarativePixmapReply::staticMetaObject.indexOfSignal("finished()"); + downloadProgressIndex = QDeclarativePixmapReply::staticMetaObject.indexOfSignal("downloadProgress(qint64,qint64)"); + } +} + +QDeclarativePixmapReply::~QDeclarativePixmapReply() +{ +} + +bool QDeclarativePixmapReply::event(QEvent *event) +{ + if (event->type() == QEvent::User) { + + if (data) { + Event *de = static_cast(event); + data->pixmapStatus = (de->error == NoError) ? QDeclarativePixmap::Ready : QDeclarativePixmap::Error; + + if (data->pixmapStatus == QDeclarativePixmap::Ready) { + data->pixmap = QPixmap::fromImage(de->image); + data->implicitSize = de->implicitSize; + } else { + data->errorString = de->errorString; + data->removeFromCache(); // We don't continue to cache error'd pixmaps + } + + data->reply = 0; + emit finished(); + } + + delete this; + return true; + } else { + return QObject::event(event); + } +} + +int QDeclarativePixmapData::cost() const +{ + return (pixmap.width() * pixmap.height() * pixmap.depth()) / 8; +} + +void QDeclarativePixmapData::addref() +{ + ++refCount; + if (prevUnreferencedPtr) + pixmapStore()->referencePixmap(this); +} + +void QDeclarativePixmapData::release() +{ + Q_ASSERT(refCount > 0); + --refCount; + + if (refCount == 0) { + if (reply) { + QDeclarativePixmapReply *cancelReply = reply; + reply->data = 0; + reply = 0; + QDeclarativePixmapReader::readerMutex.lock(); + QDeclarativePixmapReader *reader = QDeclarativePixmapReader::existingInstance(cancelReply->engineForReader); + if (reader) + reader->cancel(cancelReply); + QDeclarativePixmapReader::readerMutex.unlock(); + } + + if (pixmapStatus == QDeclarativePixmap::Ready) { + pixmapStore()->unreferencePixmap(this); + } else { + removeFromCache(); + delete this; + } + } +} + +void QDeclarativePixmapData::addToCache() +{ + if (!inCache) { + QDeclarativePixmapKey key = { &url, &requestSize }; + pixmapStore()->m_cache.insert(key, this); + inCache = true; + } +} + +void QDeclarativePixmapData::removeFromCache() +{ + if (inCache) { + QDeclarativePixmapKey key = { &url, &requestSize }; + pixmapStore()->m_cache.remove(key); + inCache = false; + } +} + +static QDeclarativePixmapData* createPixmapDataSync(QDeclarativeEngine *engine, const QUrl &url, const QSize &requestSize, bool *ok) +{ + if (url.scheme() == QLatin1String("image")) { + QSize readSize; + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); + QDeclarativeImageProvider::ImageType imageType = ep->getImageProviderType(url); + + switch (imageType) { + case QDeclarativeImageProvider::Image: + { + QImage image = ep->getImageFromProvider(url, &readSize, requestSize); + if (!image.isNull()) { + *ok = true; + return new QDeclarativePixmapData(url, QPixmap::fromImage(image), readSize, requestSize); + } + } + case QDeclarativeImageProvider::Pixmap: + { + QPixmap pixmap = ep->getPixmapFromProvider(url, &readSize, requestSize); + if (!pixmap.isNull()) { + *ok = true; + return new QDeclarativePixmapData(url, pixmap, readSize, requestSize); + } + } + } + + // no matching provider, or provider has bad image type, or provider returned null image + return new QDeclarativePixmapData(url, requestSize, + QDeclarativePixmap::tr("Failed to get image from provider: %1").arg(url.toString())); + } + + QString localFile = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url); + if (localFile.isEmpty()) + return 0; + + QFile f(localFile); + QSize readSize; + QString errorString; + + if (f.open(QIODevice::ReadOnly)) { + QImage image; + if (readImage(url, &f, &image, &errorString, &readSize, requestSize)) { + *ok = true; + return new QDeclarativePixmapData(url, QPixmap::fromImage(image), readSize, requestSize); + } + } else { + errorString = QDeclarativePixmap::tr("Cannot open: %1").arg(url.toString()); + } + return new QDeclarativePixmapData(url, requestSize, errorString); +} + + +struct QDeclarativePixmapNull { + QUrl url; + QPixmap pixmap; + QSize size; +}; +Q_GLOBAL_STATIC(QDeclarativePixmapNull, nullPixmap); + +QDeclarativePixmap::QDeclarativePixmap() +: d(0) +{ +} + +QDeclarativePixmap::QDeclarativePixmap(QDeclarativeEngine *engine, const QUrl &url) +: d(0) +{ + load(engine, url); +} + +QDeclarativePixmap::QDeclarativePixmap(QDeclarativeEngine *engine, const QUrl &url, const QSize &size) +: d(0) +{ + load(engine, url, size); +} + +QDeclarativePixmap::~QDeclarativePixmap() +{ + if (d) { + d->release(); + d = 0; + } +} + +bool QDeclarativePixmap::isNull() const +{ + return d == 0; +} + +bool QDeclarativePixmap::isReady() const +{ + return status() == Ready; +} + +bool QDeclarativePixmap::isError() const +{ + return status() == Error; +} + +bool QDeclarativePixmap::isLoading() const +{ + return status() == Loading; +} + +QString QDeclarativePixmap::error() const +{ + if (d) + return d->errorString; + else + return QString(); +} + +QDeclarativePixmap::Status QDeclarativePixmap::status() const +{ + if (d) + return d->pixmapStatus; + else + return Null; +} + +const QUrl &QDeclarativePixmap::url() const +{ + if (d) + return d->url; + else + return nullPixmap()->url; +} + +const QSize &QDeclarativePixmap::implicitSize() const +{ + if (d) + return d->implicitSize; + else + return nullPixmap()->size; +} + +const QSize &QDeclarativePixmap::requestSize() const +{ + if (d) + return d->requestSize; + else + return nullPixmap()->size; +} + +const QPixmap &QDeclarativePixmap::pixmap() const +{ + if (d) + return d->pixmap; + else + return nullPixmap()->pixmap; +} + +void QDeclarativePixmap::setPixmap(const QPixmap &p) +{ + clear(); + + if (!p.isNull()) + d = new QDeclarativePixmapData(p); +} + +int QDeclarativePixmap::width() const +{ + if (d) + return d->pixmap.width(); + else + return 0; +} + +int QDeclarativePixmap::height() const +{ + if (d) + return d->pixmap.height(); + else + return 0; +} + +QRect QDeclarativePixmap::rect() const +{ + if (d) + return d->pixmap.rect(); + else + return QRect(); +} + +void QDeclarativePixmap::load(QDeclarativeEngine *engine, const QUrl &url) +{ + load(engine, url, QSize(), QDeclarativePixmap::Cache); +} + +void QDeclarativePixmap::load(QDeclarativeEngine *engine, const QUrl &url, QDeclarativePixmap::Options options) +{ + load(engine, url, QSize(), options); +} + +void QDeclarativePixmap::load(QDeclarativeEngine *engine, const QUrl &url, const QSize &size) +{ + load(engine, url, size, QDeclarativePixmap::Cache); +} + +void QDeclarativePixmap::load(QDeclarativeEngine *engine, const QUrl &url, const QSize &requestSize, QDeclarativePixmap::Options options) +{ + if (d) { d->release(); d = 0; } + + QDeclarativePixmapKey key = { &url, &requestSize }; + QDeclarativePixmapStore *store = pixmapStore(); + + QHash::Iterator iter = store->m_cache.find(key); + + if (iter == store->m_cache.end()) { + if (options & QDeclarativePixmap::Asynchronous) { + // pixmaps can only be loaded synchronously + if (url.scheme() == QLatin1String("image") + && QDeclarativeEnginePrivate::get(engine)->getImageProviderType(url) == QDeclarativeImageProvider::Pixmap) { + options &= ~QDeclarativePixmap::Asynchronous; + } + } + + if (!(options & QDeclarativePixmap::Asynchronous)) { + bool ok = false; + d = createPixmapDataSync(engine, url, requestSize, &ok); + if (ok) { + if (options & QDeclarativePixmap::Cache) + d->addToCache(); + return; + } + if (d) // loadable, but encountered error while loading + return; + } + + if (!engine) + return; + + d = new QDeclarativePixmapData(url, requestSize); + if (options & QDeclarativePixmap::Cache) + d->addToCache(); + QDeclarativePixmapReader::readerMutex.lock(); + d->reply = QDeclarativePixmapReader::instance(engine)->getImage(d); + QDeclarativePixmapReader::readerMutex.unlock(); + } else { + d = *iter; + d->addref(); + } +} + +void QDeclarativePixmap::clear() +{ + if (d) { + d->release(); + d = 0; + } +} + +void QDeclarativePixmap::clear(QObject *obj) +{ + if (d) { + if (d->reply) + QObject::disconnect(d->reply, 0, obj, 0); + d->release(); + d = 0; + } +} + +bool QDeclarativePixmap::connectFinished(QObject *object, const char *method) +{ + if (!d || !d->reply) { + qWarning("QDeclarativePixmap: connectFinished() called when not loading."); + return false; + } + + return QObject::connect(d->reply, SIGNAL(finished()), object, method); +} + +bool QDeclarativePixmap::connectFinished(QObject *object, int method) +{ + if (!d || !d->reply) { + qWarning("QDeclarativePixmap: connectFinished() called when not loading."); + return false; + } + + return QMetaObject::connect(d->reply, QDeclarativePixmapReply::finishedIndex, object, method); +} + +bool QDeclarativePixmap::connectDownloadProgress(QObject *object, const char *method) +{ + if (!d || !d->reply) { + qWarning("QDeclarativePixmap: connectDownloadProgress() called when not loading."); + return false; + } + + return QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)), object, method); +} + +bool QDeclarativePixmap::connectDownloadProgress(QObject *object, int method) +{ + if (!d || !d->reply) { + qWarning("QDeclarativePixmap: connectDownloadProgress() called when not loading."); + return false; + } + + return QMetaObject::connect(d->reply, QDeclarativePixmapReply::downloadProgressIndex, object, method); +} + +void QDeclarativePixmap::flushCache() +{ + pixmapStore()->flushCache(); +} + +QT_END_NAMESPACE + +#include diff --git a/src/declarative/util/qdeclarativepixmapcache_p.h b/src/declarative/util/qdeclarativepixmapcache_p.h new file mode 100644 index 00000000..33c3a55a --- /dev/null +++ b/src/declarative/util/qdeclarativepixmapcache_p.h @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPIXMAPCACHE_H +#define QDECLARATIVEPIXMAPCACHE_H + +#include +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeEngine; +class QDeclarativePixmapData; +class Q_DECLARATIVE_EXPORT QDeclarativePixmap +{ + Q_DECLARE_TR_FUNCTIONS(QDeclarativePixmap) +public: + QDeclarativePixmap(); + QDeclarativePixmap(QDeclarativeEngine *, const QUrl &); + QDeclarativePixmap(QDeclarativeEngine *, const QUrl &, const QSize &); + ~QDeclarativePixmap(); + + enum Status { Null, Ready, Error, Loading }; + + enum Option { + Asynchronous = 0x00000001, + Cache = 0x00000002 + }; + Q_DECLARE_FLAGS(Options, Option) + + bool isNull() const; + bool isReady() const; + bool isError() const; + bool isLoading() const; + + Status status() const; + QString error() const; + const QUrl &url() const; + const QSize &implicitSize() const; + const QSize &requestSize() const; + const QPixmap &pixmap() const; + void setPixmap(const QPixmap &); + + QRect rect() const; + int width() const; + int height() const; + inline operator const QPixmap &() const; + + void load(QDeclarativeEngine *, const QUrl &); + void load(QDeclarativeEngine *, const QUrl &, QDeclarativePixmap::Options options); + void load(QDeclarativeEngine *, const QUrl &, const QSize &); + void load(QDeclarativeEngine *, const QUrl &, const QSize &, QDeclarativePixmap::Options options); + + void clear(); + void clear(QObject *); + + bool connectFinished(QObject *, const char *); + bool connectFinished(QObject *, int); + bool connectDownloadProgress(QObject *, const char *); + bool connectDownloadProgress(QObject *, int); + + static void flushCache(); + +private: + Q_DISABLE_COPY(QDeclarativePixmap) + QDeclarativePixmapData *d; +}; + +inline QDeclarativePixmap::operator const QPixmap &() const +{ + return pixmap(); +} + +Q_DECLARE_OPERATORS_FOR_FLAGS(QDeclarativePixmap::Options) + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEPIXMAPCACHE_H diff --git a/src/declarative/util/qdeclarativepropertychanges.cpp b/src/declarative/util/qdeclarativepropertychanges.cpp new file mode 100644 index 00000000..97c6a1a1 --- /dev/null +++ b/src/declarative/util/qdeclarativepropertychanges.cpp @@ -0,0 +1,798 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativepropertychanges_p.h" + +#include "private/qdeclarativeopenmetaobject_p.h" +#include "private/qdeclarativerewrite_p.h" +#include "private/qdeclarativeengine_p.h" +#include "private/qdeclarativecompiler_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmlclass PropertyChanges QDeclarativePropertyChanges + \ingroup qml-state-elements + \since 4.7 + \brief The PropertyChanges element describes new property bindings or values for a state. + + PropertyChanges is used to define the property values or bindings in a + \l State. This enables an item's property values to be changed when it + \l {QML States}{changes between states}. + + To create a PropertyChanges object, specify the \l target item whose + properties are to be modified, and define the new property values or + bindings. For example: + + \snippet doc/src/snippets/declarative/propertychanges.qml import + \codeline + \snippet doc/src/snippets/declarative/propertychanges.qml 0 + + When the mouse is pressed, the \l Rectangle changes to the \e resized + state. In this state, the PropertyChanges object sets the rectangle's + color to blue and the \c height value to that of \c container.height. + + Note this automatically binds \c rect.height to \c container.height + in the \e resized state. If a property binding should not be + established, and the height should just be set to the value of + \c container.height at the time of the state change, set the \l explicit + property to \c true. + + A PropertyChanges object can also override the default signal handler + for an object to implement a signal handler specific to the new state: + + \qml + PropertyChanges { + target: myMouseArea + onClicked: doSomethingDifferent() + } + \endqml + + \note PropertyChanges can be used to change anchor margins, but not other anchor + values; use AnchorChanges for this instead. Similarly, to change an \l Item's + \l {Item::}{parent} value, use ParentChanges instead. + + + \section2 Resetting property values + + The \c undefined value can be used to reset the property value for a state. + In the following example, when \c theText changes to the \e widerText + state, its \c width property is reset, giving the text its natural width + and displaying the whole string on a single line. + + \snippet doc/src/snippets/declarative/propertychanges.qml reset + + + \section2 Immediate property changes in transitions + + When \l{QML Animation and Transitions}{Transitions} are used to animate + state changes, they animate properties from their values in the current + state to those defined in the new state (as defined by PropertyChanges + objects). However, it is sometimes desirable to set a property value + \e immediately during a \l Transition, without animation; in these cases, + the PropertyAction element can be used to force an immediate property + change. + + See the PropertyAction documentation for more details. + + \sa {declarative/animation/states}{states example}, {qmlstate}{States}, QtDeclarative +*/ + +/*! + \qmlproperty Object PropertyChanges::target + This property holds the object which contains the properties to be changed. +*/ + +class QDeclarativeReplaceSignalHandler : public QDeclarativeActionEvent +{ +public: + QDeclarativeReplaceSignalHandler() : expression(0), reverseExpression(0), + rewindExpression(0), ownedExpression(0) {} + ~QDeclarativeReplaceSignalHandler() { + delete ownedExpression; + } + + virtual QString typeName() const { return QLatin1String("ReplaceSignalHandler"); } + + QDeclarativeProperty property; + QDeclarativeExpression *expression; + QDeclarativeExpression *reverseExpression; + QDeclarativeExpression *rewindExpression; + QDeclarativeGuard ownedExpression; + + virtual void execute(Reason) { + ownedExpression = QDeclarativePropertyPrivate::setSignalExpression(property, expression); + if (ownedExpression == expression) + ownedExpression = 0; + } + + virtual bool isReversable() { return true; } + virtual void reverse(Reason) { + ownedExpression = QDeclarativePropertyPrivate::setSignalExpression(property, reverseExpression); + if (ownedExpression == reverseExpression) + ownedExpression = 0; + } + + virtual void saveOriginals() { + saveCurrentValues(); + reverseExpression = rewindExpression; + } + + virtual bool needsCopy() { return true; } + virtual void copyOriginals(QDeclarativeActionEvent *other) + { + QDeclarativeReplaceSignalHandler *rsh = static_cast(other); + saveCurrentValues(); + if (rsh == this) + return; + reverseExpression = rsh->reverseExpression; + if (rsh->ownedExpression == reverseExpression) { + ownedExpression = rsh->ownedExpression; + rsh->ownedExpression = 0; + } + } + + virtual void rewind() { + ownedExpression = QDeclarativePropertyPrivate::setSignalExpression(property, rewindExpression); + if (ownedExpression == rewindExpression) + ownedExpression = 0; + } + virtual void saveCurrentValues() { + rewindExpression = QDeclarativePropertyPrivate::signalExpression(property); + } + + virtual bool override(QDeclarativeActionEvent*other) { + if (other == this) + return true; + if (other->typeName() != typeName()) + return false; + if (static_cast(other)->property == property) + return true; + return false; + } +}; + + +class QDeclarativePropertyChangesPrivate : public QDeclarativeStateOperationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativePropertyChanges) +public: + QDeclarativePropertyChangesPrivate() : decoded(true), restore(true), + isExplicit(false) {} + + QDeclarativeGuard object; + QByteArray data; + + bool decoded : 1; + bool restore : 1; + bool isExplicit : 1; + + void decode(); + + class ExpressionChange { + public: + ExpressionChange(const QString &_name, + QDeclarativeBinding::Identifier _id, + QDeclarativeExpression *_expr) + : name(_name), id(_id), expression(_expr) {} + QString name; + QDeclarativeBinding::Identifier id; + QDeclarativeExpression *expression; + }; + + QList > properties; + QList expressions; + QList signalReplacements; + + QDeclarativeProperty property(const QString &); +}; + +void +QDeclarativePropertyChangesParser::compileList(QList > &list, + const QByteArray &pre, + const QDeclarativeCustomParserProperty &prop) +{ + QByteArray propName = pre + prop.name(); + + QList values = prop.assignedValues(); + for (int ii = 0; ii < values.count(); ++ii) { + const QVariant &value = values.at(ii); + + if (value.userType() == qMetaTypeId()) { + error(qvariant_cast(value), + QDeclarativePropertyChanges::tr("PropertyChanges does not support creating state-specific objects.")); + continue; + } else if(value.userType() == qMetaTypeId()) { + + QDeclarativeCustomParserProperty prop = + qvariant_cast(value); + QByteArray pre = propName + '.'; + compileList(list, pre, prop); + + } else { + list << qMakePair(propName, value); + } + } +} + +QByteArray +QDeclarativePropertyChangesParser::compile(const QList &props) +{ + QList > data; + for(int ii = 0; ii < props.count(); ++ii) + compileList(data, QByteArray(), props.at(ii)); + + QByteArray rv; + QDataStream ds(&rv, QIODevice::WriteOnly); + + ds << data.count(); + for(int ii = 0; ii < data.count(); ++ii) { + QDeclarativeParser::Variant v = qvariant_cast(data.at(ii).second); + QVariant var; + bool isScript = v.isScript(); + QDeclarativeBinding::Identifier id = 0; + switch(v.type()) { + case QDeclarativeParser::Variant::Boolean: + var = QVariant(v.asBoolean()); + break; + case QDeclarativeParser::Variant::Number: + var = QVariant(v.asNumber()); + break; + case QDeclarativeParser::Variant::String: + var = QVariant(v.asString()); + break; + case QDeclarativeParser::Variant::Invalid: + case QDeclarativeParser::Variant::Script: + var = QVariant(v.asScript()); + { + // Pre-rewrite the expression + QString expression = v.asScript(); + id = rewriteBinding(expression, data.at(ii).first); //### recreates the AST, which is slow + } + break; + } + + ds << QString::fromUtf8(data.at(ii).first) << isScript << var; + if (isScript) + ds << id; + } + + return rv; +} + +void QDeclarativePropertyChangesPrivate::decode() +{ + Q_Q(QDeclarativePropertyChanges); + if (decoded) + return; + + QDataStream ds(&data, QIODevice::ReadOnly); + + int count; + ds >> count; + for (int ii = 0; ii < count; ++ii) { + QString name; + bool isScript; + QVariant data; + QDeclarativeBinding::Identifier id = QDeclarativeBinding::Invalid; + ds >> name; + ds >> isScript; + ds >> data; + if (isScript) + ds >> id; + + QDeclarativeProperty prop = property(name); //### better way to check for signal property? + if (prop.type() & QDeclarativeProperty::SignalProperty) { + QDeclarativeExpression *expression = new QDeclarativeExpression(qmlContext(q), object, data.toString()); + QDeclarativeData *ddata = QDeclarativeData::get(q); + if (ddata && ddata->outerContext && !ddata->outerContext->url.isEmpty()) + expression->setSourceLocation(ddata->outerContext->url.toString(), ddata->lineNumber); + QDeclarativeReplaceSignalHandler *handler = new QDeclarativeReplaceSignalHandler; + handler->property = prop; + handler->expression = expression; + signalReplacements << handler; + } else if (isScript) { + QDeclarativeExpression *expression = new QDeclarativeExpression(qmlContext(q), object, data.toString()); + QDeclarativeData *ddata = QDeclarativeData::get(q); + if (ddata && ddata->outerContext && !ddata->outerContext->url.isEmpty()) + expression->setSourceLocation(ddata->outerContext->url.toString(), ddata->lineNumber); + expressions << ExpressionChange(name, id, expression); + } else { + properties << qMakePair(name, data); + } + } + + decoded = true; + data.clear(); +} + +void QDeclarativePropertyChangesParser::setCustomData(QObject *object, + const QByteArray &data) +{ + QDeclarativePropertyChangesPrivate *p = + static_cast(QObjectPrivate::get(object)); + p->data = data; + p->decoded = false; +} + +QDeclarativePropertyChanges::QDeclarativePropertyChanges() +: QDeclarativeStateOperation(*(new QDeclarativePropertyChangesPrivate)) +{ +} + +QDeclarativePropertyChanges::~QDeclarativePropertyChanges() +{ + Q_D(QDeclarativePropertyChanges); + for(int ii = 0; ii < d->expressions.count(); ++ii) + delete d->expressions.at(ii).expression; + for(int ii = 0; ii < d->signalReplacements.count(); ++ii) + delete d->signalReplacements.at(ii); +} + +QObject *QDeclarativePropertyChanges::object() const +{ + Q_D(const QDeclarativePropertyChanges); + return d->object; +} + +void QDeclarativePropertyChanges::setObject(QObject *o) +{ + Q_D(QDeclarativePropertyChanges); + d->object = o; +} + +/*! + \qmlproperty bool PropertyChanges::restoreEntryValues + + This property holds whether the previous values should be restored when + leaving the state. + + The default value is \c true. Setting this value to \c false creates a + temporary state that has permanent effects on property values. +*/ +bool QDeclarativePropertyChanges::restoreEntryValues() const +{ + Q_D(const QDeclarativePropertyChanges); + return d->restore; +} + +void QDeclarativePropertyChanges::setRestoreEntryValues(bool v) +{ + Q_D(QDeclarativePropertyChanges); + d->restore = v; +} + +QDeclarativeProperty +QDeclarativePropertyChangesPrivate::property(const QString &property) +{ + Q_Q(QDeclarativePropertyChanges); + QDeclarativeProperty prop(object, property, qmlContext(q)); + if (!prop.isValid()) { + qmlInfo(q) << QDeclarativePropertyChanges::tr("Cannot assign to non-existent property \"%1\"").arg(property); + return QDeclarativeProperty(); + } else if (!(prop.type() & QDeclarativeProperty::SignalProperty) && !prop.isWritable()) { + qmlInfo(q) << QDeclarativePropertyChanges::tr("Cannot assign to read-only property \"%1\"").arg(property); + return QDeclarativeProperty(); + } + return prop; +} + +QDeclarativePropertyChanges::ActionList QDeclarativePropertyChanges::actions() +{ + Q_D(QDeclarativePropertyChanges); + + d->decode(); + + ActionList list; + + for (int ii = 0; ii < d->properties.count(); ++ii) { + + QDeclarativeAction a(d->object, d->properties.at(ii).first, + qmlContext(this), d->properties.at(ii).second); + + if (a.property.isValid()) { + a.restore = restoreEntryValues(); + list << a; + } + } + + for (int ii = 0; ii < d->signalReplacements.count(); ++ii) { + + QDeclarativeReplaceSignalHandler *handler = d->signalReplacements.at(ii); + + if (handler->property.isValid()) { + QDeclarativeAction a; + a.event = handler; + list << a; + } + } + + for (int ii = 0; ii < d->expressions.count(); ++ii) { + + const QString &property = d->expressions.at(ii).name; + QDeclarativeProperty prop = d->property(property); + + if (prop.isValid()) { + QDeclarativeAction a; + a.restore = restoreEntryValues(); + a.property = prop; + a.fromValue = a.property.read(); + a.specifiedObject = d->object; + a.specifiedProperty = property; + + if (d->isExplicit) { + a.toValue = d->expressions.at(ii).expression->evaluate(); + } else { + QDeclarativeExpression *e = d->expressions.at(ii).expression; + + QDeclarativeBinding::Identifier id = d->expressions.at(ii).id; + QDeclarativeBinding *newBinding = id != QDeclarativeBinding::Invalid ? QDeclarativeBinding::createBinding(id, object(), qmlContext(this), e->sourceFile(), e->lineNumber()) : 0; + if (!newBinding) { + newBinding = new QDeclarativeBinding(e->expression(), object(), qmlContext(this)); + newBinding->setSourceLocation(e->sourceFile(), e->lineNumber()); + } + newBinding->setTarget(prop); + a.toBinding = newBinding; + a.deletableToBinding = true; + } + + list << a; + } + } + + return list; +} + +/*! + \qmlproperty bool PropertyChanges::explicit + + If explicit is set to true, any potential bindings will be interpreted as + once-off assignments that occur when the state is entered. + + In the following example, the addition of explicit prevents \c myItem.width from + being bound to \c parent.width. Instead, it is assigned the value of \c parent.width + at the time of the state change. + \qml + PropertyChanges { + target: myItem + explicit: true + width: parent.width + } + \endqml + + By default, explicit is false. +*/ +bool QDeclarativePropertyChanges::isExplicit() const +{ + Q_D(const QDeclarativePropertyChanges); + return d->isExplicit; +} + +void QDeclarativePropertyChanges::setIsExplicit(bool e) +{ + Q_D(QDeclarativePropertyChanges); + d->isExplicit = e; +} + +bool QDeclarativePropertyChanges::containsValue(const QString &name) const +{ + Q_D(const QDeclarativePropertyChanges); + typedef QPair PropertyEntry; + + QListIterator propertyIterator(d->properties); + while (propertyIterator.hasNext()) { + const PropertyEntry &entry = propertyIterator.next(); + if (entry.first == name) { + return true; + } + } + + return false; +} + +bool QDeclarativePropertyChanges::containsExpression(const QString &name) const +{ + Q_D(const QDeclarativePropertyChanges); + typedef QDeclarativePropertyChangesPrivate::ExpressionChange ExpressionEntry; + + QListIterator expressionIterator(d->expressions); + while (expressionIterator.hasNext()) { + const ExpressionEntry &entry = expressionIterator.next(); + if (entry.name == name) { + return true; + } + } + + return false; +} + +bool QDeclarativePropertyChanges::containsProperty(const QString &name) const +{ + return containsValue(name) || containsExpression(name); +} + +void QDeclarativePropertyChanges::changeValue(const QString &name, const QVariant &value) +{ + Q_D(QDeclarativePropertyChanges); + typedef QPair PropertyEntry; + typedef QDeclarativePropertyChangesPrivate::ExpressionChange ExpressionEntry; + + QMutableListIterator expressionIterator(d->expressions); + while (expressionIterator.hasNext()) { + const ExpressionEntry &entry = expressionIterator.next(); + if (entry.name == name) { + expressionIterator.remove(); + if (state() && state()->isStateActive()) { + QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(d->property(name)); + if (oldBinding) { + QDeclarativePropertyPrivate::setBinding(d->property(name), 0); + oldBinding->destroy(); + } + d->property(name).write(value); + } + + d->properties.append(PropertyEntry(name, value)); + return; + } + } + + QMutableListIterator propertyIterator(d->properties); + while (propertyIterator.hasNext()) { + PropertyEntry &entry = propertyIterator.next(); + if (entry.first == name) { + entry.second = value; + if (state() && state()->isStateActive()) + d->property(name).write(value); + return; + } + } + + QDeclarativeAction action; + action.restore = restoreEntryValues(); + action.property = d->property(name); + action.fromValue = action.property.read(); + action.specifiedObject = object(); + action.specifiedProperty = name; + action.toValue = value; + + propertyIterator.insert(PropertyEntry(name, value)); + if (state() && state()->isStateActive()) { + state()->addEntryToRevertList(action); + QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(action.property); + if (oldBinding) + oldBinding->setEnabled(false, QDeclarativePropertyPrivate::DontRemoveBinding | QDeclarativePropertyPrivate::BypassInterceptor); + d->property(name).write(value); + } +} + +void QDeclarativePropertyChanges::changeExpression(const QString &name, const QString &expression) +{ + Q_D(QDeclarativePropertyChanges); + typedef QPair PropertyEntry; + typedef QDeclarativePropertyChangesPrivate::ExpressionChange ExpressionEntry; + + bool hadValue = false; + + QMutableListIterator propertyIterator(d->properties); + while (propertyIterator.hasNext()) { + PropertyEntry &entry = propertyIterator.next(); + if (entry.first == name) { + propertyIterator.remove(); + hadValue = true; + break; + } + } + + QMutableListIterator expressionIterator(d->expressions); + while (expressionIterator.hasNext()) { + const ExpressionEntry &entry = expressionIterator.next(); + if (entry.name == name) { + entry.expression->setExpression(expression); + if (state() && state()->isStateActive()) { + QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(d->property(name)); + if (oldBinding) { + QDeclarativePropertyPrivate::setBinding(d->property(name), 0); + oldBinding->destroy(); + } + + QDeclarativeBinding *newBinding = new QDeclarativeBinding(expression, object(), qmlContext(this)); + newBinding->setTarget(d->property(name)); + QDeclarativePropertyPrivate::setBinding(d->property(name), newBinding, QDeclarativePropertyPrivate::DontRemoveBinding | QDeclarativePropertyPrivate::BypassInterceptor); + } + return; + } + } + + QDeclarativeExpression *newExpression = new QDeclarativeExpression(qmlContext(this), d->object, expression); + expressionIterator.insert(ExpressionEntry(name, QDeclarativeBinding::Invalid, newExpression)); + + if (state() && state()->isStateActive()) { + if (hadValue) { + QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(d->property(name)); + if (oldBinding) { + oldBinding->setEnabled(false, QDeclarativePropertyPrivate::DontRemoveBinding | QDeclarativePropertyPrivate::BypassInterceptor); + state()->changeBindingInRevertList(object(), name, oldBinding); + } + + QDeclarativeBinding *newBinding = new QDeclarativeBinding(expression, object(), qmlContext(this)); + newBinding->setTarget(d->property(name)); + QDeclarativePropertyPrivate::setBinding(d->property(name), newBinding, QDeclarativePropertyPrivate::DontRemoveBinding | QDeclarativePropertyPrivate::BypassInterceptor); + } else { + QDeclarativeAction action; + action.restore = restoreEntryValues(); + action.property = d->property(name); + action.fromValue = action.property.read(); + action.specifiedObject = object(); + action.specifiedProperty = name; + + + if (d->isExplicit) { + action.toValue = newExpression->evaluate(); + } else { + QDeclarativeBinding *newBinding = new QDeclarativeBinding(newExpression->expression(), object(), qmlContext(this)); + newBinding->setTarget(d->property(name)); + action.toBinding = newBinding; + action.deletableToBinding = true; + + state()->addEntryToRevertList(action); + QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(action.property); + if (oldBinding) + oldBinding->setEnabled(false, QDeclarativePropertyPrivate::DontRemoveBinding | QDeclarativePropertyPrivate::BypassInterceptor); + + QDeclarativePropertyPrivate::setBinding(action.property, newBinding, QDeclarativePropertyPrivate::DontRemoveBinding | QDeclarativePropertyPrivate::BypassInterceptor); + } + } + } + // what about the signal handler? +} + +QVariant QDeclarativePropertyChanges::property(const QString &name) const +{ + Q_D(const QDeclarativePropertyChanges); + typedef QPair PropertyEntry; + typedef QDeclarativePropertyChangesPrivate::ExpressionChange ExpressionEntry; + + QListIterator propertyIterator(d->properties); + while (propertyIterator.hasNext()) { + const PropertyEntry &entry = propertyIterator.next(); + if (entry.first == name) { + return entry.second; + } + } + + QListIterator expressionIterator(d->expressions); + while (expressionIterator.hasNext()) { + const ExpressionEntry &entry = expressionIterator.next(); + if (entry.name == name) { + return QVariant(entry.expression->expression()); + } + } + + return QVariant(); +} + +void QDeclarativePropertyChanges::removeProperty(const QString &name) +{ + Q_D(QDeclarativePropertyChanges); + typedef QPair PropertyEntry; + typedef QDeclarativePropertyChangesPrivate::ExpressionChange ExpressionEntry; + + QMutableListIterator expressionIterator(d->expressions); + while (expressionIterator.hasNext()) { + const ExpressionEntry &entry = expressionIterator.next(); + if (entry.name == name) { + expressionIterator.remove(); + state()->removeEntryFromRevertList(object(), name); + return; + } + } + + QMutableListIterator propertyIterator(d->properties); + while (propertyIterator.hasNext()) { + const PropertyEntry &entry = propertyIterator.next(); + if (entry.first == name) { + propertyIterator.remove(); + state()->removeEntryFromRevertList(object(), name); + return; + } + } +} + +QVariant QDeclarativePropertyChanges::value(const QString &name) const +{ + Q_D(const QDeclarativePropertyChanges); + typedef QPair PropertyEntry; + + QListIterator propertyIterator(d->properties); + while (propertyIterator.hasNext()) { + const PropertyEntry &entry = propertyIterator.next(); + if (entry.first == name) { + return entry.second; + } + } + + return QVariant(); +} + +QString QDeclarativePropertyChanges::expression(const QString &name) const +{ + Q_D(const QDeclarativePropertyChanges); + typedef QDeclarativePropertyChangesPrivate::ExpressionChange ExpressionEntry; + + QListIterator expressionIterator(d->expressions); + while (expressionIterator.hasNext()) { + const ExpressionEntry &entry = expressionIterator.next(); + if (entry.name == name) { + return entry.expression->expression(); + } + } + + return QString(); +} + +void QDeclarativePropertyChanges::detachFromState() +{ + if (state()) + state()->removeAllEntriesFromRevertList(object()); +} + +void QDeclarativePropertyChanges::attachToState() +{ + if (state()) + state()->addEntriesToRevertList(actions()); +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativepropertychanges_p.h b/src/declarative/util/qdeclarativepropertychanges_p.h new file mode 100644 index 00000000..7b03c3f0 --- /dev/null +++ b/src/declarative/util/qdeclarativepropertychanges_p.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPROPERTYCHANGES_H +#define QDECLARATIVEPROPERTYCHANGES_H + +#include "private/qdeclarativestateoperations_p.h" +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativePropertyChangesPrivate; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativePropertyChanges : public QDeclarativeStateOperation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativePropertyChanges) + + 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: + QDeclarativePropertyChanges(); + ~QDeclarativePropertyChanges(); + + QObject *object() const; + void setObject(QObject *); + + bool restoreEntryValues() const; + void setRestoreEntryValues(bool); + + bool isExplicit() const; + void setIsExplicit(bool); + + virtual ActionList actions(); + + bool containsProperty(const QString &name) const; + bool containsValue(const QString &name) const; + bool containsExpression(const QString &name) const; + void changeValue(const QString &name, const QVariant &value); + void changeExpression(const QString &name, const QString &expression); + void removeProperty(const QString &name); + QVariant value(const QString &name) const; + QString expression(const QString &name) const; + + void detachFromState(); + void attachToState(); + + QVariant property(const QString &name) const; +}; + +class QDeclarativePropertyChangesParser : public QDeclarativeCustomParser +{ +public: + QDeclarativePropertyChangesParser() + : QDeclarativeCustomParser(AcceptsAttachedProperties) {} + + void compileList(QList > &list, const QByteArray &pre, const QDeclarativeCustomParserProperty &prop); + + virtual QByteArray compile(const QList &); + virtual void setCustomData(QObject *, const QByteArray &); +}; + + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativePropertyChanges) + +QT_END_HEADER + +#endif // QDECLARATIVEPROPERTYCHANGES_H diff --git a/src/declarative/util/qdeclarativepropertymap.cpp b/src/declarative/util/qdeclarativepropertymap.cpp new file mode 100644 index 00000000..6e2886ce --- /dev/null +++ b/src/declarative/util/qdeclarativepropertymap.cpp @@ -0,0 +1,296 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativepropertymap.h" + +#include +#include "private/qdeclarativeopenmetaobject_p.h" + +#include + +QT_BEGIN_NAMESPACE + +//QDeclarativePropertyMapMetaObject lets us listen for changes coming from QML +//so we can emit the changed signal. +class QDeclarativePropertyMapMetaObject : public QDeclarativeOpenMetaObject +{ +public: + QDeclarativePropertyMapMetaObject(QDeclarativePropertyMap *obj, QDeclarativePropertyMapPrivate *objPriv); + +protected: + virtual void propertyWritten(int index); + virtual void propertyCreated(int, QMetaPropertyBuilder &); + +private: + QDeclarativePropertyMap *map; + QDeclarativePropertyMapPrivate *priv; +}; + +class QDeclarativePropertyMapPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativePropertyMap) +public: + QDeclarativePropertyMapMetaObject *mo; + QStringList keys; + void emitChanged(const QString &key, const QVariant &value); +}; + +void QDeclarativePropertyMapPrivate::emitChanged(const QString &key, const QVariant &value) +{ + Q_Q(QDeclarativePropertyMap); + emit q->valueChanged(key, value); +} + +QDeclarativePropertyMapMetaObject::QDeclarativePropertyMapMetaObject(QDeclarativePropertyMap *obj, QDeclarativePropertyMapPrivate *objPriv) : QDeclarativeOpenMetaObject(obj) +{ + map = obj; + priv = objPriv; +} + +void QDeclarativePropertyMapMetaObject::propertyWritten(int index) +{ + priv->emitChanged(QString::fromUtf8(name(index)), operator[](index)); +} + +void QDeclarativePropertyMapMetaObject::propertyCreated(int, QMetaPropertyBuilder &b) +{ + priv->keys.append(QString::fromUtf8(b.name())); +} + +/*! + \class QDeclarativePropertyMap + \since 4.7 + \brief The QDeclarativePropertyMap class allows you to set key-value pairs that can be used in QML bindings. + + QDeclarativePropertyMap provides a convenient way to expose domain data to the UI layer. + The following example shows how you might declare data in C++ and then + access it in QML. + + In the C++ file: + \code + // create our data + QDeclarativePropertyMap ownerData; + ownerData.insert("name", QVariant(QString("John Smith"))); + ownerData.insert("phone", QVariant(QString("555-5555"))); + + // expose it to the UI layer + QDeclarativeView view; + QDeclarativeContext *ctxt = view.rootContext(); + ctxt->setContextProperty("owner", &ownerData); + + view.setSource(QUrl::fromLocalFile("main.qml")); + view.show(); + \endcode + + Then, in \c main.qml: + \code + Text { text: owner.name + " " + owner.phone } + \endcode + + The binding is dynamic - whenever a key's value is updated, anything bound to that + key will be updated as well. + + To detect value changes made in the UI layer you can connect to the valueChanged() signal. + However, note that valueChanged() is \bold NOT emitted when changes are made by calling insert() + or clear() - it is only emitted when a value is updated from QML. + + \note It is not possible to remove keys from the map; once a key has been added, you can only + modify or clear its associated value. +*/ + +/*! + Constructs a bindable map with parent object \a parent. +*/ +QDeclarativePropertyMap::QDeclarativePropertyMap(QObject *parent) +: QObject(*(new QDeclarativePropertyMapPrivate), parent) +{ + Q_D(QDeclarativePropertyMap); + d->mo = new QDeclarativePropertyMapMetaObject(this, d); +} + +/*! + Destroys the bindable map. +*/ +QDeclarativePropertyMap::~QDeclarativePropertyMap() +{ +} + +/*! + Clears the value (if any) associated with \a key. +*/ +void QDeclarativePropertyMap::clear(const QString &key) +{ + Q_D(QDeclarativePropertyMap); + d->mo->setValue(key.toUtf8(), QVariant()); +} + +/*! + Returns the value associated with \a key. + + If no value has been set for this key (or if the value has been cleared), + an invalid QVariant is returned. +*/ +QVariant QDeclarativePropertyMap::value(const QString &key) const +{ + Q_D(const QDeclarativePropertyMap); + return d->mo->value(key.toUtf8()); +} + +/*! + Sets the value associated with \a key to \a value. + + If the key doesn't exist, it is automatically created. +*/ +void QDeclarativePropertyMap::insert(const QString &key, const QVariant &value) +{ + Q_D(QDeclarativePropertyMap); + //The following strings shouldn't be used as property names + if (key != QLatin1String("keys") + && key != QLatin1String("valueChanged") + && key != QLatin1String("QObject") + && key != QLatin1String("destroyed") + && key != QLatin1String("deleteLater")) { + d->mo->setValue(key.toUtf8(), value); + } else { + qWarning() << "Creating property with name" + << key + << "is not permitted, conflicts with internal symbols."; + } +} + +/*! + Returns the list of keys. + + Keys that have been cleared will still appear in this list, even though their + associated values are invalid QVariants. +*/ +QStringList QDeclarativePropertyMap::keys() const +{ + Q_D(const QDeclarativePropertyMap); + return d->keys; +} + +/*! + \overload + + Same as size(). +*/ +int QDeclarativePropertyMap::count() const +{ + Q_D(const QDeclarativePropertyMap); + return d->keys.count(); +} + +/*! + Returns the number of keys in the map. + + \sa isEmpty(), count() +*/ +int QDeclarativePropertyMap::size() const +{ + Q_D(const QDeclarativePropertyMap); + return d->keys.size(); +} + +/*! + Returns true if the map contains no keys; otherwise returns + false. + + \sa size() +*/ +bool QDeclarativePropertyMap::isEmpty() const +{ + Q_D(const QDeclarativePropertyMap); + return d->keys.isEmpty(); +} + +/*! + Returns true if the map contains \a key. + + \sa size() +*/ +bool QDeclarativePropertyMap::contains(const QString &key) const +{ + Q_D(const QDeclarativePropertyMap); + return d->keys.contains(key); +} + +/*! + Returns the value associated with the key \a key as a modifiable + reference. + + If the map contains no item with key \a key, the function inserts + an invalid QVariant into the map with key \a key, and + returns a reference to it. + + \sa insert(), value() +*/ +QVariant &QDeclarativePropertyMap::operator[](const QString &key) +{ + //### optimize + Q_D(QDeclarativePropertyMap); + QByteArray utf8key = key.toUtf8(); + if (!d->keys.contains(key)) + insert(key, QVariant());//force creation -- needed below + + return (*(d->mo))[utf8key]; +} + +/*! + \overload + + Same as value(). +*/ +QVariant QDeclarativePropertyMap::operator[](const QString &key) const +{ + return value(key); +} + +/*! + \fn void QDeclarativePropertyMap::valueChanged(const QString &key, const QVariant &value) + This signal is emitted whenever one of the values in the map is changed. \a key + is the key corresponding to the \a value that was changed. + + \note valueChanged() is \bold NOT emitted when changes are made by calling insert() + or clear() - it is only emitted when a value is updated from QML. +*/ + +QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativepropertymap.h b/src/declarative/util/qdeclarativepropertymap.h new file mode 100644 index 00000000..0f217253 --- /dev/null +++ b/src/declarative/util/qdeclarativepropertymap.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPROPERTYMAP_H +#define QDECLARATIVEPROPERTYMAP_H + +#include +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativePropertyMapPrivate; +class Q_DECLARATIVE_EXPORT QDeclarativePropertyMap : public QObject +{ + Q_OBJECT +public: + QDeclarativePropertyMap(QObject *parent = 0); + virtual ~QDeclarativePropertyMap(); + + QVariant value(const QString &key) const; + void insert(const QString &key, const QVariant &value); + void clear(const QString &key); + + Q_INVOKABLE QStringList keys() const; + + int count() const; + int size() const; + bool isEmpty() const; + bool contains(const QString &key) const; + + QVariant &operator[](const QString &key); + QVariant operator[](const QString &key) const; + +Q_SIGNALS: + void valueChanged(const QString &key, const QVariant &value); + +private: + Q_DECLARE_PRIVATE(QDeclarativePropertyMap) + Q_DISABLE_COPY(QDeclarativePropertyMap) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/declarative/util/qdeclarativesmoothedanimation.cpp b/src/declarative/util/qdeclarativesmoothedanimation.cpp new file mode 100644 index 00000000..79b4eff4 --- /dev/null +++ b/src/declarative/util/qdeclarativesmoothedanimation.cpp @@ -0,0 +1,493 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativesmoothedanimation_p.h" +#include "private/qdeclarativesmoothedanimation_p_p.h" + +#include "private/qdeclarativeanimation_p_p.h" + +#include +#include "private/qdeclarativeproperty_p.h" + +#include "private/qdeclarativeglobal_p.h" + +#include + +#include + +#define DELAY_STOP_TIMER_INTERVAL 32 + +QT_BEGIN_NAMESPACE + +QSmoothedAnimation::QSmoothedAnimation(QObject *parent) + : QAbstractAnimation(parent), to(0), velocity(200), userDuration(-1), maximumEasingTime(-1), + reversingMode(QDeclarativeSmoothedAnimation::Eased), initialVelocity(0), + trackVelocity(0), initialValue(0), invert(false), finalDuration(-1), lastTime(0) +{ + delayedStopTimer.setInterval(DELAY_STOP_TIMER_INTERVAL); + delayedStopTimer.setSingleShot(true); + connect(&delayedStopTimer, SIGNAL(timeout()), this, SLOT(stop())); +} + +void QSmoothedAnimation::restart() +{ + initialVelocity = trackVelocity; + if (state() != QAbstractAnimation::Running) + start(); + else + init(); +} + +void QSmoothedAnimation::updateState(QAbstractAnimation::State newState, QAbstractAnimation::State /*oldState*/) +{ + if (newState == QAbstractAnimation::Running) + init(); +} + +void QSmoothedAnimation::delayedStop() +{ + if (!delayedStopTimer.isActive()) + delayedStopTimer.start(); +} + +int QSmoothedAnimation::duration() const +{ + return -1; +} + +bool QSmoothedAnimation::recalc() +{ + s = to - initialValue; + vi = initialVelocity; + + s = (invert? qreal(-1.0): qreal(1.0)) * s; + + if (userDuration > 0 && velocity > 0) { + tf = s / velocity; + if (tf > (userDuration / qreal(1000.))) tf = (userDuration / qreal(1000.)); + } else if (userDuration > 0) { + tf = userDuration / qreal(1000.); + } else if (velocity > 0) { + tf = s / velocity; + } else { + return false; + } + + finalDuration = ceil(tf * qreal(1000.0)); + + if (maximumEasingTime == 0) { + a = 0; + d = 0; + tp = 0; + td = tf; + vp = velocity; + sp = 0; + sd = s; + } else if (maximumEasingTime != -1 && tf > (maximumEasingTime / qreal(1000.))) { + qreal met = maximumEasingTime / qreal(1000.); + td = tf - met; + + qreal c1 = td; + qreal c2 = (tf - td) * vi - tf * velocity; + qreal c3 = qreal(-0.5) * (tf - td) * vi * vi; + + qreal vp1 = (-c2 + qSqrt(c2 * c2 - 4 * c1 * c3)) / (qreal(2.) * c1); + + vp = vp1; + a = vp / met; + d = a; + tp = (vp - vi) / a; + sp = vi * tp + qreal(0.5) * a * tp * tp; + sd = sp + (td - tp) * vp; + } else { + qreal c1 = qreal(0.25) * tf * tf; + qreal c2 = qreal(0.5) * vi * tf - s; + qreal c3 = qreal(-0.25) * vi * vi; + + qreal a1 = (-c2 + qSqrt(c2 * c2 - 4 * c1 * c3)) / (qreal(2.) * c1); + + qreal tp1 = qreal(0.5) * tf - qreal(0.5) * vi / a1; + qreal vp1 = a1 * tp1 + vi; + + qreal sp1 = qreal(0.5) * a1 * tp1 * tp1 + vi * tp1; + + a = a1; + d = a1; + tp = tp1; + td = tp1; + vp = vp1; + sp = sp1; + sd = sp1; + } + return true; +} + +qreal QSmoothedAnimation::easeFollow(qreal time_seconds) +{ + qreal value; + if (time_seconds < tp) { + trackVelocity = vi + time_seconds * a; + value = qreal(0.5) * a * time_seconds * time_seconds + vi * time_seconds; + } else if (time_seconds < td) { + time_seconds -= tp; + trackVelocity = vp; + value = sp + time_seconds * vp; + } else if (time_seconds < tf) { + time_seconds -= td; + trackVelocity = vp - time_seconds * a; + value = sd - qreal(0.5) * d * time_seconds * time_seconds + vp * time_seconds; + } else { + trackVelocity = 0; + value = s; + delayedStop(); + } + + // to normalize 's' between [0..1], divide 'value' by 's' + return value; +} + +void QSmoothedAnimation::updateCurrentTime(int t) +{ + qreal time_seconds = qreal(t - lastTime) / qreal(1000.); + + qreal value = easeFollow(time_seconds); + value *= (invert? qreal(-1.0): qreal(1.0)); + QDeclarativePropertyPrivate::write(target, initialValue + value, + QDeclarativePropertyPrivate::BypassInterceptor + | QDeclarativePropertyPrivate::DontRemoveBinding); +} + +void QSmoothedAnimation::init() +{ + if (velocity == 0) { + stop(); + return; + } + + if (delayedStopTimer.isActive()) + delayedStopTimer.stop(); + + initialValue = target.read().toReal(); + lastTime = this->currentTime(); + + if (to == initialValue) { + stop(); + return; + } + + bool hasReversed = trackVelocity != qreal(0.) && + ((!invert) == ((initialValue - to) > 0)); + + if (hasReversed) { + switch (reversingMode) { + default: + case QDeclarativeSmoothedAnimation::Eased: + initialVelocity = -trackVelocity; + break; + case QDeclarativeSmoothedAnimation::Sync: + QDeclarativePropertyPrivate::write(target, to, + QDeclarativePropertyPrivate::BypassInterceptor + | QDeclarativePropertyPrivate::DontRemoveBinding); + trackVelocity = 0; + stop(); + return; + case QDeclarativeSmoothedAnimation::Immediate: + initialVelocity = 0; + break; + } + } + + trackVelocity = initialVelocity; + + invert = (to < initialValue); + + if (!recalc()) { + QDeclarativePropertyPrivate::write(target, to, + QDeclarativePropertyPrivate::BypassInterceptor + | QDeclarativePropertyPrivate::DontRemoveBinding); + stop(); + return; + } +} + +/*! + \qmlclass SmoothedAnimation QDeclarativeSmoothedAnimation + \ingroup qml-animation-transition + \since 4.7 + \inherits NumberAnimation + \brief The SmoothedAnimation element allows a property to smoothly track a value. + + A SmoothedAnimation animates a property's value to a set target value + using an ease in/out quad easing curve. When the target value changes, + the easing curves used to animate between the old and new target values + are smoothly spliced together to create a smooth movement to the new + target value that maintains the current velocity. + + The follow example shows one \l Rectangle tracking the position of another + using SmoothedAnimation. The green rectangle's \c x and \c y values are + bound to those of the red rectangle. Whenever these values change, the + green rectangle smoothly animates to its new position: + + \snippet doc/src/snippets/declarative/smoothedanimation.qml 0 + + A SmoothedAnimation can be configured by setting the \l velocity at which the + animation should occur, or the \l duration that the animation should take. + If both the \l velocity and \l duration are specified, the one that results in + the quickest animation is chosen for each change in the target value. + + For example, animating from 0 to 800 will take 4 seconds if a velocity + of 200 is set, will take 8 seconds with a duration of 8000 set, and will + take 4 seconds with both a velocity of 200 and a duration of 8000 set. + Animating from 0 to 20000 will take 10 seconds if a velocity of 200 is set, + will take 8 seconds with a duration of 8000 set, and will take 8 seconds + with both a velocity of 200 and a duration of 8000 set. + + The default velocity of SmoothedAnimation is 200 units/second. Note that if the range of the + value being animated is small, then the velocity will need to be adjusted + appropriately. For example, the opacity of an item ranges from 0 - 1.0. + To enable a smooth animation in this range the velocity will need to be + set to a value such as 0.5 units/second. Animating from 0 to 1.0 with a velocity + of 0.5 will take 2000 ms to complete. + + Like any other animation element, a SmoothedAnimation can be applied in a + number of ways, including transitions, behaviors and property value + sources. The \l {QML Animation and Transitions} documentation shows a + variety of methods for creating animations. + + \sa SpringAnimation, NumberAnimation, {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example} +*/ + +QDeclarativeSmoothedAnimation::QDeclarativeSmoothedAnimation(QObject *parent) +: QDeclarativeNumberAnimation(*(new QDeclarativeSmoothedAnimationPrivate), parent) +{ +} + +QDeclarativeSmoothedAnimation::~QDeclarativeSmoothedAnimation() +{ +} + +QDeclarativeSmoothedAnimationPrivate::QDeclarativeSmoothedAnimationPrivate() + : wrapperGroup(new QParallelAnimationGroup), anim(new QSmoothedAnimation) +{ + Q_Q(QDeclarativeSmoothedAnimation); + QDeclarative_setParent_noEvent(wrapperGroup, q); + QDeclarative_setParent_noEvent(anim, q); +} + +void QDeclarativeSmoothedAnimationPrivate::updateRunningAnimations() +{ + foreach(QSmoothedAnimation* ease, activeAnimations.values()){ + ease->maximumEasingTime = anim->maximumEasingTime; + ease->reversingMode = anim->reversingMode; + ease->velocity = anim->velocity; + ease->userDuration = anim->userDuration; + ease->init(); + } +} + +QAbstractAnimation* QDeclarativeSmoothedAnimation::qtAnimation() +{ + Q_D(QDeclarativeSmoothedAnimation); + return d->wrapperGroup; +} + +void QDeclarativeSmoothedAnimation::transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) +{ + Q_D(QDeclarativeSmoothedAnimation); + QDeclarativeNumberAnimation::transition(actions, modified, direction); + + if (!d->actions) + return; + + QSet anims; + for (int i = 0; i < d->actions->size(); i++) { + QSmoothedAnimation *ease; + bool needsRestart; + if (!d->activeAnimations.contains((*d->actions)[i].property)) { + ease = new QSmoothedAnimation(); + d->wrapperGroup->addAnimation(ease); + d->activeAnimations.insert((*d->actions)[i].property, ease); + needsRestart = false; + } else { + ease = d->activeAnimations.value((*d->actions)[i].property); + needsRestart = true; + } + ease->target = (*d->actions)[i].property; + ease->to = (*d->actions)[i].toValue.toReal(); + + // copying public members from main value holder animation + ease->maximumEasingTime = d->anim->maximumEasingTime; + ease->reversingMode = d->anim->reversingMode; + ease->velocity = d->anim->velocity; + ease->userDuration = d->anim->userDuration; + + ease->initialVelocity = ease->trackVelocity; + + if (needsRestart) + ease->init(); + anims.insert(ease); + } + + for (int i = d->wrapperGroup->animationCount() - 1; i >= 0 ; --i) { + if (!anims.contains(d->wrapperGroup->animationAt(i))) { + QSmoothedAnimation *ease = static_cast(d->wrapperGroup->animationAt(i)); + d->activeAnimations.remove(ease->target); + d->wrapperGroup->takeAnimation(i); + delete ease; + } + } +} + +/*! + \qmlproperty enumeration SmoothedAnimation::reversingMode + + Sets how the SmoothedAnimation behaves if an animation direction is reversed. + + Possible values are: + + \list + \o SmoothedAnimation.Eased (default) - the animation will smoothly decelerate, and then reverse direction + \o SmoothedAnimation.Immediate - the animation will immediately begin accelerating in the reverse direction, beginning with a velocity of 0 + \o SmoothedAnimation.Sync - the property is immediately set to the target value + \endlist +*/ +QDeclarativeSmoothedAnimation::ReversingMode QDeclarativeSmoothedAnimation::reversingMode() const +{ + Q_D(const QDeclarativeSmoothedAnimation); + return (QDeclarativeSmoothedAnimation::ReversingMode) d->anim->reversingMode; +} + +void QDeclarativeSmoothedAnimation::setReversingMode(ReversingMode m) +{ + Q_D(QDeclarativeSmoothedAnimation); + if (d->anim->reversingMode == m) + return; + + d->anim->reversingMode = m; + emit reversingModeChanged(); + d->updateRunningAnimations(); +} + +/*! + \qmlproperty int SmoothedAnimation::duration + + This property holds the animation duration, in msecs, used when tracking the source. + + Setting this to -1 (the default) disables the duration value. + + If the velocity value and the duration value are both enabled, then the animation will + use whichever gives the shorter duration. +*/ +int QDeclarativeSmoothedAnimation::duration() const +{ + Q_D(const QDeclarativeSmoothedAnimation); + return d->anim->userDuration; +} + +void QDeclarativeSmoothedAnimation::setDuration(int duration) +{ + Q_D(QDeclarativeSmoothedAnimation); + if (duration != -1) + QDeclarativeNumberAnimation::setDuration(duration); + if(duration == d->anim->userDuration) + return; + d->anim->userDuration = duration; + d->updateRunningAnimations(); +} + +qreal QDeclarativeSmoothedAnimation::velocity() const +{ + Q_D(const QDeclarativeSmoothedAnimation); + return d->anim->velocity; +} + +/*! + \qmlproperty real SmoothedAnimation::velocity + + This property holds the average velocity allowed when tracking the 'to' value. + + The default velocity of SmoothedAnimation is 200 units/second. + + Setting this to -1 disables the velocity value. + + If the velocity value and the duration value are both enabled, then the animation will + use whichever gives the shorter duration. +*/ +void QDeclarativeSmoothedAnimation::setVelocity(qreal v) +{ + Q_D(QDeclarativeSmoothedAnimation); + if (d->anim->velocity == v) + return; + + d->anim->velocity = v; + emit velocityChanged(); + d->updateRunningAnimations(); +} + +/*! + \qmlproperty int SmoothedAnimation::maximumEasingTime + + This property specifies the maximum time, in msecs, any "eases" during the follow should take. + Setting this property causes the velocity to "level out" after at a time. Setting + a negative value reverts to the normal mode of easing over the entire animation + duration. + + The default value is -1. +*/ +int QDeclarativeSmoothedAnimation::maximumEasingTime() const +{ + Q_D(const QDeclarativeSmoothedAnimation); + return d->anim->maximumEasingTime; +} + +void QDeclarativeSmoothedAnimation::setMaximumEasingTime(int v) +{ + Q_D(QDeclarativeSmoothedAnimation); + if(v == d->anim->maximumEasingTime) + return; + d->anim->maximumEasingTime = v; + emit maximumEasingTimeChanged(); + d->updateRunningAnimations(); +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativesmoothedanimation_p.h b/src/declarative/util/qdeclarativesmoothedanimation_p.h new file mode 100644 index 00000000..dd030dcf --- /dev/null +++ b/src/declarative/util/qdeclarativesmoothedanimation_p.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESMOOTHEDANIMATION_H +#define QDECLARATIVESMOOTHEDANIMATION_H + +#include +#include "private/qdeclarativeanimation_p.h" + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeProperty; +class QDeclarativeSmoothedAnimationPrivate; +class Q_AUTOTEST_EXPORT QDeclarativeSmoothedAnimation : public QDeclarativeNumberAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeSmoothedAnimation) + Q_ENUMS(ReversingMode) + + Q_PROPERTY(qreal velocity READ velocity WRITE setVelocity NOTIFY velocityChanged) + Q_PROPERTY(ReversingMode reversingMode READ reversingMode WRITE setReversingMode NOTIFY reversingModeChanged) + Q_PROPERTY(qreal maximumEasingTime READ maximumEasingTime WRITE setMaximumEasingTime NOTIFY maximumEasingTimeChanged) + +public: + enum ReversingMode { Eased, Immediate, Sync }; + + QDeclarativeSmoothedAnimation(QObject *parent = 0); + ~QDeclarativeSmoothedAnimation(); + + ReversingMode reversingMode() const; + void setReversingMode(ReversingMode); + + virtual int duration() const; + virtual void setDuration(int); + + qreal velocity() const; + void setVelocity(qreal); + + int maximumEasingTime() const; + void setMaximumEasingTime(int); + + virtual void transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction); + QAbstractAnimation* qtAnimation(); + +Q_SIGNALS: + void velocityChanged(); + void reversingModeChanged(); + void maximumEasingTimeChanged(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeSmoothedAnimation) + +QT_END_HEADER + +#endif // QDECLARATIVESMOOTHEDANIMATION_H diff --git a/src/declarative/util/qdeclarativesmoothedanimation_p_p.h b/src/declarative/util/qdeclarativesmoothedanimation_p_p.h new file mode 100644 index 00000000..8f8da8d3 --- /dev/null +++ b/src/declarative/util/qdeclarativesmoothedanimation_p_p.h @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESMOOTHEDANIMATION_P_H +#define QDECLARATIVESMOOTHEDANIMATION_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 "private/qdeclarativesmoothedanimation_p.h" +#include "private/qdeclarativeanimation_p.h" + +#include "private/qdeclarativeanimation_p_p.h" + +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_AUTOTEST_EXPORT QSmoothedAnimation : public QAbstractAnimation +{ +public: + QSmoothedAnimation(QObject *parent=0); + + qreal to; + qreal velocity; + int userDuration; + + int maximumEasingTime; + QDeclarativeSmoothedAnimation::ReversingMode reversingMode; + + qreal initialVelocity; + qreal trackVelocity; + + QDeclarativeProperty target; + + int duration() const; + void restart(); + void init(); + +protected: + virtual void updateCurrentTime(int); + virtual void updateState(QAbstractAnimation::State, QAbstractAnimation::State); + +private: + qreal easeFollow(qreal); + qreal initialValue; + + bool invert; + + int finalDuration; + + // Parameters for use in updateCurrentTime() + qreal a; // Acceleration + qreal d; // Deceleration + qreal tf; // Total time + qreal tp; // Time at which peak velocity occurs + qreal td; // Time at which decelleration begins + qreal vp; // Velocity at tp + qreal sp; // Displacement at tp + qreal sd; // Displacement at td + qreal vi; // "Normalized" initialvelocity + qreal s; // Total s + + int lastTime; + + bool recalc(); + void delayedStop(); + + QTimer delayedStopTimer; +}; + +class QDeclarativeSmoothedAnimationPrivate : public QDeclarativePropertyAnimationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeSmoothedAnimation) +public: + QDeclarativeSmoothedAnimationPrivate(); + void updateRunningAnimations(); + + QParallelAnimationGroup *wrapperGroup; + QSmoothedAnimation *anim; + QHash activeAnimations; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVESMOOTHEDANIMATION_P_H diff --git a/src/declarative/util/qdeclarativespringanimation.cpp b/src/declarative/util/qdeclarativespringanimation.cpp new file mode 100644 index 00000000..034c54b2 --- /dev/null +++ b/src/declarative/util/qdeclarativespringanimation.cpp @@ -0,0 +1,462 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativespringanimation_p.h" + +#include "private/qdeclarativeanimation_p_p.h" +#include + +#include + +#include + +#include +#include + +QT_BEGIN_NAMESPACE + + +class QDeclarativeSpringAnimationPrivate : public QDeclarativePropertyAnimationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeSpringAnimation) +public: + + + struct SpringAnimation { + SpringAnimation() + : currentValue(0), to(0), velocity(0), start(0), duration(0) {} + qreal currentValue; + qreal to; + qreal velocity; + int start; + int duration; + }; + QHash activeAnimations; + + qreal maxVelocity; + qreal velocityms; + int lastTime; + qreal mass; + qreal spring; + qreal damping; + qreal epsilon; + qreal modulus; + + bool useMass : 1; + bool haveModulus : 1; + + enum Mode { + Track, + Velocity, + Spring + }; + Mode mode; + + QDeclarativeSpringAnimationPrivate() + : maxVelocity(0), velocityms(0), lastTime(0) + , mass(1.0), spring(0.), damping(0.), epsilon(0.01) + , modulus(0.0), useMass(false), haveModulus(false) + , mode(Track), clock(0) + { } + + void tick(int time); + bool animate(const QDeclarativeProperty &property, SpringAnimation &animation, int elapsed); + void updateMode(); + + typedef QTickAnimationProxy Clock; + Clock *clock; +}; + +void QDeclarativeSpringAnimationPrivate::tick(int time) +{ + if (mode == Track) { + clock->stop(); + return; + } + int elapsed = time - lastTime; + if (!elapsed) + return; + + if (mode == Spring) { + if (elapsed < 16) // capped at 62fps. + return; + int count = elapsed / 16; + lastTime = time - (elapsed - count * 16); + } else { + lastTime = time; + } + + QMutableHashIterator it(activeAnimations); + while (it.hasNext()) { + it.next(); + if (animate(it.key(), it.value(), elapsed)) + it.remove(); + } + + if (activeAnimations.isEmpty()) + clock->stop(); +} + +bool QDeclarativeSpringAnimationPrivate::animate(const QDeclarativeProperty &property, SpringAnimation &animation, int elapsed) +{ + qreal srcVal = animation.to; + + bool stop = false; + + if (haveModulus) { + animation.currentValue = fmod(animation.currentValue, modulus); + srcVal = fmod(srcVal, modulus); + } + if (mode == Spring) { + // Real men solve the spring DEs using RK4. + // We'll do something much simpler which gives a result that looks fine. + int count = elapsed / 16; + for (int i = 0; i < count; ++i) { + qreal diff = srcVal - animation.currentValue; + if (haveModulus && qAbs(diff) > modulus / 2) { + if (diff < 0) + diff += modulus; + else + diff -= modulus; + } + if (useMass) + animation.velocity = animation.velocity + (spring * diff - damping * animation.velocity) / mass; + else + animation.velocity = animation.velocity + spring * diff - damping * animation.velocity; + if (maxVelocity > qreal(0.)) { + // limit velocity + if (animation.velocity > maxVelocity) + animation.velocity = maxVelocity; + else if (animation.velocity < -maxVelocity) + animation.velocity = -maxVelocity; + } + animation.currentValue += animation.velocity * qreal(16.0) / qreal(1000.0); + if (haveModulus) { + animation.currentValue = fmod(animation.currentValue, modulus); + if (animation.currentValue < qreal(0.0)) + animation.currentValue += modulus; + } + } + if (qAbs(animation.velocity) < epsilon && qAbs(srcVal - animation.currentValue) < epsilon) { + animation.velocity = 0.0; + animation.currentValue = srcVal; + stop = true; + } + } else { + qreal moveBy = elapsed * velocityms; + qreal diff = srcVal - animation.currentValue; + if (haveModulus && qAbs(diff) > modulus / 2) { + if (diff < 0) + diff += modulus; + else + diff -= modulus; + } + if (diff > 0) { + animation.currentValue += moveBy; + if (haveModulus) + animation.currentValue = fmod(animation.currentValue, modulus); + } else { + animation.currentValue -= moveBy; + if (haveModulus && animation.currentValue < qreal(0.0)) + animation.currentValue = fmod(animation.currentValue, modulus) + modulus; + } + if (lastTime - animation.start >= animation.duration) { + animation.currentValue = animation.to; + stop = true; + } + } + + qreal old_to = animation.to; + + QDeclarativePropertyPrivate::write(property, animation.currentValue, + QDeclarativePropertyPrivate::BypassInterceptor | + QDeclarativePropertyPrivate::DontRemoveBinding); + + return (stop && old_to == animation.to); // do not stop if we got restarted +} + +void QDeclarativeSpringAnimationPrivate::updateMode() +{ + if (spring == 0. && maxVelocity == 0.) + mode = Track; + else if (spring > 0.) + mode = Spring; + else { + mode = Velocity; + QHash::iterator it; + for (it = activeAnimations.begin(); it != activeAnimations.end(); ++it) { + SpringAnimation &animation = *it; + animation.start = lastTime; + qreal dist = qAbs(animation.currentValue - animation.to); + if (haveModulus && dist > modulus / 2) + dist = modulus - fmod(dist, modulus); + animation.duration = dist / velocityms; + } + } +} + +/*! + \qmlclass SpringAnimation QDeclarativeSpringAnimation + \ingroup qml-animation-transition + \inherits NumberAnimation + \since 4.7 + + \brief The SpringAnimation element allows a property to track a value in a spring-like motion. + + SpringAnimation mimics the oscillatory behavior of a spring, with the appropriate \l spring constant to + control the acceleration and the \l damping to control how quickly the effect dies away. + + You can also limit the maximum \l velocity of the animation. + + The following \l Rectangle moves to the position of the mouse using a + SpringAnimation when the mouse is clicked. The use of the \l Behavior + on the \c x and \c y values indicates that whenever these values are + changed, a SpringAnimation should be applied. + + \snippet doc/src/snippets/declarative/springanimation.qml 0 + + Like any other animation element, a SpringAnimation can be applied in a + number of ways, including transitions, behaviors and property value + sources. The \l {QML Animation and Transitions} documentation shows a + variety of methods for creating animations. + + \sa SmoothedAnimation, {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example}, {declarative/toys/clocks}{Clocks example} +*/ + +QDeclarativeSpringAnimation::QDeclarativeSpringAnimation(QObject *parent) +: QDeclarativeNumberAnimation(*(new QDeclarativeSpringAnimationPrivate),parent) +{ + Q_D(QDeclarativeSpringAnimation); + d->clock = new QDeclarativeSpringAnimationPrivate::Clock(d, this); +} + +QDeclarativeSpringAnimation::~QDeclarativeSpringAnimation() +{ +} + +/*! + \qmlproperty real SpringAnimation::velocity + + This property holds the maximum velocity allowed when tracking the source. + + The default value is 0 (no maximum velocity). +*/ + +qreal QDeclarativeSpringAnimation::velocity() const +{ + Q_D(const QDeclarativeSpringAnimation); + return d->maxVelocity; +} + +void QDeclarativeSpringAnimation::setVelocity(qreal velocity) +{ + Q_D(QDeclarativeSpringAnimation); + d->maxVelocity = velocity; + d->velocityms = velocity / 1000.0; + d->updateMode(); +} + +/*! + \qmlproperty real SpringAnimation::spring + + This property describes how strongly the target is pulled towards the + source. The default value is 0 (that is, the spring-like motion is disabled). + + The useful value range is 0 - 5.0. + + When this property is set and the \l velocity value is greater than 0, + the \l velocity limits the maximum speed. +*/ +qreal QDeclarativeSpringAnimation::spring() const +{ + Q_D(const QDeclarativeSpringAnimation); + return d->spring; +} + +void QDeclarativeSpringAnimation::setSpring(qreal spring) +{ + Q_D(QDeclarativeSpringAnimation); + d->spring = spring; + d->updateMode(); +} + +/*! + \qmlproperty real SpringAnimation::damping + This property holds the spring damping value. + + This value describes how quickly the spring-like motion comes to rest. + The default value is 0. + + The useful value range is 0 - 1.0. The lower the value, the faster it + comes to rest. +*/ +qreal QDeclarativeSpringAnimation::damping() const +{ + Q_D(const QDeclarativeSpringAnimation); + return d->damping; +} + +void QDeclarativeSpringAnimation::setDamping(qreal damping) +{ + Q_D(QDeclarativeSpringAnimation); + if (damping > 1.) + damping = 1.; + + d->damping = damping; +} + + +/*! + \qmlproperty real SpringAnimation::epsilon + This property holds the spring epsilon. + + The epsilon is the rate and amount of change in the value which is close enough + to 0 to be considered equal to zero. This will depend on the usage of the value. + For pixel positions, 0.25 would suffice. For scale, 0.005 will suffice. + + The default is 0.01. Tuning this value can provide small performance improvements. +*/ +qreal QDeclarativeSpringAnimation::epsilon() const +{ + Q_D(const QDeclarativeSpringAnimation); + return d->epsilon; +} + +void QDeclarativeSpringAnimation::setEpsilon(qreal epsilon) +{ + Q_D(QDeclarativeSpringAnimation); + d->epsilon = epsilon; +} + +/*! + \qmlproperty real SpringAnimation::modulus + This property holds the modulus value. The default value is 0. + + Setting a \a modulus forces the target value to "wrap around" at the modulus. + For example, setting the modulus to 360 will cause a value of 370 to wrap around to 10. +*/ +qreal QDeclarativeSpringAnimation::modulus() const +{ + Q_D(const QDeclarativeSpringAnimation); + return d->modulus; +} + +void QDeclarativeSpringAnimation::setModulus(qreal modulus) +{ + Q_D(QDeclarativeSpringAnimation); + if (d->modulus != modulus) { + d->haveModulus = modulus != 0.0; + d->modulus = modulus; + d->updateMode(); + emit modulusChanged(); + } +} + +/*! + \qmlproperty real SpringAnimation::mass + This property holds the "mass" of the property being moved. + + The value is 1.0 by default. + + A greater mass causes slower movement and a greater spring-like + motion when an item comes to rest. +*/ +qreal QDeclarativeSpringAnimation::mass() const +{ + Q_D(const QDeclarativeSpringAnimation); + return d->mass; +} + +void QDeclarativeSpringAnimation::setMass(qreal mass) +{ + Q_D(QDeclarativeSpringAnimation); + if (d->mass != mass && mass > 0.0) { + d->useMass = mass != 1.0; + d->mass = mass; + emit massChanged(); + } +} + +void QDeclarativeSpringAnimation::transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) +{ + Q_D(QDeclarativeSpringAnimation); + Q_UNUSED(direction); + + if (d->clock->state() != QAbstractAnimation::Running) { + d->lastTime = 0; + } + + QDeclarativeNumberAnimation::transition(actions, modified, direction); + + if (!d->actions) + return; + + if (!d->actions->isEmpty()) { + for (int i = 0; i < d->actions->size(); ++i) { + const QDeclarativeProperty &property = d->actions->at(i).property; + QDeclarativeSpringAnimationPrivate::SpringAnimation &animation + = d->activeAnimations[property]; + animation.to = d->actions->at(i).toValue.toReal(); + animation.start = d->lastTime; + if (d->fromIsDefined) + animation.currentValue = d->actions->at(i).fromValue.toReal(); + else + animation.currentValue = property.read().toReal(); + if (d->mode == QDeclarativeSpringAnimationPrivate::Velocity) { + qreal dist = qAbs(animation.currentValue - animation.to); + if (d->haveModulus && dist > d->modulus / 2) + dist = d->modulus - fmod(dist, d->modulus); + animation.duration = dist / d->velocityms; + } + } + } +} + + +QAbstractAnimation *QDeclarativeSpringAnimation::qtAnimation() +{ + Q_D(QDeclarativeSpringAnimation); + return d->clock; +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativespringanimation_p.h b/src/declarative/util/qdeclarativespringanimation_p.h new file mode 100644 index 00000000..0ae7dd4b --- /dev/null +++ b/src/declarative/util/qdeclarativespringanimation_p.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESPRINGANIMATION_H +#define QDECLARATIVESPRINGANIMATION_H + +#include +#include "private/qdeclarativeanimation_p.h" + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeSpringAnimationPrivate; +class Q_AUTOTEST_EXPORT QDeclarativeSpringAnimation : public QDeclarativeNumberAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeSpringAnimation) + Q_INTERFACES(QDeclarativePropertyValueSource) + + Q_PROPERTY(qreal velocity READ velocity WRITE setVelocity) + Q_PROPERTY(qreal spring READ spring WRITE setSpring) + Q_PROPERTY(qreal damping READ damping WRITE setDamping) + Q_PROPERTY(qreal epsilon READ epsilon WRITE setEpsilon) + Q_PROPERTY(qreal modulus READ modulus WRITE setModulus NOTIFY modulusChanged) + Q_PROPERTY(qreal mass READ mass WRITE setMass NOTIFY massChanged) + +public: + QDeclarativeSpringAnimation(QObject *parent=0); + ~QDeclarativeSpringAnimation(); + + qreal velocity() const; + void setVelocity(qreal velocity); + + qreal spring() const; + void setSpring(qreal spring); + + qreal damping() const; + void setDamping(qreal damping); + + qreal epsilon() const; + void setEpsilon(qreal epsilon); + + qreal mass() const; + void setMass(qreal modulus); + + qreal modulus() const; + void setModulus(qreal modulus); + + virtual void transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction); + +protected: + virtual QAbstractAnimation *qtAnimation(); + +Q_SIGNALS: + void modulusChanged(); + void massChanged(); + void syncChanged(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeSpringAnimation) + +QT_END_HEADER + +#endif // QDECLARATIVESPRINGANIMATION_H diff --git a/src/declarative/util/qdeclarativestate.cpp b/src/declarative/util/qdeclarativestate.cpp new file mode 100644 index 00000000..23871088 --- /dev/null +++ b/src/declarative/util/qdeclarativestate.cpp @@ -0,0 +1,742 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativestate_p_p.h" +#include "private/qdeclarativestate_p.h" + +#include "private/qdeclarativetransition_p.h" +#include "private/qdeclarativestategroup_p.h" +#include "private/qdeclarativestateoperations_p.h" +#include "private/qdeclarativeanimation_p.h" +#include "private/qdeclarativeanimation_p_p.h" + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +DEFINE_BOOL_CONFIG_OPTION(stateChangeDebug, STATECHANGE_DEBUG); + +QDeclarativeAction::QDeclarativeAction() +: restore(true), actionDone(false), reverseEvent(false), deletableToBinding(false), fromBinding(0), event(0), + specifiedObject(0) +{ +} + +QDeclarativeAction::QDeclarativeAction(QObject *target, const QString &propertyName, + const QVariant &value) +: restore(true), actionDone(false), reverseEvent(false), deletableToBinding(false), + property(target, propertyName, qmlEngine(target)), toValue(value), + fromBinding(0), event(0), + specifiedObject(target), specifiedProperty(propertyName) +{ + if (property.isValid()) + fromValue = property.read(); +} + +QDeclarativeAction::QDeclarativeAction(QObject *target, const QString &propertyName, + QDeclarativeContext *context, const QVariant &value) +: restore(true), actionDone(false), reverseEvent(false), deletableToBinding(false), + property(target, propertyName, context), toValue(value), + fromBinding(0), event(0), + specifiedObject(target), specifiedProperty(propertyName) +{ + if (property.isValid()) + fromValue = property.read(); +} + + +QDeclarativeActionEvent::~QDeclarativeActionEvent() +{ +} + +QString QDeclarativeActionEvent::typeName() const +{ + return QString(); +} + +void QDeclarativeActionEvent::execute(Reason) +{ +} + +bool QDeclarativeActionEvent::isReversable() +{ + return false; +} + +void QDeclarativeActionEvent::reverse(Reason) +{ +} + +bool QDeclarativeActionEvent::changesBindings() +{ + return false; +} + +void QDeclarativeActionEvent::clearBindings() +{ +} + +bool QDeclarativeActionEvent::override(QDeclarativeActionEvent *other) +{ + Q_UNUSED(other); + return false; +} + +QDeclarativeStateOperation::QDeclarativeStateOperation(QObjectPrivate &dd, QObject *parent) + : QObject(dd, parent) +{ +} + +/*! + \qmlclass State QDeclarativeState + \ingroup qml-state-elements + \since 4.7 + \brief The State element defines configurations of objects and properties. + + A \e state is a set of batched changes from the default configuration. + + All items have a default state that defines the default configuration of objects + and property values. New states can be defined by adding State items to the \l {Item::states}{states} property to + allow items to switch between different configurations. These configurations + can, for example, be used to apply different sets of property values or execute + different scripts. + + The following example displays a single \l Rectangle. In the default state, the rectangle + is colored black. In the "clicked" state, a PropertyChanges element changes the + rectangle's color to red. Clicking within the MouseArea toggles the rectangle's state + between the default state and the "clicked" state, thus toggling the color of the + rectangle between black and red. + + \snippet doc/src/snippets/declarative/state.qml 0 + + Notice the default state is referred to using an empty string (""). + + States are commonly used together with \l{QML Animation and Transitions}{Transitions} to provide + animations when state changes occur. + + \note Setting the state of an object from within another state of the same object is + not allowed. + + \sa {declarative/animation/states}{states example}, {qmlstates}{States}, + {QML Animation and Transitions}{Transitions}, QtDeclarative +*/ +QDeclarativeState::QDeclarativeState(QObject *parent) +: QObject(*(new QDeclarativeStatePrivate), parent) +{ + Q_D(QDeclarativeState); + d->transitionManager.setState(this); +} + +QDeclarativeState::~QDeclarativeState() +{ + Q_D(QDeclarativeState); + if (d->group) + d->group->removeState(this); + + /* + destroying an active state does not return us to the + base state, so we need to clean up our revert list to + prevent leaks. In the future we may want to redconsider + this overall architecture. + */ + for (int i = 0; i < d->revertList.count(); ++i) { + if (d->revertList.at(i).binding()) { + d->revertList.at(i).binding()->destroy(); + } + } +} + +/*! + \qmlproperty string State::name + This property holds the name of the state. + + Each state should have a unique name within its item. +*/ +QString QDeclarativeState::name() const +{ + Q_D(const QDeclarativeState); + return d->name; +} + +void QDeclarativeState::setName(const QString &n) +{ + Q_D(QDeclarativeState); + d->name = n; + d->named = true; +} + +bool QDeclarativeState::isNamed() const +{ + Q_D(const QDeclarativeState); + return d->named; +} + +bool QDeclarativeState::isWhenKnown() const +{ + Q_D(const QDeclarativeState); + return d->when != 0; +} + +/*! + \qmlproperty bool State::when + This property holds when the state should be applied. + + This should be set to an expression that evaluates to \c true when you want the state to + be applied. For example, the following \l Rectangle changes in and out of the "hidden" + state when the \l MouseArea is pressed: + + \snippet doc/src/snippets/declarative/state-when.qml 0 + + If multiple states in a group have \c when clauses that evaluate to \c true + at the same time, the first matching state will be applied. For example, in + the following snippet \c state1 will always be selected rather than + \c state2 when sharedCondition becomes \c true. + \qml + Item { + states: [ + State { name: "state1"; when: sharedCondition }, + State { name: "state2"; when: sharedCondition } + ] + // ... + } + \endqml +*/ +QDeclarativeBinding *QDeclarativeState::when() const +{ + Q_D(const QDeclarativeState); + return d->when; +} + +void QDeclarativeState::setWhen(QDeclarativeBinding *when) +{ + Q_D(QDeclarativeState); + d->when = when; + if (d->group) + d->group->updateAutoState(); +} + +/*! + \qmlproperty string State::extend + This property holds the state that this state extends. + + When a state extends another state, it inherits all the changes of that state. + + The state being extended is treated as the base state in regards to + the changes specified by the extending state. +*/ +QString QDeclarativeState::extends() const +{ + Q_D(const QDeclarativeState); + return d->extends; +} + +void QDeclarativeState::setExtends(const QString &extends) +{ + Q_D(QDeclarativeState); + d->extends = extends; +} + +/*! + \qmlproperty list State::changes + This property holds the changes to apply for this state + \default + + By default these changes are applied against the default state. If the state + extends another state, then the changes are applied against the state being + extended. +*/ +QDeclarativeListProperty QDeclarativeState::changes() +{ + Q_D(QDeclarativeState); + return QDeclarativeListProperty(this, &d->operations, QDeclarativeStatePrivate::operations_append, + QDeclarativeStatePrivate::operations_count, QDeclarativeStatePrivate::operations_at, + QDeclarativeStatePrivate::operations_clear); +} + +int QDeclarativeState::operationCount() const +{ + Q_D(const QDeclarativeState); + return d->operations.count(); +} + +QDeclarativeStateOperation *QDeclarativeState::operationAt(int index) const +{ + Q_D(const QDeclarativeState); + return d->operations.at(index); +} + +QDeclarativeState &QDeclarativeState::operator<<(QDeclarativeStateOperation *op) +{ + Q_D(QDeclarativeState); + d->operations.append(QDeclarativeStatePrivate::OperationGuard(op, &d->operations)); + return *this; +} + +void QDeclarativeStatePrivate::complete() +{ + Q_Q(QDeclarativeState); + + for (int ii = 0; ii < reverting.count(); ++ii) { + for (int jj = 0; jj < revertList.count(); ++jj) { + if (revertList.at(jj).property() == reverting.at(ii)) { + revertList.removeAt(jj); + break; + } + } + } + reverting.clear(); + + emit q->completed(); +} + +// Generate a list of actions for this state. This includes coelescing state +// actions that this state "extends" +QDeclarativeStateOperation::ActionList +QDeclarativeStatePrivate::generateActionList(QDeclarativeStateGroup *group) const +{ + QDeclarativeStateOperation::ActionList applyList; + if (inState) + return applyList; + + // Prevent "extends" recursion + inState = true; + + if (!extends.isEmpty()) { + QList states = group->states(); + for (int ii = 0; ii < states.count(); ++ii) + if (states.at(ii)->name() == extends) { + qmlExecuteDeferred(states.at(ii)); + applyList = static_cast(states.at(ii)->d_func())->generateActionList(group); + } + } + + foreach(QDeclarativeStateOperation *op, operations) + applyList << op->actions(); + + inState = false; + return applyList; +} + +QDeclarativeStateGroup *QDeclarativeState::stateGroup() const +{ + Q_D(const QDeclarativeState); + return d->group; +} + +void QDeclarativeState::setStateGroup(QDeclarativeStateGroup *group) +{ + Q_D(QDeclarativeState); + d->group = group; +} + +void QDeclarativeState::cancel() +{ + Q_D(QDeclarativeState); + d->transitionManager.cancel(); +} + +void QDeclarativeAction::deleteFromBinding() +{ + if (fromBinding) { + QDeclarativePropertyPrivate::setBinding(property, 0); + fromBinding->destroy(); + fromBinding = 0; + } +} + +bool QDeclarativeState::containsPropertyInRevertList(QObject *target, const QString &name) const +{ + Q_D(const QDeclarativeState); + + if (isStateActive()) { + QListIterator revertListIterator(d->revertList); + + while (revertListIterator.hasNext()) { + const QDeclarativeSimpleAction &simpleAction = revertListIterator.next(); + if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name) + return true; + } + } + + return false; +} + +bool QDeclarativeState::changeValueInRevertList(QObject *target, const QString &name, const QVariant &revertValue) +{ + Q_D(QDeclarativeState); + + if (isStateActive()) { + QMutableListIterator revertListIterator(d->revertList); + + while (revertListIterator.hasNext()) { + QDeclarativeSimpleAction &simpleAction = revertListIterator.next(); + if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name) { + simpleAction.setValue(revertValue); + return true; + } + } + } + + return false; +} + +bool QDeclarativeState::changeBindingInRevertList(QObject *target, const QString &name, QDeclarativeAbstractBinding *binding) +{ + Q_D(QDeclarativeState); + + if (isStateActive()) { + QMutableListIterator revertListIterator(d->revertList); + + while (revertListIterator.hasNext()) { + QDeclarativeSimpleAction &simpleAction = revertListIterator.next(); + if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name) { + if (simpleAction.binding()) + simpleAction.binding()->destroy(); + + simpleAction.setBinding(binding); + return true; + } + } + } + + return false; +} + +bool QDeclarativeState::removeEntryFromRevertList(QObject *target, const QString &name) +{ + Q_D(QDeclarativeState); + + if (isStateActive()) { + QMutableListIterator revertListIterator(d->revertList); + + while (revertListIterator.hasNext()) { + QDeclarativeSimpleAction &simpleAction = revertListIterator.next(); + if (simpleAction.property().object() == target && simpleAction.property().name() == name) { + QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(simpleAction.property()); + if (oldBinding) { + QDeclarativePropertyPrivate::setBinding(simpleAction.property(), 0); + oldBinding->destroy(); + } + + simpleAction.property().write(simpleAction.value()); + if (simpleAction.binding()) + QDeclarativePropertyPrivate::setBinding(simpleAction.property(), simpleAction.binding()); + + revertListIterator.remove(); + return true; + } + } + } + + return false; +} + +void QDeclarativeState::addEntryToRevertList(const QDeclarativeAction &action) +{ + Q_D(QDeclarativeState); + + QDeclarativeSimpleAction simpleAction(action); + + d->revertList.append(simpleAction); +} + +void QDeclarativeState::removeAllEntriesFromRevertList(QObject *target) +{ + Q_D(QDeclarativeState); + + if (isStateActive()) { + QMutableListIterator revertListIterator(d->revertList); + + while (revertListIterator.hasNext()) { + QDeclarativeSimpleAction &simpleAction = revertListIterator.next(); + if (simpleAction.property().object() == target) { + QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(simpleAction.property()); + if (oldBinding) { + QDeclarativePropertyPrivate::setBinding(simpleAction.property(), 0); + oldBinding->destroy(); + } + + simpleAction.property().write(simpleAction.value()); + if (simpleAction.binding()) + QDeclarativePropertyPrivate::setBinding(simpleAction.property(), simpleAction.binding()); + + revertListIterator.remove(); + } + } + } +} + +void QDeclarativeState::addEntriesToRevertList(const QList &actionList) +{ + Q_D(QDeclarativeState); + if (isStateActive()) { + QList simpleActionList; + + QListIterator actionListIterator(actionList); + while(actionListIterator.hasNext()) { + const QDeclarativeAction &action = actionListIterator.next(); + QDeclarativeSimpleAction simpleAction(action); + action.property.write(action.toValue); + if (!action.toBinding.isNull()) { + QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(simpleAction.property()); + if (oldBinding) + QDeclarativePropertyPrivate::setBinding(simpleAction.property(), 0); + QDeclarativePropertyPrivate::setBinding(simpleAction.property(), action.toBinding.data(), QDeclarativePropertyPrivate::DontRemoveBinding); + } + + simpleActionList.append(simpleAction); + } + + d->revertList.append(simpleActionList); + } +} + +QVariant QDeclarativeState::valueInRevertList(QObject *target, const QString &name) const +{ + Q_D(const QDeclarativeState); + + if (isStateActive()) { + QListIterator revertListIterator(d->revertList); + + while (revertListIterator.hasNext()) { + const QDeclarativeSimpleAction &simpleAction = revertListIterator.next(); + if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name) + return simpleAction.value(); + } + } + + return QVariant(); +} + +QDeclarativeAbstractBinding *QDeclarativeState::bindingInRevertList(QObject *target, const QString &name) const +{ + Q_D(const QDeclarativeState); + + if (isStateActive()) { + QListIterator revertListIterator(d->revertList); + + while (revertListIterator.hasNext()) { + const QDeclarativeSimpleAction &simpleAction = revertListIterator.next(); + if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name) + return simpleAction.binding(); + } + } + + return 0; +} + +bool QDeclarativeState::isStateActive() const +{ + return stateGroup() && stateGroup()->state() == name(); +} + +void QDeclarativeState::apply(QDeclarativeStateGroup *group, QDeclarativeTransition *trans, QDeclarativeState *revert) +{ + Q_D(QDeclarativeState); + + qmlExecuteDeferred(this); + + cancel(); + if (revert) + revert->cancel(); + d->revertList.clear(); + d->reverting.clear(); + + if (revert) { + QDeclarativeStatePrivate *revertPrivate = + static_cast(revert->d_func()); + d->revertList = revertPrivate->revertList; + revertPrivate->revertList.clear(); + } + + // List of actions caused by this state + QDeclarativeStateOperation::ActionList applyList = d->generateActionList(group); + + // List of actions that need to be reverted to roll back (just) this state + QDeclarativeStatePrivate::SimpleActionList additionalReverts; + // First add the reverse of all the applyList actions + for (int ii = 0; ii < applyList.count(); ++ii) { + QDeclarativeAction &action = applyList[ii]; + + if (action.event) { + if (!action.event->isReversable()) + continue; + bool found = false; + for (int jj = 0; jj < d->revertList.count(); ++jj) { + QDeclarativeActionEvent *event = d->revertList.at(jj).event(); + if (event && event->typeName() == action.event->typeName()) { + if (action.event->override(event)) { + found = true; + + if (action.event != d->revertList.at(jj).event() && action.event->needsCopy()) { + action.event->copyOriginals(d->revertList.at(jj).event()); + + QDeclarativeSimpleAction r(action); + additionalReverts << r; + d->revertList.removeAt(jj); + --jj; + } else if (action.event->isRewindable()) //###why needed? + action.event->saveCurrentValues(); + + break; + } + } + } + if (!found) { + action.event->saveOriginals(); + // Only need to revert the applyList action if the previous + // state doesn't have a higher priority revert already + QDeclarativeSimpleAction r(action); + additionalReverts << r; + } + } else { + bool found = false; + action.fromBinding = QDeclarativePropertyPrivate::binding(action.property); + + for (int jj = 0; jj < d->revertList.count(); ++jj) { + if (d->revertList.at(jj).property() == action.property) { + found = true; + if (d->revertList.at(jj).binding() != action.fromBinding) { + action.deleteFromBinding(); + } + break; + } + } + + if (!found) { + if (!action.restore) { + action.deleteFromBinding();; + } else { + // Only need to revert the applyList action if the previous + // state doesn't have a higher priority revert already + QDeclarativeSimpleAction r(action); + additionalReverts << r; + } + } + } + } + + // Any reverts from a previous state that aren't carried forth + // into this state need to be translated into apply actions + for (int ii = 0; ii < d->revertList.count(); ++ii) { + bool found = false; + if (d->revertList.at(ii).event()) { + QDeclarativeActionEvent *event = d->revertList.at(ii).event(); + if (!event->isReversable()) + continue; + for (int jj = 0; !found && jj < applyList.count(); ++jj) { + const QDeclarativeAction &action = applyList.at(jj); + if (action.event && action.event->typeName() == event->typeName()) { + if (action.event->override(event)) + found = true; + } + } + } else { + for (int jj = 0; !found && jj < applyList.count(); ++jj) { + const QDeclarativeAction &action = applyList.at(jj); + if (action.property == d->revertList.at(ii).property()) + found = true; + } + } + if (!found) { + QVariant cur = d->revertList.at(ii).property().read(); + QDeclarativeAbstractBinding *delBinding = + QDeclarativePropertyPrivate::setBinding(d->revertList.at(ii).property(), 0); + if (delBinding) + delBinding->destroy(); + + QDeclarativeAction a; + a.property = d->revertList.at(ii).property(); + a.fromValue = cur; + a.toValue = d->revertList.at(ii).value(); + a.toBinding = QDeclarativeAbstractBinding::getPointer(d->revertList.at(ii).binding()); + a.specifiedObject = d->revertList.at(ii).specifiedObject(); + a.specifiedProperty = d->revertList.at(ii).specifiedProperty(); + a.event = d->revertList.at(ii).event(); + a.reverseEvent = d->revertList.at(ii).reverseEvent(); + if (a.event && a.event->isRewindable()) + a.event->saveCurrentValues(); + applyList << a; + // Store these special reverts in the reverting list + d->reverting << d->revertList.at(ii).property(); + } + } + // All the local reverts now become part of the ongoing revertList + d->revertList << additionalReverts; + +#ifndef QT_NO_DEBUG_STREAM + // Output for debugging + if (stateChangeDebug()) { + foreach(const QDeclarativeAction &action, applyList) { + if (action.event) + qWarning() << " QDeclarativeAction event:" << action.event->typeName(); + else + qWarning() << " QDeclarativeAction:" << action.property.object() + << action.property.name() << "From:" << action.fromValue + << "To:" << action.toValue; + } + } +#endif + + d->transitionManager.transition(applyList, trans); +} + +QDeclarativeStateOperation::ActionList QDeclarativeStateOperation::actions() +{ + return ActionList(); +} + +QDeclarativeState *QDeclarativeStateOperation::state() const +{ + Q_D(const QDeclarativeStateOperation); + return d->m_state; +} + +void QDeclarativeStateOperation::setState(QDeclarativeState *state) +{ + Q_D(QDeclarativeStateOperation); + d->m_state = state; +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativestate_p.h b/src/declarative/util/qdeclarativestate_p.h new file mode 100644 index 00000000..849361f5 --- /dev/null +++ b/src/declarative/util/qdeclarativestate_p.h @@ -0,0 +1,210 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESTATE_H +#define QDECLARATIVESTATE_H + +#include +#include +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeActionEvent; +class QDeclarativeAbstractBinding; +class QDeclarativeBinding; +class QDeclarativeExpression; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeAction +{ +public: + QDeclarativeAction(); + QDeclarativeAction(QObject *, const QString &, const QVariant &); + QDeclarativeAction(QObject *, const QString &, + QDeclarativeContext *, const QVariant &); + + bool restore:1; + bool actionDone:1; + bool reverseEvent:1; + bool deletableToBinding:1; + + QDeclarativeProperty property; + QVariant fromValue; + QVariant toValue; + + QDeclarativeAbstractBinding *fromBinding; + QWeakPointer toBinding; + QDeclarativeActionEvent *event; + + //strictly for matching + QObject *specifiedObject; + QString specifiedProperty; + + void deleteFromBinding(); +}; + +class Q_AUTOTEST_EXPORT QDeclarativeActionEvent +{ +public: + virtual ~QDeclarativeActionEvent(); + virtual QString typeName() const; + + enum Reason { ActualChange, FastForward }; + + virtual void execute(Reason reason = ActualChange); + virtual bool isReversable(); + virtual void reverse(Reason reason = ActualChange); + virtual void saveOriginals() {} + virtual bool needsCopy() { return false; } + virtual void copyOriginals(QDeclarativeActionEvent *) {} + + virtual bool isRewindable() { return isReversable(); } + virtual void rewind() {} + virtual void saveCurrentValues() {} + virtual void saveTargetValues() {} + + virtual bool changesBindings(); + virtual void clearBindings(); + virtual bool override(QDeclarativeActionEvent*other); +}; + +//### rename to QDeclarativeStateChange? +class QDeclarativeStateGroup; +class QDeclarativeState; +class QDeclarativeStateOperationPrivate; +class Q_DECLARATIVE_EXPORT QDeclarativeStateOperation : public QObject +{ + Q_OBJECT +public: + QDeclarativeStateOperation(QObject *parent = 0) + : QObject(parent) {} + typedef QList ActionList; + + virtual ActionList actions(); + + QDeclarativeState *state() const; + void setState(QDeclarativeState *state); + +protected: + QDeclarativeStateOperation(QObjectPrivate &dd, QObject *parent = 0); + +private: + Q_DECLARE_PRIVATE(QDeclarativeStateOperation) + Q_DISABLE_COPY(QDeclarativeStateOperation) +}; + +typedef QDeclarativeStateOperation::ActionList QDeclarativeStateActions; + +class QDeclarativeTransition; +class QDeclarativeStatePrivate; +class Q_DECLARATIVE_EXPORT QDeclarativeState : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QString name READ name WRITE setName) + Q_PROPERTY(QDeclarativeBinding *when READ when WRITE setWhen) + Q_PROPERTY(QString extend READ extends WRITE setExtends) + Q_PROPERTY(QDeclarativeListProperty changes READ changes) + Q_CLASSINFO("DefaultProperty", "changes") + Q_CLASSINFO("DeferredPropertyNames", "changes") + +public: + QDeclarativeState(QObject *parent=0); + virtual ~QDeclarativeState(); + + QString name() const; + void setName(const QString &); + bool isNamed() const; + + /*'when' is a QDeclarativeBinding to limit state changes oscillation + due to the unpredictable order of evaluation of bound expressions*/ + bool isWhenKnown() const; + QDeclarativeBinding *when() const; + void setWhen(QDeclarativeBinding *); + + QString extends() const; + void setExtends(const QString &); + + QDeclarativeListProperty changes(); + int operationCount() const; + QDeclarativeStateOperation *operationAt(int) const; + + QDeclarativeState &operator<<(QDeclarativeStateOperation *); + + void apply(QDeclarativeStateGroup *, QDeclarativeTransition *, QDeclarativeState *revert); + void cancel(); + + QDeclarativeStateGroup *stateGroup() const; + void setStateGroup(QDeclarativeStateGroup *); + + bool containsPropertyInRevertList(QObject *target, const QString &name) const; + bool changeValueInRevertList(QObject *target, const QString &name, const QVariant &revertValue); + bool changeBindingInRevertList(QObject *target, const QString &name, QDeclarativeAbstractBinding *binding); + bool removeEntryFromRevertList(QObject *target, const QString &name); + void addEntryToRevertList(const QDeclarativeAction &action); + void removeAllEntriesFromRevertList(QObject *target); + void addEntriesToRevertList(const QList &actions); + QVariant valueInRevertList(QObject *target, const QString &name) const; + QDeclarativeAbstractBinding *bindingInRevertList(QObject *target, const QString &name) const; + + bool isStateActive() const; + +Q_SIGNALS: + void completed(); + +private: + Q_DECLARE_PRIVATE(QDeclarativeState) + Q_DISABLE_COPY(QDeclarativeState) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeStateOperation) +QML_DECLARE_TYPE(QDeclarativeState) + +QT_END_HEADER + +#endif // QDECLARATIVESTATE_H diff --git a/src/declarative/util/qdeclarativestate_p_p.h b/src/declarative/util/qdeclarativestate_p_p.h new file mode 100644 index 00000000..192d02db --- /dev/null +++ b/src/declarative/util/qdeclarativestate_p_p.h @@ -0,0 +1,255 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESTATE_P_H +#define QDECLARATIVESTATE_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 "private/qdeclarativestate_p.h" + +#include "private/qdeclarativeanimation_p_p.h" +#include "private/qdeclarativetransitionmanager_p_p.h" + +#include +#include + +#include + +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeSimpleAction +{ +public: + enum State { StartState, EndState }; + QDeclarativeSimpleAction(const QDeclarativeAction &a, State state = StartState) + { + m_property = a.property; + m_specifiedObject = a.specifiedObject; + m_specifiedProperty = a.specifiedProperty; + m_event = a.event; + if (state == StartState) { + m_value = a.fromValue; + if (QDeclarativePropertyPrivate::binding(m_property)) { + m_binding = QDeclarativeAbstractBinding::getPointer(QDeclarativePropertyPrivate::binding(m_property)); + } + m_reverseEvent = true; + } else { + m_value = a.toValue; + m_binding = a.toBinding; + m_reverseEvent = false; + } + } + + ~QDeclarativeSimpleAction() + { + } + + QDeclarativeSimpleAction(const QDeclarativeSimpleAction &other) + : m_property(other.m_property), + m_value(other.m_value), + m_binding(QDeclarativeAbstractBinding::getPointer(other.binding())), + m_specifiedObject(other.m_specifiedObject), + m_specifiedProperty(other.m_specifiedProperty), + m_event(other.m_event), + m_reverseEvent(other.m_reverseEvent) + { + } + + QDeclarativeSimpleAction &operator =(const QDeclarativeSimpleAction &other) + { + m_property = other.m_property; + m_value = other.m_value; + m_binding = QDeclarativeAbstractBinding::getPointer(other.binding()); + m_specifiedObject = other.m_specifiedObject; + m_specifiedProperty = other.m_specifiedProperty; + m_event = other.m_event; + m_reverseEvent = other.m_reverseEvent; + + return *this; + } + + void setProperty(const QDeclarativeProperty &property) + { + m_property = property; + } + + const QDeclarativeProperty &property() const + { + return m_property; + } + + void setValue(const QVariant &value) + { + m_value = value; + } + + const QVariant &value() const + { + return m_value; + } + + void setBinding(QDeclarativeAbstractBinding *binding) + { + m_binding = QDeclarativeAbstractBinding::getPointer(binding); + } + + QDeclarativeAbstractBinding *binding() const + { + return m_binding.data(); + } + + QObject *specifiedObject() const + { + return m_specifiedObject; + } + + const QString &specifiedProperty() const + { + return m_specifiedProperty; + } + + QDeclarativeActionEvent *event() const + { + return m_event; + } + + bool reverseEvent() const + { + return m_reverseEvent; + } + +private: + QDeclarativeProperty m_property; + QVariant m_value; + QDeclarativeAbstractBinding::Pointer m_binding; + QObject *m_specifiedObject; + QString m_specifiedProperty; + QDeclarativeActionEvent *m_event; + bool m_reverseEvent; +}; + +class QDeclarativeStateOperationPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeStateOperation) + +public: + + QDeclarativeStateOperationPrivate() + : m_state(0) {} + + QDeclarativeState *m_state; +}; + +class QDeclarativeStatePrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeState) + +public: + QDeclarativeStatePrivate() + : when(0), named(false), inState(false), group(0) {} + + typedef QList SimpleActionList; + + QString name; + QDeclarativeBinding *when; + bool named; + + struct OperationGuard : public QDeclarativeGuard + { + OperationGuard(QObject *obj, QList *l) : list(l) { + setObject(static_cast(obj)); + } + QList *list; + void objectDestroyed(QDeclarativeStateOperation *) { + // we assume priv will always be destroyed after objectDestroyed calls + list->removeOne(*this); + } + }; + QList operations; + + static void operations_append(QDeclarativeListProperty *prop, QDeclarativeStateOperation *op) { + QList *list = static_cast *>(prop->data); + op->setState(qobject_cast(prop->object)); + list->append(OperationGuard(op, list)); + } + static void operations_clear(QDeclarativeListProperty *prop) { + QList *list = static_cast *>(prop->data); + QMutableListIterator listIterator(*list); + while(listIterator.hasNext()) + listIterator.next()->setState(0); + list->clear(); + } + static int operations_count(QDeclarativeListProperty *prop) { + QList *list = static_cast *>(prop->data); + return list->count(); + } + static QDeclarativeStateOperation *operations_at(QDeclarativeListProperty *prop, int index) { + QList *list = static_cast *>(prop->data); + return list->at(index); + } + + QDeclarativeTransitionManager transitionManager; + + SimpleActionList revertList; + QList reverting; + QString extends; + mutable bool inState; + QDeclarativeStateGroup *group; + + QDeclarativeStateOperation::ActionList generateActionList(QDeclarativeStateGroup *) const; + void complete(); +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVESTATE_P_H diff --git a/src/declarative/util/qdeclarativestategroup.cpp b/src/declarative/util/qdeclarativestategroup.cpp new file mode 100644 index 00000000..29be45e9 --- /dev/null +++ b/src/declarative/util/qdeclarativestategroup.cpp @@ -0,0 +1,502 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativestategroup_p.h" + +#include "private/qdeclarativetransition_p.h" +#include "private/qdeclarativestate_p_p.h" + +#include +#include + +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +DEFINE_BOOL_CONFIG_OPTION(stateChangeDebug, STATECHANGE_DEBUG); + +class QDeclarativeStateGroupPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeStateGroup) +public: + QDeclarativeStateGroupPrivate() + : nullState(0), componentComplete(true), + ignoreTrans(false), applyingState(false), unnamedCount(0) {} + + QString currentState; + QDeclarativeState *nullState; + + static void append_state(QDeclarativeListProperty *list, QDeclarativeState *state); + static int count_state(QDeclarativeListProperty *list); + static QDeclarativeState *at_state(QDeclarativeListProperty *list, int index); + static void clear_states(QDeclarativeListProperty *list); + + static void append_transition(QDeclarativeListProperty *list, QDeclarativeTransition *state); + static int count_transitions(QDeclarativeListProperty *list); + static QDeclarativeTransition *at_transition(QDeclarativeListProperty *list, int index); + static void clear_transitions(QDeclarativeListProperty *list); + + QList states; + QList transitions; + + bool componentComplete; + bool ignoreTrans; + bool applyingState; + int unnamedCount; + + QDeclarativeTransition *findTransition(const QString &from, const QString &to); + void setCurrentStateInternal(const QString &state, bool = false); + bool updateAutoState(); +}; + +/*! + \qmlclass StateGroup QDeclarativeStateGroup + \ingroup qml-state-elements + \since 4.7 + \brief The StateGroup element provides state support for non-Item elements. + + Item (and all derived elements) provides built in support for states and transitions + via its \l{Item::state}{state}, \l{Item::states}{states} and \l{Item::transitions}{transitions} properties. StateGroup provides an easy way to + use this support in other (non-Item-derived) elements. + + \qml + MyCustomObject { + StateGroup { + id: myStateGroup + states: State { + name: "state1" + // ... + } + transitions: Transition { + // ... + } + } + + onSomethingHappened: myStateGroup.state = "state1"; + } + \endqml + + \sa {qmlstate}{States} {QML Animation and Transitions}{Transitions}, {QtDeclarative} +*/ + +QDeclarativeStateGroup::QDeclarativeStateGroup(QObject *parent) + : QObject(*(new QDeclarativeStateGroupPrivate), parent) +{ +} + +QDeclarativeStateGroup::~QDeclarativeStateGroup() +{ + Q_D(const QDeclarativeStateGroup); + for (int i = 0; i < d->states.count(); ++i) + d->states.at(i)->setStateGroup(0); +} + +QList QDeclarativeStateGroup::states() const +{ + Q_D(const QDeclarativeStateGroup); + return d->states; +} + +/*! + \qmlproperty list StateGroup::states + This property holds a list of states defined by the state group. + + \qml + StateGroup { + states: [ + State { + // State definition... + }, + State { + // ... + } + // Other states... + ] + } + \endqml + + \sa {qmlstate}{States} +*/ +QDeclarativeListProperty QDeclarativeStateGroup::statesProperty() +{ + Q_D(QDeclarativeStateGroup); + return QDeclarativeListProperty(this, &d->states, &QDeclarativeStateGroupPrivate::append_state, + &QDeclarativeStateGroupPrivate::count_state, + &QDeclarativeStateGroupPrivate::at_state, + &QDeclarativeStateGroupPrivate::clear_states); +} + +void QDeclarativeStateGroupPrivate::append_state(QDeclarativeListProperty *list, QDeclarativeState *state) +{ + QDeclarativeStateGroup *_this = static_cast(list->object); + if (state) { + _this->d_func()->states.append(state); + state->setStateGroup(_this); + } + +} + +int QDeclarativeStateGroupPrivate::count_state(QDeclarativeListProperty *list) +{ + QDeclarativeStateGroup *_this = static_cast(list->object); + return _this->d_func()->states.count(); +} + +QDeclarativeState *QDeclarativeStateGroupPrivate::at_state(QDeclarativeListProperty *list, int index) +{ + QDeclarativeStateGroup *_this = static_cast(list->object); + return _this->d_func()->states.at(index); +} + +void QDeclarativeStateGroupPrivate::clear_states(QDeclarativeListProperty *list) +{ + QDeclarativeStateGroup *_this = static_cast(list->object); + _this->d_func()->setCurrentStateInternal(QString(), true); + for (int i = 0; i < _this->d_func()->states.count(); ++i) { + _this->d_func()->states.at(i)->setStateGroup(0); + } + _this->d_func()->states.clear(); +} + +/*! + \qmlproperty list StateGroup::transitions + This property holds a list of transitions defined by the state group. + + \qml + StateGroup { + transitions: [ + Transition { + // ... + }, + Transition { + // ... + } + // ... + ] + } + \endqml + + \sa {QML Animation and Transitions}{Transitions} +*/ +QDeclarativeListProperty QDeclarativeStateGroup::transitionsProperty() +{ + Q_D(QDeclarativeStateGroup); + return QDeclarativeListProperty(this, &d->transitions, &QDeclarativeStateGroupPrivate::append_transition, + &QDeclarativeStateGroupPrivate::count_transitions, + &QDeclarativeStateGroupPrivate::at_transition, + &QDeclarativeStateGroupPrivate::clear_transitions); +} + +void QDeclarativeStateGroupPrivate::append_transition(QDeclarativeListProperty *list, QDeclarativeTransition *trans) +{ + QDeclarativeStateGroup *_this = static_cast(list->object); + if (trans) + _this->d_func()->transitions.append(trans); +} + +int QDeclarativeStateGroupPrivate::count_transitions(QDeclarativeListProperty *list) +{ + QDeclarativeStateGroup *_this = static_cast(list->object); + return _this->d_func()->transitions.count(); +} + +QDeclarativeTransition *QDeclarativeStateGroupPrivate::at_transition(QDeclarativeListProperty *list, int index) +{ + QDeclarativeStateGroup *_this = static_cast(list->object); + return _this->d_func()->transitions.at(index); +} + +void QDeclarativeStateGroupPrivate::clear_transitions(QDeclarativeListProperty *list) +{ + QDeclarativeStateGroup *_this = static_cast(list->object); + _this->d_func()->transitions.clear(); +} + +/*! + \qmlproperty string StateGroup::state + + This property holds the name of the current state of the state group. + + This property is often used in scripts to change between states. For + example: + + \js + function toggle() { + if (button.state == 'On') + button.state = 'Off'; + else + button.state = 'On'; + } + \endjs + + If the state group is in its base state (i.e. no explicit state has been + set), \c state will be a blank string. Likewise, you can return a + state group to its base state by setting its current state to \c ''. + + \sa {qmlstates}{States} +*/ +QString QDeclarativeStateGroup::state() const +{ + Q_D(const QDeclarativeStateGroup); + return d->currentState; +} + +void QDeclarativeStateGroup::setState(const QString &state) +{ + Q_D(QDeclarativeStateGroup); + if (d->currentState == state) + return; + + d->setCurrentStateInternal(state); +} + +void QDeclarativeStateGroup::classBegin() +{ + Q_D(QDeclarativeStateGroup); + d->componentComplete = false; +} + +void QDeclarativeStateGroup::componentComplete() +{ + Q_D(QDeclarativeStateGroup); + d->componentComplete = true; + + for (int ii = 0; ii < d->states.count(); ++ii) { + QDeclarativeState *state = d->states.at(ii); + if (!state->isNamed()) + state->setName(QLatin1String("anonymousState") % QString::number(++d->unnamedCount)); + } + + if (d->updateAutoState()) { + return; + } else if (!d->currentState.isEmpty()) { + QString cs = d->currentState; + d->currentState.clear(); + d->setCurrentStateInternal(cs, true); + } +} + +/*! + Returns true if the state was changed, otherwise false. +*/ +bool QDeclarativeStateGroup::updateAutoState() +{ + Q_D(QDeclarativeStateGroup); + return d->updateAutoState(); +} + +bool QDeclarativeStateGroupPrivate::updateAutoState() +{ + Q_Q(QDeclarativeStateGroup); + if (!componentComplete) + return false; + + bool revert = false; + for (int ii = 0; ii < states.count(); ++ii) { + QDeclarativeState *state = states.at(ii); + if (state->isWhenKnown()) { + if (state->isNamed()) { + if (state->when() && state->when()->evaluate().toBool()) { + if (stateChangeDebug()) + qWarning() << "Setting auto state due to:" + << state->when()->expression(); + if (currentState != state->name()) { + q->setState(state->name()); + return true; + } else { + return false; + } + } else if (state->name() == currentState) { + revert = true; + } + } + } + } + if (revert) { + bool rv = !currentState.isEmpty(); + q->setState(QString()); + return rv; + } else { + return false; + } +} + +QDeclarativeTransition *QDeclarativeStateGroupPrivate::findTransition(const QString &from, const QString &to) +{ + QDeclarativeTransition *highest = 0; + int score = 0; + bool reversed = false; + bool done = false; + + for (int ii = 0; !done && ii < transitions.count(); ++ii) { + QDeclarativeTransition *t = transitions.at(ii); + for (int ii = 0; ii < 2; ++ii) + { + if (ii && (!t->reversible() || + (t->fromState() == QLatin1String("*") && + t->toState() == QLatin1String("*")))) + break; + QStringList fromState; + QStringList toState; + + fromState = t->fromState().split(QLatin1Char(',')); + toState = t->toState().split(QLatin1Char(',')); + if (ii == 1) + qSwap(fromState, toState); + int tScore = 0; + if (fromState.contains(from)) + tScore += 2; + else if (fromState.contains(QLatin1String("*"))) + tScore += 1; + else + continue; + + if (toState.contains(to)) + tScore += 2; + else if (toState.contains(QLatin1String("*"))) + tScore += 1; + else + continue; + + if (ii == 1) + reversed = true; + else + reversed = false; + + if (tScore == 4) { + highest = t; + done = true; + break; + } else if (tScore > score) { + score = tScore; + highest = t; + } + } + } + + if (highest) + highest->setReversed(reversed); + + return highest; +} + +void QDeclarativeStateGroupPrivate::setCurrentStateInternal(const QString &state, + bool ignoreTrans) +{ + Q_Q(QDeclarativeStateGroup); + if (!componentComplete) { + currentState = state; + return; + } + + if (applyingState) { + qmlInfo(q) << "Can't apply a state change as part of a state definition."; + return; + } + + applyingState = true; + + QDeclarativeTransition *transition = (ignoreTrans || ignoreTrans) ? 0 : findTransition(currentState, state); + if (stateChangeDebug()) { + qWarning() << this << "Changing state. From" << currentState << ". To" << state; + if (transition) + qWarning() << " using transition" << transition->fromState() + << transition->toState(); + } + + QDeclarativeState *oldState = 0; + if (!currentState.isEmpty()) { + for (int ii = 0; ii < states.count(); ++ii) { + if (states.at(ii)->name() == currentState) { + oldState = states.at(ii); + break; + } + } + } + + currentState = state; + emit q->stateChanged(currentState); + + QDeclarativeState *newState = 0; + for (int ii = 0; ii < states.count(); ++ii) { + if (states.at(ii)->name() == currentState) { + newState = states.at(ii); + break; + } + } + + if (oldState == 0 || newState == 0) { + if (!nullState) { nullState = new QDeclarativeState; QDeclarative_setParent_noEvent(nullState, q); } + if (!oldState) oldState = nullState; + if (!newState) newState = nullState; + } + + newState->apply(q, transition, oldState); + applyingState = false; + if (!transition) + static_cast(QObjectPrivate::get(newState))->complete(); +} + +QDeclarativeState *QDeclarativeStateGroup::findState(const QString &name) const +{ + Q_D(const QDeclarativeStateGroup); + for (int i = 0; i < d->states.count(); ++i) { + QDeclarativeState *state = d->states.at(i); + if (state->name() == name) + return state; + } + + return 0; +} + +void QDeclarativeStateGroup::removeState(QDeclarativeState *state) +{ + Q_D(QDeclarativeStateGroup); + d->states.removeOne(state); +} + +QT_END_NAMESPACE + + diff --git a/src/declarative/util/qdeclarativestategroup_p.h b/src/declarative/util/qdeclarativestategroup_p.h new file mode 100644 index 00000000..c99d6361 --- /dev/null +++ b/src/declarative/util/qdeclarativestategroup_p.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESTATEGROUP_H +#define QDECLARATIVESTATEGROUP_H + +#include "private/qdeclarativestate_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeStateGroupPrivate; +class Q_DECLARATIVE_EXPORT QDeclarativeStateGroup : public QObject, public QDeclarativeParserStatus +{ + Q_OBJECT + Q_INTERFACES(QDeclarativeParserStatus) + Q_DECLARE_PRIVATE(QDeclarativeStateGroup) + + Q_PROPERTY(QString state READ state WRITE setState NOTIFY stateChanged) + Q_PROPERTY(QDeclarativeListProperty states READ statesProperty DESIGNABLE false) + Q_PROPERTY(QDeclarativeListProperty transitions READ transitionsProperty DESIGNABLE false) + +public: + QDeclarativeStateGroup(QObject * = 0); + virtual ~QDeclarativeStateGroup(); + + QString state() const; + void setState(const QString &); + + QDeclarativeListProperty statesProperty(); + QList states() const; + + QDeclarativeListProperty transitionsProperty(); + + QDeclarativeState *findState(const QString &name) const; + + virtual void classBegin(); + virtual void componentComplete(); +Q_SIGNALS: + void stateChanged(const QString &); + +private: + friend class QDeclarativeState; + bool updateAutoState(); + void removeState(QDeclarativeState *state); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeStateGroup) + +QT_END_HEADER + +#endif // QDECLARATIVESTATEGROUP_H diff --git a/src/declarative/util/qdeclarativestateoperations.cpp b/src/declarative/util/qdeclarativestateoperations.cpp new file mode 100644 index 00000000..30c5730d --- /dev/null +++ b/src/declarative/util/qdeclarativestateoperations.cpp @@ -0,0 +1,1587 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativestateoperations_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include "private/qdeclarativecontext_p.h" +#include "private/qdeclarativeproperty_p.h" +#include "private/qdeclarativebinding_p.h" +#include "private/qdeclarativestate_p_p.h" + +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeParentChangePrivate : public QDeclarativeStateOperationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeParentChange) +public: + QDeclarativeParentChangePrivate() : target(0), parent(0), origParent(0), origStackBefore(0), + rewindParent(0), rewindStackBefore(0) {} + + QDeclarativeItem *target; + QDeclarativeGuard parent; + QDeclarativeGuard origParent; + QDeclarativeGuard origStackBefore; + QDeclarativeItem *rewindParent; + QDeclarativeItem *rewindStackBefore; + + QDeclarativeNullableValue xString; + QDeclarativeNullableValue yString; + QDeclarativeNullableValue widthString; + QDeclarativeNullableValue heightString; + QDeclarativeNullableValue scaleString; + QDeclarativeNullableValue rotationString; + + QDeclarativeNullableValue x; + QDeclarativeNullableValue y; + QDeclarativeNullableValue width; + QDeclarativeNullableValue height; + QDeclarativeNullableValue scale; + QDeclarativeNullableValue rotation; + + void doChange(QDeclarativeItem *targetParent, QDeclarativeItem *stackBefore = 0); +}; + +void QDeclarativeParentChangePrivate::doChange(QDeclarativeItem *targetParent, QDeclarativeItem *stackBefore) +{ + if (targetParent && target && target->parentItem()) { + Q_Q(QDeclarativeParentChange); + bool ok; + const QTransform &transform = target->parentItem()->itemTransform(targetParent, &ok); + if (transform.type() >= QTransform::TxShear || !ok) { + qmlInfo(q) << QDeclarativeParentChange::tr("Unable to preserve appearance under complex transform"); + ok = false; + } + + qreal scale = 1; + qreal rotation = 0; + bool isRotate = (transform.type() == QTransform::TxRotate) || (transform.m11() < 0); + if (ok && !isRotate) { + if (transform.m11() == transform.m22()) + scale = transform.m11(); + else { + qmlInfo(q) << QDeclarativeParentChange::tr("Unable to preserve appearance under non-uniform scale"); + ok = false; + } + } else if (ok && isRotate) { + if (transform.m11() == transform.m22()) + scale = qSqrt(transform.m11()*transform.m11() + transform.m12()*transform.m12()); + else { + qmlInfo(q) << QDeclarativeParentChange::tr("Unable to preserve appearance under non-uniform scale"); + ok = false; + } + + if (scale != 0) + rotation = atan2(transform.m12()/scale, transform.m11()/scale) * 180/qreal(M_PI); + else { + qmlInfo(q) << QDeclarativeParentChange::tr("Unable to preserve appearance under scale of 0"); + ok = false; + } + } + + const QPointF &point = transform.map(QPointF(target->x(),target->y())); + qreal x = point.x(); + qreal y = point.y(); + + // setParentItem will update the transformOriginPoint if needed + target->setParentItem(targetParent); + + if (ok && target->transformOrigin() != QDeclarativeItem::TopLeft) { + qreal tempxt = target->transformOriginPoint().x(); + qreal tempyt = target->transformOriginPoint().y(); + QTransform t; + t.translate(-tempxt, -tempyt); + t.rotate(rotation); + t.scale(scale, scale); + t.translate(tempxt, tempyt); + const QPointF &offset = t.map(QPointF(0,0)); + x += offset.x(); + y += offset.y(); + } + + if (ok) { + //qDebug() << x << y << rotation << scale; + target->setX(x); + target->setY(y); + target->setRotation(target->rotation() + rotation); + target->setScale(target->scale() * scale); + } + } else if (target) { + target->setParentItem(targetParent); + } + + //restore the original stack position. + //### if stackBefore has also been reparented this won't work + if (stackBefore) + target->stackBefore(stackBefore); +} + +/*! + \preliminary + \qmlclass ParentChange QDeclarativeParentChange + \ingroup qml-state-elements + \brief The ParentChange element allows you to reparent an Item in a state change. + + ParentChange reparents an item while preserving its visual appearance (position, size, + rotation, and scale) on screen. You can then specify a transition to move/resize/rotate/scale + the item to its final intended appearance. + + ParentChange can only preserve visual appearance if no complex transforms are involved. + More specifically, it will not work if the transform property has been set for any + items involved in the reparenting (i.e. items in the common ancestor tree + for the original and new parent). + + The example below displays a large red rectangle and a small blue rectangle, side by side. + When the \c blueRect is clicked, it changes to the "reparented" state: its parent is changed to \c redRect and it is + positioned at (10, 10) within the red rectangle, as specified in the ParentChange. + + \snippet doc/src/snippets/declarative/parentchange.qml 0 + + \image parentchange.png + + You can specify at which point in a transition you want a ParentChange to occur by + using a ParentAnimation. +*/ + + +QDeclarativeParentChange::QDeclarativeParentChange(QObject *parent) + : QDeclarativeStateOperation(*(new QDeclarativeParentChangePrivate), parent) +{ +} + +QDeclarativeParentChange::~QDeclarativeParentChange() +{ +} + +/*! + \qmlproperty real ParentChange::x + \qmlproperty real ParentChange::y + \qmlproperty real ParentChange::width + \qmlproperty real ParentChange::height + \qmlproperty real ParentChange::scale + \qmlproperty real ParentChange::rotation + These properties hold the new position, size, scale, and rotation + for the item in this state. +*/ +QDeclarativeScriptString QDeclarativeParentChange::x() const +{ + Q_D(const QDeclarativeParentChange); + return d->xString.value; +} + +void tryReal(QDeclarativeNullableValue &value, const QString &string) +{ + bool ok = false; + qreal realValue = string.toFloat(&ok); + if (ok) + value = realValue; + else + value.invalidate(); +} + +void QDeclarativeParentChange::setX(QDeclarativeScriptString x) +{ + Q_D(QDeclarativeParentChange); + d->xString = x; + tryReal(d->x, x.script()); +} + +bool QDeclarativeParentChange::xIsSet() const +{ + Q_D(const QDeclarativeParentChange); + return d->xString.isValid(); +} + +QDeclarativeScriptString QDeclarativeParentChange::y() const +{ + Q_D(const QDeclarativeParentChange); + return d->yString.value; +} + +void QDeclarativeParentChange::setY(QDeclarativeScriptString y) +{ + Q_D(QDeclarativeParentChange); + d->yString = y; + tryReal(d->y, y.script()); +} + +bool QDeclarativeParentChange::yIsSet() const +{ + Q_D(const QDeclarativeParentChange); + return d->yString.isValid(); +} + +QDeclarativeScriptString QDeclarativeParentChange::width() const +{ + Q_D(const QDeclarativeParentChange); + return d->widthString.value; +} + +void QDeclarativeParentChange::setWidth(QDeclarativeScriptString width) +{ + Q_D(QDeclarativeParentChange); + d->widthString = width; + tryReal(d->width, width.script()); +} + +bool QDeclarativeParentChange::widthIsSet() const +{ + Q_D(const QDeclarativeParentChange); + return d->widthString.isValid(); +} + +QDeclarativeScriptString QDeclarativeParentChange::height() const +{ + Q_D(const QDeclarativeParentChange); + return d->heightString.value; +} + +void QDeclarativeParentChange::setHeight(QDeclarativeScriptString height) +{ + Q_D(QDeclarativeParentChange); + d->heightString = height; + tryReal(d->height, height.script()); +} + +bool QDeclarativeParentChange::heightIsSet() const +{ + Q_D(const QDeclarativeParentChange); + return d->heightString.isValid(); +} + +QDeclarativeScriptString QDeclarativeParentChange::scale() const +{ + Q_D(const QDeclarativeParentChange); + return d->scaleString.value; +} + +void QDeclarativeParentChange::setScale(QDeclarativeScriptString scale) +{ + Q_D(QDeclarativeParentChange); + d->scaleString = scale; + tryReal(d->scale, scale.script()); +} + +bool QDeclarativeParentChange::scaleIsSet() const +{ + Q_D(const QDeclarativeParentChange); + return d->scaleString.isValid(); +} + +QDeclarativeScriptString QDeclarativeParentChange::rotation() const +{ + Q_D(const QDeclarativeParentChange); + return d->rotationString.value; +} + +void QDeclarativeParentChange::setRotation(QDeclarativeScriptString rotation) +{ + Q_D(QDeclarativeParentChange); + d->rotationString = rotation; + tryReal(d->rotation, rotation.script()); +} + +bool QDeclarativeParentChange::rotationIsSet() const +{ + Q_D(const QDeclarativeParentChange); + return d->rotationString.isValid(); +} + +QDeclarativeItem *QDeclarativeParentChange::originalParent() const +{ + Q_D(const QDeclarativeParentChange); + return d->origParent; +} + +/*! + \qmlproperty Item ParentChange::target + This property holds the item to be reparented +*/ + +QDeclarativeItem *QDeclarativeParentChange::object() const +{ + Q_D(const QDeclarativeParentChange); + return d->target; +} + +void QDeclarativeParentChange::setObject(QDeclarativeItem *target) +{ + Q_D(QDeclarativeParentChange); + d->target = target; +} + +/*! + \qmlproperty Item ParentChange::parent + This property holds the new parent for the item in this state. +*/ + +QDeclarativeItem *QDeclarativeParentChange::parent() const +{ + Q_D(const QDeclarativeParentChange); + return d->parent; +} + +void QDeclarativeParentChange::setParent(QDeclarativeItem *parent) +{ + Q_D(QDeclarativeParentChange); + d->parent = parent; +} + +QDeclarativeStateOperation::ActionList QDeclarativeParentChange::actions() +{ + Q_D(QDeclarativeParentChange); + if (!d->target || !d->parent) + return ActionList(); + + ActionList actions; + + QDeclarativeAction a; + a.event = this; + actions << a; + + QDeclarativeContext *ctxt = qmlContext(this); + + if (d->xString.isValid()) { + if (d->x.isValid()) { + QDeclarativeAction xa(d->target, QLatin1String("x"), ctxt, d->x.value); + actions << xa; + } else { + QDeclarativeBinding *newBinding = new QDeclarativeBinding(d->xString.value.script(), d->target, ctxt); + newBinding->setTarget(QDeclarativeProperty(d->target, QLatin1String("x"), ctxt)); + QDeclarativeAction xa; + xa.property = newBinding->property(); + xa.toBinding = newBinding; + xa.fromValue = xa.property.read(); + xa.deletableToBinding = true; + actions << xa; + } + } + + if (d->yString.isValid()) { + if (d->y.isValid()) { + QDeclarativeAction ya(d->target, QLatin1String("y"), ctxt, d->y.value); + actions << ya; + } else { + QDeclarativeBinding *newBinding = new QDeclarativeBinding(d->yString.value.script(), d->target, ctxt); + newBinding->setTarget(QDeclarativeProperty(d->target, QLatin1String("y"), ctxt)); + QDeclarativeAction ya; + ya.property = newBinding->property(); + ya.toBinding = newBinding; + ya.fromValue = ya.property.read(); + ya.deletableToBinding = true; + actions << ya; + } + } + + if (d->scaleString.isValid()) { + if (d->scale.isValid()) { + QDeclarativeAction sa(d->target, QLatin1String("scale"), ctxt, d->scale.value); + actions << sa; + } else { + QDeclarativeBinding *newBinding = new QDeclarativeBinding(d->scaleString.value.script(), d->target, ctxt); + newBinding->setTarget(QDeclarativeProperty(d->target, QLatin1String("scale"), ctxt)); + QDeclarativeAction sa; + sa.property = newBinding->property(); + sa.toBinding = newBinding; + sa.fromValue = sa.property.read(); + sa.deletableToBinding = true; + actions << sa; + } + } + + if (d->rotationString.isValid()) { + if (d->rotation.isValid()) { + QDeclarativeAction ra(d->target, QLatin1String("rotation"), ctxt, d->rotation.value); + actions << ra; + } else { + QDeclarativeBinding *newBinding = new QDeclarativeBinding(d->rotationString.value.script(), d->target, ctxt); + newBinding->setTarget(QDeclarativeProperty(d->target, QLatin1String("rotation"), ctxt)); + QDeclarativeAction ra; + ra.property = newBinding->property(); + ra.toBinding = newBinding; + ra.fromValue = ra.property.read(); + ra.deletableToBinding = true; + actions << ra; + } + } + + if (d->widthString.isValid()) { + if (d->width.isValid()) { + QDeclarativeAction wa(d->target, QLatin1String("width"), ctxt, d->width.value); + actions << wa; + } else { + QDeclarativeBinding *newBinding = new QDeclarativeBinding(d->widthString.value.script(), d->target, ctxt); + newBinding->setTarget(QDeclarativeProperty(d->target, QLatin1String("width"), ctxt)); + QDeclarativeAction wa; + wa.property = newBinding->property(); + wa.toBinding = newBinding; + wa.fromValue = wa.property.read(); + wa.deletableToBinding = true; + actions << wa; + } + } + + if (d->heightString.isValid()) { + if (d->height.isValid()) { + QDeclarativeAction ha(d->target, QLatin1String("height"), ctxt, d->height.value); + actions << ha; + } else { + QDeclarativeBinding *newBinding = new QDeclarativeBinding(d->heightString.value.script(), d->target, ctxt); + newBinding->setTarget(QDeclarativeProperty(d->target, QLatin1String("height"), ctxt)); + QDeclarativeAction ha; + ha.property = newBinding->property(); + ha.toBinding = newBinding; + ha.fromValue = ha.property.read(); + ha.deletableToBinding = true; + actions << ha; + } + } + + return actions; +} + +class AccessibleFxItem : public QDeclarativeItem +{ + Q_OBJECT + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarativeItem) +public: + int siblingIndex() { + Q_D(QDeclarativeItem); + return d->siblingIndex; + } +}; + +void QDeclarativeParentChange::saveOriginals() +{ + Q_D(QDeclarativeParentChange); + saveCurrentValues(); + d->origParent = d->rewindParent; + d->origStackBefore = d->rewindStackBefore; +} + +/*void QDeclarativeParentChange::copyOriginals(QDeclarativeActionEvent *other) +{ + Q_D(QDeclarativeParentChange); + QDeclarativeParentChange *pc = static_cast(other); + + d->origParent = pc->d_func()->rewindParent; + d->origStackBefore = pc->d_func()->rewindStackBefore; + + saveCurrentValues(); +}*/ + +void QDeclarativeParentChange::execute(Reason) +{ + Q_D(QDeclarativeParentChange); + d->doChange(d->parent); +} + +bool QDeclarativeParentChange::isReversable() +{ + return true; +} + +void QDeclarativeParentChange::reverse(Reason) +{ + Q_D(QDeclarativeParentChange); + d->doChange(d->origParent, d->origStackBefore); +} + +QString QDeclarativeParentChange::typeName() const +{ + return QLatin1String("ParentChange"); +} + +bool QDeclarativeParentChange::override(QDeclarativeActionEvent*other) +{ + Q_D(QDeclarativeParentChange); + if (other->typeName() != QLatin1String("ParentChange")) + return false; + if (QDeclarativeParentChange *otherPC = static_cast(other)) + return (d->target == otherPC->object()); + return false; +} + +void QDeclarativeParentChange::saveCurrentValues() +{ + Q_D(QDeclarativeParentChange); + if (!d->target) { + d->rewindParent = 0; + d->rewindStackBefore = 0; + return; + } + + d->rewindParent = d->target->parentItem(); + d->rewindStackBefore = 0; + + if (!d->rewindParent) + return; + + //try to determine the item's original stack position so we can restore it + int siblingIndex = ((AccessibleFxItem*)d->target)->siblingIndex() + 1; + QList children = d->rewindParent->childItems(); + for (int i = 0; i < children.count(); ++i) { + QDeclarativeItem *child = qobject_cast(children.at(i)); + if (!child) + continue; + if (((AccessibleFxItem*)child)->siblingIndex() == siblingIndex) { + d->rewindStackBefore = child; + break; + } + } +} + +void QDeclarativeParentChange::rewind() +{ + Q_D(QDeclarativeParentChange); + d->doChange(d->rewindParent, d->rewindStackBefore); +} + +class QDeclarativeStateChangeScriptPrivate : public QDeclarativeStateOperationPrivate +{ +public: + QDeclarativeStateChangeScriptPrivate() {} + + QDeclarativeScriptString script; + QString name; +}; + +/*! + \qmlclass StateChangeScript QDeclarativeStateChangeScript + \ingroup qml-state-elements + \brief The StateChangeScript element allows you to run a script in a state. + + A StateChangeScript is run upon entering a state. You can optionally use + ScriptAction to specify the point in the transition at which + the StateChangeScript should to be run. + + \snippet snippets/declarative/states/statechangescript.qml state and transition + + \sa ScriptAction +*/ + +QDeclarativeStateChangeScript::QDeclarativeStateChangeScript(QObject *parent) +: QDeclarativeStateOperation(*(new QDeclarativeStateChangeScriptPrivate), parent) +{ +} + +QDeclarativeStateChangeScript::~QDeclarativeStateChangeScript() +{ +} + +/*! + \qmlproperty script StateChangeScript::script + This property holds the script to run when the state is current. +*/ +QDeclarativeScriptString QDeclarativeStateChangeScript::script() const +{ + Q_D(const QDeclarativeStateChangeScript); + return d->script; +} + +void QDeclarativeStateChangeScript::setScript(const QDeclarativeScriptString &s) +{ + Q_D(QDeclarativeStateChangeScript); + d->script = s; +} + +/*! + \qmlproperty string StateChangeScript::name + This property holds the name of the script. This name can be used by a + ScriptAction to target a specific script. + + \sa ScriptAction::scriptName +*/ +QString QDeclarativeStateChangeScript::name() const +{ + Q_D(const QDeclarativeStateChangeScript); + return d->name; +} + +void QDeclarativeStateChangeScript::setName(const QString &n) +{ + Q_D(QDeclarativeStateChangeScript); + d->name = n; +} + +void QDeclarativeStateChangeScript::execute(Reason) +{ + Q_D(QDeclarativeStateChangeScript); + const QString &script = d->script.script(); + if (!script.isEmpty()) { + QDeclarativeExpression expr(d->script.context(), d->script.scopeObject(), script); + QDeclarativeData *ddata = QDeclarativeData::get(this); + if (ddata && ddata->outerContext && !ddata->outerContext->url.isEmpty()) + expr.setSourceLocation(ddata->outerContext->url.toString(), ddata->lineNumber); + expr.evaluate(); + if (expr.hasError()) + qmlInfo(this, expr.error()); + } +} + +QDeclarativeStateChangeScript::ActionList QDeclarativeStateChangeScript::actions() +{ + ActionList rv; + QDeclarativeAction a; + a.event = this; + rv << a; + return rv; +} + +QString QDeclarativeStateChangeScript::typeName() const +{ + return QLatin1String("StateChangeScript"); +} + +/*! + \qmlclass AnchorChanges QDeclarativeAnchorChanges + \ingroup qml-state-elements + \brief The AnchorChanges element allows you to change the anchors of an item in a state. + + The AnchorChanges element is used to modify the anchors of an item in a \l State. + + AnchorChanges cannot be used to modify the margins on an item. For this, use + PropertyChanges intead. + + In the following example we change the top and bottom anchors of an item + using AnchorChanges, and the top and bottom anchor margins using + PropertyChanges: + + \snippet doc/src/snippets/declarative/anchorchanges.qml 0 + + \image anchorchanges.png + + AnchorChanges can be animated using AnchorAnimation. + \qml + //animate our anchor changes + Transition { + AnchorAnimation {} + } + \endqml + + Margin animations can be animated using NumberAnimation. + + For more information on anchors see \l {anchor-layout}{Anchor Layouts}. +*/ + +class QDeclarativeAnchorSetPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeAnchorSet) +public: + QDeclarativeAnchorSetPrivate() + : usedAnchors(0), resetAnchors(0), fill(0), + centerIn(0)/*, leftMargin(0), rightMargin(0), topMargin(0), bottomMargin(0), + margins(0), vCenterOffset(0), hCenterOffset(0), baselineOffset(0)*/ + { + } + + QDeclarativeAnchors::Anchors usedAnchors; + QDeclarativeAnchors::Anchors resetAnchors; + + QDeclarativeItem *fill; + QDeclarativeItem *centerIn; + + QDeclarativeScriptString leftScript; + QDeclarativeScriptString rightScript; + QDeclarativeScriptString topScript; + QDeclarativeScriptString bottomScript; + QDeclarativeScriptString hCenterScript; + QDeclarativeScriptString vCenterScript; + QDeclarativeScriptString baselineScript; + + /*qreal leftMargin; + qreal rightMargin; + qreal topMargin; + qreal bottomMargin; + qreal margins; + qreal vCenterOffset; + qreal hCenterOffset; + qreal baselineOffset;*/ +}; + +QDeclarativeAnchorSet::QDeclarativeAnchorSet(QObject *parent) + : QObject(*new QDeclarativeAnchorSetPrivate, parent) +{ +} + +QDeclarativeAnchorSet::~QDeclarativeAnchorSet() +{ +} + +QDeclarativeScriptString QDeclarativeAnchorSet::top() const +{ + Q_D(const QDeclarativeAnchorSet); + return d->topScript; +} + +void QDeclarativeAnchorSet::setTop(const QDeclarativeScriptString &edge) +{ + Q_D(QDeclarativeAnchorSet); + d->usedAnchors |= QDeclarativeAnchors::TopAnchor; + d->topScript = edge; + if (edge.script() == QLatin1String("undefined")) + resetTop(); +} + +void QDeclarativeAnchorSet::resetTop() +{ + Q_D(QDeclarativeAnchorSet); + d->usedAnchors &= ~QDeclarativeAnchors::TopAnchor; + d->topScript = QDeclarativeScriptString(); + d->resetAnchors |= QDeclarativeAnchors::TopAnchor; +} + +QDeclarativeScriptString QDeclarativeAnchorSet::bottom() const +{ + Q_D(const QDeclarativeAnchorSet); + return d->bottomScript; +} + +void QDeclarativeAnchorSet::setBottom(const QDeclarativeScriptString &edge) +{ + Q_D(QDeclarativeAnchorSet); + d->usedAnchors |= QDeclarativeAnchors::BottomAnchor; + d->bottomScript = edge; + if (edge.script() == QLatin1String("undefined")) + resetBottom(); +} + +void QDeclarativeAnchorSet::resetBottom() +{ + Q_D(QDeclarativeAnchorSet); + d->usedAnchors &= ~QDeclarativeAnchors::BottomAnchor; + d->bottomScript = QDeclarativeScriptString(); + d->resetAnchors |= QDeclarativeAnchors::BottomAnchor; +} + +QDeclarativeScriptString QDeclarativeAnchorSet::verticalCenter() const +{ + Q_D(const QDeclarativeAnchorSet); + return d->vCenterScript; +} + +void QDeclarativeAnchorSet::setVerticalCenter(const QDeclarativeScriptString &edge) +{ + Q_D(QDeclarativeAnchorSet); + d->usedAnchors |= QDeclarativeAnchors::VCenterAnchor; + d->vCenterScript = edge; + if (edge.script() == QLatin1String("undefined")) + resetVerticalCenter(); +} + +void QDeclarativeAnchorSet::resetVerticalCenter() +{ + Q_D(QDeclarativeAnchorSet); + d->usedAnchors &= ~QDeclarativeAnchors::VCenterAnchor; + d->vCenterScript = QDeclarativeScriptString(); + d->resetAnchors |= QDeclarativeAnchors::VCenterAnchor; +} + +QDeclarativeScriptString QDeclarativeAnchorSet::baseline() const +{ + Q_D(const QDeclarativeAnchorSet); + return d->baselineScript; +} + +void QDeclarativeAnchorSet::setBaseline(const QDeclarativeScriptString &edge) +{ + Q_D(QDeclarativeAnchorSet); + d->usedAnchors |= QDeclarativeAnchors::BaselineAnchor; + d->baselineScript = edge; + if (edge.script() == QLatin1String("undefined")) + resetBaseline(); +} + +void QDeclarativeAnchorSet::resetBaseline() +{ + Q_D(QDeclarativeAnchorSet); + d->usedAnchors &= ~QDeclarativeAnchors::BaselineAnchor; + d->baselineScript = QDeclarativeScriptString(); + d->resetAnchors |= QDeclarativeAnchors::BaselineAnchor; +} + +QDeclarativeScriptString QDeclarativeAnchorSet::left() const +{ + Q_D(const QDeclarativeAnchorSet); + return d->leftScript; +} + +void QDeclarativeAnchorSet::setLeft(const QDeclarativeScriptString &edge) +{ + Q_D(QDeclarativeAnchorSet); + d->usedAnchors |= QDeclarativeAnchors::LeftAnchor; + d->leftScript = edge; + if (edge.script() == QLatin1String("undefined")) + resetLeft(); +} + +void QDeclarativeAnchorSet::resetLeft() +{ + Q_D(QDeclarativeAnchorSet); + d->usedAnchors &= ~QDeclarativeAnchors::LeftAnchor; + d->leftScript = QDeclarativeScriptString(); + d->resetAnchors |= QDeclarativeAnchors::LeftAnchor; +} + +QDeclarativeScriptString QDeclarativeAnchorSet::right() const +{ + Q_D(const QDeclarativeAnchorSet); + return d->rightScript; +} + +void QDeclarativeAnchorSet::setRight(const QDeclarativeScriptString &edge) +{ + Q_D(QDeclarativeAnchorSet); + d->usedAnchors |= QDeclarativeAnchors::RightAnchor; + d->rightScript = edge; + if (edge.script() == QLatin1String("undefined")) + resetRight(); +} + +void QDeclarativeAnchorSet::resetRight() +{ + Q_D(QDeclarativeAnchorSet); + d->usedAnchors &= ~QDeclarativeAnchors::RightAnchor; + d->rightScript = QDeclarativeScriptString(); + d->resetAnchors |= QDeclarativeAnchors::RightAnchor; +} + +QDeclarativeScriptString QDeclarativeAnchorSet::horizontalCenter() const +{ + Q_D(const QDeclarativeAnchorSet); + return d->hCenterScript; +} + +void QDeclarativeAnchorSet::setHorizontalCenter(const QDeclarativeScriptString &edge) +{ + Q_D(QDeclarativeAnchorSet); + d->usedAnchors |= QDeclarativeAnchors::HCenterAnchor; + d->hCenterScript = edge; + if (edge.script() == QLatin1String("undefined")) + resetHorizontalCenter(); +} + +void QDeclarativeAnchorSet::resetHorizontalCenter() +{ + Q_D(QDeclarativeAnchorSet); + d->usedAnchors &= ~QDeclarativeAnchors::HCenterAnchor; + d->hCenterScript = QDeclarativeScriptString(); + d->resetAnchors |= QDeclarativeAnchors::HCenterAnchor; +} + +QDeclarativeItem *QDeclarativeAnchorSet::fill() const +{ + Q_D(const QDeclarativeAnchorSet); + return d->fill; +} + +void QDeclarativeAnchorSet::setFill(QDeclarativeItem *f) +{ + Q_D(QDeclarativeAnchorSet); + d->fill = f; +} + +void QDeclarativeAnchorSet::resetFill() +{ + setFill(0); +} + +QDeclarativeItem *QDeclarativeAnchorSet::centerIn() const +{ + Q_D(const QDeclarativeAnchorSet); + return d->centerIn; +} + +void QDeclarativeAnchorSet::setCenterIn(QDeclarativeItem* c) +{ + Q_D(QDeclarativeAnchorSet); + d->centerIn = c; +} + +void QDeclarativeAnchorSet::resetCenterIn() +{ + setCenterIn(0); +} + + +class QDeclarativeAnchorChangesPrivate : public QDeclarativeStateOperationPrivate +{ +public: + QDeclarativeAnchorChangesPrivate() + : target(0), anchorSet(new QDeclarativeAnchorSet), + leftBinding(0), rightBinding(0), hCenterBinding(0), + topBinding(0), bottomBinding(0), vCenterBinding(0), baselineBinding(0), + origLeftBinding(0), origRightBinding(0), origHCenterBinding(0), + origTopBinding(0), origBottomBinding(0), origVCenterBinding(0), + origBaselineBinding(0) + { + + } + ~QDeclarativeAnchorChangesPrivate() { delete anchorSet; } + + QDeclarativeItem *target; + QDeclarativeAnchorSet *anchorSet; + + QDeclarativeBinding *leftBinding; + QDeclarativeBinding *rightBinding; + QDeclarativeBinding *hCenterBinding; + QDeclarativeBinding *topBinding; + QDeclarativeBinding *bottomBinding; + QDeclarativeBinding *vCenterBinding; + QDeclarativeBinding *baselineBinding; + + QDeclarativeAbstractBinding *origLeftBinding; + QDeclarativeAbstractBinding *origRightBinding; + QDeclarativeAbstractBinding *origHCenterBinding; + QDeclarativeAbstractBinding *origTopBinding; + QDeclarativeAbstractBinding *origBottomBinding; + QDeclarativeAbstractBinding *origVCenterBinding; + QDeclarativeAbstractBinding *origBaselineBinding; + + QDeclarativeAnchorLine rewindLeft; + QDeclarativeAnchorLine rewindRight; + QDeclarativeAnchorLine rewindHCenter; + QDeclarativeAnchorLine rewindTop; + QDeclarativeAnchorLine rewindBottom; + QDeclarativeAnchorLine rewindVCenter; + QDeclarativeAnchorLine rewindBaseline; + + qreal fromX; + qreal fromY; + qreal fromWidth; + qreal fromHeight; + + qreal toX; + qreal toY; + qreal toWidth; + qreal toHeight; + + qreal rewindX; + qreal rewindY; + qreal rewindWidth; + qreal rewindHeight; + + bool applyOrigLeft; + bool applyOrigRight; + bool applyOrigHCenter; + bool applyOrigTop; + bool applyOrigBottom; + bool applyOrigVCenter; + bool applyOrigBaseline; + + QDeclarativeNullableValue origWidth; + QDeclarativeNullableValue origHeight; + qreal origX; + qreal origY; + + QList oldBindings; + + QDeclarativeProperty leftProp; + QDeclarativeProperty rightProp; + QDeclarativeProperty hCenterProp; + QDeclarativeProperty topProp; + QDeclarativeProperty bottomProp; + QDeclarativeProperty vCenterProp; + QDeclarativeProperty baselineProp; +}; + +/*! + \qmlproperty Item AnchorChanges::target + This property holds the \l Item for which the anchor changes will be applied. +*/ + +QDeclarativeAnchorChanges::QDeclarativeAnchorChanges(QObject *parent) + : QDeclarativeStateOperation(*(new QDeclarativeAnchorChangesPrivate), parent) +{ +} + +QDeclarativeAnchorChanges::~QDeclarativeAnchorChanges() +{ +} + +QDeclarativeAnchorChanges::ActionList QDeclarativeAnchorChanges::actions() +{ + Q_D(QDeclarativeAnchorChanges); + d->leftBinding = d->rightBinding = d->hCenterBinding = d->topBinding + = d->bottomBinding = d->vCenterBinding = d->baselineBinding = 0; + + d->leftProp = QDeclarativeProperty(d->target, QLatin1String("anchors.left")); + d->rightProp = QDeclarativeProperty(d->target, QLatin1String("anchors.right")); + d->hCenterProp = QDeclarativeProperty(d->target, QLatin1String("anchors.horizontalCenter")); + d->topProp = QDeclarativeProperty(d->target, QLatin1String("anchors.top")); + d->bottomProp = QDeclarativeProperty(d->target, QLatin1String("anchors.bottom")); + d->vCenterProp = QDeclarativeProperty(d->target, QLatin1String("anchors.verticalCenter")); + d->baselineProp = QDeclarativeProperty(d->target, QLatin1String("anchors.baseline")); + + QDeclarativeContext *ctxt = qmlContext(this); + + if (d->anchorSet->d_func()->usedAnchors & QDeclarativeAnchors::LeftAnchor) { + d->leftBinding = new QDeclarativeBinding(d->anchorSet->d_func()->leftScript.script(), d->target, ctxt); + d->leftBinding->setTarget(d->leftProp); + } + if (d->anchorSet->d_func()->usedAnchors & QDeclarativeAnchors::RightAnchor) { + d->rightBinding = new QDeclarativeBinding(d->anchorSet->d_func()->rightScript.script(), d->target, ctxt); + d->rightBinding->setTarget(d->rightProp); + } + if (d->anchorSet->d_func()->usedAnchors & QDeclarativeAnchors::HCenterAnchor) { + d->hCenterBinding = new QDeclarativeBinding(d->anchorSet->d_func()->hCenterScript.script(), d->target, ctxt); + d->hCenterBinding->setTarget(d->hCenterProp); + } + if (d->anchorSet->d_func()->usedAnchors & QDeclarativeAnchors::TopAnchor) { + d->topBinding = new QDeclarativeBinding(d->anchorSet->d_func()->topScript.script(), d->target, ctxt); + d->topBinding->setTarget(d->topProp); + } + if (d->anchorSet->d_func()->usedAnchors & QDeclarativeAnchors::BottomAnchor) { + d->bottomBinding = new QDeclarativeBinding(d->anchorSet->d_func()->bottomScript.script(), d->target, ctxt); + d->bottomBinding->setTarget(d->bottomProp); + } + if (d->anchorSet->d_func()->usedAnchors & QDeclarativeAnchors::VCenterAnchor) { + d->vCenterBinding = new QDeclarativeBinding(d->anchorSet->d_func()->vCenterScript.script(), d->target, ctxt); + d->vCenterBinding->setTarget(d->vCenterProp); + } + if (d->anchorSet->d_func()->usedAnchors & QDeclarativeAnchors::BaselineAnchor) { + d->baselineBinding = new QDeclarativeBinding(d->anchorSet->d_func()->baselineScript.script(), d->target, ctxt); + d->baselineBinding->setTarget(d->baselineProp); + } + + QDeclarativeAction a; + a.event = this; + return ActionList() << a; +} + +QDeclarativeAnchorSet *QDeclarativeAnchorChanges::anchors() +{ + Q_D(QDeclarativeAnchorChanges); + return d->anchorSet; +} + +QDeclarativeItem *QDeclarativeAnchorChanges::object() const +{ + Q_D(const QDeclarativeAnchorChanges); + return d->target; +} + +void QDeclarativeAnchorChanges::setObject(QDeclarativeItem *target) +{ + Q_D(QDeclarativeAnchorChanges); + d->target = target; +} + +/*! + \qmlproperty AnchorLine AnchorChanges::anchors.left + \qmlproperty AnchorLine AnchorChanges::anchors.right + \qmlproperty AnchorLine AnchorChanges::anchors.horizontalCenter + \qmlproperty AnchorLine AnchorChanges::anchors.top + \qmlproperty AnchorLine AnchorChanges::anchors.bottom + \qmlproperty AnchorLine AnchorChanges::anchors.verticalCenter + \qmlproperty AnchorLine AnchorChanges::anchors.baseline + + These properties change the respective anchors of the item. + + To reset an anchor you can assign \c undefined: + \qml + AnchorChanges { + target: myItem + anchors.left: undefined //remove myItem's left anchor + anchors.right: otherItem.right + } + \endqml +*/ + +void QDeclarativeAnchorChanges::execute(Reason reason) +{ + Q_D(QDeclarativeAnchorChanges); + if (!d->target) + return; + + QDeclarativeItemPrivate *targetPrivate = QDeclarativeItemPrivate::get(d->target); + //incorporate any needed "reverts" + if (d->applyOrigLeft) { + if (!d->origLeftBinding) + targetPrivate->anchors()->resetLeft(); + QDeclarativePropertyPrivate::setBinding(d->leftProp, d->origLeftBinding); + } + if (d->applyOrigRight) { + if (!d->origRightBinding) + targetPrivate->anchors()->resetRight(); + QDeclarativePropertyPrivate::setBinding(d->rightProp, d->origRightBinding); + } + if (d->applyOrigHCenter) { + if (!d->origHCenterBinding) + targetPrivate->anchors()->resetHorizontalCenter(); + QDeclarativePropertyPrivate::setBinding(d->hCenterProp, d->origHCenterBinding); + } + if (d->applyOrigTop) { + if (!d->origTopBinding) + targetPrivate->anchors()->resetTop(); + QDeclarativePropertyPrivate::setBinding(d->topProp, d->origTopBinding); + } + if (d->applyOrigBottom) { + if (!d->origBottomBinding) + targetPrivate->anchors()->resetBottom(); + QDeclarativePropertyPrivate::setBinding(d->bottomProp, d->origBottomBinding); + } + if (d->applyOrigVCenter) { + if (!d->origVCenterBinding) + targetPrivate->anchors()->resetVerticalCenter(); + QDeclarativePropertyPrivate::setBinding(d->vCenterProp, d->origVCenterBinding); + } + if (d->applyOrigBaseline) { + if (!d->origBaselineBinding) + targetPrivate->anchors()->resetBaseline(); + QDeclarativePropertyPrivate::setBinding(d->baselineProp, d->origBaselineBinding); + } + + //destroy old bindings + if (reason == ActualChange) { + for (int i = 0; i < d->oldBindings.size(); ++i) { + QDeclarativeAbstractBinding *binding = d->oldBindings.at(i); + if (binding) + binding->destroy(); + } + d->oldBindings.clear(); + } + + //reset any anchors that have been specified as "undefined" + if (d->anchorSet->d_func()->resetAnchors & QDeclarativeAnchors::LeftAnchor) { + targetPrivate->anchors()->resetLeft(); + QDeclarativePropertyPrivate::setBinding(d->leftProp, 0); + } + if (d->anchorSet->d_func()->resetAnchors & QDeclarativeAnchors::RightAnchor) { + targetPrivate->anchors()->resetRight(); + QDeclarativePropertyPrivate::setBinding(d->rightProp, 0); + } + if (d->anchorSet->d_func()->resetAnchors & QDeclarativeAnchors::HCenterAnchor) { + targetPrivate->anchors()->resetHorizontalCenter(); + QDeclarativePropertyPrivate::setBinding(d->hCenterProp, 0); + } + if (d->anchorSet->d_func()->resetAnchors & QDeclarativeAnchors::TopAnchor) { + targetPrivate->anchors()->resetTop(); + QDeclarativePropertyPrivate::setBinding(d->topProp, 0); + } + if (d->anchorSet->d_func()->resetAnchors & QDeclarativeAnchors::BottomAnchor) { + targetPrivate->anchors()->resetBottom(); + QDeclarativePropertyPrivate::setBinding(d->bottomProp, 0); + } + if (d->anchorSet->d_func()->resetAnchors & QDeclarativeAnchors::VCenterAnchor) { + targetPrivate->anchors()->resetVerticalCenter(); + QDeclarativePropertyPrivate::setBinding(d->vCenterProp, 0); + } + if (d->anchorSet->d_func()->resetAnchors & QDeclarativeAnchors::BaselineAnchor) { + targetPrivate->anchors()->resetBaseline(); + QDeclarativePropertyPrivate::setBinding(d->baselineProp, 0); + } + + //set any anchors that have been specified + if (d->leftBinding) + QDeclarativePropertyPrivate::setBinding(d->leftBinding->property(), d->leftBinding); + if (d->rightBinding) + QDeclarativePropertyPrivate::setBinding(d->rightBinding->property(), d->rightBinding); + if (d->hCenterBinding) + QDeclarativePropertyPrivate::setBinding(d->hCenterBinding->property(), d->hCenterBinding); + if (d->topBinding) + QDeclarativePropertyPrivate::setBinding(d->topBinding->property(), d->topBinding); + if (d->bottomBinding) + QDeclarativePropertyPrivate::setBinding(d->bottomBinding->property(), d->bottomBinding); + if (d->vCenterBinding) + QDeclarativePropertyPrivate::setBinding(d->vCenterBinding->property(), d->vCenterBinding); + if (d->baselineBinding) + QDeclarativePropertyPrivate::setBinding(d->baselineBinding->property(), d->baselineBinding); +} + +bool QDeclarativeAnchorChanges::isReversable() +{ + return true; +} + +void QDeclarativeAnchorChanges::reverse(Reason reason) +{ + Q_D(QDeclarativeAnchorChanges); + if (!d->target) + return; + + QDeclarativeItemPrivate *targetPrivate = QDeclarativeItemPrivate::get(d->target); + //reset any anchors set by the state + if (d->leftBinding) { + targetPrivate->anchors()->resetLeft(); + QDeclarativePropertyPrivate::setBinding(d->leftBinding->property(), 0); + if (reason == ActualChange) { + d->leftBinding->destroy(); d->leftBinding = 0; + } + } + if (d->rightBinding) { + targetPrivate->anchors()->resetRight(); + QDeclarativePropertyPrivate::setBinding(d->rightBinding->property(), 0); + if (reason == ActualChange) { + d->rightBinding->destroy(); d->rightBinding = 0; + } + } + if (d->hCenterBinding) { + targetPrivate->anchors()->resetHorizontalCenter(); + QDeclarativePropertyPrivate::setBinding(d->hCenterBinding->property(), 0); + if (reason == ActualChange) { + d->hCenterBinding->destroy(); d->hCenterBinding = 0; + } + } + if (d->topBinding) { + targetPrivate->anchors()->resetTop(); + QDeclarativePropertyPrivate::setBinding(d->topBinding->property(), 0); + if (reason == ActualChange) { + d->topBinding->destroy(); d->topBinding = 0; + } + } + if (d->bottomBinding) { + targetPrivate->anchors()->resetBottom(); + QDeclarativePropertyPrivate::setBinding(d->bottomBinding->property(), 0); + if (reason == ActualChange) { + d->bottomBinding->destroy(); d->bottomBinding = 0; + } + } + if (d->vCenterBinding) { + targetPrivate->anchors()->resetVerticalCenter(); + QDeclarativePropertyPrivate::setBinding(d->vCenterBinding->property(), 0); + if (reason == ActualChange) { + d->vCenterBinding->destroy(); d->vCenterBinding = 0; + } + } + if (d->baselineBinding) { + targetPrivate->anchors()->resetBaseline(); + QDeclarativePropertyPrivate::setBinding(d->baselineBinding->property(), 0); + if (reason == ActualChange) { + d->baselineBinding->destroy(); d->baselineBinding = 0; + } + } + + //restore previous anchors + if (d->origLeftBinding) + QDeclarativePropertyPrivate::setBinding(d->leftProp, d->origLeftBinding); + if (d->origRightBinding) + QDeclarativePropertyPrivate::setBinding(d->rightProp, d->origRightBinding); + if (d->origHCenterBinding) + QDeclarativePropertyPrivate::setBinding(d->hCenterProp, d->origHCenterBinding); + if (d->origTopBinding) + QDeclarativePropertyPrivate::setBinding(d->topProp, d->origTopBinding); + if (d->origBottomBinding) + QDeclarativePropertyPrivate::setBinding(d->bottomProp, d->origBottomBinding); + if (d->origVCenterBinding) + QDeclarativePropertyPrivate::setBinding(d->vCenterProp, d->origVCenterBinding); + if (d->origBaselineBinding) + QDeclarativePropertyPrivate::setBinding(d->baselineProp, d->origBaselineBinding); + + //restore any absolute geometry changed by the state's anchors + QDeclarativeAnchors::Anchors stateVAnchors = d->anchorSet->d_func()->usedAnchors & QDeclarativeAnchors::Vertical_Mask; + QDeclarativeAnchors::Anchors origVAnchors = targetPrivate->anchors()->usedAnchors() & QDeclarativeAnchors::Vertical_Mask; + QDeclarativeAnchors::Anchors stateHAnchors = d->anchorSet->d_func()->usedAnchors & QDeclarativeAnchors::Horizontal_Mask; + QDeclarativeAnchors::Anchors origHAnchors = targetPrivate->anchors()->usedAnchors() & QDeclarativeAnchors::Horizontal_Mask; + + bool stateSetWidth = (stateHAnchors && + stateHAnchors != QDeclarativeAnchors::LeftAnchor && + stateHAnchors != QDeclarativeAnchors::RightAnchor && + stateHAnchors != QDeclarativeAnchors::HCenterAnchor); + bool origSetWidth = (origHAnchors && + origHAnchors != QDeclarativeAnchors::LeftAnchor && + origHAnchors != QDeclarativeAnchors::RightAnchor && + origHAnchors != QDeclarativeAnchors::HCenterAnchor); + if (d->origWidth.isValid() && stateSetWidth && !origSetWidth) + d->target->setWidth(d->origWidth.value); + + bool stateSetHeight = (stateVAnchors && + stateVAnchors != QDeclarativeAnchors::TopAnchor && + stateVAnchors != QDeclarativeAnchors::BottomAnchor && + stateVAnchors != QDeclarativeAnchors::VCenterAnchor && + stateVAnchors != QDeclarativeAnchors::BaselineAnchor); + bool origSetHeight = (origVAnchors && + origVAnchors != QDeclarativeAnchors::TopAnchor && + origVAnchors != QDeclarativeAnchors::BottomAnchor && + origVAnchors != QDeclarativeAnchors::VCenterAnchor && + origVAnchors != QDeclarativeAnchors::BaselineAnchor); + if (d->origHeight.isValid() && stateSetHeight && !origSetHeight) + d->target->setHeight(d->origHeight.value); + + if (stateHAnchors && !origHAnchors) + d->target->setX(d->origX); + + if (stateVAnchors && !origVAnchors) + d->target->setY(d->origY); +} + +QString QDeclarativeAnchorChanges::typeName() const +{ + return QLatin1String("AnchorChanges"); +} + +QList QDeclarativeAnchorChanges::additionalActions() +{ + Q_D(QDeclarativeAnchorChanges); + QList extra; + + QDeclarativeAnchors::Anchors combined = d->anchorSet->d_func()->usedAnchors | d->anchorSet->d_func()->resetAnchors; + bool hChange = combined & QDeclarativeAnchors::Horizontal_Mask; + bool vChange = combined & QDeclarativeAnchors::Vertical_Mask; + + if (d->target) { + QDeclarativeContext *ctxt = qmlContext(this); + QDeclarativeAction a; + if (hChange && d->fromX != d->toX) { + a.property = QDeclarativeProperty(d->target, QLatin1String("x"), ctxt); + a.toValue = d->toX; + extra << a; + } + if (vChange && d->fromY != d->toY) { + a.property = QDeclarativeProperty(d->target, QLatin1String("y"), ctxt); + a.toValue = d->toY; + extra << a; + } + if (hChange && d->fromWidth != d->toWidth) { + a.property = QDeclarativeProperty(d->target, QLatin1String("width"), ctxt); + a.toValue = d->toWidth; + extra << a; + } + if (vChange && d->fromHeight != d->toHeight) { + a.property = QDeclarativeProperty(d->target, QLatin1String("height"), ctxt); + a.toValue = d->toHeight; + extra << a; + } + } + + return extra; +} + +bool QDeclarativeAnchorChanges::changesBindings() +{ + return true; +} + +void QDeclarativeAnchorChanges::saveOriginals() +{ + Q_D(QDeclarativeAnchorChanges); + if (!d->target) + return; + + d->origLeftBinding = QDeclarativePropertyPrivate::binding(d->leftProp); + d->origRightBinding = QDeclarativePropertyPrivate::binding(d->rightProp); + d->origHCenterBinding = QDeclarativePropertyPrivate::binding(d->hCenterProp); + d->origTopBinding = QDeclarativePropertyPrivate::binding(d->topProp); + d->origBottomBinding = QDeclarativePropertyPrivate::binding(d->bottomProp); + d->origVCenterBinding = QDeclarativePropertyPrivate::binding(d->vCenterProp); + d->origBaselineBinding = QDeclarativePropertyPrivate::binding(d->baselineProp); + + QDeclarativeItemPrivate *targetPrivate = QDeclarativeItemPrivate::get(d->target); + if (targetPrivate->widthValid) + d->origWidth = d->target->width(); + if (targetPrivate->heightValid) + d->origHeight = d->target->height(); + d->origX = d->target->x(); + d->origY = d->target->y(); + + d->applyOrigLeft = d->applyOrigRight = d->applyOrigHCenter = d->applyOrigTop + = d->applyOrigBottom = d->applyOrigVCenter = d->applyOrigBaseline = false; + + saveCurrentValues(); +} + +void QDeclarativeAnchorChanges::copyOriginals(QDeclarativeActionEvent *other) +{ + Q_D(QDeclarativeAnchorChanges); + QDeclarativeAnchorChanges *ac = static_cast(other); + QDeclarativeAnchorChangesPrivate *acp = ac->d_func(); + + QDeclarativeAnchors::Anchors combined = acp->anchorSet->d_func()->usedAnchors | + acp->anchorSet->d_func()->resetAnchors; + + //probably also need to revert some things + d->applyOrigLeft = (combined & QDeclarativeAnchors::LeftAnchor); + d->applyOrigRight = (combined & QDeclarativeAnchors::RightAnchor); + d->applyOrigHCenter = (combined & QDeclarativeAnchors::HCenterAnchor); + d->applyOrigTop = (combined & QDeclarativeAnchors::TopAnchor); + d->applyOrigBottom = (combined & QDeclarativeAnchors::BottomAnchor); + d->applyOrigVCenter = (combined & QDeclarativeAnchors::VCenterAnchor); + d->applyOrigBaseline = (combined & QDeclarativeAnchors::BaselineAnchor); + + d->origLeftBinding = acp->origLeftBinding; + d->origRightBinding = acp->origRightBinding; + d->origHCenterBinding = acp->origHCenterBinding; + d->origTopBinding = acp->origTopBinding; + d->origBottomBinding = acp->origBottomBinding; + d->origVCenterBinding = acp->origVCenterBinding; + d->origBaselineBinding = acp->origBaselineBinding; + + d->origWidth = acp->origWidth; + d->origHeight = acp->origHeight; + d->origX = acp->origX; + d->origY = acp->origY; + + d->oldBindings.clear(); + d->oldBindings << acp->leftBinding << acp->rightBinding << acp->hCenterBinding + << acp->topBinding << acp->bottomBinding << acp->baselineBinding; + + saveCurrentValues(); +} + +void QDeclarativeAnchorChanges::clearBindings() +{ + Q_D(QDeclarativeAnchorChanges); + if (!d->target) + return; + + //### should this (saving "from" values) be moved to saveCurrentValues()? + d->fromX = d->target->x(); + d->fromY = d->target->y(); + d->fromWidth = d->target->width(); + d->fromHeight = d->target->height(); + + QDeclarativeItemPrivate *targetPrivate = QDeclarativeItemPrivate::get(d->target); + //reset any anchors with corresponding reverts + //reset any anchors that have been specified as "undefined" + //reset any anchors that we'll be setting in the state + QDeclarativeAnchors::Anchors combined = d->anchorSet->d_func()->resetAnchors | + d->anchorSet->d_func()->usedAnchors; + if (d->applyOrigLeft || (combined & QDeclarativeAnchors::LeftAnchor)) { + targetPrivate->anchors()->resetLeft(); + QDeclarativePropertyPrivate::setBinding(d->leftProp, 0); + } + if (d->applyOrigRight || (combined & QDeclarativeAnchors::RightAnchor)) { + targetPrivate->anchors()->resetRight(); + QDeclarativePropertyPrivate::setBinding(d->rightProp, 0); + } + if (d->applyOrigHCenter || (combined & QDeclarativeAnchors::HCenterAnchor)) { + targetPrivate->anchors()->resetHorizontalCenter(); + QDeclarativePropertyPrivate::setBinding(d->hCenterProp, 0); + } + if (d->applyOrigTop || (combined & QDeclarativeAnchors::TopAnchor)) { + targetPrivate->anchors()->resetTop(); + QDeclarativePropertyPrivate::setBinding(d->topProp, 0); + } + if (d->applyOrigBottom || (combined & QDeclarativeAnchors::BottomAnchor)) { + targetPrivate->anchors()->resetBottom(); + QDeclarativePropertyPrivate::setBinding(d->bottomProp, 0); + } + if (d->applyOrigVCenter || (combined & QDeclarativeAnchors::VCenterAnchor)) { + targetPrivate->anchors()->resetVerticalCenter(); + QDeclarativePropertyPrivate::setBinding(d->vCenterProp, 0); + } + if (d->applyOrigBaseline || (combined & QDeclarativeAnchors::BaselineAnchor)) { + targetPrivate->anchors()->resetBaseline(); + QDeclarativePropertyPrivate::setBinding(d->baselineProp, 0); + } +} + +bool QDeclarativeAnchorChanges::override(QDeclarativeActionEvent*other) +{ + if (other->typeName() != QLatin1String("AnchorChanges")) + return false; + if (static_cast(this) == other) + return true; + if (static_cast(other)->object() == object()) + return true; + return false; +} + +void QDeclarativeAnchorChanges::rewind() +{ + Q_D(QDeclarativeAnchorChanges); + if (!d->target) + return; + + QDeclarativeItemPrivate *targetPrivate = QDeclarativeItemPrivate::get(d->target); + + //restore previous values (but not previous bindings, i.e. anchors) + d->target->setX(d->rewindX); + d->target->setY(d->rewindY); + if (targetPrivate->widthValid) { + d->target->setWidth(d->rewindWidth); + } + if (targetPrivate->heightValid) { + d->target->setHeight(d->rewindHeight); + } +} + +void QDeclarativeAnchorChanges::saveCurrentValues() +{ + Q_D(QDeclarativeAnchorChanges); + if (!d->target) + return; + + QDeclarativeItemPrivate *targetPrivate = QDeclarativeItemPrivate::get(d->target); + d->rewindLeft = targetPrivate->anchors()->left(); + d->rewindRight = targetPrivate->anchors()->right(); + d->rewindHCenter = targetPrivate->anchors()->horizontalCenter(); + d->rewindTop = targetPrivate->anchors()->top(); + d->rewindBottom = targetPrivate->anchors()->bottom(); + d->rewindVCenter = targetPrivate->anchors()->verticalCenter(); + d->rewindBaseline = targetPrivate->anchors()->baseline(); + + d->rewindX = d->target->x(); + d->rewindY = d->target->y(); + d->rewindWidth = d->target->width(); + d->rewindHeight = d->target->height(); +} + +void QDeclarativeAnchorChanges::saveTargetValues() +{ + Q_D(QDeclarativeAnchorChanges); + if (!d->target) + return; + + d->toX = d->target->x(); + d->toY = d->target->y(); + d->toWidth = d->target->width(); + d->toHeight = d->target->height(); +} + +#include +#include + +QT_END_NAMESPACE + diff --git a/src/declarative/util/qdeclarativestateoperations_p.h b/src/declarative/util/qdeclarativestateoperations_p.h new file mode 100644 index 00000000..6848b8a4 --- /dev/null +++ b/src/declarative/util/qdeclarativestateoperations_p.h @@ -0,0 +1,299 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESTATEOPERATIONS_H +#define QDECLARATIVESTATEOPERATIONS_H + +#include "private/qdeclarativestate_p.h" + +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeParentChangePrivate; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeParentChange : public QDeclarativeStateOperation, public QDeclarativeActionEvent +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeParentChange) + + Q_PROPERTY(QDeclarativeItem *target READ object WRITE setObject) + Q_PROPERTY(QDeclarativeItem *parent READ parent WRITE setParent) + Q_PROPERTY(QDeclarativeScriptString x READ x WRITE setX) + Q_PROPERTY(QDeclarativeScriptString y READ y WRITE setY) + Q_PROPERTY(QDeclarativeScriptString width READ width WRITE setWidth) + Q_PROPERTY(QDeclarativeScriptString height READ height WRITE setHeight) + Q_PROPERTY(QDeclarativeScriptString scale READ scale WRITE setScale) + Q_PROPERTY(QDeclarativeScriptString rotation READ rotation WRITE setRotation) +public: + QDeclarativeParentChange(QObject *parent=0); + ~QDeclarativeParentChange(); + + QDeclarativeItem *object() const; + void setObject(QDeclarativeItem *); + + QDeclarativeItem *parent() const; + void setParent(QDeclarativeItem *); + + QDeclarativeItem *originalParent() const; + + QDeclarativeScriptString x() const; + void setX(QDeclarativeScriptString x); + bool xIsSet() const; + + QDeclarativeScriptString y() const; + void setY(QDeclarativeScriptString y); + bool yIsSet() const; + + QDeclarativeScriptString width() const; + void setWidth(QDeclarativeScriptString width); + bool widthIsSet() const; + + QDeclarativeScriptString height() const; + void setHeight(QDeclarativeScriptString height); + bool heightIsSet() const; + + QDeclarativeScriptString scale() const; + void setScale(QDeclarativeScriptString scale); + bool scaleIsSet() const; + + QDeclarativeScriptString rotation() const; + void setRotation(QDeclarativeScriptString rotation); + bool rotationIsSet() const; + + virtual ActionList actions(); + + virtual void saveOriginals(); + //virtual void copyOriginals(QDeclarativeActionEvent*); + virtual void execute(Reason reason = ActualChange); + virtual bool isReversable(); + virtual void reverse(Reason reason = ActualChange); + virtual QString typeName() const; + virtual bool override(QDeclarativeActionEvent*other); + virtual void rewind(); + virtual void saveCurrentValues(); +}; + +class QDeclarativeStateChangeScriptPrivate; +class Q_AUTOTEST_EXPORT QDeclarativeStateChangeScript : public QDeclarativeStateOperation, public QDeclarativeActionEvent +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeStateChangeScript) + + Q_PROPERTY(QDeclarativeScriptString script READ script WRITE setScript) + Q_PROPERTY(QString name READ name WRITE setName) + +public: + QDeclarativeStateChangeScript(QObject *parent=0); + ~QDeclarativeStateChangeScript(); + + virtual ActionList actions(); + + virtual QString typeName() const; + + QDeclarativeScriptString script() const; + void setScript(const QDeclarativeScriptString &); + + QString name() const; + void setName(const QString &); + + virtual void execute(Reason reason = ActualChange); +}; + +class QDeclarativeAnchorChanges; +class QDeclarativeAnchorSetPrivate; +class Q_AUTOTEST_EXPORT QDeclarativeAnchorSet : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QDeclarativeScriptString left READ left WRITE setLeft RESET resetLeft) + Q_PROPERTY(QDeclarativeScriptString right READ right WRITE setRight RESET resetRight) + Q_PROPERTY(QDeclarativeScriptString horizontalCenter READ horizontalCenter WRITE setHorizontalCenter RESET resetHorizontalCenter) + Q_PROPERTY(QDeclarativeScriptString top READ top WRITE setTop RESET resetTop) + Q_PROPERTY(QDeclarativeScriptString bottom READ bottom WRITE setBottom RESET resetBottom) + Q_PROPERTY(QDeclarativeScriptString verticalCenter READ verticalCenter WRITE setVerticalCenter RESET resetVerticalCenter) + Q_PROPERTY(QDeclarativeScriptString baseline READ baseline WRITE setBaseline RESET resetBaseline) + //Q_PROPERTY(QDeclarativeItem *fill READ fill WRITE setFill RESET resetFill) + //Q_PROPERTY(QDeclarativeItem *centerIn READ centerIn WRITE setCenterIn RESET resetCenterIn) + + /*Q_PROPERTY(qreal margins READ margins WRITE setMargins NOTIFY marginsChanged) + Q_PROPERTY(qreal leftMargin READ leftMargin WRITE setLeftMargin NOTIFY leftMarginChanged) + Q_PROPERTY(qreal rightMargin READ rightMargin WRITE setRightMargin NOTIFY rightMarginChanged) + Q_PROPERTY(qreal horizontalCenterOffset READ horizontalCenterOffset WRITE setHorizontalCenterOffset NOTIFY horizontalCenterOffsetChanged()) + Q_PROPERTY(qreal topMargin READ topMargin WRITE setTopMargin NOTIFY topMarginChanged) + Q_PROPERTY(qreal bottomMargin READ bottomMargin WRITE setBottomMargin NOTIFY bottomMarginChanged) + Q_PROPERTY(qreal verticalCenterOffset READ verticalCenterOffset WRITE setVerticalCenterOffset NOTIFY verticalCenterOffsetChanged()) + Q_PROPERTY(qreal baselineOffset READ baselineOffset WRITE setBaselineOffset NOTIFY baselineOffsetChanged())*/ + +public: + QDeclarativeAnchorSet(QObject *parent=0); + virtual ~QDeclarativeAnchorSet(); + + QDeclarativeScriptString left() const; + void setLeft(const QDeclarativeScriptString &edge); + void resetLeft(); + + QDeclarativeScriptString right() const; + void setRight(const QDeclarativeScriptString &edge); + void resetRight(); + + QDeclarativeScriptString horizontalCenter() const; + void setHorizontalCenter(const QDeclarativeScriptString &edge); + void resetHorizontalCenter(); + + QDeclarativeScriptString top() const; + void setTop(const QDeclarativeScriptString &edge); + void resetTop(); + + QDeclarativeScriptString bottom() const; + void setBottom(const QDeclarativeScriptString &edge); + void resetBottom(); + + QDeclarativeScriptString verticalCenter() const; + void setVerticalCenter(const QDeclarativeScriptString &edge); + void resetVerticalCenter(); + + QDeclarativeScriptString baseline() const; + void setBaseline(const QDeclarativeScriptString &edge); + void resetBaseline(); + + QDeclarativeItem *fill() const; + void setFill(QDeclarativeItem *); + void resetFill(); + + QDeclarativeItem *centerIn() const; + void setCenterIn(QDeclarativeItem *); + void resetCenterIn(); + + /*qreal leftMargin() const; + void setLeftMargin(qreal); + + qreal rightMargin() const; + void setRightMargin(qreal); + + qreal horizontalCenterOffset() const; + void setHorizontalCenterOffset(qreal); + + qreal topMargin() const; + void setTopMargin(qreal); + + qreal bottomMargin() const; + void setBottomMargin(qreal); + + qreal margins() const; + void setMargins(qreal); + + qreal verticalCenterOffset() const; + void setVerticalCenterOffset(qreal); + + qreal baselineOffset() const; + void setBaselineOffset(qreal);*/ + + QDeclarativeAnchors::Anchors usedAnchors() const; + +/*Q_SIGNALS: + void leftMarginChanged(); + void rightMarginChanged(); + void topMarginChanged(); + void bottomMarginChanged(); + void marginsChanged(); + void verticalCenterOffsetChanged(); + void horizontalCenterOffsetChanged(); + void baselineOffsetChanged();*/ + +private: + friend class QDeclarativeAnchorChanges; + Q_DISABLE_COPY(QDeclarativeAnchorSet) + Q_DECLARE_PRIVATE(QDeclarativeAnchorSet) +}; + +class QDeclarativeAnchorChangesPrivate; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeAnchorChanges : public QDeclarativeStateOperation, public QDeclarativeActionEvent +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeAnchorChanges) + + Q_PROPERTY(QDeclarativeItem *target READ object WRITE setObject) + Q_PROPERTY(QDeclarativeAnchorSet *anchors READ anchors CONSTANT) + +public: + QDeclarativeAnchorChanges(QObject *parent=0); + ~QDeclarativeAnchorChanges(); + + virtual ActionList actions(); + + QDeclarativeAnchorSet *anchors(); + + QDeclarativeItem *object() const; + void setObject(QDeclarativeItem *); + + virtual void execute(Reason reason = ActualChange); + virtual bool isReversable(); + virtual void reverse(Reason reason = ActualChange); + virtual QString typeName() const; + virtual bool override(QDeclarativeActionEvent*other); + virtual bool changesBindings(); + virtual void saveOriginals(); + virtual bool needsCopy() { return true; } + virtual void copyOriginals(QDeclarativeActionEvent*); + virtual void clearBindings(); + virtual void rewind(); + virtual void saveCurrentValues(); + + QList additionalActions(); + virtual void saveTargetValues(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeParentChange) +QML_DECLARE_TYPE(QDeclarativeStateChangeScript) +QML_DECLARE_TYPE(QDeclarativeAnchorSet) +QML_DECLARE_TYPE(QDeclarativeAnchorChanges) + +QT_END_HEADER + +#endif // QDECLARATIVESTATEOPERATIONS_H diff --git a/src/declarative/util/qdeclarativestyledtext.cpp b/src/declarative/util/qdeclarativestyledtext.cpp new file mode 100644 index 00000000..022a8220 --- /dev/null +++ b/src/declarative/util/qdeclarativestyledtext.cpp @@ -0,0 +1,347 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "private/qdeclarativestyledtext_p.h" + +/* + QDeclarativeStyledText supports few tags: + + - bold + - italic +
- new line + + + The opening and closing tags must be correctly nested. +*/ + +QT_BEGIN_NAMESPACE + +class QDeclarativeStyledTextPrivate +{ +public: + QDeclarativeStyledTextPrivate(const QString &t, QTextLayout &l) : text(t), layout(l), baseFont(layout.font()) {} + + void parse(); + bool parseTag(const QChar *&ch, const QString &textIn, QString &textOut, QTextCharFormat &format); + bool parseCloseTag(const QChar *&ch, const QString &textIn); + void parseEntity(const QChar *&ch, const QString &textIn, QString &textOut); + bool parseFontAttributes(const QChar *&ch, const QString &textIn, QTextCharFormat &format); + QPair parseAttribute(const QChar *&ch, const QString &textIn); + QStringRef parseValue(const QChar *&ch, const QString &textIn); + + inline void skipSpace(const QChar *&ch) { + while (ch->isSpace() && !ch->isNull()) + ++ch; + } + + QString text; + QTextLayout &layout; + QFont baseFont; + + static const QChar lessThan; + static const QChar greaterThan; + static const QChar equals; + static const QChar singleQuote; + static const QChar doubleQuote; + static const QChar slash; + static const QChar ampersand; +}; + +const QChar QDeclarativeStyledTextPrivate::lessThan(QLatin1Char('<')); +const QChar QDeclarativeStyledTextPrivate::greaterThan(QLatin1Char('>')); +const QChar QDeclarativeStyledTextPrivate::equals(QLatin1Char('=')); +const QChar QDeclarativeStyledTextPrivate::singleQuote(QLatin1Char('\'')); +const QChar QDeclarativeStyledTextPrivate::doubleQuote(QLatin1Char('\"')); +const QChar QDeclarativeStyledTextPrivate::slash(QLatin1Char('/')); +const QChar QDeclarativeStyledTextPrivate::ampersand(QLatin1Char('&')); + +QDeclarativeStyledText::QDeclarativeStyledText(const QString &string, QTextLayout &layout) +: d(new QDeclarativeStyledTextPrivate(string, layout)) +{ +} + +QDeclarativeStyledText::~QDeclarativeStyledText() +{ + delete d; +} + +void QDeclarativeStyledText::parse(const QString &string, QTextLayout &layout) +{ + if (string.isEmpty()) + return; + QDeclarativeStyledText styledText(string, layout); + styledText.d->parse(); +} + +void QDeclarativeStyledTextPrivate::parse() +{ + QList ranges; + QStack formatStack; + + QString drawText; + drawText.reserve(text.count()); + + int textStart = 0; + int textLength = 0; + int rangeStart = 0; + const QChar *ch = text.constData(); + while (!ch->isNull()) { + if (*ch == lessThan) { + if (textLength) + drawText.append(QStringRef(&text, textStart, textLength)); + if (rangeStart != drawText.length() && formatStack.count()) { + QTextLayout::FormatRange formatRange; + formatRange.format = formatStack.top(); + formatRange.start = rangeStart; + formatRange.length = drawText.length() - rangeStart; + ranges.append(formatRange); + } + rangeStart = drawText.length(); + ++ch; + if (*ch == slash) { + ++ch; + if (parseCloseTag(ch, text)) { + if (formatStack.count()) + formatStack.pop(); + } + } else { + QTextCharFormat format; + if (formatStack.count()) + format = formatStack.top(); + if (parseTag(ch, text, drawText, format)) + formatStack.push(format); + } + textStart = ch - text.constData() + 1; + textLength = 0; + } else if (*ch == ampersand) { + ++ch; + drawText.append(QStringRef(&text, textStart, textLength)); + parseEntity(ch, text, drawText); + textStart = ch - text.constData() + 1; + textLength = 0; + } else { + ++textLength; + } + if (!ch->isNull()) + ++ch; + } + if (textLength) + drawText.append(QStringRef(&text, textStart, textLength)); + if (rangeStart != drawText.length() && formatStack.count()) { + QTextLayout::FormatRange formatRange; + formatRange.format = formatStack.top(); + formatRange.start = rangeStart; + formatRange.length = drawText.length() - rangeStart; + ranges.append(formatRange); + } + + layout.setText(drawText); + layout.setAdditionalFormats(ranges); +} + +bool QDeclarativeStyledTextPrivate::parseTag(const QChar *&ch, const QString &textIn, QString &textOut, QTextCharFormat &format) +{ + skipSpace(ch); + + int tagStart = ch - textIn.constData(); + int tagLength = 0; + while (!ch->isNull()) { + if (*ch == greaterThan) { + QStringRef tag(&textIn, tagStart, tagLength); + const QChar char0 = tag.at(0); + if (char0 == QLatin1Char('b')) { + if (tagLength == 1) + format.setFontWeight(QFont::Bold); + else if (tagLength == 2 && tag.at(1) == QLatin1Char('r')) { + textOut.append(QChar(QChar::LineSeparator)); + return false; + } + } else if (char0 == QLatin1Char('i')) { + if (tagLength == 1) + format.setFontItalic(true); + } + return true; + } else if (ch->isSpace()) { + // may have params. + QStringRef tag(&textIn, tagStart, tagLength); + if (tag == QLatin1String("font")) + return parseFontAttributes(ch, textIn, format); + if (*ch == greaterThan || ch->isNull()) + continue; + } else if (*ch != slash){ + tagLength++; + } + ++ch; + } + + return false; +} + +bool QDeclarativeStyledTextPrivate::parseCloseTag(const QChar *&ch, const QString &textIn) +{ + skipSpace(ch); + + int tagStart = ch - textIn.constData(); + int tagLength = 0; + while (!ch->isNull()) { + if (*ch == greaterThan) { + QStringRef tag(&textIn, tagStart, tagLength); + const QChar char0 = tag.at(0); + if (char0 == QLatin1Char('b')) { + if (tagLength == 1) + return true; + else if (tag.at(1) == QLatin1Char('r') && tagLength == 2) + return true; + } else if (char0 == QLatin1Char('i')) { + if (tagLength == 1) + return true; + } else if (tag == QLatin1String("font")) { + return true; + } + return false; + } else if (!ch->isSpace()){ + tagLength++; + } + ++ch; + } + + return false; +} + +void QDeclarativeStyledTextPrivate::parseEntity(const QChar *&ch, const QString &textIn, QString &textOut) +{ + int entityStart = ch - textIn.constData(); + int entityLength = 0; + while (!ch->isNull()) { + if (*ch == QLatin1Char(';')) { + QStringRef entity(&textIn, entityStart, entityLength); + if (entity == QLatin1String("gt")) + textOut += QChar(62); + else if (entity == QLatin1String("lt")) + textOut += QChar(60); + else if (entity == QLatin1String("amp")) + textOut += QChar(38); + return; + } + ++entityLength; + ++ch; + } +} + +bool QDeclarativeStyledTextPrivate::parseFontAttributes(const QChar *&ch, const QString &textIn, QTextCharFormat &format) +{ + bool valid = false; + QPair attr; + do { + attr = parseAttribute(ch, textIn); + if (attr.first == QLatin1String("color")) { + valid = true; + format.setForeground(QColor(attr.second.toString())); + } else if (attr.first == QLatin1String("size")) { + valid = true; + int size = attr.second.toString().toInt(); + if (attr.second.at(0) == QLatin1Char('-') || attr.second.at(0) == QLatin1Char('+')) + size += 3; + if (size >= 1 && size <= 7) { + static const qreal scaling[] = { 0.7, 0.8, 1.0, 1.2, 1.5, 2.0, 2.4 }; + format.setFontPointSize(baseFont.pointSize() * scaling[size-1]); + } + } + } while (!ch->isNull() && !attr.first.isEmpty()); + + return valid; +} + +QPair QDeclarativeStyledTextPrivate::parseAttribute(const QChar *&ch, const QString &textIn) +{ + skipSpace(ch); + + int attrStart = ch - textIn.constData(); + int attrLength = 0; + while (!ch->isNull()) { + if (*ch == greaterThan) { + break; + } else if (*ch == equals) { + ++ch; + if (*ch != singleQuote && *ch != doubleQuote) { + while (*ch != greaterThan && !ch->isNull()) + ++ch; + break; + } + ++ch; + if (!attrLength) + break; + QStringRef attr(&textIn, attrStart, attrLength); + QStringRef val = parseValue(ch, textIn); + if (!val.isEmpty()) + return QPair(attr,val); + break; + } else { + ++attrLength; + } + ++ch; + } + + return QPair(); +} + +QStringRef QDeclarativeStyledTextPrivate::parseValue(const QChar *&ch, const QString &textIn) +{ + int valStart = ch - textIn.constData(); + int valLength = 0; + while (*ch != singleQuote && *ch != doubleQuote && !ch->isNull()) { + ++valLength; + ++ch; + } + if (ch->isNull()) + return QStringRef(); + ++ch; // skip quote + + return QStringRef(&textIn, valStart, valLength); +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativestyledtext_p.h b/src/declarative/util/qdeclarativestyledtext_p.h new file mode 100644 index 00000000..da86d4b3 --- /dev/null +++ b/src/declarative/util/qdeclarativestyledtext_p.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESTYLEDTEXT_H +#define QDECLARATIVESTYLEDTEXT_H + +#include + +QT_BEGIN_NAMESPACE + +class QPainter; +class QPointF; +class QString; +class QDeclarativeStyledTextPrivate; +class QTextLayout; + +class Q_AUTOTEST_EXPORT QDeclarativeStyledText +{ +public: + static void parse(const QString &string, QTextLayout &layout); + +private: + QDeclarativeStyledText(const QString &string, QTextLayout &layout); + ~QDeclarativeStyledText(); + + QDeclarativeStyledTextPrivate *d; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/declarative/util/qdeclarativesystempalette.cpp b/src/declarative/util/qdeclarativesystempalette.cpp new file mode 100644 index 00000000..984909e1 --- /dev/null +++ b/src/declarative/util/qdeclarativesystempalette.cpp @@ -0,0 +1,312 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativesystempalette_p.h" + +#include + +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeSystemPalettePrivate : public QObjectPrivate +{ +public: + QPalette palette; + QPalette::ColorGroup group; +}; + + + +/*! + \qmlclass SystemPalette QDeclarativeSystemPalette + \ingroup qml-utility-elements + \since 4.7 + \brief The SystemPalette element provides access to the Qt palettes. + + The SystemPalette element provides access to the Qt application + palettes. This provides information about the standard colors used + for application windows, buttons and other features. These colors + are grouped into three \e {color groups}: \c Active, \c Inactive, + and \c Disabled. See the QPalette documentation for details about + color groups and the properties provided by SystemPalette. + + This can be used to color items in a way that provides a more + native look and feel. + + The following example creates a palette from the \c Active color + group and uses this to color the window and text items + appropriately: + + \snippet doc/src/snippets/declarative/systempalette.qml 0 + + \sa QPalette +*/ +QDeclarativeSystemPalette::QDeclarativeSystemPalette(QObject *parent) + : QObject(*(new QDeclarativeSystemPalettePrivate), parent) +{ + Q_D(QDeclarativeSystemPalette); + d->palette = QApplication::palette(); + d->group = QPalette::Active; + qApp->installEventFilter(this); +} + +QDeclarativeSystemPalette::~QDeclarativeSystemPalette() +{ +} + +/*! + \qmlproperty color SystemPalette::window + The window (general background) color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarativeSystemPalette::window() const +{ + Q_D(const QDeclarativeSystemPalette); + return d->palette.color(d->group, QPalette::Window); +} + +/*! + \qmlproperty color SystemPalette::windowText + The window text (general foreground) color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarativeSystemPalette::windowText() const +{ + Q_D(const QDeclarativeSystemPalette); + return d->palette.color(d->group, QPalette::WindowText); +} + +/*! + \qmlproperty color SystemPalette::base + The base color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarativeSystemPalette::base() const +{ + Q_D(const QDeclarativeSystemPalette); + return d->palette.color(d->group, QPalette::Base); +} + +/*! + \qmlproperty color SystemPalette::text + The text color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarativeSystemPalette::text() const +{ + Q_D(const QDeclarativeSystemPalette); + return d->palette.color(d->group, QPalette::Text); +} + +/*! + \qmlproperty color SystemPalette::alternateBase + The alternate base color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarativeSystemPalette::alternateBase() const +{ + Q_D(const QDeclarativeSystemPalette); + return d->palette.color(d->group, QPalette::AlternateBase); +} + +/*! + \qmlproperty color SystemPalette::button + The button color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarativeSystemPalette::button() const +{ + Q_D(const QDeclarativeSystemPalette); + return d->palette.color(d->group, QPalette::Button); +} + +/*! + \qmlproperty color SystemPalette::buttonText + The button text foreground color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarativeSystemPalette::buttonText() const +{ + Q_D(const QDeclarativeSystemPalette); + return d->palette.color(d->group, QPalette::ButtonText); +} + +/*! + \qmlproperty color SystemPalette::light + The light color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarativeSystemPalette::light() const +{ + Q_D(const QDeclarativeSystemPalette); + return d->palette.color(d->group, QPalette::Light); +} + +/*! + \qmlproperty color SystemPalette::midlight + The midlight color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarativeSystemPalette::midlight() const +{ + Q_D(const QDeclarativeSystemPalette); + return d->palette.color(d->group, QPalette::Midlight); +} + +/*! + \qmlproperty color SystemPalette::dark + The dark color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarativeSystemPalette::dark() const +{ + Q_D(const QDeclarativeSystemPalette); + return d->palette.color(d->group, QPalette::Dark); +} + +/*! + \qmlproperty color SystemPalette::mid + The mid color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarativeSystemPalette::mid() const +{ + Q_D(const QDeclarativeSystemPalette); + return d->palette.color(d->group, QPalette::Mid); +} + +/*! + \qmlproperty color SystemPalette::shadow + The shadow color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarativeSystemPalette::shadow() const +{ + Q_D(const QDeclarativeSystemPalette); + return d->palette.color(d->group, QPalette::Shadow); +} + +/*! + \qmlproperty color SystemPalette::highlight + The highlight color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarativeSystemPalette::highlight() const +{ + Q_D(const QDeclarativeSystemPalette); + return d->palette.color(d->group, QPalette::Highlight); +} + +/*! + \qmlproperty color SystemPalette::highlightedText + The highlighted text color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarativeSystemPalette::highlightedText() const +{ + Q_D(const QDeclarativeSystemPalette); + return d->palette.color(d->group, QPalette::HighlightedText); +} + +/*! + \qmlproperty enumeration SystemPalette::colorGroup + + The color group of the palette. This can be one of: + + \list + \o SystemPalette.Active (default) + \o SystemPalette.Inactive + \o SystemPalette.Disabled + \endlist + + \sa QPalette::ColorGroup +*/ +QDeclarativeSystemPalette::ColorGroup QDeclarativeSystemPalette::colorGroup() const +{ + Q_D(const QDeclarativeSystemPalette); + return (QDeclarativeSystemPalette::ColorGroup)d->group; +} + +void QDeclarativeSystemPalette::setColorGroup(QDeclarativeSystemPalette::ColorGroup colorGroup) +{ + Q_D(QDeclarativeSystemPalette); + d->group = (QPalette::ColorGroup)colorGroup; + emit paletteChanged(); +} + +bool QDeclarativeSystemPalette::eventFilter(QObject *watched, QEvent *event) +{ + if (watched == qApp) { + if (event->type() == QEvent::ApplicationPaletteChange) { + QApplication::postEvent(this, new QEvent(QEvent::ApplicationPaletteChange)); + return false; + } + } + return QObject::eventFilter(watched, event); +} + +bool QDeclarativeSystemPalette::event(QEvent *event) +{ + Q_D(QDeclarativeSystemPalette); + if (event->type() == QEvent::ApplicationPaletteChange) { + d->palette = QApplication::palette(); + emit paletteChanged(); + return true; + } + return QObject::event(event); +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativesystempalette_p.h b/src/declarative/util/qdeclarativesystempalette_p.h new file mode 100644 index 00000000..780479b0 --- /dev/null +++ b/src/declarative/util/qdeclarativesystempalette_p.h @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESYSTEMPALETTE_H +#define QDECLARATIVESYSTEMPALETTE_H + +#include + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeSystemPalettePrivate; +class Q_AUTOTEST_EXPORT QDeclarativeSystemPalette : public QObject +{ + Q_OBJECT + Q_ENUMS(ColorGroup) + Q_DECLARE_PRIVATE(QDeclarativeSystemPalette) + + Q_PROPERTY(QDeclarativeSystemPalette::ColorGroup colorGroup READ colorGroup WRITE setColorGroup NOTIFY paletteChanged) + Q_PROPERTY(QColor window READ window NOTIFY paletteChanged) + Q_PROPERTY(QColor windowText READ windowText NOTIFY paletteChanged) + Q_PROPERTY(QColor base READ base NOTIFY paletteChanged) + Q_PROPERTY(QColor text READ text NOTIFY paletteChanged) + Q_PROPERTY(QColor alternateBase READ alternateBase NOTIFY paletteChanged) + Q_PROPERTY(QColor button READ button NOTIFY paletteChanged) + Q_PROPERTY(QColor buttonText READ buttonText NOTIFY paletteChanged) + Q_PROPERTY(QColor light READ light NOTIFY paletteChanged) + Q_PROPERTY(QColor midlight READ midlight NOTIFY paletteChanged) + Q_PROPERTY(QColor dark READ dark NOTIFY paletteChanged) + Q_PROPERTY(QColor mid READ mid NOTIFY paletteChanged) + Q_PROPERTY(QColor shadow READ shadow NOTIFY paletteChanged) + Q_PROPERTY(QColor highlight READ highlight NOTIFY paletteChanged) + Q_PROPERTY(QColor highlightedText READ highlightedText NOTIFY paletteChanged) + +public: + QDeclarativeSystemPalette(QObject *parent=0); + ~QDeclarativeSystemPalette(); + + enum ColorGroup { Active = QPalette::Active, Inactive = QPalette::Inactive, Disabled = QPalette::Disabled }; + + QColor window() const; + QColor windowText() const; + + QColor base() const; + QColor text() const; + QColor alternateBase() const; + + QColor button() const; + QColor buttonText() const; + + QColor light() const; + QColor midlight() const; + QColor dark() const; + QColor mid() const; + QColor shadow() const; + + QColor highlight() const; + QColor highlightedText() const; + + QDeclarativeSystemPalette::ColorGroup colorGroup() const; + void setColorGroup(QDeclarativeSystemPalette::ColorGroup); + +Q_SIGNALS: + void paletteChanged(); + +private: + bool eventFilter(QObject *watched, QEvent *event); + bool event(QEvent *event); + +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeSystemPalette) + +QT_END_HEADER + +#endif // QDECLARATIVESYSTEMPALETTE_H diff --git a/src/declarative/util/qdeclarativetimeline.cpp b/src/declarative/util/qdeclarativetimeline.cpp new file mode 100644 index 00000000..0c802092 --- /dev/null +++ b/src/declarative/util/qdeclarativetimeline.cpp @@ -0,0 +1,947 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativetimeline_p_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +struct Update { + Update(QDeclarativeTimeLineValue *_g, qreal _v) + : g(_g), v(_v) {} + Update(const QDeclarativeTimeLineCallback &_e) + : g(0), v(0), e(_e) {} + + QDeclarativeTimeLineValue *g; + qreal v; + QDeclarativeTimeLineCallback e; +}; + +struct QDeclarativeTimeLinePrivate +{ + QDeclarativeTimeLinePrivate(QDeclarativeTimeLine *); + + struct Op { + enum Type { + Pause, + Set, + Move, + MoveBy, + Accel, + AccelDistance, + Execute + }; + Op() {} + Op(Type t, int l, qreal v, qreal v2, int o, + const QDeclarativeTimeLineCallback &ev = QDeclarativeTimeLineCallback(), const QEasingCurve &es = QEasingCurve()) + : type(t), length(l), value(v), value2(v2), order(o), event(ev), + easing(es) {} + Op(const Op &o) + : type(o.type), length(o.length), value(o.value), value2(o.value2), + order(o.order), event(o.event), easing(o.easing) {} + Op &operator=(const Op &o) { + type = o.type; length = o.length; value = o.value; + value2 = o.value2; order = o.order; event = o.event; + easing = o.easing; + return *this; + } + + Type type; + int length; + qreal value; + qreal value2; + + int order; + QDeclarativeTimeLineCallback event; + QEasingCurve easing; + }; + struct TimeLine + { + TimeLine() : length(0), consumedOpLength(0), base(0.) {} + QList ops; + int length; + int consumedOpLength; + qreal base; + }; + + int length; + int syncPoint; + typedef QHash Ops; + Ops ops; + QDeclarativeTimeLine *q; + + void add(QDeclarativeTimeLineObject &, const Op &); + qreal value(const Op &op, int time, qreal base, bool *) const; + + int advance(int); + + bool clockRunning; + int prevTime; + + int order; + + QDeclarativeTimeLine::SyncMode syncMode; + int syncAdj; + QList > *updateQueue; +}; + +QDeclarativeTimeLinePrivate::QDeclarativeTimeLinePrivate(QDeclarativeTimeLine *parent) +: length(0), syncPoint(0), q(parent), clockRunning(false), prevTime(0), order(0), syncMode(QDeclarativeTimeLine::LocalSync), syncAdj(0), updateQueue(0) +{ +} + +void QDeclarativeTimeLinePrivate::add(QDeclarativeTimeLineObject &g, const Op &o) +{ + if (g._t && g._t != q) { + qWarning() << "QDeclarativeTimeLine: Cannot modify a QDeclarativeTimeLineValue owned by" + << "another timeline."; + return; + } + g._t = q; + + Ops::Iterator iter = ops.find(&g); + if (iter == ops.end()) { + iter = ops.insert(&g, TimeLine()); + if (syncPoint > 0) + q->pause(g, syncPoint); + } + if (!iter->ops.isEmpty() && + o.type == Op::Pause && + iter->ops.last().type == Op::Pause) { + iter->ops.last().length += o.length; + iter->length += o.length; + } else { + iter->ops.append(o); + iter->length += o.length; + } + + if (iter->length > length) + length = iter->length; + + if (!clockRunning) { + q->stop(); + prevTime = 0; + clockRunning = true; + + if (syncMode == QDeclarativeTimeLine::LocalSync) { + syncAdj = -1; + } else { + syncAdj = 0; + } + q->start(); +/* q->tick(0); + if (syncMode == QDeclarativeTimeLine::LocalSync) { + syncAdj = -1; + } else { + syncAdj = 0; + } + */ + } +} + +qreal QDeclarativeTimeLinePrivate::value(const Op &op, int time, qreal base, bool *changed) const +{ + Q_ASSERT(time >= 0); + Q_ASSERT(time <= op.length); + *changed = true; + + switch(op.type) { + case Op::Pause: + *changed = false; + return base; + case Op::Set: + return op.value; + case Op::Move: + if (time == 0) { + return base; + } else if (time == (op.length)) { + return op.value; + } else { + qreal delta = op.value - base; + qreal pTime = (qreal)(time) / (qreal)op.length; + if (op.easing.type() == QEasingCurve::Linear) + return base + delta * pTime; + else + return base + delta * op.easing.valueForProgress(pTime); + } + case Op::MoveBy: + if (time == 0) { + return base; + } else if (time == (op.length)) { + return base + op.value; + } else { + qreal delta = op.value; + qreal pTime = (qreal)(time) / (qreal)op.length; + if (op.easing.type() == QEasingCurve::Linear) + return base + delta * pTime; + else + return base + delta * op.easing.valueForProgress(pTime); + } + case Op::Accel: + if (time == 0) { + return base; + } else { + qreal t = (qreal)(time) / 1000.0f; + qreal delta = op.value * t + 0.5f * op.value2 * t * t; + return base + delta; + } + case Op::AccelDistance: + if (time == 0) { + return base; + } else if (time == (op.length)) { + return base + op.value2; + } else { + qreal t = (qreal)(time) / 1000.0f; + qreal accel = -1.0f * 1000.0f * op.value / (qreal)op.length; + qreal delta = op.value * t + 0.5f * accel * t * t; + return base + delta; + + } + case Op::Execute: + op.event.d0(op.event.d1); + *changed = false; + return -1; + } + + return base; +} + +/*! + \internal + \class QDeclarativeTimeLine + \brief The QDeclarativeTimeLine class provides a timeline for controlling animations. + + QDeclarativeTimeLine is similar to QTimeLine except: + \list + \i It updates QDeclarativeTimeLineValue instances directly, rather than maintaining a single + current value. + + For example, the following animates a simple value over 200 milliseconds: + \code + QDeclarativeTimeLineValue v(); + QDeclarativeTimeLine tl; + tl.move(v, 100., 200); + tl.start() + \endcode + + If your program needs to know when values are changed, it can either + connect to the QDeclarativeTimeLine's updated() signal, or inherit from QDeclarativeTimeLineValue + and reimplement the QDeclarativeTimeLineValue::setValue() method. + + \i Supports multiple QDeclarativeTimeLineValue, arbitrary start and end values and allows + animations to be strung together for more complex effects. + + For example, the following animation moves the x and y coordinates of + an object from wherever they are to the position (100, 100) in 50 + milliseconds and then further animates them to (100, 200) in 50 + milliseconds: + + \code + QDeclarativeTimeLineValue x(); + QDeclarativeTimeLineValue y(); + + QDeclarativeTimeLine tl; + tl.start(); + + tl.move(x, 100., 50); + tl.move(y, 100., 50); + tl.move(y, 200., 50); + \endcode + + \i All QDeclarativeTimeLine instances share a single, synchronized clock. + + Actions scheduled within the same event loop tick are scheduled + synchronously against each other, regardless of the wall time between the + scheduling. Synchronized scheduling applies both to within the same + QDeclarativeTimeLine and across separate QDeclarativeTimeLine's within the same process. + + \endlist + + Currently easing functions are not supported. +*/ + + +/*! + Construct a new QDeclarativeTimeLine with the specified \a parent. +*/ +QDeclarativeTimeLine::QDeclarativeTimeLine(QObject *parent) +: QAbstractAnimation(parent) +{ + d = new QDeclarativeTimeLinePrivate(this); +} + +/*! + Destroys the time line. Any inprogress animations are canceled, but not + completed. +*/ +QDeclarativeTimeLine::~QDeclarativeTimeLine() +{ + for (QDeclarativeTimeLinePrivate::Ops::Iterator iter = d->ops.begin(); + iter != d->ops.end(); + ++iter) + iter.key()->_t = 0; + + delete d; d = 0; +} + +/*! + \enum QDeclarativeTimeLine::SyncMode + */ + +/*! + Return the timeline's synchronization mode. + */ +QDeclarativeTimeLine::SyncMode QDeclarativeTimeLine::syncMode() const +{ + return d->syncMode; +} + +/*! + Set the timeline's synchronization mode to \a syncMode. + */ +void QDeclarativeTimeLine::setSyncMode(SyncMode syncMode) +{ + d->syncMode = syncMode; +} + +/*! + Pause \a obj for \a time milliseconds. +*/ +void QDeclarativeTimeLine::pause(QDeclarativeTimeLineObject &obj, int time) +{ + if (time <= 0) return; + QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::Pause, time, 0., 0., d->order++); + d->add(obj, op); +} + +/*! + Execute the \a event. + */ +void QDeclarativeTimeLine::callback(const QDeclarativeTimeLineCallback &callback) +{ + QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::Execute, 0, 0, 0., d->order++, callback); + d->add(*callback.callbackObject(), op); +} + +/*! + Set the \a value of \a timeLineValue. +*/ +void QDeclarativeTimeLine::set(QDeclarativeTimeLineValue &timeLineValue, qreal value) +{ + QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::Set, 0, value, 0., d->order++); + d->add(timeLineValue, op); +} + +/*! + Decelerate \a timeLineValue from the starting \a velocity to zero at the + given \a acceleration rate. Although the \a acceleration is technically + a deceleration, it should always be positive. The QDeclarativeTimeLine will ensure + that the deceleration is in the opposite direction to the initial velocity. +*/ +int QDeclarativeTimeLine::accel(QDeclarativeTimeLineValue &timeLineValue, qreal velocity, qreal acceleration) +{ + if (acceleration == 0.0f) + return -1; + + if ((velocity > 0.0f) == (acceleration > 0.0f)) + acceleration = acceleration * -1.0f; + + int time = static_cast(-1000 * velocity / acceleration); + + QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::Accel, time, velocity, acceleration, d->order++); + d->add(timeLineValue, op); + + return time; +} + +/*! + \overload + + Decelerate \a timeLineValue from the starting \a velocity to zero at the + given \a acceleration rate over a maximum distance of maxDistance. + + If necessary, QDeclarativeTimeLine will reduce the acceleration to ensure that the + entire operation does not require a move of more than \a maxDistance. + \a maxDistance should always be positive. +*/ +int QDeclarativeTimeLine::accel(QDeclarativeTimeLineValue &timeLineValue, qreal velocity, qreal acceleration, qreal maxDistance) +{ + if (maxDistance == 0.0f || acceleration == 0.0f) + return -1; + + Q_ASSERT(acceleration > 0.0f && maxDistance > 0.0f); + + qreal maxAccel = (velocity * velocity) / (2.0f * maxDistance); + if (maxAccel > acceleration) + acceleration = maxAccel; + + if ((velocity > 0.0f) == (acceleration > 0.0f)) + acceleration = acceleration * -1.0f; + + int time = static_cast(-1000 * velocity / acceleration); + + QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::Accel, time, velocity, acceleration, d->order++); + d->add(timeLineValue, op); + + return time; +} + +/*! + Decelerate \a timeLineValue from the starting \a velocity to zero over the given + \a distance. This is like accel(), but the QDeclarativeTimeLine calculates the exact + deceleration to use. + + \a distance should be positive. +*/ +int QDeclarativeTimeLine::accelDistance(QDeclarativeTimeLineValue &timeLineValue, qreal velocity, qreal distance) +{ + if (distance == 0.0f || velocity == 0.0f) + return -1; + + Q_ASSERT((distance >= 0.0f) == (velocity >= 0.0f)); + + int time = static_cast(1000 * (2.0f * distance) / velocity); + + QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::AccelDistance, time, velocity, distance, d->order++); + d->add(timeLineValue, op); + + return time; +} + +/*! + Linearly change the \a timeLineValue from its current value to the given + \a destination value over \a time milliseconds. +*/ +void QDeclarativeTimeLine::move(QDeclarativeTimeLineValue &timeLineValue, qreal destination, int time) +{ + if (time <= 0) return; + QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::Move, time, destination, 0.0f, d->order++); + d->add(timeLineValue, op); +} + +/*! + Change the \a timeLineValue from its current value to the given \a destination + value over \a time milliseconds using the \a easing curve. + */ +void QDeclarativeTimeLine::move(QDeclarativeTimeLineValue &timeLineValue, qreal destination, const QEasingCurve &easing, int time) +{ + if (time <= 0) return; + QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::Move, time, destination, 0.0f, d->order++, QDeclarativeTimeLineCallback(), easing); + d->add(timeLineValue, op); +} + +/*! + Linearly change the \a timeLineValue from its current value by the \a change amount + over \a time milliseconds. +*/ +void QDeclarativeTimeLine::moveBy(QDeclarativeTimeLineValue &timeLineValue, qreal change, int time) +{ + if (time <= 0) return; + QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::MoveBy, time, change, 0.0f, d->order++); + d->add(timeLineValue, op); +} + +/*! + Change the \a timeLineValue from its current value by the \a change amount over + \a time milliseconds using the \a easing curve. + */ +void QDeclarativeTimeLine::moveBy(QDeclarativeTimeLineValue &timeLineValue, qreal change, const QEasingCurve &easing, int time) +{ + if (time <= 0) return; + QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::MoveBy, time, change, 0.0f, d->order++, QDeclarativeTimeLineCallback(), easing); + d->add(timeLineValue, op); +} + +/*! + Cancel (but don't complete) all scheduled actions for \a timeLineValue. +*/ +void QDeclarativeTimeLine::reset(QDeclarativeTimeLineValue &timeLineValue) +{ + if (!timeLineValue._t) + return; + if (timeLineValue._t != this) { + qWarning() << "QDeclarativeTimeLine: Cannot reset a QDeclarativeTimeLineValue owned by another timeline."; + return; + } + remove(&timeLineValue); + timeLineValue._t = 0; +} + +int QDeclarativeTimeLine::duration() const +{ + return -1; +} + +/*! + Synchronize the end point of \a timeLineValue to the endpoint of \a syncTo + within this timeline. + + Following operations on \a timeLineValue in this timeline will be scheduled after + all the currently scheduled actions on \a syncTo are complete. In + pseudo-code this is equivalent to: + \code + QDeclarativeTimeLine::pause(timeLineValue, min(0, length_of(syncTo) - length_of(timeLineValue))) + \endcode +*/ +void QDeclarativeTimeLine::sync(QDeclarativeTimeLineValue &timeLineValue, QDeclarativeTimeLineValue &syncTo) +{ + QDeclarativeTimeLinePrivate::Ops::Iterator iter = d->ops.find(&syncTo); + if (iter == d->ops.end()) + return; + int length = iter->length; + + iter = d->ops.find(&timeLineValue); + if (iter == d->ops.end()) { + pause(timeLineValue, length); + } else { + int glength = iter->length; + pause(timeLineValue, length - glength); + } +} + +/*! + Synchronize the end point of \a timeLineValue to the endpoint of the longest + action cursrently scheduled in the timeline. + + In pseudo-code, this is equivalent to: + \code + QDeclarativeTimeLine::pause(timeLineValue, length_of(timeline) - length_of(timeLineValue)) + \endcode +*/ +void QDeclarativeTimeLine::sync(QDeclarativeTimeLineValue &timeLineValue) +{ + QDeclarativeTimeLinePrivate::Ops::Iterator iter = d->ops.find(&timeLineValue); + if (iter == d->ops.end()) { + pause(timeLineValue, d->length); + } else { + pause(timeLineValue, d->length - iter->length); + } +} + +/* + Synchronize all currently and future scheduled values in this timeline to + the longest action currently scheduled. + + For example: + \code + value1->setValue(0.); + value2->setValue(0.); + value3->setValue(0.); + QDeclarativeTimeLine tl; + ... + tl.move(value1, 10, 200); + tl.move(value2, 10, 100); + tl.sync(); + tl.move(value2, 20, 100); + tl.move(value3, 20, 100); + \endcode + + will result in: + + \table + \header \o \o 0ms \o 50ms \o 100ms \o 150ms \o 200ms \o 250ms \o 300ms + \row \o value1 \o 0 \o 2.5 \o 5.0 \o 7.5 \o 10 \o 10 \o 10 + \row \o value2 \o 0 \o 5.0 \o 10.0 \o 10.0 \o 10.0 \o 15.0 \o 20.0 + \row \o value2 \o 0 \o 0 \o 0 \o 0 \o 0 \o 10.0 \o 20.0 + \endtable +*/ + +/*void QDeclarativeTimeLine::sync() +{ + for (QDeclarativeTimeLinePrivate::Ops::Iterator iter = d->ops.begin(); + iter != d->ops.end(); + ++iter) + pause(*iter.key(), d->length - iter->length); + d->syncPoint = d->length; +}*/ + +/*! + \internal + + Temporary hack. + */ +void QDeclarativeTimeLine::setSyncPoint(int sp) +{ + d->syncPoint = sp; +} + +/*! + \internal + + Temporary hack. + */ +int QDeclarativeTimeLine::syncPoint() const +{ + return d->syncPoint; +} + +/*! + Returns true if the timeline is active. An active timeline is one where + QDeclarativeTimeLineValue actions are still pending. +*/ +bool QDeclarativeTimeLine::isActive() const +{ + return !d->ops.isEmpty(); +} + +/*! + Completes the timeline. All queued actions are played to completion, and then discarded. For example, + \code + QDeclarativeTimeLineValue v(0.); + QDeclarativeTimeLine tl; + tl.move(v, 100., 1000.); + // 500 ms passes + // v.value() == 50. + tl.complete(); + // v.value() == 100. + \endcode +*/ +void QDeclarativeTimeLine::complete() +{ + d->advance(d->length); +} + +/*! + Resets the timeline. All queued actions are discarded and QDeclarativeTimeLineValue's retain their current value. For example, + \code + QDeclarativeTimeLineValue v(0.); + QDeclarativeTimeLine tl; + tl.move(v, 100., 1000.); + // 500 ms passes + // v.value() == 50. + tl.clear(); + // v.value() == 50. + \endcode +*/ +void QDeclarativeTimeLine::clear() +{ + for (QDeclarativeTimeLinePrivate::Ops::ConstIterator iter = d->ops.begin(); iter != d->ops.end(); ++iter) + iter.key()->_t = 0; + d->ops.clear(); + d->length = 0; + d->syncPoint = 0; + //XXX need stop here? +} + +int QDeclarativeTimeLine::time() const +{ + return d->prevTime; +} + +/*! + \fn void QDeclarativeTimeLine::updated() + + Emitted each time the timeline modifies QDeclarativeTimeLineValues. Even if multiple + QDeclarativeTimeLineValues are changed, this signal is only emitted once for each clock tick. +*/ + +void QDeclarativeTimeLine::updateCurrentTime(int v) +{ + if (d->syncAdj == -1) + d->syncAdj = v; + v -= d->syncAdj; + + int timeChanged = v - d->prevTime; +#if 0 + if (!timeChanged) + return; +#endif + d->prevTime = v; + d->advance(timeChanged); + emit updated(); + + // Do we need to stop the clock? + if (d->ops.isEmpty()) { + stop(); + d->prevTime = 0; + d->clockRunning = false; + emit completed(); + } /*else if (pauseTime > 0) { + GfxClock::cancelClock(); + d->prevTime = 0; + GfxClock::pauseFor(pauseTime); + d->syncAdj = 0; + d->clockRunning = false; + }*/ else if (/*!GfxClock::isActive()*/ state() != Running) { + stop(); + d->prevTime = 0; + d->clockRunning = true; + d->syncAdj = 0; + start(); + } +} + +bool operator<(const QPair &lhs, + const QPair &rhs) +{ + return lhs.first < rhs.first; +} + +int QDeclarativeTimeLinePrivate::advance(int t) +{ + int pauseTime = -1; + + // XXX - surely there is a more efficient way? + do { + pauseTime = -1; + // Minimal advance time + int advanceTime = t; + for (Ops::Iterator iter = ops.begin(); iter != ops.end(); ++iter) { + TimeLine &tl = *iter; + Op &op = tl.ops.first(); + int length = op.length - tl.consumedOpLength; + + if (length < advanceTime) { + advanceTime = length; + if (advanceTime == 0) + break; + } + } + t -= advanceTime; + + // Process until then. A zero length advance time will only process + // sets. + QList > updates; + + for (Ops::Iterator iter = ops.begin(); iter != ops.end(); ) { + QDeclarativeTimeLineValue *v = static_cast(iter.key()); + TimeLine &tl = *iter; + Q_ASSERT(!tl.ops.isEmpty()); + + do { + Op &op = tl.ops.first(); + if (advanceTime == 0 && op.length != 0) + continue; + + if (tl.consumedOpLength == 0 && + op.type != Op::Pause && + op.type != Op::Execute) + tl.base = v->value(); + + if ((tl.consumedOpLength + advanceTime) == op.length) { + if (op.type == Op::Execute) { + updates << qMakePair(op.order, Update(op.event)); + } else { + bool changed = false; + qreal val = value(op, op.length, tl.base, &changed); + if (changed) + updates << qMakePair(op.order, Update(v, val)); + } + tl.length -= qMin(advanceTime, tl.length); + tl.consumedOpLength = 0; + tl.ops.removeFirst(); + } else { + tl.consumedOpLength += advanceTime; + bool changed = false; + qreal val = value(op, tl.consumedOpLength, tl.base, &changed); + if (changed) + updates << qMakePair(op.order, Update(v, val)); + tl.length -= qMin(advanceTime, tl.length); + break; + } + + } while(!tl.ops.isEmpty() && advanceTime == 0 && tl.ops.first().length == 0); + + + if (tl.ops.isEmpty()) { + iter = ops.erase(iter); + v->_t = 0; + } else { + if (tl.ops.first().type == Op::Pause && pauseTime != 0) { + int opPauseTime = tl.ops.first().length - tl.consumedOpLength; + if (pauseTime == -1 || opPauseTime < pauseTime) + pauseTime = opPauseTime; + } else { + pauseTime = 0; + } + ++iter; + } + } + + length -= qMin(length, advanceTime); + syncPoint -= advanceTime; + + qSort(updates.begin(), updates.end()); + updateQueue = &updates; + for (int ii = 0; ii < updates.count(); ++ii) { + const Update &v = updates.at(ii).second; + if (v.g) { + v.g->setValue(v.v); + } else { + v.e.d0(v.e.d1); + } + } + updateQueue = 0; + } while(t); + + return pauseTime; +} + +void QDeclarativeTimeLine::remove(QDeclarativeTimeLineObject *v) +{ + QDeclarativeTimeLinePrivate::Ops::Iterator iter = d->ops.find(v); + Q_ASSERT(iter != d->ops.end()); + + int len = iter->length; + d->ops.erase(iter); + if (len == d->length) { + // We need to recalculate the length + d->length = 0; + for (QDeclarativeTimeLinePrivate::Ops::Iterator iter = d->ops.begin(); + iter != d->ops.end(); + ++iter) { + + if (iter->length > d->length) + d->length = iter->length; + + } + } + if (d->ops.isEmpty()) { + stop(); + d->clockRunning = false; + } else if (/*!GfxClock::isActive()*/ state() != Running) { + stop(); + d->prevTime = 0; + d->clockRunning = true; + + if (d->syncMode == QDeclarativeTimeLine::LocalSync) { + d->syncAdj = -1; + } else { + d->syncAdj = 0; + } + start(); + } + + if (d->updateQueue) { + for (int ii = 0; ii < d->updateQueue->count(); ++ii) { + if (d->updateQueue->at(ii).second.g == v || + d->updateQueue->at(ii).second.e.callbackObject() == v) { + d->updateQueue->removeAt(ii); + --ii; + } + } + } + + +} + +/*! + \internal + \class QDeclarativeTimeLineValue + \brief The QDeclarativeTimeLineValue class provides a value that can be modified by QDeclarativeTimeLine. +*/ + +/*! + \fn QDeclarativeTimeLineValue::QDeclarativeTimeLineValue(qreal value = 0) + + Construct a new QDeclarativeTimeLineValue with an initial \a value. +*/ + +/*! + \fn qreal QDeclarativeTimeLineValue::value() const + + Return the current value. +*/ + +/*! + \fn void QDeclarativeTimeLineValue::setValue(qreal value) + + Set the current \a value. +*/ + +/*! + \fn QDeclarativeTimeLine *QDeclarativeTimeLineValue::timeLine() const + + If a QDeclarativeTimeLine is operating on this value, return a pointer to it, + otherwise return null. +*/ + + +QDeclarativeTimeLineObject::QDeclarativeTimeLineObject() +: _t(0) +{ +} + +QDeclarativeTimeLineObject::~QDeclarativeTimeLineObject() +{ + if (_t) { + _t->remove(this); + _t = 0; + } +} + +QDeclarativeTimeLineCallback::QDeclarativeTimeLineCallback() +: d0(0), d1(0), d2(0) +{ +} + +QDeclarativeTimeLineCallback::QDeclarativeTimeLineCallback(QDeclarativeTimeLineObject *b, Callback f, void *d) +: d0(f), d1(d), d2(b) +{ +} + +QDeclarativeTimeLineCallback::QDeclarativeTimeLineCallback(const QDeclarativeTimeLineCallback &o) +: d0(o.d0), d1(o.d1), d2(o.d2) +{ +} + +QDeclarativeTimeLineCallback &QDeclarativeTimeLineCallback::operator=(const QDeclarativeTimeLineCallback &o) +{ + d0 = o.d0; + d1 = o.d1; + d2 = o.d2; + return *this; +} + +QDeclarativeTimeLineObject *QDeclarativeTimeLineCallback::callbackObject() const +{ + return d2; +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativetimeline_p_p.h b/src/declarative/util/qdeclarativetimeline_p_p.h new file mode 100644 index 00000000..aa5ac211 --- /dev/null +++ b/src/declarative/util/qdeclarativetimeline_p_p.h @@ -0,0 +1,200 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVETIMELINE_H +#define QDECLARATIVETIMELINE_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 +#include + +QT_BEGIN_NAMESPACE + +class QEasingCurve; +class QDeclarativeTimeLineValue; +class QDeclarativeTimeLineCallback; +struct QDeclarativeTimeLinePrivate; +class QDeclarativeTimeLineObject; +class Q_AUTOTEST_EXPORT QDeclarativeTimeLine : public QAbstractAnimation +{ +Q_OBJECT +public: + QDeclarativeTimeLine(QObject *parent = 0); + ~QDeclarativeTimeLine(); + + enum SyncMode { LocalSync, GlobalSync }; + SyncMode syncMode() const; + void setSyncMode(SyncMode); + + void pause(QDeclarativeTimeLineObject &, int); + void callback(const QDeclarativeTimeLineCallback &); + void set(QDeclarativeTimeLineValue &, qreal); + + int accel(QDeclarativeTimeLineValue &, qreal velocity, qreal accel); + int accel(QDeclarativeTimeLineValue &, qreal velocity, qreal accel, qreal maxDistance); + int accelDistance(QDeclarativeTimeLineValue &, qreal velocity, qreal distance); + + void move(QDeclarativeTimeLineValue &, qreal destination, int time = 500); + void move(QDeclarativeTimeLineValue &, qreal destination, const QEasingCurve &, int time = 500); + void moveBy(QDeclarativeTimeLineValue &, qreal change, int time = 500); + void moveBy(QDeclarativeTimeLineValue &, qreal change, const QEasingCurve &, int time = 500); + + void sync(); + void setSyncPoint(int); + int syncPoint() const; + + void sync(QDeclarativeTimeLineValue &); + void sync(QDeclarativeTimeLineValue &, QDeclarativeTimeLineValue &); + + void reset(QDeclarativeTimeLineValue &); + + void complete(); + void clear(); + bool isActive() const; + + int time() const; + + virtual int duration() const; +Q_SIGNALS: + void updated(); + void completed(); + +protected: + virtual void updateCurrentTime(int); + +private: + void remove(QDeclarativeTimeLineObject *); + friend class QDeclarativeTimeLineObject; + friend struct QDeclarativeTimeLinePrivate; + QDeclarativeTimeLinePrivate *d; +}; + +class Q_AUTOTEST_EXPORT QDeclarativeTimeLineObject +{ +public: + QDeclarativeTimeLineObject(); + virtual ~QDeclarativeTimeLineObject(); + +protected: + friend class QDeclarativeTimeLine; + friend struct QDeclarativeTimeLinePrivate; + QDeclarativeTimeLine *_t; +}; + +class Q_AUTOTEST_EXPORT QDeclarativeTimeLineValue : public QDeclarativeTimeLineObject +{ +public: + QDeclarativeTimeLineValue(qreal v = 0.) : _v(v) {} + + virtual qreal value() const { return _v; } + virtual void setValue(qreal v) { _v = v; } + + QDeclarativeTimeLine *timeLine() const { return _t; } + + operator qreal() const { return _v; } + QDeclarativeTimeLineValue &operator=(qreal v) { setValue(v); return *this; } +private: + friend class QDeclarativeTimeLine; + friend struct QDeclarativeTimeLinePrivate; + qreal _v; +}; + +class Q_AUTOTEST_EXPORT QDeclarativeTimeLineCallback +{ +public: + typedef void (*Callback)(void *); + + QDeclarativeTimeLineCallback(); + QDeclarativeTimeLineCallback(QDeclarativeTimeLineObject *b, Callback, void * = 0); + QDeclarativeTimeLineCallback(const QDeclarativeTimeLineCallback &o); + + QDeclarativeTimeLineCallback &operator=(const QDeclarativeTimeLineCallback &o); + QDeclarativeTimeLineObject *callbackObject() const; + +private: + friend struct QDeclarativeTimeLinePrivate; + Callback d0; + void *d1; + QDeclarativeTimeLineObject *d2; +}; + +template +class QDeclarativeTimeLineValueProxy : public QDeclarativeTimeLineValue +{ +public: + QDeclarativeTimeLineValueProxy(T *cls, void (T::*func)(qreal), qreal v = 0.) + : QDeclarativeTimeLineValue(v), _class(cls), _setFunctionReal(func), _setFunctionInt(0) + { + Q_ASSERT(_class); + } + + QDeclarativeTimeLineValueProxy(T *cls, void (T::*func)(int), qreal v = 0.) + : QDeclarativeTimeLineValue(v), _class(cls), _setFunctionReal(0), _setFunctionInt(func) + { + Q_ASSERT(_class); + } + + virtual void setValue(qreal v) + { + QDeclarativeTimeLineValue::setValue(v); + if (_setFunctionReal) (_class->*_setFunctionReal)(v); + else if (_setFunctionInt) (_class->*_setFunctionInt)((int)v); + } + +private: + T *_class; + void (T::*_setFunctionReal)(qreal); + void (T::*_setFunctionInt)(int); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/declarative/util/qdeclarativetimer.cpp b/src/declarative/util/qdeclarativetimer.cpp new file mode 100644 index 00000000..8250809c --- /dev/null +++ b/src/declarative/util/qdeclarativetimer.cpp @@ -0,0 +1,324 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativetimer_p.h" + +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + + + +class QDeclarativeTimerPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeTimer) +public: + QDeclarativeTimerPrivate() + : interval(1000), running(false), repeating(false), triggeredOnStart(false) + , classBegun(false), componentComplete(false), firstTick(true) {} + int interval; + QPauseAnimation pause; + bool running : 1; + bool repeating : 1; + bool triggeredOnStart : 1; + bool classBegun : 1; + bool componentComplete : 1; + bool firstTick : 1; +}; + +/*! + \qmlclass Timer QDeclarativeTimer + \ingroup qml-utility-elements + \since 4.7 + \brief The Timer item triggers a handler at a specified interval. + + A Timer can be used to trigger an action either once, or repeatedly + at a given interval. + + Here is a Timer that shows the current date and time, and updates + the text every 500 milliseconds. It uses the JavaScript \c Date + object to access the current time. + + \qml + import QtQuick 1.0 + + Item { + Timer { + interval: 500; running: true; repeat: true + onTriggered: time.text = Date().toString() + } + + Text { id: time } + } + \endqml + + The Timer element is synchronized with the animation timer. Since the animation + timer is usually set to 60fps, the resolution of Timer will be + at best 16ms. + + If the Timer is running and one of its properties is changed, the + elapsed time will be reset. For example, if a Timer with interval of + 1000ms has its \e repeat property changed 500ms after starting, the + elapsed time will be reset to 0, and the Timer will be triggered + 1000ms later. + + \sa {declarative/toys/clocks}{Clocks example} +*/ + +QDeclarativeTimer::QDeclarativeTimer(QObject *parent) + : QObject(*(new QDeclarativeTimerPrivate), parent) +{ + Q_D(QDeclarativeTimer); + connect(&d->pause, SIGNAL(currentLoopChanged(int)), this, SLOT(ticked())); + connect(&d->pause, SIGNAL(finished()), this, SLOT(finished())); + d->pause.setLoopCount(1); + d->pause.setDuration(d->interval); +} + +/*! + \qmlproperty int Timer::interval + + Sets the \a interval between triggers, in milliseconds. + + The default interval is 1000 milliseconds. +*/ +void QDeclarativeTimer::setInterval(int interval) +{ + Q_D(QDeclarativeTimer); + if (interval != d->interval) { + d->interval = interval; + update(); + emit intervalChanged(); + } +} + +int QDeclarativeTimer::interval() const +{ + Q_D(const QDeclarativeTimer); + return d->interval; +} + +/*! + \qmlproperty bool Timer::running + + If set to true, starts the timer; otherwise stops the timer. + For a non-repeating timer, \a running is set to false after the + timer has been triggered. + + \a running defaults to false. + + \sa repeat +*/ +bool QDeclarativeTimer::isRunning() const +{ + Q_D(const QDeclarativeTimer); + return d->running; +} + +void QDeclarativeTimer::setRunning(bool running) +{ + Q_D(QDeclarativeTimer); + if (d->running != running) { + d->running = running; + d->firstTick = true; + emit runningChanged(); + update(); + } +} + +/*! + \qmlproperty bool Timer::repeat + + If \a repeat is true the timer is triggered repeatedly at the + specified interval; otherwise, the timer will trigger once at the + specified interval and then stop (i.e. running will be set to false). + + \a repeat defaults to false. + + \sa running +*/ +bool QDeclarativeTimer::isRepeating() const +{ + Q_D(const QDeclarativeTimer); + return d->repeating; +} + +void QDeclarativeTimer::setRepeating(bool repeating) +{ + Q_D(QDeclarativeTimer); + if (repeating != d->repeating) { + d->repeating = repeating; + update(); + emit repeatChanged(); + } +} + +/*! + \qmlproperty bool Timer::triggeredOnStart + + When a timer is started, the first trigger is usually after the specified + interval has elapsed. It is sometimes desirable to trigger immediately + when the timer is started; for example, to establish an initial + state. + + If \a triggeredOnStart is true, the timer is triggered immediately + when started, and subsequently at the specified interval. Note that if + \e repeat is set to false, the timer is triggered twice; once on start, + and again at the interval. + + \a triggeredOnStart defaults to false. + + \sa running +*/ +bool QDeclarativeTimer::triggeredOnStart() const +{ + Q_D(const QDeclarativeTimer); + return d->triggeredOnStart; +} + +void QDeclarativeTimer::setTriggeredOnStart(bool triggeredOnStart) +{ + Q_D(QDeclarativeTimer); + if (d->triggeredOnStart != triggeredOnStart) { + d->triggeredOnStart = triggeredOnStart; + update(); + emit triggeredOnStartChanged(); + } +} + +/*! + \qmlmethod Timer::start() + \brief Starts the timer. + + If the timer is already running, calling this method has no effect. The + \c running property will be true following a call to \c start(). +*/ +void QDeclarativeTimer::start() +{ + setRunning(true); +} + +/*! + \qmlmethod Timer::stop() + \brief Stops the timer. + + If the timer is not running, calling this method has no effect. The + \c running property will be false following a call to \c stop(). +*/ +void QDeclarativeTimer::stop() +{ + setRunning(false); +} + +/*! + \qmlmethod Timer::restart() + \brief Restarts the timer. + + If the Timer is not running it will be started, otherwise it will be + stopped, reset to initial state and started. The \c running property + will be true following a call to \c restart(). +*/ +void QDeclarativeTimer::restart() +{ + setRunning(false); + setRunning(true); +} + +void QDeclarativeTimer::update() +{ + Q_D(QDeclarativeTimer); + if (d->classBegun && !d->componentComplete) + return; + d->pause.stop(); + if (d->running) { + d->pause.setCurrentTime(0); + d->pause.setLoopCount(d->repeating ? -1 : 1); + d->pause.setDuration(d->interval); + d->pause.start(); + if (d->triggeredOnStart && d->firstTick) { + QCoreApplication::removePostedEvents(this, QEvent::MetaCall); + QMetaObject::invokeMethod(this, "ticked", Qt::QueuedConnection); + } + } +} + +void QDeclarativeTimer::classBegin() +{ + Q_D(QDeclarativeTimer); + d->classBegun = true; +} + +void QDeclarativeTimer::componentComplete() +{ + Q_D(QDeclarativeTimer); + d->componentComplete = true; + update(); +} + +/*! + \qmlsignal Timer::onTriggered() + + This handler is called when the Timer is triggered. +*/ +void QDeclarativeTimer::ticked() +{ + Q_D(QDeclarativeTimer); + if (d->running && (d->pause.currentTime() > 0 || (d->triggeredOnStart && d->firstTick))) + emit triggered(); + d->firstTick = false; +} + +void QDeclarativeTimer::finished() +{ + Q_D(QDeclarativeTimer); + if (d->repeating || !d->running) + return; + emit triggered(); + d->running = false; + d->firstTick = false; + emit runningChanged(); +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativetimer_p.h b/src/declarative/util/qdeclarativetimer_p.h new file mode 100644 index 00000000..f7b30a85 --- /dev/null +++ b/src/declarative/util/qdeclarativetimer_p.h @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVETIMER_H +#define QDECLARATIVETIMER_H + +#include + +#include +#include + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeTimerPrivate; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeTimer : public QObject, public QDeclarativeParserStatus +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeTimer) + Q_INTERFACES(QDeclarativeParserStatus) + Q_PROPERTY(int interval READ interval WRITE setInterval NOTIFY intervalChanged) + Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged) + Q_PROPERTY(bool repeat READ isRepeating WRITE setRepeating NOTIFY repeatChanged) + Q_PROPERTY(bool triggeredOnStart READ triggeredOnStart WRITE setTriggeredOnStart NOTIFY triggeredOnStartChanged) + Q_PROPERTY(QObject *parent READ parent CONSTANT) + +public: + QDeclarativeTimer(QObject *parent=0); + + void setInterval(int interval); + int interval() const; + + bool isRunning() const; + void setRunning(bool running); + + bool isRepeating() const; + void setRepeating(bool repeating); + + bool triggeredOnStart() const; + void setTriggeredOnStart(bool triggeredOnStart); + +protected: + void classBegin(); + void componentComplete(); + +public Q_SLOTS: + void start(); + void stop(); + void restart(); + +Q_SIGNALS: + void triggered(); + void runningChanged(); + void intervalChanged(); + void repeatChanged(); + void triggeredOnStartChanged(); + +private: + void update(); + +private Q_SLOTS: + void ticked(); + void finished(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeTimer) + +QT_END_HEADER + +#endif diff --git a/src/declarative/util/qdeclarativetransition.cpp b/src/declarative/util/qdeclarativetransition.cpp new file mode 100644 index 00000000..c3003087 --- /dev/null +++ b/src/declarative/util/qdeclarativetransition.cpp @@ -0,0 +1,345 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativestate_p.h" +#include "private/qdeclarativestategroup_p.h" +#include "private/qdeclarativestate_p_p.h" +#include "private/qdeclarativestateoperations_p.h" +#include "private/qdeclarativeanimation_p.h" +#include "private/qdeclarativeanimation_p_p.h" +#include "private/qdeclarativetransitionmanager_p_p.h" + +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmlclass Transition QDeclarativeTransition + \ingroup qml-animation-transition + \since 4.7 + \brief The Transition element defines animated transitions that occur on state changes. + + A Transition defines the animations to be applied when a \l State change occurs. + + For example, the following \l Rectangle has two states: the default state, and + an added "moved" state. In the "moved state, the rectangle's position changes + to (50, 50). The added Transition specifies that when the rectangle + changes between the default and the "moved" state, any changes + to the \c x and \c y properties should be animated, using an \c Easing.InOutQuad. + + \snippet doc/src/snippets/declarative/transition.qml 0 + + Notice the example does not require \l{PropertyAnimation::}{to} and + \l{PropertyAnimation::}{from} values for the NumberAnimation. As a convenience, + these properties are automatically set to the values of \c x and \c y before + and after the state change; the \c from values are provided by + the current values of \c x and \c y, and the \c to values are provided by + the PropertyChanges object. If you wish, you can provide \l{PropertyAnimation::}{to} and + \l{PropertyAnimation::}{from} values anyway to override the default values. + + By default, a Transition's animations are applied for any state change in the + parent item. The Transition \l {Transition::}{from} and \l {Transition::}{to} + values can be set to restrict the animations to only be applied when changing + from one particular state to another. + + To define multiple transitions, specify \l Item::transitions as a list: + + \snippet doc/src/snippets/declarative/transitions-list.qml list of transitions + + If multiple Transitions are specified, only a single (best-matching) Transition will be applied for any particular + state change. In the example above, when changing to \c state1, the first transition will be used, rather + than the more generic second transition. + + If a state change has a Transition that matches the same property as a + \l Behavior, the Transition animation overrides the \l Behavior for that + state change. + + \sa {QML Animation and Transitions}, {declarative/animation/states}{states example}, {qmlstates}{States}, {QtDeclarative} +*/ + +//ParallelAnimationWrapper allows us to do a "callback" when the animation finishes, rather than connecting +//and disconnecting signals and slots frequently +class ParallelAnimationWrapper : public QParallelAnimationGroup +{ + Q_OBJECT +public: + ParallelAnimationWrapper(QObject *parent = 0) : QParallelAnimationGroup(parent) {} + QDeclarativeTransitionPrivate *trans; +protected: + virtual void updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState); +}; + +class QDeclarativeTransitionPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeTransition) +public: + QDeclarativeTransitionPrivate() + : fromState(QLatin1String("*")), toState(QLatin1String("*")), + reversed(false), reversible(false), endState(0) + { + group.trans = this; + } + + QString fromState; + QString toState; + bool reversed; + bool reversible; + ParallelAnimationWrapper group; + QDeclarativeTransitionManager *endState; + + void complete() + { + endState->complete(); + } + static void append_animation(QDeclarativeListProperty *list, QDeclarativeAbstractAnimation *a); + static int animation_count(QDeclarativeListProperty *list); + static QDeclarativeAbstractAnimation* animation_at(QDeclarativeListProperty *list, int pos); + static void clear_animations(QDeclarativeListProperty *list); + QList animations; +}; + +void QDeclarativeTransitionPrivate::append_animation(QDeclarativeListProperty *list, QDeclarativeAbstractAnimation *a) +{ + QDeclarativeTransition *q = static_cast(list->object); + q->d_func()->animations.append(a); + q->d_func()->group.addAnimation(a->qtAnimation()); + a->setDisableUserControl(); +} + +int QDeclarativeTransitionPrivate::animation_count(QDeclarativeListProperty *list) +{ + QDeclarativeTransition *q = static_cast(list->object); + return q->d_func()->animations.count(); +} + +QDeclarativeAbstractAnimation* QDeclarativeTransitionPrivate::animation_at(QDeclarativeListProperty *list, int pos) +{ + QDeclarativeTransition *q = static_cast(list->object); + return q->d_func()->animations.at(pos); +} + +void QDeclarativeTransitionPrivate::clear_animations(QDeclarativeListProperty *list) +{ + QDeclarativeTransition *q = static_cast(list->object); + while (q->d_func()->animations.count()) { + QDeclarativeAbstractAnimation *firstAnim = q->d_func()->animations.at(0); + q->d_func()->group.removeAnimation(firstAnim->qtAnimation()); + q->d_func()->animations.removeAll(firstAnim); + } +} + +void ParallelAnimationWrapper::updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState) +{ + QParallelAnimationGroup::updateState(newState, oldState); + if (newState == Stopped && (duration() == -1 + || (direction() == QAbstractAnimation::Forward && currentLoopTime() == duration()) + || (direction() == QAbstractAnimation::Backward && currentLoopTime() == 0))) + { + trans->complete(); + } +} + + + +QDeclarativeTransition::QDeclarativeTransition(QObject *parent) + : QObject(*(new QDeclarativeTransitionPrivate), parent) +{ +} + +QDeclarativeTransition::~QDeclarativeTransition() +{ +} + +void QDeclarativeTransition::stop() +{ + Q_D(QDeclarativeTransition); + d->group.stop(); +} + +void QDeclarativeTransition::setReversed(bool r) +{ + Q_D(QDeclarativeTransition); + d->reversed = r; +} + +void QDeclarativeTransition::prepare(QDeclarativeStateOperation::ActionList &actions, + QList &after, + QDeclarativeTransitionManager *endState) +{ + Q_D(QDeclarativeTransition); + + qmlExecuteDeferred(this); + + if (d->reversed) { + for (int ii = d->animations.count() - 1; ii >= 0; --ii) { + d->animations.at(ii)->transition(actions, after, QDeclarativeAbstractAnimation::Backward); + } + } else { + for (int ii = 0; ii < d->animations.count(); ++ii) { + d->animations.at(ii)->transition(actions, after, QDeclarativeAbstractAnimation::Forward); + } + } + + d->endState = endState; + d->group.setDirection(d->reversed ? QAbstractAnimation::Backward : QAbstractAnimation::Forward); + d->group.start(); +} + +/*! + \qmlproperty string Transition::from + \qmlproperty string Transition::to + + These properties indicate the state changes that trigger the transition. + + The default values for these properties is "*" (that is, any state). + + For example, the following transition has not set the \c to and \c from + properties, so the animation is always applied when changing between + the two states (i.e. when the mouse is pressed and released). + + \snippet doc/src/snippets/declarative/transition-from-to.qml 0 + + If the transition was changed to this: + + \snippet doc/src/snippets/declarative/transition-from-to-modified.qml modified transition + + The animation would only be applied when changing from the default state to + the "brighter" state (i.e. when the mouse is pressed, but not on release). + + \sa reversible +*/ +QString QDeclarativeTransition::fromState() const +{ + Q_D(const QDeclarativeTransition); + return d->fromState; +} + +void QDeclarativeTransition::setFromState(const QString &f) +{ + Q_D(QDeclarativeTransition); + if (f == d->fromState) + return; + + d->fromState = f; + emit fromChanged(); +} + +/*! + \qmlproperty bool Transition::reversible + This property holds whether the transition should be automatically reversed when the conditions that triggered this transition are reversed. + + The default value is false. + + By default, transitions run in parallel and are applied to all state + changes if the \l from and \l to states have not been set. In this + situation, the transition is automatically applied when a state change + is reversed, and it is not necessary to set this property to reverse + the transition. + + However, if a SequentialAnimation is used, or if the \l from or \l to + properties have been set, this property will need to be set to reverse + a transition when a state change is reverted. For example, the following + transition applies a sequential animation when the mouse is pressed, + and reverses the sequence of the animation when the mouse is released: + + \snippet doc/src/snippets/declarative/transition-reversible.qml 0 + + If the transition did not set the \c to and \c reversible values, then + on the mouse release, the transition would play the PropertyAnimation + before the ColorAnimation instead of reversing the sequence. +*/ +bool QDeclarativeTransition::reversible() const +{ + Q_D(const QDeclarativeTransition); + return d->reversible; +} + +void QDeclarativeTransition::setReversible(bool r) +{ + Q_D(QDeclarativeTransition); + if (r == d->reversible) + return; + + d->reversible = r; + emit reversibleChanged(); +} + +QString QDeclarativeTransition::toState() const +{ + Q_D(const QDeclarativeTransition); + return d->toState; +} + +void QDeclarativeTransition::setToState(const QString &t) +{ + Q_D(QDeclarativeTransition); + if (t == d->toState) + return; + + d->toState = t; + emit toChanged(); +} + +/*! + \qmlproperty list Transition::animations + \default + + This property holds a list of the animations to be run for this transition. + + \snippet examples/declarative/toys/dynamicscene/dynamicscene.qml top-level transitions + + The top-level animations are run in parallel. To run them sequentially, + define them within a SequentialAnimation: + + \snippet doc/src/snippets/declarative/transition-reversible.qml sequential animations +*/ +QDeclarativeListProperty QDeclarativeTransition::animations() +{ + Q_D(QDeclarativeTransition); + return QDeclarativeListProperty(this, &d->animations, QDeclarativeTransitionPrivate::append_animation, + QDeclarativeTransitionPrivate::animation_count, + QDeclarativeTransitionPrivate::animation_at, + QDeclarativeTransitionPrivate::clear_animations); +} + +QT_END_NAMESPACE + +#include diff --git a/src/declarative/util/qdeclarativetransition_p.h b/src/declarative/util/qdeclarativetransition_p.h new file mode 100644 index 00000000..6b84c0fd --- /dev/null +++ b/src/declarative/util/qdeclarativetransition_p.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVETRANSITION_H +#define QDECLARATIVETRANSITION_H + +#include "private/qdeclarativestate_p.h" + +#include + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeAbstractAnimation; +class QDeclarativeTransitionPrivate; +class QDeclarativeTransitionManager; +class Q_DECLARATIVE_EXPORT QDeclarativeTransition : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeTransition) + + Q_PROPERTY(QString from READ fromState WRITE setFromState NOTIFY fromChanged) + Q_PROPERTY(QString to READ toState WRITE setToState NOTIFY toChanged) + Q_PROPERTY(bool reversible READ reversible WRITE setReversible NOTIFY reversibleChanged) + Q_PROPERTY(QDeclarativeListProperty animations READ animations) + Q_CLASSINFO("DefaultProperty", "animations") + Q_CLASSINFO("DeferredPropertyNames", "animations") + +public: + QDeclarativeTransition(QObject *parent=0); + ~QDeclarativeTransition(); + + QString fromState() const; + void setFromState(const QString &); + + QString toState() const; + void setToState(const QString &); + + bool reversible() const; + void setReversible(bool); + + QDeclarativeListProperty animations(); + + void prepare(QDeclarativeStateOperation::ActionList &actions, + QList &after, + QDeclarativeTransitionManager *end); + + void setReversed(bool r); + void stop(); + +Q_SIGNALS: + void fromChanged(); + void toChanged(); + void reversibleChanged(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeTransition) + +QT_END_HEADER + +#endif // QDECLARATIVETRANSITION_H diff --git a/src/declarative/util/qdeclarativetransitionmanager.cpp b/src/declarative/util/qdeclarativetransitionmanager.cpp new file mode 100644 index 00000000..0cd66c05 --- /dev/null +++ b/src/declarative/util/qdeclarativetransitionmanager.cpp @@ -0,0 +1,276 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativetransitionmanager_p_p.h" + +#include "private/qdeclarativestate_p_p.h" +#include "private/qdeclarativestate_p.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +DEFINE_BOOL_CONFIG_OPTION(stateChangeDebug, STATECHANGE_DEBUG); + +class QDeclarativeTransitionManagerPrivate +{ +public: + QDeclarativeTransitionManagerPrivate() + : state(0) {} + + void applyBindings(); + typedef QList SimpleActionList; + QDeclarativeState *state; + QDeclarativeGuard transition; + QDeclarativeStateOperation::ActionList bindingsList; + SimpleActionList completeList; +}; + +QDeclarativeTransitionManager::QDeclarativeTransitionManager() +: d(new QDeclarativeTransitionManagerPrivate) +{ +} + +void QDeclarativeTransitionManager::setState(QDeclarativeState *s) +{ + d->state = s; +} + +QDeclarativeTransitionManager::~QDeclarativeTransitionManager() +{ + delete d; d = 0; +} + +void QDeclarativeTransitionManager::complete() +{ + d->applyBindings(); + + for (int ii = 0; ii < d->completeList.count(); ++ii) { + const QDeclarativeProperty &prop = d->completeList.at(ii).property(); + prop.write(d->completeList.at(ii).value()); + } + + d->completeList.clear(); + + if (d->state) + static_cast(QObjectPrivate::get(d->state))->complete(); +} + +void QDeclarativeTransitionManagerPrivate::applyBindings() +{ + foreach(const QDeclarativeAction &action, bindingsList) { + if (!action.toBinding.isNull()) { + QDeclarativePropertyPrivate::setBinding(action.property, action.toBinding.data()); + } else if (action.event) { + if (action.reverseEvent) + action.event->reverse(); + else + action.event->execute(); + } + + } + + bindingsList.clear(); +} + +void QDeclarativeTransitionManager::transition(const QList &list, + QDeclarativeTransition *transition) +{ + cancel(); + + QDeclarativeStateOperation::ActionList applyList = list; + // Determine which actions are binding changes. + foreach(const QDeclarativeAction &action, applyList) { + if (action.toBinding) + d->bindingsList << action; + if (action.fromBinding) + QDeclarativePropertyPrivate::setBinding(action.property, 0); // Disable current binding + if (action.event && action.event->changesBindings()) { //### assume isReversable()? + d->bindingsList << action; + action.event->clearBindings(); + } + } + + // Animated transitions need both the start and the end value for + // each property change. In the presence of bindings, the end values + // are non-trivial to calculate. As a "best effort" attempt, we first + // apply all the property and binding changes, then read all the actual + // final values, then roll back the changes and proceed as normal. + // + // This doesn't catch everything, and it might be a little fragile in + // some cases - but whatcha going to do? + + if (!d->bindingsList.isEmpty()) { + + // Apply all the property and binding changes + for (int ii = 0; ii < applyList.size(); ++ii) { + const QDeclarativeAction &action = applyList.at(ii); + if (!action.toBinding.isNull()) { + QDeclarativePropertyPrivate::setBinding(action.property, action.toBinding.data(), QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding); + } else if (!action.event) { + QDeclarativePropertyPrivate::write(action.property, action.toValue, QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding); + } else if (action.event->isReversable()) { + if (action.reverseEvent) + action.event->reverse(QDeclarativeActionEvent::FastForward); + else + action.event->execute(QDeclarativeActionEvent::FastForward); + } + } + + // Read all the end values for binding changes + for (int ii = 0; ii < applyList.size(); ++ii) { + QDeclarativeAction *action = &applyList[ii]; + if (action->event) { + action->event->saveTargetValues(); + continue; + } + const QDeclarativeProperty &prop = action->property; + if (!action->toBinding.isNull() || !action->toValue.isValid()) { + action->toValue = prop.read(); + } + } + + // Revert back to the original values + foreach(const QDeclarativeAction &action, applyList) { + if (action.event) { + if (action.event->isReversable()) { + action.event->clearBindings(); + action.event->rewind(); + action.event->clearBindings(); //### shouldn't be needed + } + continue; + } + + if (action.toBinding) + QDeclarativePropertyPrivate::setBinding(action.property, 0); // Make sure this is disabled during the transition + + QDeclarativePropertyPrivate::write(action.property, action.fromValue, QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding); + } + } + + if (transition) { + QList touched; + d->transition = transition; + d->transition->prepare(applyList, touched, this); + + // Modify the action list to remove actions handled in the transition + for (int ii = 0; ii < applyList.count(); ++ii) { + const QDeclarativeAction &action = applyList.at(ii); + + if (action.event) { + + if (action.actionDone) { + applyList.removeAt(ii); + --ii; + } + + } else { + + if (touched.contains(action.property)) { + if (action.toValue != action.fromValue) + d->completeList << + QDeclarativeSimpleAction(action, QDeclarativeSimpleAction::EndState); + + applyList.removeAt(ii); + --ii; + } + + } + } + } + + // Any actions remaining have not been handled by the transition and should + // be applied immediately. We skip applying bindings, as they are all + // applied at the end in applyBindings() to avoid any nastiness mid + // transition + foreach(const QDeclarativeAction &action, applyList) { + if (action.event && !action.event->changesBindings()) { + if (action.event->isReversable() && action.reverseEvent) + action.event->reverse(); + else + action.event->execute(); + } else if (!action.event && !action.toBinding) { + action.property.write(action.toValue); + } + } +#ifndef QT_NO_DEBUG_STREAM + if (stateChangeDebug()) { + foreach(const QDeclarativeAction &action, applyList) { + if (action.event) + qWarning() << " No transition for event:" << action.event->typeName(); + else + qWarning() << " No transition for:" << action.property.object() + << action.property.name() << "From:" << action.fromValue + << "To:" << action.toValue; + } + } +#endif + if (!transition) + d->applyBindings(); +} + +void QDeclarativeTransitionManager::cancel() +{ + if (d->transition) { + // ### this could potentially trigger a complete in rare circumstances + d->transition->stop(); + d->transition = 0; + } + + for(int i = 0; i < d->bindingsList.count(); ++i) { + QDeclarativeAction action = d->bindingsList[i]; + if (!action.toBinding.isNull() && action.deletableToBinding) { + QDeclarativePropertyPrivate::setBinding(action.property, 0); + action.toBinding.data()->destroy(); + action.toBinding.clear(); + action.deletableToBinding = false; + } else if (action.event) { + //### what do we do here? + } + + } + d->bindingsList.clear(); + d->completeList.clear(); +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativetransitionmanager_p_p.h b/src/declarative/util/qdeclarativetransitionmanager_p_p.h new file mode 100644 index 00000000..64948cdb --- /dev/null +++ b/src/declarative/util/qdeclarativetransitionmanager_p_p.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVETRANSITIONMANAGER_P_H +#define QDECLARATIVETRANSITIONMANAGER_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 "private/qdeclarativestateoperations_p.h" + +QT_BEGIN_NAMESPACE + +class QDeclarativeStatePrivate; +class QDeclarativeTransitionManagerPrivate; +class Q_AUTOTEST_EXPORT QDeclarativeTransitionManager +{ +public: + QDeclarativeTransitionManager(); + ~QDeclarativeTransitionManager(); + + void transition(const QList &, QDeclarativeTransition *transition); + + void cancel(); + +private: + Q_DISABLE_COPY(QDeclarativeTransitionManager) + QDeclarativeTransitionManagerPrivate *d; + + void complete(); + void setState(QDeclarativeState *); + + friend class QDeclarativeState; + friend class QDeclarativeTransitionPrivate; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVETRANSITIONMANAGER_P_H diff --git a/src/declarative/util/qdeclarativeutilmodule.cpp b/src/declarative/util/qdeclarativeutilmodule.cpp new file mode 100644 index 00000000..c6590978 --- /dev/null +++ b/src/declarative/util/qdeclarativeutilmodule.cpp @@ -0,0 +1,174 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativeutilmodule_p.h" +#include "private/qdeclarativeanimation_p.h" +#include "private/qdeclarativeanimation_p_p.h" +#include "private/qdeclarativebehavior_p.h" +#include "private/qdeclarativebind_p.h" +#include "private/qdeclarativeconnections_p.h" +#include "private/qdeclarativesmoothedanimation_p.h" +#include "private/qdeclarativefontloader_p.h" +#include "private/qdeclarativelistaccessor_p.h" +#include "private/qdeclarativelistmodel_p.h" +#include "private/qdeclarativenullablevalue_p_p.h" +#include "private/qdeclarativeopenmetaobject_p.h" +#include "private/qdeclarativepackage_p.h" +#include "private/qdeclarativepixmapcache_p.h" +#include "private/qdeclarativepropertychanges_p.h" +#include "qdeclarativepropertymap.h" +#include "private/qdeclarativespringanimation_p.h" +#include "private/qdeclarativestategroup_p.h" +#include "private/qdeclarativestateoperations_p.h" +#include "private/qdeclarativestate_p.h" +#include "private/qdeclarativestate_p_p.h" +#include "private/qdeclarativestyledtext_p.h" +#include "private/qdeclarativesystempalette_p.h" +#include "private/qdeclarativetimeline_p_p.h" +#include "private/qdeclarativetimer_p.h" +#include "private/qdeclarativetransitionmanager_p_p.h" +#include "private/qdeclarativetransition_p.h" +#include "private/qdeclarativeapplication_p.h" +#include "qdeclarativeview.h" +#include "qdeclarativeinfo.h" +#include "private/qdeclarativetypenotavailable_p.h" +#ifndef QT_NO_XMLPATTERNS +#include "private/qdeclarativexmllistmodel_p.h" +#endif + +void QDeclarativeUtilModule::defineModule() +{ + qmlRegisterUncreatableType("QtQuick",1,1,"Application", QDeclarativeApplication::tr("Application is an abstract class")); + + qmlRegisterType("QtQuick",1,0,"AnchorAnimation"); + qmlRegisterType("QtQuick",1,0,"AnchorChanges"); + qmlRegisterType("QtQuick",1,0,"Behavior"); + qmlRegisterType("QtQuick",1,0,"Binding"); + qmlRegisterType("QtQuick",1,0,"ColorAnimation"); + qmlRegisterType("QtQuick",1,0,"Connections"); + qmlRegisterType("QtQuick",1,0,"SmoothedAnimation"); + qmlRegisterType("QtQuick",1,0,"FontLoader"); + qmlRegisterType("QtQuick",1,0,"ListElement"); + qmlRegisterType("QtQuick",1,0,"NumberAnimation"); + qmlRegisterType("QtQuick",1,0,"Package"); + qmlRegisterType("QtQuick",1,0,"ParallelAnimation"); + qmlRegisterType("QtQuick",1,0,"ParentAnimation"); + qmlRegisterType("QtQuick",1,0,"ParentChange"); + qmlRegisterType("QtQuick",1,0,"PauseAnimation"); + qmlRegisterType("QtQuick",1,0,"PropertyAction"); + qmlRegisterType("QtQuick",1,0,"PropertyAnimation"); + qmlRegisterType("QtQuick",1,0,"RotationAnimation"); + qmlRegisterType("QtQuick",1,0,"ScriptAction"); + qmlRegisterType("QtQuick",1,0,"SequentialAnimation"); + qmlRegisterType("QtQuick",1,0,"SpringAnimation"); + qmlRegisterType("QtQuick",1,0,"StateChangeScript"); + qmlRegisterType("QtQuick",1,0,"StateGroup"); + qmlRegisterType("QtQuick",1,0,"State"); + qmlRegisterType("QtQuick",1,0,"SystemPalette"); + qmlRegisterType("QtQuick",1,0,"Timer"); + qmlRegisterType("QtQuick",1,0,"Transition"); + qmlRegisterType("QtQuick",1,0,"Vector3dAnimation"); +#ifdef QT_NO_XMLPATTERNS + qmlRegisterTypeNotAvailable("QtQuick",1,0,"XmlListModel", + qApp->translate("QDeclarativeXmlListModel","Qt was built without support for xmlpatterns")); + qmlRegisterTypeNotAvailable("QtQuick",1,0,"XmlRole", + qApp->translate("QDeclarativeXmlListModel","Qt was built without support for xmlpatterns")); +#else + qmlRegisterType("QtQuick",1,0,"XmlListModel"); + qmlRegisterType("QtQuick",1,0,"XmlRole"); +#endif + + qmlRegisterType(); + qmlRegisterType(); + qmlRegisterType(); + + qmlRegisterUncreatableType("QtQuick",1,0,"Animation",QDeclarativeAbstractAnimation::tr("Animation is an abstract class")); + + qmlRegisterCustomType("QtQuick",1,0,"ListModel", new QDeclarativeListModelParser); + qmlRegisterCustomType("QtQuick",1,0,"PropertyChanges", new QDeclarativePropertyChangesParser); + qmlRegisterCustomType("QtQuick",1,0,"Connections", new QDeclarativeConnectionsParser); + +#ifndef QT_NO_IMPORT_QT47_QML + qmlRegisterType("Qt",4,7,"AnchorAnimation"); + qmlRegisterType("Qt",4,7,"AnchorChanges"); + qmlRegisterType("Qt",4,7,"Behavior"); + qmlRegisterType("Qt",4,7,"Binding"); + qmlRegisterType("Qt",4,7,"ColorAnimation"); + qmlRegisterType("Qt",4,7,"Connections"); + qmlRegisterType("Qt",4,7,"SmoothedAnimation"); + qmlRegisterType("Qt",4,7,"FontLoader"); + qmlRegisterType("Qt",4,7,"ListElement"); + qmlRegisterType("Qt",4,7,"NumberAnimation"); + qmlRegisterType("Qt",4,7,"Package"); + qmlRegisterType("Qt",4,7,"ParallelAnimation"); + qmlRegisterType("Qt",4,7,"ParentAnimation"); + qmlRegisterType("Qt",4,7,"ParentChange"); + qmlRegisterType("Qt",4,7,"PauseAnimation"); + qmlRegisterType("Qt",4,7,"PropertyAction"); + qmlRegisterType("Qt",4,7,"PropertyAnimation"); + qmlRegisterType("Qt",4,7,"RotationAnimation"); + qmlRegisterType("Qt",4,7,"ScriptAction"); + qmlRegisterType("Qt",4,7,"SequentialAnimation"); + qmlRegisterType("Qt",4,7,"SpringAnimation"); + qmlRegisterType("Qt",4,7,"StateChangeScript"); + qmlRegisterType("Qt",4,7,"StateGroup"); + qmlRegisterType("Qt",4,7,"State"); + qmlRegisterType("Qt",4,7,"SystemPalette"); + qmlRegisterType("Qt",4,7,"Timer"); + qmlRegisterType("Qt",4,7,"Transition"); + qmlRegisterType("Qt",4,7,"Vector3dAnimation"); +#ifdef QT_NO_XMLPATTERNS + qmlRegisterTypeNotAvailable("Qt",4,7,"XmlListModel", + qApp->translate("QDeclarativeXmlListModel","Qt was built without support for xmlpatterns")); + qmlRegisterTypeNotAvailable("Qt",4,7,"XmlRole", + qApp->translate("QDeclarativeXmlListModel","Qt was built without support for xmlpatterns")); +#else + qmlRegisterType("Qt",4,7,"XmlListModel"); + qmlRegisterType("Qt",4,7,"XmlRole"); +#endif + + qmlRegisterUncreatableType("Qt",4,7,"Animation",QDeclarativeAbstractAnimation::tr("Animation is an abstract class")); + + qmlRegisterCustomType("Qt", 4,7, "ListModel", new QDeclarativeListModelParser); + qmlRegisterCustomType("Qt", 4, 7, "PropertyChanges", new QDeclarativePropertyChangesParser); + qmlRegisterCustomType("Qt", 4, 7, "Connections", new QDeclarativeConnectionsParser); +#endif +} diff --git a/src/declarative/util/qdeclarativeutilmodule_p.h b/src/declarative/util/qdeclarativeutilmodule_p.h new file mode 100644 index 00000000..addfad6c --- /dev/null +++ b/src/declarative/util/qdeclarativeutilmodule_p.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEUTILMODULE_H +#define QDECLARATIVEUTILMODULE_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeUtilModule +{ +public: + static void defineModule(); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEUTILMODULE_H diff --git a/src/declarative/util/qdeclarativeview.cpp b/src/declarative/util/qdeclarativeview.cpp new file mode 100644 index 00000000..ec357ffb --- /dev/null +++ b/src/declarative/util/qdeclarativeview.cpp @@ -0,0 +1,739 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativeview.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +DEFINE_BOOL_CONFIG_OPTION(frameRateDebug, QML_SHOW_FRAMERATE) + +class QDeclarativeScene : public QGraphicsScene +{ +public: + QDeclarativeScene(QObject *parent = 0); + +protected: + virtual void keyPressEvent(QKeyEvent *); + virtual void keyReleaseEvent(QKeyEvent *); + + virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *); + virtual void mousePressEvent(QGraphicsSceneMouseEvent *); + virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *); +}; + +QDeclarativeScene::QDeclarativeScene(QObject *parent) : QGraphicsScene(parent) +{ +} + +void QDeclarativeScene::keyPressEvent(QKeyEvent *e) +{ + QDeclarativeDebugTrace::addEvent(QDeclarativeDebugTrace::Key); + + QGraphicsScene::keyPressEvent(e); +} + +void QDeclarativeScene::keyReleaseEvent(QKeyEvent *e) +{ + QDeclarativeDebugTrace::addEvent(QDeclarativeDebugTrace::Key); + + QGraphicsScene::keyReleaseEvent(e); +} + +void QDeclarativeScene::mouseMoveEvent(QGraphicsSceneMouseEvent *e) +{ + QDeclarativeDebugTrace::addEvent(QDeclarativeDebugTrace::Mouse); + + QGraphicsScene::mouseMoveEvent(e); +} + +void QDeclarativeScene::mousePressEvent(QGraphicsSceneMouseEvent *e) +{ + QDeclarativeDebugTrace::addEvent(QDeclarativeDebugTrace::Mouse); + + QGraphicsScene::mousePressEvent(e); +} + +void QDeclarativeScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *e) +{ + QDeclarativeDebugTrace::addEvent(QDeclarativeDebugTrace::Mouse); + + QGraphicsScene::mouseReleaseEvent(e); +} + +class QDeclarativeViewPrivate : public QGraphicsViewPrivate, public QDeclarativeItemChangeListener +{ + Q_DECLARE_PUBLIC(QDeclarativeView) +public: + QDeclarativeViewPrivate() + : root(0), declarativeItemRoot(0), graphicsWidgetRoot(0), component(0), + resizeMode(QDeclarativeView::SizeViewToRootObject), initialSize(0,0) {} + ~QDeclarativeViewPrivate() { delete root; delete engine; } + void execute(); + void itemGeometryChanged(QDeclarativeItem *item, const QRectF &newGeometry, const QRectF &oldGeometry); + void initResize(); + void updateSize(); + inline QSize rootObjectSize() const; + + QDeclarativeGuard root; + QDeclarativeGuard declarativeItemRoot; + QDeclarativeGuard graphicsWidgetRoot; + + QUrl source; + + QDeclarativeEngine* engine; + QDeclarativeComponent *component; + QBasicTimer resizetimer; + + QDeclarativeView::ResizeMode resizeMode; + QSize initialSize; + QElapsedTimer frameTimer; + + void init(); +}; + +void QDeclarativeViewPrivate::execute() +{ + Q_Q(QDeclarativeView); + if (root) { + delete root; + root = 0; + } + if (component) { + delete component; + component = 0; + } + if (!source.isEmpty()) { + component = new QDeclarativeComponent(engine, source, q); + if (!component->isLoading()) { + q->continueExecute(); + } else { + QObject::connect(component, SIGNAL(statusChanged(QDeclarativeComponent::Status)), q, SLOT(continueExecute())); + } + } +} + +void QDeclarativeViewPrivate::itemGeometryChanged(QDeclarativeItem *resizeItem, const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_Q(QDeclarativeView); + if (resizeItem == root && resizeMode == QDeclarativeView::SizeViewToRootObject) { + // wait for both width and height to be changed + resizetimer.start(0,q); + } + QDeclarativeItemChangeListener::itemGeometryChanged(resizeItem, newGeometry, oldGeometry); +} + +/*! + \class QDeclarativeView + \since 4.7 + \brief The QDeclarativeView class provides a widget for displaying a Qt Declarative user interface. + + QDeclarativeItem objects can be placed on a standard QGraphicsScene and + displayed with QGraphicsView. QDeclarativeView is a QGraphicsView subclass + provided as a convenience for displaying QML files, and connecting between + QML and C++ Qt objects. + + QDeclarativeView provides: + + \list + \o Management of QDeclarativeComponent loading and object creation + \o Initialization of QGraphicsView for optimal performance with QML using these settings: + \list + \o QGraphicsView::setOptimizationFlags(QGraphicsView::DontSavePainterState) + \o QGraphicsView::setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate) + \o QGraphicsScene::setItemIndexMethod(QGraphicsScene::NoIndex) + \endlist + \o Initialization of QGraphicsView for QML key handling using these settings: + \list + \o QGraphicsView::viewport()->setFocusPolicy(Qt::NoFocus) + \o QGraphicsView::setFocusPolicy(Qt::StrongFocus) + \o QGraphicsScene::setStickyFocus(true) + \endlist + \endlist + + Typical usage: + + \code + QDeclarativeView *view = new QDeclarativeView; + view->setSource(QUrl::fromLocalFile("myqmlfile.qml")); + view->show(); + \endcode + + Since QDeclarativeView is a QWidget-based class, it can be used to + display QML interfaces within QWidget-based GUI applications that do not + use the Graphics View framework. + + To receive errors related to loading and executing QML with QDeclarativeView, + you can connect to the statusChanged() signal and monitor for QDeclarativeView::Error. + The errors are available via QDeclarativeView::errors(). + + If you're using your own QGraphicsScene-based scene with QDeclarativeView, remember to + enable scene's sticky focus mode and to set itemIndexMethod to QGraphicsScene::NoIndex. + + \sa {Integrating QML Code with Existing Qt UI Code}, {Using QML Bindings in C++ Applications} +*/ + + +/*! \fn void QDeclarativeView::sceneResized(QSize size) + This signal is emitted when the view is resized to \a size. +*/ + +/*! \fn void QDeclarativeView::statusChanged(QDeclarativeView::Status status) + This signal is emitted when the component's current \a status changes. +*/ + +/*! + \fn QDeclarativeView::QDeclarativeView(QWidget *parent) + + Constructs a QDeclarativeView with the given \a parent. +*/ +QDeclarativeView::QDeclarativeView(QWidget *parent) + : QGraphicsView(*(new QDeclarativeViewPrivate), parent) +{ + Q_D(QDeclarativeView); + setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Preferred); + d->init(); +} + +/*! + \fn QDeclarativeView::QDeclarativeView(const QUrl &source, QWidget *parent) + + Constructs a QDeclarativeView with the given QML \a source and \a parent. +*/ +QDeclarativeView::QDeclarativeView(const QUrl &source, QWidget *parent) + : QGraphicsView(*(new QDeclarativeViewPrivate), parent) +{ + Q_D(QDeclarativeView); + setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Preferred); + d->init(); + setSource(source); +} + +void QDeclarativeViewPrivate::init() +{ + Q_Q(QDeclarativeView); + engine = new QDeclarativeEngine(); + q->setScene(new QDeclarativeScene(q)); + + q->setOptimizationFlags(QGraphicsView::DontSavePainterState); + q->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + q->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + q->setFrameStyle(0); + + // These seem to give the best performance + q->setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate); + q->scene()->setItemIndexMethod(QGraphicsScene::NoIndex); + q->viewport()->setFocusPolicy(Qt::NoFocus); + q->setFocusPolicy(Qt::StrongFocus); + + q->scene()->setStickyFocus(true); //### needed for correct focus handling + +#ifdef QDECLARATIVEVIEW_NOBACKGROUND + q->setAttribute(Qt::WA_OpaquePaintEvent); + q->setAttribute(Qt::WA_NoSystemBackground); + q->viewport()->setAttribute(Qt::WA_OpaquePaintEvent); + q->viewport()->setAttribute(Qt::WA_NoSystemBackground); +#endif + + QDeclarativeInspectorService::instance()->addView(q); +} + +/*! + Destroys the view. + */ +QDeclarativeView::~QDeclarativeView() +{ + QDeclarativeInspectorService::instance()->removeView(this); +} + +/*! \property QDeclarativeView::source + \brief The URL of the source of the QML component. + + Changing this property causes the QML component to be reloaded. + + Ensure that the URL provided is full and correct, in particular, use + \l QUrl::fromLocalFile() when loading a file from the local filesystem. + + \sa {Network Transparency}{Loading Resources in QML} + */ + +/*! + Sets the source to the \a url, loads the QML component and instantiates it. + + Ensure that the URL provided is full and correct, in particular, use + \l QUrl::fromLocalFile() when loading a file from the local filesystem. + + Calling this methods multiple times with the same url will result + in the QML being reloaded. + */ +void QDeclarativeView::setSource(const QUrl& url) +{ + Q_D(QDeclarativeView); + d->source = url; + d->execute(); +} + +/*! + Returns the source URL, if set. + + \sa setSource() + */ +QUrl QDeclarativeView::source() const +{ + Q_D(const QDeclarativeView); + return d->source; +} + +/*! + Returns a pointer to the QDeclarativeEngine used for instantiating + QML Components. + */ +QDeclarativeEngine* QDeclarativeView::engine() const +{ + Q_D(const QDeclarativeView); + return d->engine; +} + +/*! + This function returns the root of the context hierarchy. Each QML + component is instantiated in a QDeclarativeContext. QDeclarativeContext's are + essential for passing data to QML components. In QML, contexts are + arranged hierarchically and this hierarchy is managed by the + QDeclarativeEngine. + */ +QDeclarativeContext* QDeclarativeView::rootContext() const +{ + Q_D(const QDeclarativeView); + return d->engine->rootContext(); +} + +/*! + \enum QDeclarativeView::Status + Specifies the loading status of the QDeclarativeView. + + \value Null This QDeclarativeView has no source set. + \value Ready This QDeclarativeView has loaded and created the QML component. + \value Loading This QDeclarativeView is loading network data. + \value Error One or more errors has occurred. Call errors() to retrieve a list + of errors. +*/ + +/*! \enum QDeclarativeView::ResizeMode + + This enum specifies how to resize the view. + + \value SizeViewToRootObject The view resizes with the root item in the QML. + \value SizeRootObjectToView The view will automatically resize the root item to the size of the view. +*/ + +/*! + \property QDeclarativeView::status + The component's current \l{QDeclarativeView::Status} {status}. +*/ + +QDeclarativeView::Status QDeclarativeView::status() const +{ + Q_D(const QDeclarativeView); + if (!d->component) + return QDeclarativeView::Null; + + return QDeclarativeView::Status(d->component->status()); +} + +/*! + Return the list of errors that occurred during the last compile or create + operation. When the status is not Error, an empty list is returned. +*/ +QList QDeclarativeView::errors() const +{ + Q_D(const QDeclarativeView); + if (d->component) + return d->component->errors(); + return QList(); +} + +/*! + \property QDeclarativeView::resizeMode + \brief whether the view should resize the canvas contents + + If this property is set to SizeViewToRootObject (the default), the view + resizes with the root item in the QML. + + If this property is set to SizeRootObjectToView, the view will + automatically resize the root item. + + Regardless of this property, the sizeHint of the view + is the initial size of the root item. Note though that + since QML may load dynamically, that size may change. +*/ + +void QDeclarativeView::setResizeMode(ResizeMode mode) +{ + Q_D(QDeclarativeView); + if (d->resizeMode == mode) + return; + + if (d->declarativeItemRoot) { + if (d->resizeMode == SizeViewToRootObject) { + QDeclarativeItemPrivate *p = + static_cast(QGraphicsItemPrivate::get(d->declarativeItemRoot)); + p->removeItemChangeListener(d, QDeclarativeItemPrivate::Geometry); + } + } else if (d->graphicsWidgetRoot) { + if (d->resizeMode == QDeclarativeView::SizeViewToRootObject) { + d->graphicsWidgetRoot->removeEventFilter(this); + } + } + + d->resizeMode = mode; + if (d->root) { + d->initResize(); + } +} + +void QDeclarativeViewPrivate::initResize() +{ + Q_Q(QDeclarativeView); + if (declarativeItemRoot) { + if (resizeMode == QDeclarativeView::SizeViewToRootObject) { + QDeclarativeItemPrivate *p = + static_cast(QGraphicsItemPrivate::get(declarativeItemRoot)); + p->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry); + } + } else if (graphicsWidgetRoot) { + if (resizeMode == QDeclarativeView::SizeViewToRootObject) { + graphicsWidgetRoot->installEventFilter(q); + } + } + updateSize(); +} + +void QDeclarativeViewPrivate::updateSize() +{ + Q_Q(QDeclarativeView); + if (!root) + return; + if (declarativeItemRoot) { + if (resizeMode == QDeclarativeView::SizeViewToRootObject) { + QSize newSize = QSize(declarativeItemRoot->width(), declarativeItemRoot->height()); + if (newSize.isValid() && newSize != q->size()) { + q->resize(newSize); + } + } else if (resizeMode == QDeclarativeView::SizeRootObjectToView) { + if (!qFuzzyCompare(q->width(), declarativeItemRoot->width())) + declarativeItemRoot->setWidth(q->width()); + if (!qFuzzyCompare(q->height(), declarativeItemRoot->height())) + declarativeItemRoot->setHeight(q->height()); + } + } else if (graphicsWidgetRoot) { + if (resizeMode == QDeclarativeView::SizeViewToRootObject) { + QSize newSize = QSize(graphicsWidgetRoot->size().width(), graphicsWidgetRoot->size().height()); + if (newSize.isValid() && newSize != q->size()) { + q->resize(newSize); + } + } else if (resizeMode == QDeclarativeView::SizeRootObjectToView) { + QSizeF newSize = QSize(q->size().width(), q->size().height()); + if (newSize.isValid() && newSize != graphicsWidgetRoot->size()) { + graphicsWidgetRoot->resize(newSize); + } + } + } + q->updateGeometry(); +} + +QSize QDeclarativeViewPrivate::rootObjectSize() const +{ + QSize rootObjectSize(0,0); + int widthCandidate = -1; + int heightCandidate = -1; + if (root) { + QSizeF size = root->boundingRect().size(); + widthCandidate = size.width(); + heightCandidate = size.height(); + } + if (widthCandidate > 0) { + rootObjectSize.setWidth(widthCandidate); + } + if (heightCandidate > 0) { + rootObjectSize.setHeight(heightCandidate); + } + return rootObjectSize; +} + +QDeclarativeView::ResizeMode QDeclarativeView::resizeMode() const +{ + Q_D(const QDeclarativeView); + return d->resizeMode; +} + +/*! + \internal + */ +void QDeclarativeView::continueExecute() +{ + Q_D(QDeclarativeView); + disconnect(d->component, SIGNAL(statusChanged(QDeclarativeComponent::Status)), this, SLOT(continueExecute())); + + if (d->component->isError()) { + QList errorList = d->component->errors(); + foreach (const QDeclarativeError &error, errorList) { + qWarning() << error; + } + emit statusChanged(status()); + return; + } + + QObject *obj = d->component->create(); + + if(d->component->isError()) { + QList errorList = d->component->errors(); + foreach (const QDeclarativeError &error, errorList) { + qWarning() << error; + } + emit statusChanged(status()); + return; + } + + setRootObject(obj); + emit statusChanged(status()); +} + +/*! + \internal +*/ +void QDeclarativeView::setRootObject(QObject *obj) +{ + Q_D(QDeclarativeView); + if (d->root == obj || !scene()) + return; + if (QDeclarativeItem *declarativeItem = qobject_cast(obj)) { + scene()->addItem(declarativeItem); + d->root = declarativeItem; + d->declarativeItemRoot = declarativeItem; + } else if (QGraphicsObject *graphicsObject = qobject_cast(obj)) { + scene()->addItem(graphicsObject); + d->root = graphicsObject; + if (graphicsObject->isWidget()) { + d->graphicsWidgetRoot = static_cast(graphicsObject); + } else { + qWarning() << "QDeclarativeView::resizeMode is not honored for components of type QGraphicsObject"; + } + } else if (obj) { + qWarning() << "QDeclarativeView only supports loading of root objects that derive from QGraphicsObject"; + if (QWidget* widget = qobject_cast(obj)) { + window()->setAttribute(Qt::WA_OpaquePaintEvent, false); + window()->setAttribute(Qt::WA_NoSystemBackground, false); + if (layout() && layout()->count()) { + // Hide the QGraphicsView in GV mode. + QLayoutItem *item = layout()->itemAt(0); + if (item->widget()) + item->widget()->hide(); + } + widget->setParent(this); + if (isVisible()) { + widget->setVisible(true); + } + resize(widget->size()); + } + } + + if (d->root) { + d->initialSize = d->rootObjectSize(); + if (d->initialSize != size()) { + if (!(parentWidget() && parentWidget()->layout())) { + resize(d->initialSize); + } + } + d->initResize(); + } +} + +/*! + \internal + If the \l {QTimerEvent} {timer event} \a e is this + view's resize timer, sceneResized() is emitted. + */ +void QDeclarativeView::timerEvent(QTimerEvent* e) +{ + Q_D(QDeclarativeView); + if (!e || e->timerId() == d->resizetimer.timerId()) { + d->updateSize(); + d->resizetimer.stop(); + } +} + +/*! \internal */ +bool QDeclarativeView::eventFilter(QObject *watched, QEvent *e) +{ + Q_D(QDeclarativeView); + if (watched == d->root && d->resizeMode == SizeViewToRootObject) { + if (d->graphicsWidgetRoot) { + if (e->type() == QEvent::GraphicsSceneResize) { + d->updateSize(); + } + } + } + return QGraphicsView::eventFilter(watched, e); +} + +/*! + \internal + Preferred size follows the root object geometry. +*/ +QSize QDeclarativeView::sizeHint() const +{ + Q_D(const QDeclarativeView); + QSize rootObjectSize = d->rootObjectSize(); + if (rootObjectSize.isEmpty()) { + return size(); + } else { + return rootObjectSize; + } +} + +/*! + Returns the initial size of the root object +*/ +QSize QDeclarativeView::initialSize() const +{ + Q_D(const QDeclarativeView); + return d->initialSize; +} + +/*! + Returns the view's root \l {QGraphicsObject} {item}. + */ +QGraphicsObject *QDeclarativeView::rootObject() const +{ + Q_D(const QDeclarativeView); + return d->root; +} + +/*! + \internal + This function handles the \l {QResizeEvent} {resize event} + \a e. + */ +void QDeclarativeView::resizeEvent(QResizeEvent *e) +{ + Q_D(QDeclarativeView); + if (d->resizeMode == SizeRootObjectToView) { + d->updateSize(); + } + if (d->declarativeItemRoot) { + setSceneRect(QRectF(0, 0, d->declarativeItemRoot->width(), d->declarativeItemRoot->height())); + } else if (d->root) { + setSceneRect(d->root->boundingRect()); + } else { + setSceneRect(rect()); + } + emit sceneResized(e->size()); + QGraphicsView::resizeEvent(e); +} + +/*! + \internal +*/ +void QDeclarativeView::paintEvent(QPaintEvent *event) +{ + Q_D(QDeclarativeView); + + QDeclarativeDebugTrace::addEvent(QDeclarativeDebugTrace::FramePaint); + QDeclarativeDebugTrace::startRange(QDeclarativeDebugTrace::Painting); + + int time = 0; + if (frameRateDebug()) + time = d->frameTimer.restart(); + + QGraphicsView::paintEvent(event); + + QDeclarativeDebugTrace::endRange(QDeclarativeDebugTrace::Painting); + + if (frameRateDebug()) + qDebug() << "paintEvent:" << d->frameTimer.elapsed() << "time since last frame:" << time; + +#if QT_SHOW_DECLARATIVEVIEW_FPS + static QTime timer; + static int frames; + + if (frames == 0) { + timer.start(); + } else if (timer.elapsed() > 5000) { + qreal avgtime = timer.elapsed() / (qreal) frames; + qDebug("Average time per frame: %f ms (%i fps)", avgtime, int(1000 / avgtime)); + timer.start(); + frames = 0; + } + ++frames; + scene()->update(); +#endif + +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativeview.h b/src/declarative/util/qdeclarativeview.h new file mode 100644 index 00000000..043460c8 --- /dev/null +++ b/src/declarative/util/qdeclarativeview.h @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEVIEW_H +#define QDECLARATIVEVIEW_H + +#include +#include +#include +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QGraphicsObject; +class QDeclarativeEngine; +class QDeclarativeContext; +class QDeclarativeError; + +class QDeclarativeViewPrivate; +class Q_DECLARATIVE_EXPORT QDeclarativeView : public QGraphicsView +{ + Q_OBJECT + Q_PROPERTY(ResizeMode resizeMode READ resizeMode WRITE setResizeMode) + Q_PROPERTY(Status status READ status NOTIFY statusChanged) + Q_PROPERTY(QUrl source READ source WRITE setSource DESIGNABLE true) + Q_ENUMS(ResizeMode Status) +public: + explicit QDeclarativeView(QWidget *parent = 0); + QDeclarativeView(const QUrl &source, QWidget *parent = 0); + virtual ~QDeclarativeView(); + + QUrl source() const; + void setSource(const QUrl&); + + QDeclarativeEngine* engine() const; + QDeclarativeContext* rootContext() const; + + QGraphicsObject *rootObject() const; + + enum ResizeMode { SizeViewToRootObject, SizeRootObjectToView }; + ResizeMode resizeMode() const; + void setResizeMode(ResizeMode); + + enum Status { Null, Ready, Loading, Error }; + Status status() const; + + QList errors() const; + + QSize sizeHint() const; + QSize initialSize() const; + +Q_SIGNALS: + void sceneResized(QSize size); // ??? + void statusChanged(QDeclarativeView::Status); + +private Q_SLOTS: + void continueExecute(); + +protected: + virtual void resizeEvent(QResizeEvent *); + virtual void paintEvent(QPaintEvent *event); + virtual void timerEvent(QTimerEvent*); + virtual void setRootObject(QObject *obj); + virtual bool eventFilter(QObject *watched, QEvent *e); + +private: + Q_DISABLE_COPY(QDeclarativeView) + Q_DECLARE_PRIVATE(QDeclarativeView) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEVIEW_H diff --git a/src/declarative/util/qdeclarativexmllistmodel.cpp b/src/declarative/util/qdeclarativexmllistmodel.cpp new file mode 100644 index 00000000..a789996e --- /dev/null +++ b/src/declarative/util/qdeclarativexmllistmodel.cpp @@ -0,0 +1,1157 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativexmllistmodel_p.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +Q_DECLARE_METATYPE(QDeclarativeXmlQueryResult) + +QT_BEGIN_NAMESPACE + + +typedef QPair QDeclarativeXmlListRange; + +#define XMLLISTMODEL_CLEAR_ID 0 + +/*! + \qmlclass XmlRole QDeclarativeXmlListModelRole + \ingroup qml-working-with-data + \since 4.7 + \brief The XmlRole element allows you to specify a role for an XmlListModel. + + \sa {QtDeclarative} +*/ + +/*! + \qmlproperty string XmlRole::name + + The name for the role. This name is used to access the model data for this role. + + For example, the following model has a role named "title", which can be accessed + from the view's delegate: + + \qml + XmlListModel { + id: xmlModel + // ... + XmlRole { + name: "title" + query: "title/string()" + } + } + \endqml + + \qml + ListView { + model: xmlModel + delegate: Text { text: title } + } + \endqml +*/ + +/*! + \qmlproperty string XmlRole::query + The relative XPath expression query for this role. The query must be relative; it cannot start + with a '/'. + + For example, if there is an XML document like this: + + \quotefile doc/src/snippets/declarative/xmlrole.xml + + Here are some valid XPath expressions for XmlRole queries on this document: + + \snippet doc/src/snippets/declarative/xmlrole.qml 0 + \dots 4 + \snippet doc/src/snippets/declarative/xmlrole.qml 1 + + See the \l{http://www.w3.org/TR/xpath20/}{W3C XPath 2.0 specification} for more information. +*/ + +/*! + \qmlproperty bool XmlRole::isKey + Defines whether this is a key role. + + Key roles are used to to determine whether a set of values should + be updated or added to the XML list model when XmlListModel::reload() + is called. + + \sa XmlListModel +*/ + +struct XmlQueryJob +{ + int queryId; + QByteArray data; + QString query; + QString namespaces; + QStringList roleQueries; + QList roleQueryErrorId; // the ptr to send back if there is an error + QStringList keyRoleQueries; + QStringList keyRoleResultsCache; + QString prefix; +}; + + +class QDeclarativeXmlQueryEngine; +class QDeclarativeXmlQueryThreadObject : public QObject +{ + Q_OBJECT +public: + QDeclarativeXmlQueryThreadObject(QDeclarativeXmlQueryEngine *); + + void processJobs(); + virtual bool event(QEvent *e); + +private: + QDeclarativeXmlQueryEngine *m_queryEngine; +}; + + +class QDeclarativeXmlQueryEngine : public QThread +{ + Q_OBJECT +public: + QDeclarativeXmlQueryEngine(QDeclarativeEngine *eng); + ~QDeclarativeXmlQueryEngine(); + + int doQuery(QString query, QString namespaces, QByteArray data, QList* roleObjects, QStringList keyRoleResultsCache); + void abort(int id); + + void processJobs(); + + static QDeclarativeXmlQueryEngine *instance(QDeclarativeEngine *engine); + +signals: + void queryCompleted(const QDeclarativeXmlQueryResult &); + void error(void*, const QString&); + +protected: + void run(); + +private: + void processQuery(XmlQueryJob *job); + void doQueryJob(XmlQueryJob *job, QDeclarativeXmlQueryResult *currentResult); + void doSubQueryJob(XmlQueryJob *job, QDeclarativeXmlQueryResult *currentResult); + void getValuesOfKeyRoles(const XmlQueryJob& currentJob, QStringList *values, QXmlQuery *query) const; + void addIndexToRangeList(QList *ranges, int index) const; + + QMutex m_mutex; + QDeclarativeXmlQueryThreadObject *m_threadObject; + QList m_jobs; + QSet m_cancelledJobs; + QAtomicInt m_queryIds; + + QDeclarativeEngine *m_engine; + QObject *m_eventLoopQuitHack; + + static QHash queryEngines; + static QMutex queryEnginesMutex; +}; +QHash QDeclarativeXmlQueryEngine::queryEngines; +QMutex QDeclarativeXmlQueryEngine::queryEnginesMutex; + + +QDeclarativeXmlQueryThreadObject::QDeclarativeXmlQueryThreadObject(QDeclarativeXmlQueryEngine *e) + : m_queryEngine(e) +{ +} + +void QDeclarativeXmlQueryThreadObject::processJobs() +{ + QCoreApplication::postEvent(this, new QEvent(QEvent::User)); +} + +bool QDeclarativeXmlQueryThreadObject::event(QEvent *e) +{ + if (e->type() == QEvent::User) { + m_queryEngine->processJobs(); + return true; + } else { + return QObject::event(e); + } +} + + + +QDeclarativeXmlQueryEngine::QDeclarativeXmlQueryEngine(QDeclarativeEngine *eng) +: QThread(eng), m_threadObject(0), m_queryIds(XMLLISTMODEL_CLEAR_ID + 1), m_engine(eng), m_eventLoopQuitHack(0) +{ + qRegisterMetaType("QDeclarativeXmlQueryResult"); + + m_eventLoopQuitHack = new QObject; + m_eventLoopQuitHack->moveToThread(this); + connect(m_eventLoopQuitHack, SIGNAL(destroyed(QObject*)), SLOT(quit()), Qt::DirectConnection); + start(QThread::IdlePriority); +} + +QDeclarativeXmlQueryEngine::~QDeclarativeXmlQueryEngine() +{ + queryEnginesMutex.lock(); + queryEngines.remove(m_engine); + queryEnginesMutex.unlock(); + + m_eventLoopQuitHack->deleteLater(); + wait(); +} + +int QDeclarativeXmlQueryEngine::doQuery(QString query, QString namespaces, QByteArray data, QList* roleObjects, QStringList keyRoleResultsCache) { + { + QMutexLocker m1(&m_mutex); + m_queryIds.ref(); + if (m_queryIds <= 0) + m_queryIds = 1; + } + + XmlQueryJob job; + job.queryId = m_queryIds; + job.data = data; + job.query = QLatin1String("doc($src)") + query; + job.namespaces = namespaces; + job.keyRoleResultsCache = keyRoleResultsCache; + + for (int i=0; icount(); i++) { + if (!roleObjects->at(i)->isValid()) { + job.roleQueries << QString(); + continue; + } + job.roleQueries << roleObjects->at(i)->query(); + job.roleQueryErrorId << static_cast(roleObjects->at(i)); + if (roleObjects->at(i)->isKey()) + job.keyRoleQueries << job.roleQueries.last(); + } + + { + QMutexLocker ml(&m_mutex); + m_jobs.append(job); + if (m_threadObject) + m_threadObject->processJobs(); + } + + return job.queryId; +} + +void QDeclarativeXmlQueryEngine::abort(int id) +{ + QMutexLocker ml(&m_mutex); + if (id != -1) + m_cancelledJobs.insert(id); +} + +void QDeclarativeXmlQueryEngine::run() +{ + m_mutex.lock(); + m_threadObject = new QDeclarativeXmlQueryThreadObject(this); + m_mutex.unlock(); + + processJobs(); + exec(); + + delete m_threadObject; + m_threadObject = 0; +} + +void QDeclarativeXmlQueryEngine::processJobs() +{ + QMutexLocker locker(&m_mutex); + + while (true) { + if (m_jobs.isEmpty()) + return; + + XmlQueryJob currentJob = m_jobs.takeLast(); + while (m_cancelledJobs.remove(currentJob.queryId)) { + if (m_jobs.isEmpty()) + return; + currentJob = m_jobs.takeLast(); + } + + locker.unlock(); + processQuery(¤tJob); + locker.relock(); + } +} + +QDeclarativeXmlQueryEngine *QDeclarativeXmlQueryEngine::instance(QDeclarativeEngine *engine) +{ + queryEnginesMutex.lock(); + QDeclarativeXmlQueryEngine *queryEng = queryEngines.value(engine); + if (!queryEng) { + queryEng = new QDeclarativeXmlQueryEngine(engine); + queryEngines.insert(engine, queryEng); + } + queryEnginesMutex.unlock(); + + return queryEng; +} + +void QDeclarativeXmlQueryEngine::processQuery(XmlQueryJob *job) +{ + QDeclarativeXmlQueryResult result; + result.queryId = job->queryId; + doQueryJob(job, &result); + doSubQueryJob(job, &result); + + { + QMutexLocker ml(&m_mutex); + if (m_cancelledJobs.contains(job->queryId)) { + m_cancelledJobs.remove(job->queryId); + } else { + emit queryCompleted(result); + } + } +} + +void QDeclarativeXmlQueryEngine::doQueryJob(XmlQueryJob *currentJob, QDeclarativeXmlQueryResult *currentResult) +{ + Q_ASSERT(currentJob->queryId != -1); + + QString r; + QXmlQuery query; + QBuffer buffer(¤tJob->data); + buffer.open(QIODevice::ReadOnly); + query.bindVariable(QLatin1String("src"), &buffer); + query.setQuery(currentJob->namespaces + currentJob->query); + query.evaluateTo(&r); + + //always need a single root element + QByteArray xml = "\n" + r.toUtf8() + ""; + QBuffer b(&xml); + b.open(QIODevice::ReadOnly); + + QString namespaces = QLatin1String("declare namespace dummy=\"http://qtsotware.com/dummy\";\n") + currentJob->namespaces; + QString prefix = QLatin1String("doc($inputDocument)/dummy:items") + + currentJob->query.mid(currentJob->query.lastIndexOf(QLatin1Char('/'))); + + //figure out how many items we are dealing with + int count = -1; + { + QXmlResultItems result; + QXmlQuery countquery; + countquery.bindVariable(QLatin1String("inputDocument"), &b); + countquery.setQuery(namespaces + QLatin1String("count(") + prefix + QLatin1Char(')')); + countquery.evaluateTo(&result); + QXmlItem item(result.next()); + if (item.isAtomicValue()) + count = item.toAtomicValue().toInt(); + } + + currentJob->data = xml; + currentJob->prefix = namespaces + prefix + QLatin1Char('/'); + currentResult->size = (count > 0 ? count : 0); +} + +void QDeclarativeXmlQueryEngine::getValuesOfKeyRoles(const XmlQueryJob& currentJob, QStringList *values, QXmlQuery *query) const +{ + const QStringList &keysQueries = currentJob.keyRoleQueries; + QString keysQuery; + if (keysQueries.count() == 1) + keysQuery = currentJob.prefix + keysQueries[0]; + else if (keysQueries.count() > 1) + keysQuery = currentJob.prefix + QLatin1String("concat(") + keysQueries.join(QLatin1String(",")) + QLatin1String(")"); + + if (!keysQuery.isEmpty()) { + query->setQuery(keysQuery); + QXmlResultItems resultItems; + query->evaluateTo(&resultItems); + QXmlItem item(resultItems.next()); + while (!item.isNull()) { + values->append(item.toAtomicValue().toString()); + item = resultItems.next(); + } + } +} + +void QDeclarativeXmlQueryEngine::addIndexToRangeList(QList *ranges, int index) const { + if (ranges->isEmpty()) + ranges->append(qMakePair(index, 1)); + else if (ranges->last().first + ranges->last().second == index) + ranges->last().second += 1; + else + ranges->append(qMakePair(index, 1)); +} + +void QDeclarativeXmlQueryEngine::doSubQueryJob(XmlQueryJob *currentJob, QDeclarativeXmlQueryResult *currentResult) +{ + Q_ASSERT(currentJob->queryId != -1); + + QBuffer b(¤tJob->data); + b.open(QIODevice::ReadOnly); + + QXmlQuery subquery; + subquery.bindVariable(QLatin1String("inputDocument"), &b); + + QStringList keyRoleResults; + getValuesOfKeyRoles(*currentJob, &keyRoleResults, &subquery); + + // See if any values of key roles have been inserted or removed. + + if (currentJob->keyRoleResultsCache.isEmpty()) { + currentResult->inserted << qMakePair(0, currentResult->size); + } else { + if (keyRoleResults != currentJob->keyRoleResultsCache) { + QStringList temp; + for (int i=0; ikeyRoleResultsCache.count(); i++) { + if (!keyRoleResults.contains(currentJob->keyRoleResultsCache[i])) + addIndexToRangeList(¤tResult->removed, i); + else + temp << currentJob->keyRoleResultsCache[i]; + } + + for (int i=0; iinserted, i); + } + } + } + } + currentResult->keyRoleResultsCache = keyRoleResults; + + // Get the new values for each role. + //### we might be able to condense even further (query for everything in one go) + const QStringList &queries = currentJob->roleQueries; + for (int i = 0; i < queries.size(); ++i) { + QList resultList; + if (!queries[i].isEmpty()) { + subquery.setQuery(currentJob->prefix + QLatin1String("(let $v := string(") + queries[i] + QLatin1String(") return if ($v) then ") + queries[i] + QLatin1String(" else \"\")")); + if (subquery.isValid()) { + QXmlResultItems resultItems; + subquery.evaluateTo(&resultItems); + QXmlItem item(resultItems.next()); + while (!item.isNull()) { + resultList << item.toAtomicValue(); //### we used to trim strings + item = resultItems.next(); + } + } else { + emit error(currentJob->roleQueryErrorId.at(i), queries[i]); + } + } + //### should warn here if things have gone wrong. + while (resultList.count() < currentResult->size) + resultList << QVariant(); + currentResult->data << resultList; + b.seek(0); + } + + //this method is much slower, but works better for incremental loading + /*for (int j = 0; j < m_size; ++j) { + QList resultList; + for (int i = 0; i < m_roleObjects->size(); ++i) { + QDeclarativeXmlListModelRole *role = m_roleObjects->at(i); + subquery.setQuery(m_prefix.arg(j+1) + role->query()); + if (role->isStringList()) { + QStringList data; + subquery.evaluateTo(&data); + resultList << QVariant(data); + //qDebug() << data; + } else { + QString s; + subquery.evaluateTo(&s); + if (role->isCData()) { + //un-escape + s.replace(QLatin1String("<"), QLatin1String("<")); + s.replace(QLatin1String(">"), QLatin1String(">")); + s.replace(QLatin1String("&"), QLatin1String("&")); + } + resultList << s.trimmed(); + //qDebug() << s; + } + b.seek(0); + } + m_modelData << resultList; + }*/ +} + +class QDeclarativeXmlListModelPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeXmlListModel) +public: + QDeclarativeXmlListModelPrivate() + : isComponentComplete(true), size(-1), highestRole(Qt::UserRole) + , reply(0), status(QDeclarativeXmlListModel::Null), progress(0.0) + , queryId(-1), roleObjects(), redirectCount(0) {} + + + void notifyQueryStarted(bool remoteSource) { + Q_Q(QDeclarativeXmlListModel); + progress = remoteSource ? qreal(0.0) : qreal(1.0); + status = QDeclarativeXmlListModel::Loading; + errorString.clear(); + emit q->progressChanged(progress); + emit q->statusChanged(status); + } + + void deleteReply() { + Q_Q(QDeclarativeXmlListModel); + if (reply) { + QObject::disconnect(reply, 0, q, 0); + reply->deleteLater(); + reply = 0; + } + } + + bool isComponentComplete; + QUrl src; + QString xml; + QString query; + QString namespaces; + int size; + QList roles; + QStringList roleNames; + int highestRole; + + QNetworkReply *reply; + QDeclarativeXmlListModel::Status status; + QString errorString; + qreal progress; + int queryId; + QStringList keyRoleResultsCache; + QList roleObjects; + + static void append_role(QDeclarativeListProperty *list, QDeclarativeXmlListModelRole *role); + static void clear_role(QDeclarativeListProperty *list); + QList > data; + int redirectCount; +}; + + +void QDeclarativeXmlListModelPrivate::append_role(QDeclarativeListProperty *list, QDeclarativeXmlListModelRole *role) +{ + QDeclarativeXmlListModel *_this = qobject_cast(list->object); + if (_this && role) { + int i = _this->d_func()->roleObjects.count(); + _this->d_func()->roleObjects.append(role); + if (_this->d_func()->roleNames.contains(role->name())) { + qmlInfo(role) << QObject::tr("\"%1\" duplicates a previous role name and will be disabled.").arg(role->name()); + return; + } + _this->d_func()->roles.insert(i, _this->d_func()->highestRole); + _this->d_func()->roleNames.insert(i, role->name()); + ++_this->d_func()->highestRole; + } +} + +//### clear needs to invalidate any cached data (in data table) as well +// (and the model should emit the appropriate signals) +void QDeclarativeXmlListModelPrivate::clear_role(QDeclarativeListProperty *list) +{ + QDeclarativeXmlListModel *_this = static_cast(list->object); + _this->d_func()->roles.clear(); + _this->d_func()->roleNames.clear(); + _this->d_func()->roleObjects.clear(); +} + +/*! + \qmlclass XmlListModel QDeclarativeXmlListModel + \ingroup qml-working-with-data + \since 4.7 + \brief The XmlListModel element is used to specify a read-only model using XPath expressions. + + XmlListModel is used to create a read-only model from XML data. It can be used as a data source + for view elements (such as ListView, PathView, GridView) and other elements that interact with model + data (such as \l Repeater). + + For example, if there is a XML document at http://www.mysite.com/feed.xml like this: + + \code + + + ... + + + A blog post + Sat, 07 Sep 2010 10:00:01 GMT + + + Another blog post + Sat, 07 Sep 2010 15:35:01 GMT + + + + \endcode + + A XmlListModel could create a model from this data, like this: + + \qml + import QtQuick 1.0 + + XmlListModel { + id: xmlModel + source: "http://www.mysite.com/feed.xml" + query: "/rss/channel/item" + + XmlRole { name: "title"; query: "title/string()" } + XmlRole { name: "pubDate"; query: "pubDate/string()" } + } + \endqml + + The \l {XmlListModel::query}{query} value of "/rss/channel/item" specifies that the XmlListModel should generate + a model item for each \c in the XML document. + + The XmlRole objects define the + model item attributes. Here, each model item will have \c title and \c pubDate + attributes that match the \c title and \c pubDate values of its corresponding \c . + (See \l XmlRole::query for more examples of valid XPath expressions for XmlRole.) + + The model could be used in a ListView, like this: + + \qml + ListView { + width: 180; height: 300 + model: xmlModel + delegate: Text { text: title + ": " + pubDate } + } + \endqml + + \image qml-xmllistmodel-example.png + + The XmlListModel data is loaded asynchronously, and \l status + is set to \c XmlListModel.Ready when loading is complete. + Note this means when XmlListModel is used for a view, the view is not + populated until the model is loaded. + + + \section2 Using key XML roles + + You can define certain roles as "keys" so that when reload() is called, + the model will only add and refresh data that contains new values for + these keys. + + For example, if above role for "pubDate" was defined like this instead: + + \qml + XmlRole { name: "pubDate"; query: "pubDate/string()"; isKey: true } + \endqml + + Then when reload() is called, the model will only add and reload + items with a "pubDate" value that is not already + present in the model. + + This is useful when displaying the contents of XML documents that + are incrementally updated (such as RSS feeds) to avoid repainting the + entire contents of a model in a view. + + If multiple key roles are specified, the model only adds and reload items + with a combined value of all key roles that is not already present in + the model. + + \sa {RSS News} +*/ + +QDeclarativeXmlListModel::QDeclarativeXmlListModel(QObject *parent) + : QListModelInterface(*(new QDeclarativeXmlListModelPrivate), parent) +{ +} + +QDeclarativeXmlListModel::~QDeclarativeXmlListModel() +{ +} + +/*! + \qmlproperty list XmlListModel::roles + + The roles to make available for this model. +*/ +QDeclarativeListProperty QDeclarativeXmlListModel::roleObjects() +{ + Q_D(QDeclarativeXmlListModel); + QDeclarativeListProperty list(this, d->roleObjects); + list.append = &QDeclarativeXmlListModelPrivate::append_role; + list.clear = &QDeclarativeXmlListModelPrivate::clear_role; + return list; +} + +QHash QDeclarativeXmlListModel::data(int index, const QList &roles) const +{ + Q_D(const QDeclarativeXmlListModel); + QHash rv; + for (int i = 0; i < roles.size(); ++i) { + int role = roles.at(i); + int roleIndex = d->roles.indexOf(role); + rv.insert(role, roleIndex == -1 ? QVariant() : d->data.value(roleIndex).value(index)); + } + return rv; +} + +QVariant QDeclarativeXmlListModel::data(int index, int role) const +{ + Q_D(const QDeclarativeXmlListModel); + int roleIndex = d->roles.indexOf(role); + return (roleIndex == -1) ? QVariant() : d->data.value(roleIndex).value(index); +} + +/*! + \qmlproperty int XmlListModel::count + The number of data entries in the model. +*/ +int QDeclarativeXmlListModel::count() const +{ + Q_D(const QDeclarativeXmlListModel); + return d->size; +} + +QList QDeclarativeXmlListModel::roles() const +{ + Q_D(const QDeclarativeXmlListModel); + return d->roles; +} + +QString QDeclarativeXmlListModel::toString(int role) const +{ + Q_D(const QDeclarativeXmlListModel); + int index = d->roles.indexOf(role); + if (index == -1) + return QString(); + return d->roleNames.at(index); +} + +/*! + \qmlproperty url XmlListModel::source + The location of the XML data source. + + If both \c source and \l xml are set, \l xml is used. +*/ +QUrl QDeclarativeXmlListModel::source() const +{ + Q_D(const QDeclarativeXmlListModel); + return d->src; +} + +void QDeclarativeXmlListModel::setSource(const QUrl &src) +{ + Q_D(QDeclarativeXmlListModel); + if (d->src != src) { + d->src = src; + if (d->xml.isEmpty()) // src is only used if d->xml is not set + reload(); + emit sourceChanged(); + } +} + +/*! + \qmlproperty string XmlListModel::xml + This property holds the XML data for this model, if set. + + The text is assumed to be UTF-8 encoded. + + If both \l source and \c xml are set, \c xml is used. +*/ +QString QDeclarativeXmlListModel::xml() const +{ + Q_D(const QDeclarativeXmlListModel); + return d->xml; +} + +void QDeclarativeXmlListModel::setXml(const QString &xml) +{ + Q_D(QDeclarativeXmlListModel); + if (d->xml != xml) { + d->xml = xml; + reload(); + emit xmlChanged(); + } +} + +/*! + \qmlproperty string XmlListModel::query + An absolute XPath query representing the base query for creating model items + from this model's XmlRole objects. The query should start with '/' or '//'. +*/ +QString QDeclarativeXmlListModel::query() const +{ + Q_D(const QDeclarativeXmlListModel); + return d->query; +} + +void QDeclarativeXmlListModel::setQuery(const QString &query) +{ + Q_D(QDeclarativeXmlListModel); + if (!query.startsWith(QLatin1Char('/'))) { + qmlInfo(this) << QCoreApplication::translate("QDeclarativeXmlRoleList", "An XmlListModel query must start with '/' or \"//\""); + return; + } + + if (d->query != query) { + d->query = query; + reload(); + emit queryChanged(); + } +} + +/*! + \qmlproperty string XmlListModel::namespaceDeclarations + The namespace declarations to be used in the XPath queries. + + The namespaces should be declared as in XQuery. For example, if a requested document + at http://mysite.com/feed.xml uses the namespace "http://www.w3.org/2005/Atom", + this can be declared as the default namespace: + + \qml + XmlListModel { + source: "http://mysite.com/feed.xml" + query: "/feed/entry" + namespaceDeclarations: "declare default element namespace 'http://www.w3.org/2005/Atom';" + + XmlRole { name: "title"; query: "title/string()" } + } + \endqml +*/ +QString QDeclarativeXmlListModel::namespaceDeclarations() const +{ + Q_D(const QDeclarativeXmlListModel); + return d->namespaces; +} + +void QDeclarativeXmlListModel::setNamespaceDeclarations(const QString &declarations) +{ + Q_D(QDeclarativeXmlListModel); + if (d->namespaces != declarations) { + d->namespaces = declarations; + reload(); + emit namespaceDeclarationsChanged(); + } +} + +/*! + \qmlmethod object XmlListModel::get(int index) + + Returns the item at \a index in the model. + + For example, for a model like this: + + \qml + XmlListModel { + id: model + source: "http://mysite.com/feed.xml" + query: "/feed/entry" + XmlRole { name: "title"; query: "title/string()" } + } + \endqml + + This will access the \c title value for the first item in the model: + + \js + var title = model.get(0).title; + \endjs +*/ +QScriptValue QDeclarativeXmlListModel::get(int index) const +{ + Q_D(const QDeclarativeXmlListModel); + + QScriptEngine *sengine = QDeclarativeEnginePrivate::getScriptEngine(qmlContext(this)->engine()); + if (index < 0 || index >= count()) + return sengine->undefinedValue(); + + QScriptValue sv = sengine->newObject(); + for (int i=0; iroleObjects.count(); i++) + sv.setProperty(d->roleObjects[i]->name(), sengine->toScriptValue(d->data.value(i).value(index))); + return sv; +} + +/*! + \qmlproperty enumeration XmlListModel::status + Specifies the model loading status, which can be one of the following: + + \list + \o XmlListModel.Null - No XML data has been set for this model. + \o XmlListModel.Ready - The XML data has been loaded into the model. + \o XmlListModel.Loading - The model is in the process of reading and loading XML data. + \o XmlListModel.Error - An error occurred while the model was loading. See errorString() for details + about the error. + \endlist + + \sa progress + +*/ +QDeclarativeXmlListModel::Status QDeclarativeXmlListModel::status() const +{ + Q_D(const QDeclarativeXmlListModel); + return d->status; +} + +/*! + \qmlproperty real XmlListModel::progress + + This indicates the current progress of the downloading of the XML data + source. This value ranges from 0.0 (no data downloaded) to + 1.0 (all data downloaded). If the XML data is not from a remote source, + the progress becomes 1.0 as soon as the data is read. + + Note that when the progress is 1.0, the XML data has been downloaded, but + it is yet to be loaded into the model at this point. Use the status + property to find out when the XML data has been read and loaded into + the model. + + \sa status, source +*/ +qreal QDeclarativeXmlListModel::progress() const +{ + Q_D(const QDeclarativeXmlListModel); + return d->progress; +} + +/*! + \qmlmethod void XmlListModel::errorString() + + Returns a string description of the last error that occurred + if \l status is XmlListModel::Error. +*/ +QString QDeclarativeXmlListModel::errorString() const +{ + Q_D(const QDeclarativeXmlListModel); + return d->errorString; +} + +void QDeclarativeXmlListModel::classBegin() +{ + Q_D(QDeclarativeXmlListModel); + d->isComponentComplete = false; + + QDeclarativeXmlQueryEngine *queryEngine = QDeclarativeXmlQueryEngine::instance(qmlEngine(this)); + connect(queryEngine, SIGNAL(queryCompleted(QDeclarativeXmlQueryResult)), + SLOT(queryCompleted(QDeclarativeXmlQueryResult))); + connect(queryEngine, SIGNAL(error(void*,QString)), + SLOT(queryError(void*,QString))); +} + +void QDeclarativeXmlListModel::componentComplete() +{ + Q_D(QDeclarativeXmlListModel); + d->isComponentComplete = true; + reload(); +} + +/*! + \qmlmethod XmlListModel::reload() + + Reloads the model. + + If no key roles have been specified, all existing model + data is removed, and the model is rebuilt from scratch. + + Otherwise, items are only added if the model does not already + contain items with matching key role values. + + \sa {Using key XML roles}, XmlRole::isKey +*/ +void QDeclarativeXmlListModel::reload() +{ + Q_D(QDeclarativeXmlListModel); + + if (!d->isComponentComplete) + return; + + QDeclarativeXmlQueryEngine::instance(qmlEngine(this))->abort(d->queryId); + d->queryId = -1; + + if (d->size < 0) + d->size = 0; + + if (d->reply) { + d->reply->abort(); + d->deleteReply(); + } + + if (!d->xml.isEmpty()) { + d->queryId = QDeclarativeXmlQueryEngine::instance(qmlEngine(this))->doQuery(d->query, d->namespaces, d->xml.toUtf8(), &d->roleObjects, d->keyRoleResultsCache); + d->notifyQueryStarted(false); + + } else if (d->src.isEmpty()) { + d->queryId = XMLLISTMODEL_CLEAR_ID; + d->notifyQueryStarted(false); + QTimer::singleShot(0, this, SLOT(dataCleared())); + + } else { + d->notifyQueryStarted(true); + QNetworkRequest req(d->src); + req.setRawHeader("Accept", "application/xml,*/*"); + d->reply = qmlContext(this)->engine()->networkAccessManager()->get(req); + QObject::connect(d->reply, SIGNAL(finished()), this, SLOT(requestFinished())); + QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)), + this, SLOT(requestProgress(qint64,qint64))); + } +} + +#define XMLLISTMODEL_MAX_REDIRECT 16 + +void QDeclarativeXmlListModel::requestFinished() +{ + Q_D(QDeclarativeXmlListModel); + + d->redirectCount++; + if (d->redirectCount < XMLLISTMODEL_MAX_REDIRECT) { + QVariant redirect = d->reply->attribute(QNetworkRequest::RedirectionTargetAttribute); + if (redirect.isValid()) { + QUrl url = d->reply->url().resolved(redirect.toUrl()); + d->deleteReply(); + setSource(url); + return; + } + } + d->redirectCount = 0; + + if (d->reply->error() != QNetworkReply::NoError) { + d->errorString = d->reply->errorString(); + d->deleteReply(); + + int count = this->count(); + d->data.clear(); + d->size = 0; + if (count > 0) { + emit itemsRemoved(0, count); + emit countChanged(); + } + + d->status = Error; + d->queryId = -1; + emit statusChanged(d->status); + } else { + QByteArray data = d->reply->readAll(); + if (data.isEmpty()) { + d->queryId = XMLLISTMODEL_CLEAR_ID; + QTimer::singleShot(0, this, SLOT(dataCleared())); + } else { + d->queryId = QDeclarativeXmlQueryEngine::instance(qmlEngine(this))->doQuery(d->query, d->namespaces, data, &d->roleObjects, d->keyRoleResultsCache); + } + d->deleteReply(); + + d->progress = 1.0; + emit progressChanged(d->progress); + } +} + +void QDeclarativeXmlListModel::requestProgress(qint64 received, qint64 total) +{ + Q_D(QDeclarativeXmlListModel); + if (d->status == Loading && total > 0) { + d->progress = qreal(received)/total; + emit progressChanged(d->progress); + } +} + +void QDeclarativeXmlListModel::dataCleared() +{ + Q_D(QDeclarativeXmlListModel); + QDeclarativeXmlQueryResult r; + r.queryId = XMLLISTMODEL_CLEAR_ID; + r.size = 0; + r.removed << qMakePair(0, count()); + r.keyRoleResultsCache = d->keyRoleResultsCache; + queryCompleted(r); +} + +void QDeclarativeXmlListModel::queryError(void* object, const QString& error) +{ + // Be extra careful, object may no longer exist, it's just an ID. + Q_D(QDeclarativeXmlListModel); + for (int i=0; iroleObjects.count(); i++) { + if (d->roleObjects.at(i) == static_cast(object)) { + qmlInfo(d->roleObjects.at(i)) << QObject::tr("invalid query: \"%1\"").arg(error); + return; + } + } + qmlInfo(this) << QObject::tr("invalid query: \"%1\"").arg(error); +} + +void QDeclarativeXmlListModel::queryCompleted(const QDeclarativeXmlQueryResult &result) +{ + Q_D(QDeclarativeXmlListModel); + if (result.queryId != d->queryId) + return; + + int origCount = d->size; + bool sizeChanged = result.size != d->size; + + d->size = result.size; + d->data = result.data; + d->keyRoleResultsCache = result.keyRoleResultsCache; + d->status = Ready; + d->errorString.clear(); + d->queryId = -1; + + bool hasKeys = false; + for (int i=0; iroleObjects.count(); i++) { + if (d->roleObjects[i]->isKey()) { + hasKeys = true; + break; + } + } + if (!hasKeys) { + if (!(origCount == 0 && d->size == 0)) { + emit itemsRemoved(0, origCount); + emit itemsInserted(0, d->size); + emit countChanged(); + } + + } else { + for (int i=0; istatus); +} + +QT_END_NAMESPACE + +#include diff --git a/src/declarative/util/qdeclarativexmllistmodel_p.h b/src/declarative/util/qdeclarativexmllistmodel_p.h new file mode 100644 index 00000000..cb0e12b0 --- /dev/null +++ b/src/declarative/util/qdeclarativexmllistmodel_p.h @@ -0,0 +1,213 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEXMLLISTMODEL_H +#define QDECLARATIVEXMLLISTMODEL_H + +#include +#include + +#include +#include +#include + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeContext; +class QDeclarativeXmlListModelRole; +class QDeclarativeXmlListModelPrivate; + +struct QDeclarativeXmlQueryResult { + int queryId; + int size; + QList > data; + QList > inserted; + QList > removed; + QStringList keyRoleResultsCache; +}; + +class Q_AUTOTEST_EXPORT QDeclarativeXmlListModel : public QListModelInterface, public QDeclarativeParserStatus +{ + Q_OBJECT + Q_INTERFACES(QDeclarativeParserStatus) + Q_ENUMS(Status) + + Q_PROPERTY(Status status READ status NOTIFY statusChanged) + Q_PROPERTY(qreal progress READ progress NOTIFY progressChanged) + Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) + Q_PROPERTY(QString xml READ xml WRITE setXml NOTIFY xmlChanged) + Q_PROPERTY(QString query READ query WRITE setQuery NOTIFY queryChanged) + Q_PROPERTY(QString namespaceDeclarations READ namespaceDeclarations WRITE setNamespaceDeclarations NOTIFY namespaceDeclarationsChanged) + Q_PROPERTY(QDeclarativeListProperty roles READ roleObjects) + Q_PROPERTY(int count READ count NOTIFY countChanged) + Q_CLASSINFO("DefaultProperty", "roles") + +public: + QDeclarativeXmlListModel(QObject *parent = 0); + ~QDeclarativeXmlListModel(); + + virtual QHash data(int index, const QList &roles = (QList())) const; + virtual QVariant data(int index, int role) const; + virtual int count() const; + virtual QList roles() const; + virtual QString toString(int role) const; + + QDeclarativeListProperty roleObjects(); + + QUrl source() const; + void setSource(const QUrl&); + + QString xml() const; + void setXml(const QString&); + + QString query() const; + void setQuery(const QString&); + + QString namespaceDeclarations() const; + void setNamespaceDeclarations(const QString&); + + Q_INVOKABLE QScriptValue get(int index) const; + + enum Status { Null, Ready, Loading, Error }; + Status status() const; + qreal progress() const; + + Q_INVOKABLE QString errorString() const; + + virtual void classBegin(); + virtual void componentComplete(); + +Q_SIGNALS: + void statusChanged(QDeclarativeXmlListModel::Status); + void progressChanged(qreal progress); + void countChanged(); + void sourceChanged(); + void xmlChanged(); + void queryChanged(); + void namespaceDeclarationsChanged(); + +public Q_SLOTS: + // ### need to use/expose Expiry to guess when to call this? + // ### property to auto-call this on reasonable Expiry? + // ### LastModified/Age also useful to guess. + // ### Probably also applies to other network-requesting types. + void reload(); + +private Q_SLOTS: + void requestFinished(); + void requestProgress(qint64,qint64); + void dataCleared(); + void queryCompleted(const QDeclarativeXmlQueryResult &); + void queryError(void* object, const QString& error); + +private: + Q_DECLARE_PRIVATE(QDeclarativeXmlListModel) + Q_DISABLE_COPY(QDeclarativeXmlListModel) +}; + +class Q_AUTOTEST_EXPORT QDeclarativeXmlListModelRole : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_PROPERTY(QString query READ query WRITE setQuery NOTIFY queryChanged) + Q_PROPERTY(bool isKey READ isKey WRITE setIsKey NOTIFY isKeyChanged) +public: + QDeclarativeXmlListModelRole() : m_isKey(false) {} + ~QDeclarativeXmlListModelRole() {} + + QString name() const { return m_name; } + void setName(const QString &name) { + if (name == m_name) + return; + m_name = name; + emit nameChanged(); + } + + QString query() const { return m_query; } + void setQuery(const QString &query) + { + if (query.startsWith(QLatin1Char('/'))) { + qmlInfo(this) << tr("An XmlRole query must not start with '/'"); + return; + } + if (m_query == query) + return; + m_query = query; + emit queryChanged(); + } + + bool isKey() const { return m_isKey; } + void setIsKey(bool b) { + if (m_isKey == b) + return; + m_isKey = b; + emit isKeyChanged(); + } + + bool isValid() { + return !m_name.isEmpty() && !m_query.isEmpty(); + } + +Q_SIGNALS: + void nameChanged(); + void queryChanged(); + void isKeyChanged(); + +private: + QString m_name; + QString m_query; + bool m_isKey; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeXmlListModel) +QML_DECLARE_TYPE(QDeclarativeXmlListModelRole) + +QT_END_HEADER + +#endif // QDECLARATIVEXMLLISTMODEL_H diff --git a/src/declarative/util/qlistmodelinterface.cpp b/src/declarative/util/qlistmodelinterface.cpp new file mode 100644 index 00000000..847c5e1a --- /dev/null +++ b/src/declarative/util/qlistmodelinterface.cpp @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclaractive module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qlistmodelinterface_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \internal + \class QListModelInterface + \since 4.7 + \brief The QListModelInterface class can be subclassed to provide C++ models to QDeclarativeGraphics Views + + This class is comprised primarily of pure virtual functions which + you must implement in a subclass. You can then use the subclass + as a model for a QDeclarativeGraphics view, such as a QDeclarativeListView. +*/ + +/*! \fn QListModelInterface::QListModelInterface(QObject *parent) + Constructs a QListModelInterface with the specified \a parent. +*/ + + /*! \fn QListModelInterface::QListModelInterface(QObjectPrivate &dd, QObject *parent) + + \internal + */ + +/*! \fn QListModelInterface::~QListModelInterface() + The destructor is virtual. + */ + +/*! \fn int QListModelInterface::count() const + Returns the number of data entries in the model. +*/ + +/*! \fn QVariant QListModelInterface::data(int index, int role) const + Returns the data at the given \a index for the specified \a roles. +*/ + +/*! \fn QList QListModelInterface::roles() const + Returns the list of roles for which the list model interface + provides data. +*/ + +/*! \fn QString QListModelInterface::toString(int role) const + Returns a string description of the specified \a role. +*/ + +/*! \fn void QListModelInterface::itemsInserted(int index, int count) + Emit this signal when \a count items are inserted at \a index. + */ + +/*! \fn void QListModelInterface::itemsRemoved(int index, int count) + Emit this signal when \a count items are removed at \a index. + */ + +/*! \fn void QListModelInterface::itemsMoved(int from, int to, int count) + Emit this signal when \a count items are moved from index \a from + to index \a to. + */ + +/*! \fn void QListModelInterface::itemsChanged(int index, int count, const QList &roles) + Emit this signal when \a count items at \a index have had their + \a roles changed. + */ + +QT_END_NAMESPACE diff --git a/src/declarative/util/qlistmodelinterface_p.h b/src/declarative/util/qlistmodelinterface_p.h new file mode 100644 index 00000000..ea68b12e --- /dev/null +++ b/src/declarative/util/qlistmodelinterface_p.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QLISTMODELINTERFACE_H +#define QLISTMODELINTERFACE_H + +#include +#include + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class Q_DECLARATIVE_PRIVATE_EXPORT QListModelInterface : public QObject +{ + Q_OBJECT + public: + QListModelInterface(QObject *parent = 0) : QObject(parent) {} + virtual ~QListModelInterface() {} + + virtual int count() const = 0; + virtual QVariant data(int index, int role) const = 0; + + virtual QList roles() const = 0; + virtual QString toString(int role) const = 0; + + Q_SIGNALS: + void itemsInserted(int index, int count); + void itemsRemoved(int index, int count); + void itemsMoved(int from, int to, int count); + void itemsChanged(int index, int count, const QList &roles); + + protected: + QListModelInterface(QObjectPrivate &dd, QObject *parent) + : QObject(dd, parent) {} +}; + + +QT_END_NAMESPACE + +QT_END_HEADER +#endif //QTREEMODELINTERFACE_H diff --git a/src/declarative/util/util.pri b/src/declarative/util/util.pri new file mode 100644 index 00000000..62fa8f16 --- /dev/null +++ b/src/declarative/util/util.pri @@ -0,0 +1,72 @@ +INCLUDEPATH += $$PWD + +SOURCES += \ + $$PWD/qdeclarativeapplication.cpp \ + $$PWD/qdeclarativeutilmodule.cpp\ + $$PWD/qdeclarativeview.cpp \ + $$PWD/qdeclarativeconnections.cpp \ + $$PWD/qdeclarativepackage.cpp \ + $$PWD/qdeclarativeanimation.cpp \ + $$PWD/qdeclarativesystempalette.cpp \ + $$PWD/qdeclarativespringanimation.cpp \ + $$PWD/qdeclarativesmoothedanimation.cpp \ + $$PWD/qdeclarativestate.cpp\ + $$PWD/qdeclarativetransitionmanager.cpp \ + $$PWD/qdeclarativestateoperations.cpp \ + $$PWD/qdeclarativepropertychanges.cpp \ + $$PWD/qdeclarativestategroup.cpp \ + $$PWD/qdeclarativetransition.cpp \ + $$PWD/qdeclarativelistmodel.cpp\ + $$PWD/qdeclarativelistaccessor.cpp \ + $$PWD/qdeclarativeopenmetaobject.cpp \ + $$PWD/qdeclarativetimeline.cpp \ + $$PWD/qdeclarativetimer.cpp \ + $$PWD/qdeclarativebind.cpp \ + $$PWD/qdeclarativepropertymap.cpp \ + $$PWD/qdeclarativepixmapcache.cpp \ + $$PWD/qdeclarativebehavior.cpp \ + $$PWD/qdeclarativefontloader.cpp \ + $$PWD/qdeclarativestyledtext.cpp \ + $$PWD/qdeclarativelistmodelworkeragent.cpp \ + $$PWD/qlistmodelinterface.cpp + +HEADERS += \ + $$PWD/qdeclarativeapplication_p.h \ + $$PWD/qdeclarativeutilmodule_p.h\ + $$PWD/qdeclarativeview.h \ + $$PWD/qdeclarativeconnections_p.h \ + $$PWD/qdeclarativepackage_p.h \ + $$PWD/qdeclarativeanimation_p.h \ + $$PWD/qdeclarativeanimation_p_p.h \ + $$PWD/qdeclarativesystempalette_p.h \ + $$PWD/qdeclarativespringanimation_p.h \ + $$PWD/qdeclarativesmoothedanimation_p.h \ + $$PWD/qdeclarativesmoothedanimation_p_p.h \ + $$PWD/qdeclarativestate_p.h\ + $$PWD/qdeclarativestateoperations_p.h \ + $$PWD/qdeclarativepropertychanges_p.h \ + $$PWD/qdeclarativestate_p_p.h\ + $$PWD/qdeclarativetransitionmanager_p_p.h \ + $$PWD/qdeclarativestategroup_p.h \ + $$PWD/qdeclarativetransition_p.h \ + $$PWD/qdeclarativelistmodel_p.h\ + $$PWD/qdeclarativelistmodel_p_p.h\ + $$PWD/qdeclarativelistaccessor_p.h \ + $$PWD/qdeclarativeopenmetaobject_p.h \ + $$PWD/qdeclarativenullablevalue_p_p.h \ + $$PWD/qdeclarativetimeline_p_p.h \ + $$PWD/qdeclarativetimer_p.h \ + $$PWD/qdeclarativebind_p.h \ + $$PWD/qdeclarativepropertymap.h \ + $$PWD/qdeclarativepixmapcache_p.h \ + $$PWD/qdeclarativebehavior_p.h \ + $$PWD/qdeclarativefontloader_p.h \ + $$PWD/qdeclarativestyledtext_p.h \ + $$PWD/qdeclarativelistmodelworkeragent_p.h \ + $$PWD/qlistmodelinterface_p.h + +contains(QT_CONFIG, xmlpatterns) { + QT+=xmlpatterns + SOURCES += $$PWD/qdeclarativexmllistmodel.cpp + HEADERS += $$PWD/qdeclarativexmllistmodel_p.h +} diff --git a/src/imports/folderlistmodel/folderlistmodel.pro b/src/imports/folderlistmodel/folderlistmodel.pro new file mode 100644 index 00000000..0f639797 --- /dev/null +++ b/src/imports/folderlistmodel/folderlistmodel.pro @@ -0,0 +1,26 @@ +TARGET = qmlfolderlistmodelplugin +TARGETPATH = Qt/labs/folderlistmodel +include(../qimportbase.pri) + +QT += declarative script + +SOURCES += qdeclarativefolderlistmodel.cpp plugin.cpp +HEADERS += qdeclarativefolderlistmodel.h + +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/imports/$$TARGETPATH +target.path = $$[QT_INSTALL_IMPORTS]/$$TARGETPATH + +qmldir.files += $$PWD/qmldir +qmldir.path += $$[QT_INSTALL_IMPORTS]/$$TARGETPATH + +symbian:{ + TARGET.UID3 = 0x20021320 + + isEmpty(DESTDIR):importFiles.files = qmlfolderlistmodelplugin$${QT_LIBINFIX}.dll qmldir + else:importFiles.files = $$DESTDIR/qmlfolderlistmodelplugin$${QT_LIBINFIX}.dll qmldir + importFiles.path = $$QT_IMPORTS_BASE_DIR/$$TARGETPATH + + DEPLOYMENT += importFiles +} + +INSTALLS += target qmldir diff --git a/src/imports/folderlistmodel/plugin.cpp b/src/imports/folderlistmodel/plugin.cpp new file mode 100644 index 00000000..61054a1e --- /dev/null +++ b/src/imports/folderlistmodel/plugin.cpp @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include "qdeclarativefolderlistmodel.h" + +QT_BEGIN_NAMESPACE + +//![class decl] +class QmlFolderListModelPlugin : public QDeclarativeExtensionPlugin +{ + Q_OBJECT +public: + virtual void registerTypes(const char *uri) + { + Q_ASSERT(QLatin1String(uri) == QLatin1String("Qt.labs.folderlistmodel")); +#ifndef QT_NO_DIRMODEL + qmlRegisterType(uri,1,0,"FolderListModel"); +#endif + } +}; +//![class decl] + +QT_END_NAMESPACE + +#include "plugin.moc" + +//![plugin export decl] +Q_EXPORT_PLUGIN2(qmlfolderlistmodelplugin, QT_PREPEND_NAMESPACE(QmlFolderListModelPlugin)); +//![plugin export decl] + diff --git a/src/imports/folderlistmodel/qdeclarativefolderlistmodel.cpp b/src/imports/folderlistmodel/qdeclarativefolderlistmodel.cpp new file mode 100644 index 00000000..efc0b8be --- /dev/null +++ b/src/imports/folderlistmodel/qdeclarativefolderlistmodel.cpp @@ -0,0 +1,485 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//![code] +#include "qdeclarativefolderlistmodel.h" +#include +#include +#include + +#ifndef QT_NO_DIRMODEL + +QT_BEGIN_NAMESPACE + +class QDeclarativeFolderListModelPrivate +{ +public: + QDeclarativeFolderListModelPrivate() + : sortField(QDeclarativeFolderListModel::Name), sortReversed(false), count(0) { + nameFilters << QLatin1String("*"); + } + + void updateSorting() { + QDir::SortFlags flags = 0; + switch(sortField) { + case QDeclarativeFolderListModel::Unsorted: + flags |= QDir::Unsorted; + break; + case QDeclarativeFolderListModel::Name: + flags |= QDir::Name; + break; + case QDeclarativeFolderListModel::Time: + flags |= QDir::Time; + break; + case QDeclarativeFolderListModel::Size: + flags |= QDir::Size; + break; + case QDeclarativeFolderListModel::Type: + flags |= QDir::Type; + break; + } + + if (sortReversed) + flags |= QDir::Reversed; + + model.setSorting(flags); + } + + QDirModel model; + QUrl folder; + QStringList nameFilters; + QModelIndex folderIndex; + QDeclarativeFolderListModel::SortField sortField; + bool sortReversed; + int count; +}; + +/*! + \qmlclass FolderListModel QDeclarativeFolderListModel + \ingroup qml-working-with-data + \brief The FolderListModel provides a model of the contents of a file system folder. + + FolderListModel provides access to information about the contents of a folder + in the local file system, exposing a list of files to views and other data components. + + \note This type is made available by importing the \c Qt.labs.folderlistmodel module. + \e{Elements in the Qt.labs module are not guaranteed to remain compatible + in future versions.} + + \bold{import Qt.labs.folderlistmodel 1.0} + + The \l folder property specifies the folder to access. Information about the + files and directories in the folder is supplied via the model's interface. + Components access names and paths via the following roles: + + \list + \o fileName + \o filePath + \endlist + + Additionally a file entry can be differentiated from a folder entry via the + isFolder() method. + + \section1 Filtering + + Various properties can be set to filter the number of files and directories + exposed by the model. + + The \l nameFilters property can be set to contain a list of wildcard filters + that are applied to names of files and directories, causing only those that + match the filters to be exposed. + + Directories can be included or excluded using the \l showDirs property, and + navigation directories can also be excluded by setting the \l showDotAndDotDot + property to false. + + It is sometimes useful to limit the files and directories exposed to those + that the user can access. The \l showOnlyReadable property can be set to + enable this feature. + + \section1 Example Usage + + The following example shows a FolderListModel being used to provide a list + of QML files in a \l ListView: + + \snippet doc/src/snippets/declarative/folderlistmodel.qml 0 + + \section1 Path Separators + + Qt uses "/" as a universal directory separator in the same way that "/" is + used as a path separator in URLs. If you always use "/" as a directory + separator, Qt will translate your paths to conform to the underlying + operating system. + + \sa {QML Data Models} +*/ + +QDeclarativeFolderListModel::QDeclarativeFolderListModel(QObject *parent) + : QAbstractListModel(parent) +{ + QHash roles; + roles[FileNameRole] = "fileName"; + roles[FilePathRole] = "filePath"; + setRoleNames(roles); + + d = new QDeclarativeFolderListModelPrivate; + d->model.setFilter(QDir::AllDirs | QDir::Files | QDir::Drives | QDir::NoDotAndDotDot); + connect(&d->model, SIGNAL(rowsInserted(const QModelIndex&,int,int)) + , this, SLOT(inserted(const QModelIndex&,int,int))); + connect(&d->model, SIGNAL(rowsRemoved(const QModelIndex&,int,int)) + , this, SLOT(removed(const QModelIndex&,int,int))); + connect(&d->model, SIGNAL(dataChanged(const QModelIndex&,const QModelIndex&)) + , this, SLOT(handleDataChanged(const QModelIndex&,const QModelIndex&))); + connect(&d->model, SIGNAL(modelReset()), this, SLOT(refresh())); + connect(&d->model, SIGNAL(layoutChanged()), this, SLOT(refresh())); +} + +QDeclarativeFolderListModel::~QDeclarativeFolderListModel() +{ + delete d; +} + +QVariant QDeclarativeFolderListModel::data(const QModelIndex &index, int role) const +{ + QVariant rv; + QModelIndex modelIndex = d->model.index(index.row(), 0, d->folderIndex); + if (modelIndex.isValid()) { + if (role == FileNameRole) + rv = d->model.data(modelIndex, QDirModel::FileNameRole).toString(); + else if (role == FilePathRole) + rv = QUrl::fromLocalFile(d->model.data(modelIndex, QDirModel::FilePathRole).toString()); + } + return rv; +} + +/*! + \qmlproperty int FolderListModel::count + + Returns the number of items in the current folder that match the + filter criteria. +*/ +int QDeclarativeFolderListModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return d->count; +} + +/*! + \qmlproperty string FolderListModel::folder + + The \a folder property holds a URL for the folder that the model is + currently providing. + + The value is a URL expressed as a string, and must be a \c file: or \c qrc: + URL, or a relative URL. + + By default, the value is an invalid URL. +*/ +QUrl QDeclarativeFolderListModel::folder() const +{ + return d->folder; +} + +void QDeclarativeFolderListModel::setFolder(const QUrl &folder) +{ + if (folder == d->folder) + return; + QModelIndex index = d->model.index(folder.toLocalFile()); + if ((index.isValid() && d->model.isDir(index)) || folder.toLocalFile().isEmpty()) { + + d->folder = folder; + QMetaObject::invokeMethod(this, "refresh", Qt::QueuedConnection); + emit folderChanged(); + } +} + +/*! + \qmlproperty url FolderListModel::parentFolder + + Returns the URL of the parent of of the current \l folder. +*/ +QUrl QDeclarativeFolderListModel::parentFolder() const +{ + QString localFile = d->folder.toLocalFile(); + if (!localFile.isEmpty()) { + QDir dir(localFile); +#if defined(Q_OS_SYMBIAN) || defined(Q_OS_WIN) + if (dir.isRoot()) + dir.setPath(""); + else +#endif + dir.cdUp(); + localFile = dir.path(); + } else { + int pos = d->folder.path().lastIndexOf(QLatin1Char('/')); + if (pos == -1) + return QUrl(); + localFile = d->folder.path().left(pos); + } + return QUrl::fromLocalFile(localFile); +} + +/*! + \qmlproperty list FolderListModel::nameFilters + + The \a nameFilters property contains a list of file name filters. + The filters may include the ? and * wildcards. + + The example below filters on PNG and JPEG files: + + \qml + FolderListModel { + nameFilters: [ "*.png", "*.jpg" ] + } + \endqml + + \note Directories are not excluded by filters. +*/ +QStringList QDeclarativeFolderListModel::nameFilters() const +{ + return d->nameFilters; +} + +void QDeclarativeFolderListModel::setNameFilters(const QStringList &filters) +{ + d->nameFilters = filters; + d->model.setNameFilters(d->nameFilters); +} + +void QDeclarativeFolderListModel::classBegin() +{ +} + +void QDeclarativeFolderListModel::componentComplete() +{ + if (!d->folder.isValid() || d->folder.toLocalFile().isEmpty() || !QDir().exists(d->folder.toLocalFile())) + setFolder(QUrl(QLatin1String("file://")+QDir::currentPath())); + + if (!d->folderIndex.isValid()) + QMetaObject::invokeMethod(this, "refresh", Qt::QueuedConnection); +} + +/*! + \qmlproperty enumeration FolderListModel::sortField + + The \a sortField property contains field to use for sorting. sortField + may be one of: + \list + \o Unsorted - no sorting is applied. The order is system default. + \o Name - sort by filename + \o Time - sort by time modified + \o Size - sort by file size + \o Type - sort by file type (extension) + \endlist + + \sa sortReversed +*/ +QDeclarativeFolderListModel::SortField QDeclarativeFolderListModel::sortField() const +{ + return d->sortField; +} + +void QDeclarativeFolderListModel::setSortField(SortField field) +{ + if (field != d->sortField) { + d->sortField = field; + d->updateSorting(); + } +} + +/*! + \qmlproperty bool FolderListModel::sortReversed + + If set to true, reverses the sort order. The default is false. + + \sa sortField +*/ +bool QDeclarativeFolderListModel::sortReversed() const +{ + return d->sortReversed; +} + +void QDeclarativeFolderListModel::setSortReversed(bool rev) +{ + if (rev != d->sortReversed) { + d->sortReversed = rev; + d->updateSorting(); + } +} + +/*! + \qmlmethod bool FolderListModel::isFolder(int index) + + Returns true if the entry \a index is a folder; otherwise + returns false. +*/ +bool QDeclarativeFolderListModel::isFolder(int index) const +{ + if (index != -1) { + QModelIndex idx = d->model.index(index, 0, d->folderIndex); + if (idx.isValid()) + return d->model.isDir(idx); + } + return false; +} + +void QDeclarativeFolderListModel::refresh() +{ + d->folderIndex = QModelIndex(); + if (d->count) { + emit beginRemoveRows(QModelIndex(), 0, d->count-1); + d->count = 0; + emit endRemoveRows(); + } + d->folderIndex = d->model.index(d->folder.toLocalFile()); + int newcount = d->model.rowCount(d->folderIndex); + if (newcount) { + emit beginInsertRows(QModelIndex(), 0, newcount-1); + d->count = newcount; + emit endInsertRows(); + } +} + +void QDeclarativeFolderListModel::inserted(const QModelIndex &index, int start, int end) +{ + if (index == d->folderIndex) { + emit beginInsertRows(QModelIndex(), start, end); + d->count = d->model.rowCount(d->folderIndex); + emit endInsertRows(); + } +} + +void QDeclarativeFolderListModel::removed(const QModelIndex &index, int start, int end) +{ + if (index == d->folderIndex) { + emit beginRemoveRows(QModelIndex(), start, end); + d->count = d->model.rowCount(d->folderIndex); + emit endRemoveRows(); + } +} + +void QDeclarativeFolderListModel::handleDataChanged(const QModelIndex &start, const QModelIndex &end) +{ + if (start.parent() == d->folderIndex) + emit dataChanged(index(start.row(),0), index(end.row(),0)); +} + +/*! + \qmlproperty bool FolderListModel::showDirs + + If true, directories are included in the model; otherwise only files + are included. + + By default, this property is true. + + Note that the nameFilters are not applied to directories. + + \sa showDotAndDotDot +*/ +bool QDeclarativeFolderListModel::showDirs() const +{ + return d->model.filter() & QDir::AllDirs; +} + +void QDeclarativeFolderListModel::setShowDirs(bool on) +{ + if (!(d->model.filter() & QDir::AllDirs) == !on) + return; + if (on) + d->model.setFilter(d->model.filter() | QDir::AllDirs | QDir::Drives); + else + d->model.setFilter(d->model.filter() & ~(QDir::AllDirs | QDir::Drives)); +} + +/*! + \qmlproperty bool FolderListModel::showDotAndDotDot + + If true, the "." and ".." directories are included in the model; otherwise + they are excluded. + + By default, this property is false. + + \sa showDirs +*/ +bool QDeclarativeFolderListModel::showDotAndDotDot() const +{ + return !(d->model.filter() & QDir::NoDotAndDotDot); +} + +void QDeclarativeFolderListModel::setShowDotAndDotDot(bool on) +{ + if (!(d->model.filter() & QDir::NoDotAndDotDot) == on) + return; + if (on) + d->model.setFilter(d->model.filter() & ~QDir::NoDotAndDotDot); + else + d->model.setFilter(d->model.filter() | QDir::NoDotAndDotDot); +} + +/*! + \qmlproperty bool FolderListModel::showOnlyReadable + + If true, only readable files and directories are shown; otherwise all files + and directories are shown. + + By default, this property is false. + + \sa showDirs +*/ +bool QDeclarativeFolderListModel::showOnlyReadable() const +{ + return d->model.filter() & QDir::Readable; +} + +void QDeclarativeFolderListModel::setShowOnlyReadable(bool on) +{ + if (!(d->model.filter() & QDir::Readable) == !on) + return; + if (on) + d->model.setFilter(d->model.filter() | QDir::Readable); + else + d->model.setFilter(d->model.filter() & ~QDir::Readable); +} + +//![code] +QT_END_NAMESPACE + +#endif // QT_NO_DIRMODEL diff --git a/src/imports/folderlistmodel/qdeclarativefolderlistmodel.h b/src/imports/folderlistmodel/qdeclarativefolderlistmodel.h new file mode 100644 index 00000000..5ec2416b --- /dev/null +++ b/src/imports/folderlistmodel/qdeclarativefolderlistmodel.h @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEFOLDERLISTMODEL_H +#define QDECLARATIVEFOLDERLISTMODEL_H + +#include +#include +#include +#include + +#ifndef QT_NO_DIRMODEL + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeContext; +class QModelIndex; + +class QDeclarativeFolderListModelPrivate; + +//![class begin] +class QDeclarativeFolderListModel : public QAbstractListModel, public QDeclarativeParserStatus +{ + Q_OBJECT + Q_INTERFACES(QDeclarativeParserStatus) +//![class begin] + +//![class props] + Q_PROPERTY(QUrl folder READ folder WRITE setFolder NOTIFY folderChanged) + Q_PROPERTY(QUrl parentFolder READ parentFolder NOTIFY folderChanged) + Q_PROPERTY(QStringList nameFilters READ nameFilters WRITE setNameFilters) + Q_PROPERTY(SortField sortField READ sortField WRITE setSortField) + Q_PROPERTY(bool sortReversed READ sortReversed WRITE setSortReversed) + Q_PROPERTY(bool showDirs READ showDirs WRITE setShowDirs) + Q_PROPERTY(bool showDotAndDotDot READ showDotAndDotDot WRITE setShowDotAndDotDot) + Q_PROPERTY(bool showOnlyReadable READ showOnlyReadable WRITE setShowOnlyReadable) + Q_PROPERTY(int count READ count) +//![class props] + +//![abslistmodel] +public: + QDeclarativeFolderListModel(QObject *parent = 0); + ~QDeclarativeFolderListModel(); + + enum Roles { FileNameRole = Qt::UserRole+1, FilePathRole = Qt::UserRole+2 }; + + int rowCount(const QModelIndex &parent) const; + QVariant data(const QModelIndex &index, int role) const; +//![abslistmodel] + +//![count] + int count() const { return rowCount(QModelIndex()); } +//![count] + +//![prop funcs] + QUrl folder() const; + void setFolder(const QUrl &folder); + + QUrl parentFolder() const; + + QStringList nameFilters() const; + void setNameFilters(const QStringList &filters); + + enum SortField { Unsorted, Name, Time, Size, Type }; + SortField sortField() const; + void setSortField(SortField field); + Q_ENUMS(SortField) + + bool sortReversed() const; + void setSortReversed(bool rev); + + bool showDirs() const; + void setShowDirs(bool); + bool showDotAndDotDot() const; + void setShowDotAndDotDot(bool); + bool showOnlyReadable() const; + void setShowOnlyReadable(bool); +//![prop funcs] + +//![isfolder] + Q_INVOKABLE bool isFolder(int index) const; +//![isfolder] + +//![parserstatus] + virtual void classBegin(); + virtual void componentComplete(); +//![parserstatus] + +//![notifier] +Q_SIGNALS: + void folderChanged(); +//![notifier] + +//![class end] +private Q_SLOTS: + void refresh(); + void inserted(const QModelIndex &index, int start, int end); + void removed(const QModelIndex &index, int start, int end); + void handleDataChanged(const QModelIndex &start, const QModelIndex &end); + +private: + Q_DISABLE_COPY(QDeclarativeFolderListModel) + QDeclarativeFolderListModelPrivate *d; +}; +//![class end] + +QT_END_NAMESPACE + +//![qml decl] +QML_DECLARE_TYPE(QDeclarativeFolderListModel) +//![qml decl] + +QT_END_HEADER + +#endif // QT_NO_DIRMODEL + +#endif // QDECLARATIVEFOLDERLISTMODEL_H diff --git a/src/imports/folderlistmodel/qmldir b/src/imports/folderlistmodel/qmldir new file mode 100644 index 00000000..6e115bbc --- /dev/null +++ b/src/imports/folderlistmodel/qmldir @@ -0,0 +1 @@ +plugin qmlfolderlistmodelplugin diff --git a/src/imports/gestures/gestures.pro b/src/imports/gestures/gestures.pro new file mode 100644 index 00000000..2768cc9c --- /dev/null +++ b/src/imports/gestures/gestures.pro @@ -0,0 +1,26 @@ +TARGET = qmlgesturesplugin +TARGETPATH = Qt/labs/gestures +include(../qimportbase.pri) + +QT += declarative + +SOURCES += qdeclarativegesturearea.cpp plugin.cpp +HEADERS += qdeclarativegesturearea_p.h + +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/imports/$$TARGETPATH +target.path = $$[QT_INSTALL_IMPORTS]/$$TARGETPATH + +qmldir.files += $$PWD/qmldir +qmldir.path += $$[QT_INSTALL_IMPORTS]/$$TARGETPATH + +symbian:{ + TARGET.UID3 = 0x2002131F + + isEmpty(DESTDIR):importFiles.files = qmlgesturesplugin$${QT_LIBINFIX}.dll qmldir + else:importFiles.files = $$DESTDIR/qmlgesturesplugin$${QT_LIBINFIX}.dll qmldir + importFiles.path = $$QT_IMPORTS_BASE_DIR/$$TARGETPATH + + DEPLOYMENT += importFiles +} + +INSTALLS += target qmldir diff --git a/src/imports/gestures/plugin.cpp b/src/imports/gestures/plugin.cpp new file mode 100644 index 00000000..15c9d22e --- /dev/null +++ b/src/imports/gestures/plugin.cpp @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include "qdeclarativegesturearea_p.h" + +QT_BEGIN_NAMESPACE + +class GestureAreaQmlPlugin : public QDeclarativeExtensionPlugin +{ + Q_OBJECT +public: + virtual void registerTypes(const char *uri) + { + Q_ASSERT(QLatin1String(uri) == QLatin1String("Qt.labs.gestures")); +#ifndef QT_NO_GESTURES + qmlRegisterCustomType(uri,1,0, "GestureArea", new QDeclarativeGestureAreaParser); + + qmlRegisterUncreatableType(uri, 1, 0, "Gesture", QLatin1String("Do not create objects of this type.")); + qmlRegisterUncreatableType(uri, 1, 0, "PanGesture", QLatin1String("Do not create objects of this type.")); + qmlRegisterUncreatableType(uri, 1, 0, "TapGesture", QLatin1String("Do not create objects of this type.")); + qmlRegisterUncreatableType(uri, 1, 0, "TapAndHoldGesture", QLatin1String("Do not create objects of this type.")); + qmlRegisterUncreatableType(uri, 1, 0, "PinchGesture", QLatin1String("Do not create objects of this type.")); + qmlRegisterUncreatableType(uri, 1, 0, "SwipeGesture", QLatin1String("Do not create objects of this type.")); +#endif + } +}; + +QT_END_NAMESPACE + +#include "plugin.moc" + +Q_EXPORT_PLUGIN2(qmlgesturesplugin, QT_PREPEND_NAMESPACE(GestureAreaQmlPlugin)); diff --git a/src/imports/gestures/qdeclarativegesturearea.cpp b/src/imports/gestures/qdeclarativegesturearea.cpp new file mode 100644 index 00000000..afd92b06 --- /dev/null +++ b/src/imports/gestures/qdeclarativegesturearea.cpp @@ -0,0 +1,282 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativegesturearea_p.h" + +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include + +#ifndef QT_NO_GESTURES + +QT_BEGIN_NAMESPACE + +class QDeclarativeGestureAreaPrivate : public QDeclarativeItemPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeGestureArea) +public: + QDeclarativeGestureAreaPrivate() : componentcomplete(false), gesture(0) {} + + typedef QMap Bindings; + Bindings bindings; + + bool componentcomplete; + + QByteArray data; + + QGesture *gesture; + + bool gestureEvent(QGestureEvent *event); +}; + +/*! + \qmlclass GestureArea QDeclarativeGestureArea + \ingroup qml-basic-interaction-elements + + \brief The GestureArea item enables simple gesture handling. + \inherits Item + + A GestureArea is like a MouseArea, but it has signals for gesture events. + + \warning Elements in the Qt.labs module are not guaranteed to remain compatible + in future versions. + + \warning GestureArea is an experimental element whose development has + been discontinued. PinchArea is available in QtQuick 1.1 and handles + two finger gesture input. + + \note This element is only functional on devices with touch input. + + \qml + import Qt.labs.gestures 1.0 + + GestureArea { + anchors.fill: parent + // onPan: ... gesture.acceleration ... + // onPinch: ... gesture.rotationAngle ... + // onSwipe: ... + // onTapAndHold: ... + // onTap: ... + // onGesture: ... + } + \endqml + + Each signal has a \e gesture parameter that has the + properties of the gesture. + + \table + \header \o Signal \o Type \o Property \o Description + \row \o onTap \o point \o position \o the position of the tap + \row \o onTapAndHold \o point \o position \o the position of the tap + \row \o onPan \o real \o acceleration \o the acceleration of the pan + \row \o onPan \o point \o delta \o the offset from the previous input position to the current input + \row \o onPan \o point \o offset \o the total offset from the first input position to the current input position + \row \o onPan \o point \o lastOffset \o the previous value of offset + \row \o onPinch \o point \o centerPoint \o the midpoint between the two input points + \row \o onPinch \o point \o lastCenterPoint \o the previous value of centerPoint + \row \o onPinch \o point \o startCenterPoint \o the first value of centerPoint + \row \o onPinch \o real \o rotationAngle \o the angle covered by the gesture motion + \row \o onPinch \o real \o lastRotationAngle \o the previous value of rotationAngle + \row \o onPinch \o real \o totalRotationAngle \o the complete angle covered by the gesture + \row \o onPinch \o real \o scaleFactor \o the change in distance between the two input points + \row \o onPinch \o real \o lastScaleFactor \o the previous value of scaleFactor + \row \o onPinch \o real \o totalScaleFactor \o the complete scale factor of the gesture + \row \o onSwipe \o real \o swipeAngle \o the angle of the swipe + \endtable + + Custom gestures, handled by onGesture, will have custom properties. + + GestureArea is an invisible item: it is never painted. + + \sa MouseArea +*/ + +/*! + \internal + \class QDeclarativeGestureArea + \brief The QDeclarativeGestureArea class provides simple gesture handling. + +*/ +QDeclarativeGestureArea::QDeclarativeGestureArea(QDeclarativeItem *parent) : + QDeclarativeItem(*(new QDeclarativeGestureAreaPrivate), parent) +{ + setAcceptedMouseButtons(Qt::LeftButton); + setAcceptTouchEvents(true); +} + +QDeclarativeGestureArea::~QDeclarativeGestureArea() +{ +} + +QByteArray +QDeclarativeGestureAreaParser::compile(const QList &props) +{ + QByteArray rv; + QDataStream ds(&rv, QIODevice::WriteOnly); + + for(int ii = 0; ii < props.count(); ++ii) + { + QString propName = QString::fromUtf8(props.at(ii).name()); + Qt::GestureType type; + + if (propName == QLatin1String("onTap")) { + type = Qt::TapGesture; + } else if (propName == QLatin1String("onTapAndHold")) { + type = Qt::TapAndHoldGesture; + } else if (propName == QLatin1String("onPan")) { + type = Qt::PanGesture; + } else if (propName == QLatin1String("onPinch")) { + type = Qt::PinchGesture; + } else if (propName == QLatin1String("onSwipe")) { + type = Qt::SwipeGesture; + } else if (propName == QLatin1String("onGesture")) { + type = Qt::CustomGesture; + } else { + error(props.at(ii), QDeclarativeGestureArea::tr("Cannot assign to non-existent property \"%1\"").arg(propName)); + return QByteArray(); + } + + QList values = props.at(ii).assignedValues(); + + for (int i = 0; i < values.count(); ++i) { + const QVariant &value = values.at(i); + + if (value.userType() == qMetaTypeId()) { + error(props.at(ii), QDeclarativeGestureArea::tr("GestureArea: nested objects not allowed")); + return QByteArray(); + } else if (value.userType() == qMetaTypeId()) { + error(props.at(ii), QDeclarativeGestureArea::tr("GestureArea: syntax error")); + return QByteArray(); + } else { + QDeclarativeParser::Variant v = qvariant_cast(value); + if (v.isScript()) { + ds << propName; + ds << int(type); + ds << v.asScript(); + } else { + error(props.at(ii), QDeclarativeGestureArea::tr("GestureArea: script expected")); + return QByteArray(); + } + } + } + } + + return rv; +} + +void QDeclarativeGestureAreaParser::setCustomData(QObject *object, + const QByteArray &data) +{ + QDeclarativeGestureArea *ga = static_cast(object); + ga->d_func()->data = data; +} + + +void QDeclarativeGestureArea::connectSignals() +{ + Q_D(QDeclarativeGestureArea); + if (!d->componentcomplete) + return; + + QDataStream ds(d->data); + while (!ds.atEnd()) { + QString propName; + ds >> propName; + int gesturetype; + ds >> gesturetype; + QString script; + ds >> script; + QDeclarativeExpression *exp = new QDeclarativeExpression(qmlContext(this), this, script); + d->bindings.insert(Qt::GestureType(gesturetype),exp); + grabGesture(Qt::GestureType(gesturetype)); + } +} + +void QDeclarativeGestureArea::componentComplete() +{ + QDeclarativeItem::componentComplete(); + Q_D(QDeclarativeGestureArea); + d->componentcomplete=true; + connectSignals(); +} + +QGesture *QDeclarativeGestureArea::gesture() const +{ + Q_D(const QDeclarativeGestureArea); + return d->gesture; +} + +bool QDeclarativeGestureArea::sceneEvent(QEvent *event) +{ + Q_D(QDeclarativeGestureArea); + if (event->type() == QEvent::Gesture) + return d->gestureEvent(static_cast(event)); + return QDeclarativeItem::sceneEvent(event); +} + +bool QDeclarativeGestureAreaPrivate::gestureEvent(QGestureEvent *event) +{ + bool accept = true; + for (Bindings::Iterator it = bindings.begin(); it != bindings.end(); ++it) { + if ((gesture = event->gesture(it.key()))) { + QDeclarativeExpression *expr = it.value(); + expr->evaluate(); + if (expr->hasError()) + qmlInfo(q_func()) << expr->error(); + event->setAccepted(true); // XXX only if value returns true? + } + } + return accept; +} + +QT_END_NAMESPACE + +#endif // QT_NO_GESTURES diff --git a/src/imports/gestures/qdeclarativegesturearea_p.h b/src/imports/gestures/qdeclarativegesturearea_p.h new file mode 100644 index 00000000..8171b376 --- /dev/null +++ b/src/imports/gestures/qdeclarativegesturearea_p.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEGESTUREAREA_H +#define QDECLARATIVEGESTUREAREA_H + +#include +#include +#include + +#include +#include +#include + +#ifndef QT_NO_GESTURES + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeBoundSignal; +class QDeclarativeContext; +class QDeclarativeGestureAreaPrivate; +class QDeclarativeGestureArea : public QDeclarativeItem +{ + Q_OBJECT + + Q_PROPERTY(QGesture *gesture READ gesture) + +public: + QDeclarativeGestureArea(QDeclarativeItem *parent=0); + ~QDeclarativeGestureArea(); + + QGesture *gesture() const; + +protected: + bool sceneEvent(QEvent *event); + +private: + void connectSignals(); + void componentComplete(); + friend class QDeclarativeGestureAreaParser; + + Q_DISABLE_COPY(QDeclarativeGestureArea) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarativeGestureArea) +}; + +class QDeclarativeGestureAreaParser : public QDeclarativeCustomParser +{ +public: + virtual QByteArray compile(const QList &); + virtual void setCustomData(QObject *, const QByteArray &); +}; + + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeGestureArea) + +QT_END_HEADER + +#endif // QT_NO_GESTURES + +#endif diff --git a/src/imports/gestures/qmldir b/src/imports/gestures/qmldir new file mode 100644 index 00000000..2a31920a --- /dev/null +++ b/src/imports/gestures/qmldir @@ -0,0 +1 @@ +plugin qmlgesturesplugin diff --git a/src/imports/imports.pro b/src/imports/imports.pro new file mode 100644 index 00000000..d0e24b0b --- /dev/null +++ b/src/imports/imports.pro @@ -0,0 +1,5 @@ +TEMPLATE = subdirs + +SUBDIRS += folderlistmodel particles gestures +contains(QT_CONFIG, opengl):!contains(QT_CONFIG, opengles1): SUBDIRS += shaders + diff --git a/src/imports/particles/particles.cpp b/src/imports/particles/particles.cpp new file mode 100644 index 00000000..533ec08d --- /dev/null +++ b/src/imports/particles/particles.cpp @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include "qdeclarativeparticles_p.h" + +QT_BEGIN_NAMESPACE + +class QParticlesQmlModule : public QDeclarativeExtensionPlugin +{ + Q_OBJECT +public: + virtual void registerTypes(const char *uri) + { + Q_ASSERT(QLatin1String(uri) == QLatin1String("Qt.labs.particles")); + qmlRegisterType(uri,1,0,"ParticleMotion"); + qmlRegisterType(uri,1,0,"ParticleMotionGravity"); + qmlRegisterType(uri,1,0,"ParticleMotionLinear"); + qmlRegisterType(uri,1,0,"ParticleMotionWander"); + qmlRegisterType(uri,1,0,"Particles"); + } +}; + +QT_END_NAMESPACE + +#include "particles.moc" + +Q_EXPORT_PLUGIN2(qmlparticlesplugin, QT_PREPEND_NAMESPACE(QParticlesQmlModule)); + diff --git a/src/imports/particles/particles.pro b/src/imports/particles/particles.pro new file mode 100644 index 00000000..894d164c --- /dev/null +++ b/src/imports/particles/particles.pro @@ -0,0 +1,30 @@ +TARGET = qmlparticlesplugin +TARGETPATH = Qt/labs/particles +include(../qimportbase.pri) + +QT += declarative + +SOURCES += \ + qdeclarativeparticles.cpp \ + particles.cpp + +HEADERS += \ + qdeclarativeparticles_p.h + +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/imports/$$TARGETPATH +target.path = $$[QT_INSTALL_IMPORTS]/$$TARGETPATH + +qmldir.files += $$PWD/qmldir +qmldir.path += $$[QT_INSTALL_IMPORTS]/$$TARGETPATH + +symbian:{ + TARGET.UID3 = 0x2002131E + + isEmpty(DESTDIR):importFiles.files = qmlparticlesplugin$${QT_LIBINFIX}.dll qmldir + else:importFiles.files = $$DESTDIR/qmlparticlesplugin$${QT_LIBINFIX}.dll qmldir + importFiles.path = $$QT_IMPORTS_BASE_DIR/$$TARGETPATH + + DEPLOYMENT += importFiles +} + +INSTALLS += target qmldir diff --git a/src/imports/particles/qdeclarativeparticles.cpp b/src/imports/particles/qdeclarativeparticles.cpp new file mode 100644 index 00000000..b2b304a7 --- /dev/null +++ b/src/imports/particles/qdeclarativeparticles.cpp @@ -0,0 +1,1296 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativeparticles_p.h" + +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#define M_PI_2 (M_PI / 2.) +#endif +#ifndef INT_MAX +#define INT_MAX 2147483647 +#endif + +QT_BEGIN_NAMESPACE +#define PI_SQR 9.8696044 +// parabolic approximation +inline qreal fastSin(qreal theta) +{ + const qreal b = 4 / M_PI; + const qreal c = -4 / PI_SQR; + + qreal y = b * theta + c * theta * qAbs(theta); + return y; +} + +inline qreal fastCos(qreal theta) +{ + theta += M_PI_2; + if (theta > M_PI) + theta -= 2 * M_PI; + + return fastSin(theta); +} + +class QDeclarativeParticle +{ +public: + QDeclarativeParticle(int time) : lifeSpan(1000), fadeOutAge(800) + , opacity(0), birthTime(time), x_velocity(0), y_velocity(0) + , state(FadeIn), data(0) + { + } + + int lifeSpan; + int fadeOutAge; + qreal x; + qreal y; + qreal opacity; + int birthTime; + qreal x_velocity; + qreal y_velocity; + enum State { FadeIn, Solid, FadeOut }; + State state; + void *data; +}; + +//--------------------------------------------------------------------------- + +/*! + \class QDeclarativeParticleMotion + \ingroup group_effects + \brief The QDeclarativeParticleMotion class is the base class for particle motion. + \internal + + This class causes the particles to remain static. +*/ + +/*! + Constructs a QDeclarativeParticleMotion with parent object \a parent. +*/ +QDeclarativeParticleMotion::QDeclarativeParticleMotion(QObject *parent) : + QObject(parent) +{ +} + +/*! + Move the \a particle to its new position. \a interval is the number of + milliseconds elapsed since it was last moved. +*/ +void QDeclarativeParticleMotion::advance(QDeclarativeParticle &particle, int interval) +{ + Q_UNUSED(particle); + Q_UNUSED(interval); +} + +/*! + The \a particle has just been created. Some motion strategies require + additional state information. This can be allocated by this function. +*/ +void QDeclarativeParticleMotion::created(QDeclarativeParticle &particle) +{ + Q_UNUSED(particle); +} + +/*! + The \a particle is about to be destroyed. Any additional memory + that has been allocated for the particle should be freed. +*/ +void QDeclarativeParticleMotion::destroy(QDeclarativeParticle &particle) +{ + Q_UNUSED(particle); +} + +/*! + \qmlclass ParticleMotionLinear QDeclarativeParticleMotionLinear + \ingroup qml-particle-elements + \since 4.7 + \brief The ParticleMotionLinear object moves particles linearly. + + \sa Particles + + This is the default motion, and moves the particles according to the + properties specified in the Particles element. + + It has no further properties. +*/ +void QDeclarativeParticleMotionLinear::advance(QDeclarativeParticle &p, int interval) +{ + p.x += interval * p.x_velocity; + p.y += interval * p.y_velocity; +} + +/*! + \qmlclass ParticleMotionGravity QDeclarativeParticleMotionGravity + \ingroup qml-particle-elements + \since 4.7 + \brief The ParticleMotionGravity object moves particles towards a point. + + This motion attracts the particles to the specified point with the specified acceleration. + To mimic earth gravity, set yattractor to -6360000 and acceleration to 9.8. + + The defaults are all 0, not earth gravity, and so no motion will occur without setting + at least the acceleration property. + + + \sa Particles +*/ + +/*! + \qmlproperty real ParticleMotionGravity::xattractor + \qmlproperty real ParticleMotionGravity::yattractor + These properties hold the x and y coordinates of the point attracting the particles. +*/ + +/*! + \qmlproperty real ParticleMotionGravity::acceleration + This property holds the acceleration to apply to the particles. +*/ + +/*! + \property QDeclarativeParticleMotionGravity::xattractor + \brief the x coordinate of the point attracting the particles. +*/ + +/*! + \property QDeclarativeParticleMotionGravity::yattractor + \brief the y coordinate of the point attracting the particles. +*/ + +/*! + \property QDeclarativeParticleMotionGravity::acceleration + \brief the acceleration to apply to the particles. +*/ + +void QDeclarativeParticleMotionGravity::setXAttractor(qreal x) +{ + if (qFuzzyCompare(x, _xAttr)) + return; + _xAttr = x; + emit xattractorChanged(); +} + +void QDeclarativeParticleMotionGravity::setYAttractor(qreal y) +{ + if (qFuzzyCompare(y, _yAttr)) + return; + _yAttr = y; + emit yattractorChanged(); +} + +void QDeclarativeParticleMotionGravity::setAcceleration(qreal accel) +{ + qreal scaledAccel = accel/1000000.0; + if (qFuzzyCompare(scaledAccel, _accel)) + return; + _accel = scaledAccel; + emit accelerationChanged(); +} + +void QDeclarativeParticleMotionGravity::advance(QDeclarativeParticle &p, int interval) +{ + qreal xdiff = _xAttr - p.x; + qreal ydiff = _yAttr - p.y; + qreal absXdiff = qAbs(xdiff); + qreal absYdiff = qAbs(ydiff); + + qreal xcomp = xdiff / (absXdiff + absYdiff); + qreal ycomp = ydiff / (absXdiff + absYdiff); + + p.x_velocity += xcomp * _accel * interval; + p.y_velocity += ycomp * _accel * interval; + + p.x += interval * p.x_velocity; + p.y += interval * p.y_velocity; +} + +/*! + \qmlclass ParticleMotionWander QDeclarativeParticleMotionWander + \ingroup qml-particle-elements + \since 4.7 + \brief The ParticleMotionWander object moves particles in a somewhat random fashion. + + The particles will continue roughly in the original direction, however will randomly + drift to each side. + + The code below produces an effect similar to falling snow. + + \qml +Rectangle { + width: 240 + height: 320 + color: "black" + + Particles { + y: 0 + width: parent.width + height: 30 + source: "star.png" + lifeSpan: 5000 + count: 50 + angle: 70 + angleDeviation: 36 + velocity: 30 + velocityDeviation: 10 + ParticleMotionWander { + xvariance: 30 + pace: 100 + } + } +} + \endqml + + \sa Particles +*/ + +/*! + \qmlproperty real ParticleMotionWander::xvariance + \qmlproperty real ParticleMotionWander::yvariance + + These properties set the amount to wander in the x and y directions. +*/ + +/*! + \qmlproperty real ParticleMotionWander::pace + This property holds how quickly the paricles will move from side to side. +*/ + +void QDeclarativeParticleMotionWander::advance(QDeclarativeParticle &p, int interval) +{ + if (!particles) + particles = qobject_cast(parent()); + if (particles) { + Data *d = (Data*)p.data; + if (_xvariance != 0.) { + qreal xdiff = p.x_velocity - d->x_targetV; + if ((xdiff > d->x_peak && d->x_var > 0.0) || (xdiff < -d->x_peak && d->x_var < 0.0)) { + d->x_var = -d->x_var; + d->x_peak = _xvariance + _xvariance * qreal(qrand()) / RAND_MAX; + } + p.x_velocity += d->x_var * interval; + } + p.x += interval * p.x_velocity; + + if (_yvariance != 0.) { + qreal ydiff = p.y_velocity - d->y_targetV; + if ((ydiff > d->y_peak && d->y_var > 0.0) || (ydiff < -d->y_peak && d->y_var < 0.0)) { + d->y_var = -d->y_var; + d->y_peak = _yvariance + _yvariance * qreal(qrand()) / RAND_MAX; + } + p.y_velocity += d->y_var * interval; + } + p.y += interval * p.y_velocity; + } +} + +void QDeclarativeParticleMotionWander::created(QDeclarativeParticle &p) +{ + if (!p.data) { + Data *d = new Data; + p.data = (void*)d; + d->x_targetV = p.x_velocity; + d->y_targetV = p.y_velocity; + d->x_peak = _xvariance; + d->y_peak = _yvariance; + d->x_var = _pace * qreal(qrand()) / RAND_MAX / 1000.0; + d->y_var = _pace * qreal(qrand()) / RAND_MAX / 1000.0; + } +} + +void QDeclarativeParticleMotionWander::destroy(QDeclarativeParticle &p) +{ + if (p.data) + delete (Data*)p.data; +} + +void QDeclarativeParticleMotionWander::setXVariance(qreal var) +{ + qreal scaledVar = var / 1000.0; + if (qFuzzyCompare(scaledVar, _xvariance)) + return; + _xvariance = scaledVar; + emit xvarianceChanged(); +} + +void QDeclarativeParticleMotionWander::setYVariance(qreal var) +{ + qreal scaledVar = var / 1000.0; + if (qFuzzyCompare(scaledVar, _yvariance)) + return; + _yvariance = scaledVar; + emit yvarianceChanged(); +} + +void QDeclarativeParticleMotionWander::setPace(qreal pace) +{ + qreal scaledPace = pace / 1000.0; + if (qFuzzyCompare(scaledPace, _pace)) + return; + _pace = scaledPace; + emit paceChanged(); +} + +//--------------------------------------------------------------------------- +class QDeclarativeParticlesPainter : public QDeclarativeItem +{ +public: + QDeclarativeParticlesPainter(QDeclarativeParticlesPrivate *p, QDeclarativeItem* parent) + : QDeclarativeItem(parent), d(p) + { + setFlag(QGraphicsItem::ItemHasNoContents, false); + maxX = minX = maxY = minY = 0; + } + + void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *); + + void updateSize(); + + qreal maxX; + qreal minX; + qreal maxY; + qreal minY; + QDeclarativeParticlesPrivate* d; +}; + +//an animation that just gives a tick +template +class TickAnimationProxy : public QAbstractAnimation +{ +public: + TickAnimationProxy(T *p, QObject *parent = 0) : QAbstractAnimation(parent), m_p(p) {} + virtual int duration() const { return -1; } +protected: + virtual void updateCurrentTime(int msec) { (m_p->*method)(msec); } + +private: + T *m_p; +}; + +//--------------------------------------------------------------------------- +class QDeclarativeParticlesPrivate : public QDeclarativeItemPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeParticles) +public: + QDeclarativeParticlesPrivate() + : count(1), emissionRate(-1), emissionVariance(0.5), lifeSpan(1000) + , lifeSpanDev(1000), fadeInDur(200), fadeOutDur(300) + , angle(0), angleDev(0), velocity(0), velocityDev(0), emissionCarry(0.) + , addParticleTime(0), addParticleCount(0), lastAdvTime(0) + , motion(0), clock(this) + { + } + + ~QDeclarativeParticlesPrivate() + { + } + + void init() + { + Q_Q(QDeclarativeParticles); + paintItem = new QDeclarativeParticlesPainter(this, q); + } + + void tick(int time); + void createParticle(int time); + void updateOpacity(QDeclarativeParticle &p, int age); + + QUrl url; + QDeclarativePixmap image; + int count; + int emissionRate; + qreal emissionVariance; + int lifeSpan; + int lifeSpanDev; + int fadeInDur; + int fadeOutDur; + qreal angle; + qreal angleDev; + qreal velocity; + qreal velocityDev; + qreal emissionCarry; + int addParticleTime; + int addParticleCount; + int lastAdvTime; + QDeclarativeParticleMotion *motion; + QDeclarativeParticlesPainter *paintItem; + + + QList > bursts;//countLeft, emissionRate pairs + QList particles; + TickAnimationProxy clock; + +}; + +void QDeclarativeParticlesPrivate::tick(int time) +{ + Q_Q(QDeclarativeParticles); + if (!motion) + motion = new QDeclarativeParticleMotionLinear(q); + + int oldCount = particles.count(); + int removed = 0; + int interval = time - lastAdvTime; + for (int i = 0; i < particles.count(); ) { + QDeclarativeParticle &particle = particles[i]; + int age = time - particle.birthTime; + if (age >= particle.lifeSpan) { + QDeclarativeParticle part = particles.takeAt(i); + motion->destroy(part); + ++removed; + } else { + updateOpacity(particle, age); + motion->advance(particle, interval); + ++i; + } + } + + if(emissionRate == -1)//Otherwise leave emission to the emission rate + while(removed-- && ((count == -1) || particles.count() < count)) + createParticle(time); + + if (!addParticleTime) + addParticleTime = time; + + //Possibly emit new particles + if (((count == -1) || particles.count() < count) && emissionRate + && !(count==-1 && emissionRate==-1)) { + int emissionCount = -1; + if (emissionRate != -1){ + qreal variance = 1.; + if (emissionVariance > 0.){ + variance += (qreal(qrand())/RAND_MAX) * emissionVariance * (qrand()%2?-1.:1.); + } + qreal emission = emissionRate * (qreal(interval)/1000.); + emission = emission * variance + emissionCarry; + double tmpDbl; + emissionCarry = modf(emission, &tmpDbl); + emissionCount = (int)tmpDbl; + emissionCount = qMax(0,emissionCount); + } + while(((count == -1) || particles.count() < count) && + (emissionRate==-1 || emissionCount--)) + createParticle(time); + } + + //Deal with emissions from requested bursts + for(int i=0; i 0.){ + variance += (qreal(qrand())/RAND_MAX) * emissionVariance * (qrand()%2?-1.:1.); + } + qreal workingEmission = bursts[i].second * (qreal(interval)/1000.); + workingEmission *= variance; + emission = (int)workingEmission; + emission = qMax(emission, 0); + } + emission = qMin(emission, bursts[i].first); + bursts[i].first -= emission; + while(emission--) + createParticle(time); + } + for(int i=bursts.size()-1; i>=0; i--) + if(bursts[i].first <= 0) + bursts.removeAt(i); + + lastAdvTime = time; + paintItem->updateSize(); + paintItem->update(); + if (!(oldCount || particles.count()) && (!count || !emissionRate) && bursts.isEmpty()) { + lastAdvTime = 0; + clock.stop(); + } +} + +void QDeclarativeParticlesPrivate::createParticle(int time) +{ + Q_Q(QDeclarativeParticles); + QDeclarativeParticle p(time); + p.x = q->x() + q->width() * qreal(qrand()) / RAND_MAX - image.width()/2.0; + p.y = q->y() + q->height() * qreal(qrand()) / RAND_MAX - image.height()/2.0; + p.lifeSpan = lifeSpan; + if (lifeSpanDev) + p.lifeSpan += int(lifeSpanDev/2 - lifeSpanDev * qreal(qrand()) / RAND_MAX); + p.fadeOutAge = p.lifeSpan - fadeOutDur; + if (fadeInDur == 0.) { + p.state= QDeclarativeParticle::Solid; + p.opacity = 1.0; + } + qreal a = angle; + if (angleDev) + a += angleDev/2 - angleDev * qreal(qrand()) / RAND_MAX; + if (a > M_PI) + a = a - 2 * M_PI; + qreal v = velocity; + if (velocityDev) + v += velocityDev/2 - velocityDev * qreal(qrand()) / RAND_MAX; + p.x_velocity = v * fastCos(a); + p.y_velocity = v * fastSin(a); + particles.append(p); + motion->created(particles.last()); +} + +void QDeclarativeParticlesPrivate::updateOpacity(QDeclarativeParticle &p, int age) +{ + switch (p.state) { + case QDeclarativeParticle::FadeIn: + if (age <= fadeInDur) { + p.opacity = qreal(age) / fadeInDur; + break; + } else { + p.opacity = 1.0; + p.state = QDeclarativeParticle::Solid; + // Fall through + } + case QDeclarativeParticle::Solid: + if (age <= p.fadeOutAge) { + break; + } else { + p.state = QDeclarativeParticle::FadeOut; + // Fall through + } + case QDeclarativeParticle::FadeOut: + p.opacity = qreal(p.lifeSpan - age) / fadeOutDur; + break; + } +} + +/*! + \qmlclass Particles QDeclarativeParticles + \ingroup qml-particle-elements + \since 4.7 + \brief The Particles object generates and moves particles. + \inherits Item + + Particles are available in the \bold{Qt.labs.particles 1.0} module. + \e {Elements in the Qt.labs module are not guaranteed to remain compatible + in future versions.} + + This element provides preliminary support for particles in QML, + and may be heavily changed or removed in later versions. + + The particles created by this object cannot be dealt with + directly, they can only be controlled through the parameters of + the Particles object. The particles are all the same pixmap, + specified by the user. + + The particles are painted relative to the parent of the Particles + object. Moving the Particles object will not move the particles + already emitted. + + The below example creates two differently behaving particle + sources. The top one has particles falling from the top like + snow, the lower one has particles expelled up like a fountain. + + \qml +import QtQuick 1.0 +import Qt.labs.particles 1.0 + +Rectangle { + width: 240 + height: 320 + color: "black" + Particles { + y: 0 + width: parent.width + height: 30 + source: "star.png" + lifeSpan: 5000 + count: 50 + angle: 70 + angleDeviation: 36 + velocity: 30 + velocityDeviation: 10 + ParticleMotionWander { + xvariance: 30 + pace: 100 + } + } + Particles { + y: 300 + x: 120 + width: 1 + height: 1 + source: "star.png" + lifeSpan: 5000 + count: 200 + angle: 270 + angleDeviation: 45 + velocity: 50 + velocityDeviation: 30 + ParticleMotionGravity { + yattractor: 1000 + xattractor: 0 + acceleration: 25 + } + } +} + \endqml + \image particles.gif +*/ + +QDeclarativeParticles::QDeclarativeParticles(QDeclarativeItem *parent) + : QDeclarativeItem(*(new QDeclarativeParticlesPrivate), parent) +{ + Q_D(QDeclarativeParticles); + d->init(); +} + +QDeclarativeParticles::~QDeclarativeParticles() +{ +} + +/*! + \qmlproperty string Particles::source + This property holds the URL of the particle image. +*/ + +/*! + \property QDeclarativeParticles::source + \brief the URL of the particle image. +*/ +QUrl QDeclarativeParticles::source() const +{ + Q_D(const QDeclarativeParticles); + return d->url; +} + +void QDeclarativeParticles::imageLoaded() +{ + Q_D(QDeclarativeParticles); + if (d->image.isError()) + qmlInfo(this) << d->image.error(); + d->paintItem->updateSize(); + d->paintItem->update(); +} + +void QDeclarativeParticles::setSource(const QUrl &name) +{ + Q_D(QDeclarativeParticles); + + if ((d->url.isEmpty() == name.isEmpty()) && name == d->url) + return; + + if (name.isEmpty()) { + d->url = name; + d->image.clear(this); + d->paintItem->updateSize(); + d->paintItem->update(); + } else { + d->url = name; + Q_ASSERT(!name.isRelative()); + d->image.load(qmlEngine(this), d->url); + if (d->image.isLoading()) { + d->image.connectFinished(this, SLOT(imageLoaded())); + } else { + if (d->image.isError()) + qmlInfo(this) << d->image.error(); + //### unify with imageLoaded + d->paintItem->updateSize(); + d->paintItem->update(); + } + } + emit sourceChanged(); +} + +/*! + \qmlproperty int Particles::count + This property holds the maximum number of particles + + The particles element emits particles until it has count active + particles. When this number is reached, new particles are not emitted until + some of the current particles reach the end of their lifespan. + + If count is -1 then there is no maximum number of active particles, and + particles will be constantly emitted at the rate specified by emissionRate. + + The default value for count is 1. + + If both count and emissionRate are set to -1, nothing will be emitted. + +*/ + +/*! + \property QDeclarativeParticles::count + \brief the maximum number of particles +*/ +int QDeclarativeParticles::count() const +{ + Q_D(const QDeclarativeParticles); + return d->count; +} + +void QDeclarativeParticles::setCount(int cnt) +{ + Q_D(QDeclarativeParticles); + if (cnt == d->count) + return; + + int oldCount = d->count; + d->count = cnt; + d->addParticleTime = 0; + d->addParticleCount = d->particles.count(); + if (!oldCount && d->clock.state() != QAbstractAnimation::Running && d->count && d->emissionRate) { + d->clock.start(); + } + d->paintItem->updateSize(); + d->paintItem->update(); + emit countChanged(); +} + + +/*! + \qmlproperty int Particles::emissionRate + This property holds the target number of particles to emit every second. + + The particles element will emit up to emissionRate particles every + second. Fewer particles may be emitted per second if the maximum number of + particles has been reached. + + If emissionRate is set to -1 there is no limit to the number of + particles emitted per second, and particles will be instantly emitted to + reach the maximum number of particles specified by count. + + The default value for emissionRate is -1. + + If both count and emissionRate are set to -1, nothing will be emitted. +*/ + +/*! + \property QDeclarativeParticles::emissionRate + \brief the emission rate of particles +*/ +int QDeclarativeParticles::emissionRate() const +{ + Q_D(const QDeclarativeParticles); + return d->emissionRate; +} +void QDeclarativeParticles::setEmissionRate(int er) +{ + Q_D(QDeclarativeParticles); + if(er == d->emissionRate) + return; + d->emissionRate = er; + if (d->clock.state() != QAbstractAnimation::Running && d->count && d->emissionRate) { + d->clock.start(); + } + emit emissionRateChanged(); +} + +/*! + \qmlproperty real Particles::emissionVariance + This property holds how inconsistent the rate of particle emissions are. + It is a number between 0 (no variance) and 1 (some variance). + + The expected number of particles emitted per second is emissionRate. If + emissionVariance is 0 then particles will be emitted consistently throughout + each second to reach that number. If emissionVariance is greater than 0 the + rate of particle emission will vary randomly throughout the second, with the + consequence that the actual number of particles emitted in one second will + vary randomly as well. + + emissionVariance is the maximum deviation from emitting + emissionRate particles per second. An emissionVariance of 0 means you should + get exactly emissionRate particles emitted per second, + and an emissionVariance of 1 means you will get between zero and two times + emissionRate particles per second, but you should get emissionRate particles + per second on average. + + Note that even with an emissionVariance of 0 there may be some variance due + to performance and hardware constraints. + + The default value of emissionVariance is 0.5 +*/ + +/*! + \property QDeclarativeParticles::emissionVariance + \brief how much the particle emission amounts vary per tick +*/ + +qreal QDeclarativeParticles::emissionVariance() const +{ + Q_D(const QDeclarativeParticles); + return d->emissionVariance; +} + +void QDeclarativeParticles::setEmissionVariance(qreal ev) +{ + Q_D(QDeclarativeParticles); + if(d->emissionVariance == ev) + return; + d->emissionVariance = ev; + emit emissionVarianceChanged(); +} + +/*! + \qmlproperty int Particles::lifeSpan + \qmlproperty int Particles::lifeSpanDeviation + + These properties describe the life span of each particle. + + The default lifespan for a particle is 1000ms. + + lifeSpanDeviation randomly varies the lifeSpan up to the specified variation. For + example, the following creates particles whose lifeSpan will vary + from 150ms to 250ms: + + \qml +Particles { + source: "star.png" + lifeSpan: 200 + lifeSpanDeviation: 100 +} + \endqml +*/ + +/*! + \property QDeclarativeParticles::lifeSpan + \brief the life span of each particle. + + Default value is 1000ms. + + \sa QDeclarativeParticles::lifeSpanDeviation +*/ +int QDeclarativeParticles::lifeSpan() const +{ + Q_D(const QDeclarativeParticles); + return d->lifeSpan; +} + +void QDeclarativeParticles::setLifeSpan(int ls) +{ + Q_D(QDeclarativeParticles); + if(d->lifeSpan == ls) + return; + d->lifeSpan = ls; + emit lifeSpanChanged(); +} + +/*! + \property QDeclarativeParticles::lifeSpanDeviation + \brief the maximum possible deviation from the set lifeSpan. + + Randomly varies the lifeSpan up to the specified variation. For + example, the following creates particles whose lifeSpan will vary + from 150ms to 250ms: + +\qml +Particles { + source: "star.png" + lifeSpan: 200 + lifeSpanDeviation: 100 +} +\endqml + + \sa QDeclarativeParticles::lifeSpan +*/ +int QDeclarativeParticles::lifeSpanDeviation() const +{ + Q_D(const QDeclarativeParticles); + return d->lifeSpanDev; +} + +void QDeclarativeParticles::setLifeSpanDeviation(int dev) +{ + Q_D(QDeclarativeParticles); + if(d->lifeSpanDev == dev) + return; + d->lifeSpanDev = dev; + emit lifeSpanDeviationChanged(); +} + +/*! + \qmlproperty int Particles::fadeInDuration + \qmlproperty int Particles::fadeOutDuration + These properties hold the time taken to fade the particles in and out. + + By default fade in is 200ms and fade out is 300ms. +*/ + +/*! + \property QDeclarativeParticles::fadeInDuration + \brief the time taken to fade in the particles. + + Default value is 200ms. +*/ +int QDeclarativeParticles::fadeInDuration() const +{ + Q_D(const QDeclarativeParticles); + return d->fadeInDur; +} + +void QDeclarativeParticles::setFadeInDuration(int dur) +{ + Q_D(QDeclarativeParticles); + if (dur < 0.0 || dur == d->fadeInDur) + return; + d->fadeInDur = dur; + emit fadeInDurationChanged(); +} + +/*! + \property QDeclarativeParticles::fadeOutDuration + \brief the time taken to fade out the particles. + + Default value is 300ms. +*/ +int QDeclarativeParticles::fadeOutDuration() const +{ + Q_D(const QDeclarativeParticles); + return d->fadeOutDur; +} + +void QDeclarativeParticles::setFadeOutDuration(int dur) +{ + Q_D(QDeclarativeParticles); + if (dur < 0.0 || d->fadeOutDur == dur) + return; + d->fadeOutDur = dur; + emit fadeOutDurationChanged(); +} + +/*! + \qmlproperty real Particles::angle + \qmlproperty real Particles::angleDeviation + + These properties control particle direction. + + angleDeviation randomly varies the direction up to the specified variation. For + example, the following creates particles whose initial direction will + vary from 15 degrees to 105 degrees: + + \qml +Particles { + source: "star.png" + angle: 60 + angleDeviation: 90 +} + \endqml +*/ + +/*! + \property QDeclarativeParticles::angle + \brief the initial angle of direction. + + \sa QDeclarativeParticles::angleDeviation +*/ +qreal QDeclarativeParticles::angle() const +{ + Q_D(const QDeclarativeParticles); + return d->angle * 180.0 / M_PI; +} + +void QDeclarativeParticles::setAngle(qreal angle) +{ + Q_D(QDeclarativeParticles); + qreal radAngle = angle * M_PI / 180.0; + if(radAngle == d->angle) + return; + d->angle = radAngle; + emit angleChanged(); +} + +/*! + \property QDeclarativeParticles::angleDeviation + \brief the maximum possible deviation from the set angle. + + Randomly varies the direction up to the specified variation. For + example, the following creates particles whose initial direction will + vary from 15 degrees to 105 degrees: + +\qml +Particles { + source: "star.png" + angle: 60 + angleDeviation: 90 +} +\endqml + + \sa QDeclarativeParticles::angle +*/ +qreal QDeclarativeParticles::angleDeviation() const +{ + Q_D(const QDeclarativeParticles); + return d->angleDev * 180.0 / M_PI; +} + +void QDeclarativeParticles::setAngleDeviation(qreal dev) +{ + Q_D(QDeclarativeParticles); + qreal radDev = dev * M_PI / 180.0; + if(radDev == d->angleDev) + return; + d->angleDev = radDev; + emit angleDeviationChanged(); +} + +/*! + \qmlproperty real Particles::velocity + \qmlproperty real Particles::velocityDeviation + + These properties control the velocity of the particles. + + velocityDeviation randomly varies the velocity up to the specified variation. For + example, the following creates particles whose initial velocity will + vary from 40 to 60. + + \qml +Particles { + source: "star.png" + velocity: 50 + velocityDeviation: 20 +} + \endqml +*/ + +/*! + \property QDeclarativeParticles::velocity + \brief the initial velocity of the particles. + + \sa QDeclarativeParticles::velocityDeviation +*/ +qreal QDeclarativeParticles::velocity() const +{ + Q_D(const QDeclarativeParticles); + return d->velocity * 1000.0; +} + +void QDeclarativeParticles::setVelocity(qreal velocity) +{ + Q_D(QDeclarativeParticles); + qreal realVel = velocity / 1000.0; + if(realVel == d->velocity) + return; + d->velocity = realVel; + emit velocityChanged(); +} + +/*! + \property QDeclarativeParticles::velocityDeviation + \brief the maximum possible deviation from the set velocity. + + Randomly varies the velocity up to the specified variation. For + example, the following creates particles whose initial velocity will + vary from 40 to 60. + +\qml +Particles { + source: "star.png" + velocity: 50 + velocityDeviation: 20 +} +\endqml + + \sa QDeclarativeParticles::velocity +*/ +qreal QDeclarativeParticles::velocityDeviation() const +{ + Q_D(const QDeclarativeParticles); + return d->velocityDev * 1000.0; +} + +void QDeclarativeParticles::setVelocityDeviation(qreal velocity) +{ + Q_D(QDeclarativeParticles); + qreal realDev = velocity / 1000.0; + if(realDev == d->velocityDev) + return; + d->velocityDev = realDev; + emit velocityDeviationChanged(); +} + +/*! + \qmlproperty ParticleMotion Particles::motion + This property sets the type of motion to apply to the particles. + + When a particle is created it will have an initial direction and velocity. + The motion of the particle during its lifeSpan is then influenced by the + motion property. + + Default motion is ParticleMotionLinear. +*/ + +/*! + \property QDeclarativeParticles::motion + \brief sets the type of motion to apply to the particles. + + When a particle is created it will have an initial direction and velocity. + The motion of the particle during its lifeSpan is then influenced by the + motion property. + + Default motion is QDeclarativeParticleMotionLinear. +*/ +QDeclarativeParticleMotion *QDeclarativeParticles::motion() const +{ + Q_D(const QDeclarativeParticles); + return d->motion; +} + +void QDeclarativeParticles::setMotion(QDeclarativeParticleMotion *motion) +{ + Q_D(QDeclarativeParticles); + if (motion == d->motion) + return; + d->motion = motion; + emit motionChanged(); +} + +/*! + \qmlmethod Particles::burst(int count, int emissionRate) + + Initiates a burst of particles. + + This method takes two arguments. The first argument is the number + of particles to emit and the second argument is the emissionRate for the + burst. If the second argument is omitted, it is treated as -1. The burst + of particles has a separate emissionRate and count to the normal emission of + particles. The burst uses the same values as normal emission for all other + properties, including emissionVariance. + + The normal emission of particles will continue during the burst, however + the particles created by the burst count towards the maximum number used by + normal emission. To avoid this behavior, use two Particles elements. + +*/ +void QDeclarativeParticles::burst(int count, int emissionRate) +{ + Q_D(QDeclarativeParticles); + d->bursts << qMakePair(count, emissionRate); + if (d->clock.state() != QAbstractAnimation::Running) + d->clock.start(); +} + +void QDeclarativeParticlesPainter::updateSize() +{ + if (!d->componentComplete) + return; + + const int parentX = parentItem()->x(); + const int parentY = parentItem()->y(); + for (int i = 0; i < d->particles.count(); ++i) { + const QDeclarativeParticle &particle = d->particles.at(i); + if(particle.x > maxX) + maxX = particle.x; + if(particle.x < minX) + minX = particle.x; + if(particle.y > maxY) + maxY = particle.y; + if(particle.y < minY) + minY = particle.y; + } + + int myWidth = (int)(maxX-minX+0.5)+d->image.width(); + int myX = (int)(minX - parentX); + int myHeight = (int)(maxY-minY+0.5)+d->image.height(); + int myY = (int)(minY - parentY); + setWidth(myWidth); + setHeight(myHeight); + setX(myX); + setY(myY); +} + +void QDeclarativeParticles::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *) +{ + Q_UNUSED(p); + //painting is done by the ParticlesPainter, so it can have the right size +} + +void QDeclarativeParticlesPainter::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *) +{ + if (d->image.isNull() || d->particles.isEmpty()) + return; + + const int myX = x() + parentItem()->x(); + const int myY = y() + parentItem()->y(); + + QVarLengthArray pixmapData; + pixmapData.resize(d->particles.count()); + + const QRectF sourceRect = d->image.rect(); + qreal halfPWidth = sourceRect.width()/2.; + qreal halfPHeight = sourceRect.height()/2.; + for (int i = 0; i < d->particles.count(); ++i) { + const QDeclarativeParticle &particle = d->particles.at(i); + pixmapData[i].x = particle.x - myX + halfPWidth; + pixmapData[i].y = particle.y - myY + halfPHeight; + pixmapData[i].opacity = particle.opacity; + + //these never change + pixmapData[i].rotation = 0; + pixmapData[i].scaleX = 1; + pixmapData[i].scaleY = 1; + pixmapData[i].sourceLeft = sourceRect.left(); + pixmapData[i].sourceTop = sourceRect.top(); + pixmapData[i].width = sourceRect.width(); + pixmapData[i].height = sourceRect.height(); + } + p->drawPixmapFragments(pixmapData.data(), d->particles.count(), d->image); +} + +void QDeclarativeParticles::componentComplete() +{ + Q_D(QDeclarativeParticles); + QDeclarativeItem::componentComplete(); + if (d->count && d->emissionRate) { + d->paintItem->updateSize(); + d->clock.start(); + } + if (d->lifeSpanDev > d->lifeSpan) + d->lifeSpanDev = d->lifeSpan; +} + +QT_END_NAMESPACE diff --git a/src/imports/particles/qdeclarativeparticles_p.h b/src/imports/particles/qdeclarativeparticles_p.h new file mode 100644 index 00000000..0a2b6d8d --- /dev/null +++ b/src/imports/particles/qdeclarativeparticles_p.h @@ -0,0 +1,258 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPARTICLES_H +#define QDECLARATIVEPARTICLES_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeParticle; +class QDeclarativeParticles; +class QDeclarativeParticleMotion : public QObject +{ + Q_OBJECT +public: + QDeclarativeParticleMotion(QObject *parent=0); + + virtual void advance(QDeclarativeParticle &, int interval); + virtual void created(QDeclarativeParticle &); + virtual void destroy(QDeclarativeParticle &); +}; + +class QDeclarativeParticleMotionLinear : public QDeclarativeParticleMotion +{ + Q_OBJECT +public: + QDeclarativeParticleMotionLinear(QObject *parent=0) + : QDeclarativeParticleMotion(parent) {} + + virtual void advance(QDeclarativeParticle &, int interval); +}; + +class QDeclarativeParticleMotionGravity : public QDeclarativeParticleMotion +{ + Q_OBJECT + + Q_PROPERTY(qreal xattractor READ xAttractor WRITE setXAttractor NOTIFY xattractorChanged) + Q_PROPERTY(qreal yattractor READ yAttractor WRITE setYAttractor NOTIFY yattractorChanged) + Q_PROPERTY(qreal acceleration READ acceleration WRITE setAcceleration NOTIFY accelerationChanged) +public: + QDeclarativeParticleMotionGravity(QObject *parent=0) + : QDeclarativeParticleMotion(parent), _xAttr(0.0), _yAttr(0.0), _accel(0.00005) {} + + qreal xAttractor() const { return _xAttr; } + void setXAttractor(qreal x); + + qreal yAttractor() const { return _yAttr; } + void setYAttractor(qreal y); + + qreal acceleration() const { return _accel * 1000000; } + void setAcceleration(qreal accel); + + virtual void advance(QDeclarativeParticle &, int interval); + +Q_SIGNALS: + void xattractorChanged(); + void yattractorChanged(); + void accelerationChanged(); + +private: + qreal _xAttr; + qreal _yAttr; + qreal _accel; +}; + +class QDeclarativeParticleMotionWander : public QDeclarativeParticleMotion +{ + Q_OBJECT +public: + QDeclarativeParticleMotionWander() + : QDeclarativeParticleMotion(), particles(0), _xvariance(0), _yvariance(0), _pace(100) {} + + virtual void advance(QDeclarativeParticle &, int interval); + virtual void created(QDeclarativeParticle &); + virtual void destroy(QDeclarativeParticle &); + + struct Data { + qreal x_targetV; + qreal y_targetV; + qreal x_peak; + qreal y_peak; + qreal x_var; + qreal y_var; + }; + + Q_PROPERTY(qreal xvariance READ xVariance WRITE setXVariance NOTIFY xvarianceChanged) + qreal xVariance() const { return _xvariance * 1000.0; } + void setXVariance(qreal var); + + Q_PROPERTY(qreal yvariance READ yVariance WRITE setYVariance NOTIFY yvarianceChanged) + qreal yVariance() const { return _yvariance * 1000.0; } + void setYVariance(qreal var); + + Q_PROPERTY(qreal pace READ pace WRITE setPace NOTIFY paceChanged) + qreal pace() const { return _pace * 1000.0; } + void setPace(qreal pace); + +Q_SIGNALS: + void xvarianceChanged(); + void yvarianceChanged(); + void paceChanged(); + +private: + QDeclarativeParticles *particles; + qreal _xvariance; + qreal _yvariance; + qreal _pace; +}; + +class QDeclarativeParticlesPrivate; +class QDeclarativeParticles : public QDeclarativeItem +{ + Q_OBJECT + + Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) + Q_PROPERTY(int count READ count WRITE setCount NOTIFY countChanged) + Q_PROPERTY(int emissionRate READ emissionRate WRITE setEmissionRate NOTIFY emissionRateChanged) + Q_PROPERTY(qreal emissionVariance READ emissionVariance WRITE setEmissionVariance NOTIFY emissionVarianceChanged) + Q_PROPERTY(int lifeSpan READ lifeSpan WRITE setLifeSpan NOTIFY lifeSpanChanged) + Q_PROPERTY(int lifeSpanDeviation READ lifeSpanDeviation WRITE setLifeSpanDeviation NOTIFY lifeSpanDeviationChanged) + Q_PROPERTY(int fadeInDuration READ fadeInDuration WRITE setFadeInDuration NOTIFY fadeInDurationChanged) + Q_PROPERTY(int fadeOutDuration READ fadeOutDuration WRITE setFadeOutDuration NOTIFY fadeOutDurationChanged) + Q_PROPERTY(qreal angle READ angle WRITE setAngle NOTIFY angleChanged) + Q_PROPERTY(qreal angleDeviation READ angleDeviation WRITE setAngleDeviation NOTIFY angleDeviationChanged) + Q_PROPERTY(qreal velocity READ velocity WRITE setVelocity NOTIFY velocityChanged) + Q_PROPERTY(qreal velocityDeviation READ velocityDeviation WRITE setVelocityDeviation NOTIFY velocityDeviationChanged) + Q_PROPERTY(QDeclarativeParticleMotion *motion READ motion WRITE setMotion NOTIFY motionChanged) + Q_CLASSINFO("DefaultProperty", "motion") + +public: + QDeclarativeParticles(QDeclarativeItem *parent=0); + ~QDeclarativeParticles(); + + QUrl source() const; + void setSource(const QUrl &); + + int count() const; + void setCount(int cnt); + + int emissionRate() const; + void setEmissionRate(int); + + qreal emissionVariance() const; + void setEmissionVariance(qreal); + + int lifeSpan() const; + void setLifeSpan(int); + + int lifeSpanDeviation() const; + void setLifeSpanDeviation(int); + + int fadeInDuration() const; + void setFadeInDuration(int); + + int fadeOutDuration() const; + void setFadeOutDuration(int); + + qreal angle() const; + void setAngle(qreal); + + qreal angleDeviation() const; + void setAngleDeviation(qreal); + + qreal velocity() const; + void setVelocity(qreal); + + qreal velocityDeviation() const; + void setVelocityDeviation(qreal); + + QDeclarativeParticleMotion *motion() const; + void setMotion(QDeclarativeParticleMotion *); + + void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *); + +public Q_SLOTS: + void burst(int count, int emissionRate=-1); + +protected: + virtual void componentComplete(); + +Q_SIGNALS: + void sourceChanged(); + void countChanged(); + void emissionRateChanged(); + void emissionVarianceChanged(); + void lifeSpanChanged(); + void lifeSpanDeviationChanged(); + void fadeInDurationChanged(); + void fadeOutDurationChanged(); + void angleChanged(); + void angleDeviationChanged(); + void velocityChanged(); + void velocityDeviationChanged(); + void emittingChanged(); + void motionChanged(); + +private Q_SLOTS: + void imageLoaded(); + +private: + Q_DISABLE_COPY(QDeclarativeParticles) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarativeParticles) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeParticleMotion) +QML_DECLARE_TYPE(QDeclarativeParticleMotionLinear) +QML_DECLARE_TYPE(QDeclarativeParticleMotionGravity) +QML_DECLARE_TYPE(QDeclarativeParticleMotionWander) +QML_DECLARE_TYPE(QDeclarativeParticles) + +QT_END_HEADER + +#endif diff --git a/src/imports/particles/qmldir b/src/imports/particles/qmldir new file mode 100644 index 00000000..aeebd2c9 --- /dev/null +++ b/src/imports/particles/qmldir @@ -0,0 +1 @@ +plugin qmlparticlesplugin diff --git a/src/imports/qimportbase.pri b/src/imports/qimportbase.pri new file mode 100644 index 00000000..0f70030d --- /dev/null +++ b/src/imports/qimportbase.pri @@ -0,0 +1,36 @@ +symbian:include(../plugins/qpluginbase.pri) +TEMPLATE = lib +CONFIG += qt plugin + +win32|mac:!wince*:!win32-msvc:!macx-xcode:CONFIG += debug_and_release + +isEmpty(TARGETPATH) { + error("qimportbase.pri: You must provide a TARGETPATH!") +} +isEmpty(TARGET) { + error("qimportbase.pri: You must provide a TARGET!") +} + +QMLDIRFILE = $${_PRO_FILE_PWD_}/qmldir +copy2build.input = QMLDIRFILE +copy2build.output = $$QT_BUILD_TREE/imports/$$TARGETPATH/qmldir +!contains(TEMPLATE_PREFIX, vc):copy2build.variable_out = PRE_TARGETDEPS +copy2build.commands = $$QMAKE_COPY ${QMAKE_FILE_IN} ${QMAKE_FILE_OUT} +copy2build.name = COPY ${QMAKE_FILE_IN} +copy2build.CONFIG += no_link +# `clean' should leave the build in a runnable state, which means it shouldn't delete qmldir +copy2build.CONFIG += no_clean +QMAKE_EXTRA_COMPILERS += copy2build + +TARGET = $$qtLibraryTarget($$TARGET) +contains(QT_CONFIG, reduce_exports):CONFIG += hide_symbols + +include(../qt_targets.pri) + +wince*:LIBS += $$QMAKE_LIBS_GUI + +symbian: { + TARGET.EPOCALLOWDLLDATA=1 + TARGET.CAPABILITY = All -Tcb + load(armcc_warnings) +} diff --git a/src/imports/shaders/glfunctions.h b/src/imports/shaders/glfunctions.h new file mode 100644 index 00000000..214143a5 --- /dev/null +++ b/src/imports/shaders/glfunctions.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QML Shaders plugin of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef GLFUNCTIONS_H +#define GLFUNCTIONS_H + +#ifndef QT_OPENGL_ES + +#ifndef Q_WS_MAC +# ifndef APIENTRYP +# ifdef APIENTRY +# define APIENTRYP APIENTRY * +# else +# define APIENTRY +# define APIENTRYP * +# endif +# endif +#else +# define APIENTRY +# define APIENTRYP * +#endif + +#define GL_TEXTURE0 0x84C0 +#define GL_CLAMP_TO_EDGE 0x812F +#define GL_BGRA 0x80E1 + +typedef void (APIENTRYP type_glActiveTexture)(GLenum texture); +typedef void (APIENTRYP type_glGenerateMipmap)(GLenum target); +typedef void (APIENTRYP type_glVertexAttribPointer)(GLuint, GLint, GLenum, GLboolean, GLsizei, const GLvoid *); + +#define glActiveTexture ((type_glActiveTexture)QGLContext::currentContext()->getProcAddress(QLatin1String("glActiveTexture"))) +#define glGenerateMipmap ((type_glGenerateMipmap)QGLContext::currentContext()->getProcAddress(QLatin1String("glGenerateMipmap"))) +#define glVertexAttribPointer ((type_glVertexAttribPointer)QGLContext::currentContext()->getProcAddress(QLatin1String("glVertexAttribPointer"))) + +#endif + +#endif // GLFUNCTIONS_H diff --git a/src/imports/shaders/qmldir b/src/imports/shaders/qmldir new file mode 100644 index 00000000..b2a9de21 --- /dev/null +++ b/src/imports/shaders/qmldir @@ -0,0 +1,2 @@ +plugin qmlshadersplugin + diff --git a/src/imports/shaders/qmlshadersplugin_plugin.cpp b/src/imports/shaders/qmlshadersplugin_plugin.cpp new file mode 100644 index 00000000..613e1834 --- /dev/null +++ b/src/imports/shaders/qmlshadersplugin_plugin.cpp @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QML Shaders plugin of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmlshadersplugin_plugin.h" +#include "shadereffectitem.h" +#include "shadereffectsource.h" + +#include + +void qmlshaderspluginPlugin::registerTypes(const char *uri) +{ + qmlRegisterType(uri, 1, 0, "ShaderEffectItem"); + qmlRegisterType(uri, 1, 0, "ShaderEffectSource"); +} + +Q_EXPORT_PLUGIN2(qmlshadersplugin, qmlshaderspluginPlugin) + diff --git a/src/imports/shaders/qmlshadersplugin_plugin.h b/src/imports/shaders/qmlshadersplugin_plugin.h new file mode 100644 index 00000000..d1f7746c --- /dev/null +++ b/src/imports/shaders/qmlshadersplugin_plugin.h @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QML Shaders plugin of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMLSHADERSPLUGIN_PLUGIN_H +#define QMLSHADERSPLUGIN_PLUGIN_H + +#include + +class qmlshaderspluginPlugin : public QDeclarativeExtensionPlugin +{ + Q_OBJECT + +public: + void registerTypes(const char *uri); +}; + +#endif // QMLSHADERSPLUGIN_PLUGIN_H + diff --git a/src/imports/shaders/scenegraph/qsggeometry.cpp b/src/imports/shaders/scenegraph/qsggeometry.cpp new file mode 100644 index 00000000..371b6e88 --- /dev/null +++ b/src/imports/shaders/scenegraph/qsggeometry.cpp @@ -0,0 +1,194 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QML Shaders plugin of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsggeometry.h" + +QT_BEGIN_NAMESPACE + + +const QSGGeometry::AttributeSet &QSGGeometry::defaultAttributes_Point2D() +{ + static Attribute data[] = { + { 0, 2, GL_FLOAT } + }; + static AttributeSet attrs = { 1, sizeof(float) * 2, data }; + return attrs; +} + + +const QSGGeometry::AttributeSet &QSGGeometry::defaultAttributes_TexturedPoint2D() +{ + static Attribute data[] = { + { 0, 2, GL_FLOAT }, + { 1, 2, GL_FLOAT } + }; + static AttributeSet attrs = { 2, sizeof(float) * 4, data }; + return attrs; +} + + +const QSGGeometry::AttributeSet &QSGGeometry::defaultAttributes_ColoredPoint2D() +{ + static Attribute data[] = { + { 0, 2, GL_FLOAT }, + { 1, 4, GL_UNSIGNED_BYTE } + }; + static AttributeSet attrs = { 2, 2 * sizeof(float) + 4 * sizeof(char), data }; + return attrs; +} + + + +QSGGeometry::QSGGeometry(const QSGGeometry::AttributeSet &attributes, + int vertexCount, + int indexCount, + int indexType) + : m_drawing_mode(GL_TRIANGLE_STRIP) + , m_vertex_count(0) + , m_index_count(0) + , m_index_type(indexType) + , m_attributes(attributes) + , m_data(0) + , m_index_data_offset(-1) + , m_owns_data(false) +{ + Q_ASSERT(m_attributes.count > 0); + Q_ASSERT(m_attributes.stride > 0); + + // Because allocate reads m_vertex_count, m_index_count and m_owns_data, these + // need to be set before calling allocate... + allocate(vertexCount, indexCount); +} + +QSGGeometry::~QSGGeometry() +{ + if (m_owns_data) + qFree(m_data); +} + +void *QSGGeometry::indexData() +{ + return m_index_data_offset < 0 + ? 0 + : ((char *) m_data + m_index_data_offset); +} + +const void *QSGGeometry::indexData() const +{ + return m_index_data_offset < 0 + ? 0 + : ((char *) m_data + m_index_data_offset); +} + +void QSGGeometry::setDrawingMode(GLenum mode) +{ + m_drawing_mode = mode; +} + +void QSGGeometry::allocate(int vertexCount, int indexCount) +{ + if (vertexCount == m_vertex_count && indexCount == m_index_count) + return; + + m_vertex_count = vertexCount; + m_index_count = indexCount; + + bool canUsePrealloc = m_index_count <= 0; + int vertexByteSize = m_attributes.stride * m_vertex_count; + + if (m_owns_data) + qFree(m_data); + + if (canUsePrealloc && vertexByteSize <= (int) sizeof(m_prealloc)) { + m_data = (void *) &m_prealloc[0]; + m_index_data_offset = -1; + m_owns_data = false; + } else { + Q_ASSERT(m_index_type == GL_UNSIGNED_INT || m_index_type == GL_UNSIGNED_SHORT); + int indexByteSize = indexCount * (m_index_type == GL_UNSIGNED_SHORT ? sizeof(quint16) : sizeof(quint32)); + m_data = (void *) qMalloc(vertexByteSize + indexByteSize); + m_index_data_offset = vertexByteSize; + m_owns_data = true; + } + +} + +void QSGGeometry::updateRectGeometry(QSGGeometry *g, const QRectF &rect) +{ + Point2D *v = g->vertexDataAsPoint2D(); + v[0].x = rect.left(); + v[0].y = rect.top(); + + v[1].x = rect.right(); + v[1].y = rect.top(); + + v[2].x = rect.left(); + v[2].y = rect.bottom(); + + v[3].x = rect.right(); + v[3].y = rect.bottom(); +} + +void QSGGeometry::updateTexturedRectGeometry(QSGGeometry *g, const QRectF &rect, const QRectF &textureRect) +{ + TexturedPoint2D *v = g->vertexDataAsTexturedPoint2D(); + v[0].x = rect.left(); + v[0].y = rect.top(); + v[0].tx = textureRect.left(); + v[0].ty = textureRect.top(); + + v[1].x = rect.right(); + v[1].y = rect.top(); + v[1].tx = textureRect.right(); + v[1].ty = textureRect.top(); + + v[2].x = rect.left(); + v[2].y = rect.bottom(); + v[2].tx = textureRect.left(); + v[2].ty = textureRect.bottom(); + + v[3].x = rect.right(); + v[3].y = rect.bottom(); + v[3].tx = textureRect.right(); + v[3].ty = textureRect.bottom(); +} + +QT_END_NAMESPACE diff --git a/src/imports/shaders/scenegraph/qsggeometry.h b/src/imports/shaders/scenegraph/qsggeometry.h new file mode 100644 index 00000000..9596898b --- /dev/null +++ b/src/imports/shaders/scenegraph/qsggeometry.h @@ -0,0 +1,234 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QML Shaders plugin of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGGEOMETRY_H +#define QSGGEOMETRY_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QSGGeometry +{ +public: + struct Attribute + { + int position; + int tupleSize; + int type; + }; + + struct AttributeSet { + int count; + int stride; + const Attribute *attributes; + }; + + struct Point2D { float x, y; }; + struct TexturedPoint2D { float x, y; float tx, ty; }; + struct ColoredPoint2D { float x, y; unsigned char r, g, b, a; }; + + static const AttributeSet &defaultAttributes_Point2D(); + static const AttributeSet &defaultAttributes_TexturedPoint2D(); + static const AttributeSet &defaultAttributes_ColoredPoint2D(); + + QSGGeometry(const QSGGeometry::AttributeSet &attribs, + int vertexCount, + int indexCount = 0, + int indexType = GL_UNSIGNED_SHORT); + ~QSGGeometry(); + + void setDrawingMode(GLenum mode); + inline GLenum drawingMode() const { return m_drawing_mode; } + + void allocate(int vertexCount, int indexCount = 0); + + int vertexCount() const { return m_vertex_count; } + + void *vertexData() { return m_data; } + inline Point2D *vertexDataAsPoint2D(); + inline TexturedPoint2D *vertexDataAsTexturedPoint2D(); + inline ColoredPoint2D *vertexDataAsColoredPoint2D(); + + inline const void *vertexData() const { return m_data; } + inline const Point2D *vertexDataAsPoint2D() const; + inline const TexturedPoint2D *vertexDataAsTexturedPoint2D() const; + inline const ColoredPoint2D *vertexDataAsColoredPoint2D() const; + + inline int indexType() const { return m_index_type; } + + int indexCount() const { return m_index_count; } + + void *indexData(); + inline uint *indexDataAsUInt(); + inline quint16 *indexDataAsUShort(); + + const void *indexData() const; + inline const uint *indexDataAsUInt() const; + inline const quint16 *indexDataAsUShort() const; + + inline int attributeCount() const { return m_attributes.count; } + inline const Attribute *attributes() const { return m_attributes.attributes; } + inline int stride() const { return m_attributes.stride; } + + static void updateRectGeometry(QSGGeometry *g, const QRectF &rect); + static void updateTexturedRectGeometry(QSGGeometry *g, const QRectF &rect, const QRectF &sourceRect); + +private: + int m_drawing_mode; + int m_vertex_count; + int m_index_count; + int m_index_type; + const AttributeSet &m_attributes; + void *m_data; + int m_index_data_offset; + + void *m_reserved_pointer; + + uint m_owns_data : 1; + uint m_reserved_bits : 31; + + float m_prealloc[16]; +}; + +inline uint *QSGGeometry::indexDataAsUInt() +{ + Q_ASSERT(m_index_type == GL_UNSIGNED_INT); + return (uint *) indexData(); +} + +inline quint16 *QSGGeometry::indexDataAsUShort() +{ + Q_ASSERT(m_index_type == GL_UNSIGNED_SHORT); + return (quint16 *) indexData(); +} + +inline const uint *QSGGeometry::indexDataAsUInt() const +{ + Q_ASSERT(m_index_type == GL_UNSIGNED_INT); + return (uint *) indexData(); +} + +inline const quint16 *QSGGeometry::indexDataAsUShort() const +{ + Q_ASSERT(m_index_type == GL_UNSIGNED_SHORT); + return (quint16 *) indexData(); +} + +inline QSGGeometry::Point2D *QSGGeometry::vertexDataAsPoint2D() +{ + Q_ASSERT(m_attributes.count == 1); + Q_ASSERT(m_attributes.stride == 2 * sizeof(float)); + Q_ASSERT(m_attributes.attributes[0].tupleSize == 2); + Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT); + Q_ASSERT(m_attributes.attributes[0].position == 0); + return (Point2D *) m_data; +} + +inline QSGGeometry::TexturedPoint2D *QSGGeometry::vertexDataAsTexturedPoint2D() +{ + Q_ASSERT(m_attributes.count == 2); + Q_ASSERT(m_attributes.stride == 4 * sizeof(float)); + Q_ASSERT(m_attributes.attributes[0].position == 0); + Q_ASSERT(m_attributes.attributes[0].tupleSize == 2); + Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT); + Q_ASSERT(m_attributes.attributes[1].position == 1); + Q_ASSERT(m_attributes.attributes[1].tupleSize == 2); + Q_ASSERT(m_attributes.attributes[1].type == GL_FLOAT); + return (TexturedPoint2D *) m_data; +} + +inline QSGGeometry::ColoredPoint2D *QSGGeometry::vertexDataAsColoredPoint2D() +{ + Q_ASSERT(m_attributes.count == 2); + Q_ASSERT(m_attributes.stride == 2 * sizeof(float) + 4 * sizeof(char)); + Q_ASSERT(m_attributes.attributes[0].position == 0); + Q_ASSERT(m_attributes.attributes[0].tupleSize == 2); + Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT); + Q_ASSERT(m_attributes.attributes[1].position == 1); + Q_ASSERT(m_attributes.attributes[1].tupleSize == 4); + Q_ASSERT(m_attributes.attributes[1].type == GL_UNSIGNED_BYTE); + return (ColoredPoint2D *) m_data; +} + +inline const QSGGeometry::Point2D *QSGGeometry::vertexDataAsPoint2D() const +{ + Q_ASSERT(m_attributes.count == 1); + Q_ASSERT(m_attributes.stride == 2 * sizeof(float)); + Q_ASSERT(m_attributes.attributes[0].tupleSize == 2); + Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT); + Q_ASSERT(m_attributes.attributes[0].position == 0); + return (const Point2D *) m_data; +} + +inline const QSGGeometry::TexturedPoint2D *QSGGeometry::vertexDataAsTexturedPoint2D() const +{ + Q_ASSERT(m_attributes.count == 2); + Q_ASSERT(m_attributes.stride == 4 * sizeof(float)); + Q_ASSERT(m_attributes.attributes[0].position == 0); + Q_ASSERT(m_attributes.attributes[0].tupleSize == 2); + Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT); + Q_ASSERT(m_attributes.attributes[1].position == 1); + Q_ASSERT(m_attributes.attributes[1].tupleSize == 2); + Q_ASSERT(m_attributes.attributes[1].type == GL_FLOAT); + return (const TexturedPoint2D *) m_data; +} + +inline const QSGGeometry::ColoredPoint2D *QSGGeometry::vertexDataAsColoredPoint2D() const +{ + Q_ASSERT(m_attributes.count == 2); + Q_ASSERT(m_attributes.stride == 2 * sizeof(float) + 4 * sizeof(char)); + Q_ASSERT(m_attributes.attributes[0].position == 0); + Q_ASSERT(m_attributes.attributes[0].tupleSize == 2); + Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT); + Q_ASSERT(m_attributes.attributes[1].position == 1); + Q_ASSERT(m_attributes.attributes[1].tupleSize == 4); + Q_ASSERT(m_attributes.attributes[1].type == GL_UNSIGNED_BYTE); + return (const ColoredPoint2D *) m_data; +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSGGEOMETRY_H diff --git a/src/imports/shaders/shadereffect.cpp b/src/imports/shaders/shadereffect.cpp new file mode 100644 index 00000000..97649eb6 --- /dev/null +++ b/src/imports/shaders/shadereffect.cpp @@ -0,0 +1,196 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QML Shaders plugin of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "shadereffect.h" +#include "shadereffectbuffer.h" +#include "shadereffectsource.h" + +#include +#include +#include + +static QTransform savedWorldTransform; + +ShaderEffect::ShaderEffect(QObject *parent) + : QGraphicsEffect(parent) + , m_changed(true) +{ +} + +ShaderEffect::~ShaderEffect() +{ +} + +void ShaderEffect::prepareBufferedDraw(QPainter *painter) +{ +#ifndef QT_NO_DYNAMIC_CAST + // This workaround needed because QGraphicsEffect seems to always utilize default painters worldtransform + // instead of the active painters worldtransform. + const ShaderEffectBuffer *effectBuffer = dynamic_cast (painter->device()); + if (effectBuffer) { + savedWorldTransform = painter->worldTransform() * savedWorldTransform; + painter->setWorldTransform(savedWorldTransform); + } else { + savedWorldTransform = painter->worldTransform(); + } +#else + Q_UNUSED(painter); +#endif +} + +void ShaderEffect::draw (QPainter *painter) +{ + const QGLContext *context = QGLContext::currentContext(); + + prepareBufferedDraw(painter); + + if (context) { + updateRenderTargets(); + } + + if (!context || m_renderTargets.count() == 0 || !hideOriginal()) + drawSource(painter); +} + +void ShaderEffect::updateRenderTargets() +{ + if (!m_changed) + return; + + m_changed = false; + + int count = m_renderTargets.count(); + for (int i = 0; i < count; i++) { + if (m_renderTargets[i]->isLive() || m_renderTargets[i]->isDirtyTexture()) { + m_renderTargets[i]->updateBackbuffer(); + ShaderEffectBuffer* target = m_renderTargets[i]->fbo(); + if (target && target->isValid() && target->width() > 0 && target->height() > 0) { + QPainter p(target); + p.setCompositionMode(QPainter::CompositionMode_Clear); + p.fillRect(QRect(QPoint(0, 0), target->size()), Qt::transparent); + p.setCompositionMode(QPainter::CompositionMode_SourceOver); + + QRectF sourceRect = m_renderTargets[i]->sourceRect(); + QSize textureSize = m_renderTargets[i]->textureSize(); + + qreal yflip = m_renderTargets[i]->isMirrored() ? -1.0 : 1.0; // flip y to match scenegraph, it also flips texturecoordinates + qreal xscale = 1.0; + qreal yscale = 1.0 * yflip; + + qreal leftMargin = 0.0; + qreal rightMargin = 0.0; + qreal topMargin = 0.0; + qreal bottomMargin = 0.0; + + qreal width = m_renderTargets[i]->sourceItem()->width(); + qreal height = m_renderTargets[i]->sourceItem()->height(); + + if (!sourceRect.isEmpty()) { + leftMargin = -sourceRect.left(); + rightMargin = sourceRect.right() - width; + topMargin = -sourceRect.top(); + bottomMargin = sourceRect.bottom() - height; + } + + if ((width + leftMargin + rightMargin) > 0 && (height + topMargin + bottomMargin) > 0) { + if (!textureSize.isEmpty()) { + qreal textureWidth = textureSize.width(); + qreal textureHeight = textureSize.height(); + + xscale = width / (width + leftMargin + rightMargin); + yscale = height / (height + topMargin + bottomMargin); + + p.translate(textureWidth / 2, textureHeight / 2); + p.scale(xscale, yscale * yflip); + p.translate(-textureWidth / 2, -textureHeight / 2); + p.scale(textureWidth / width, textureHeight / height); + } else { + xscale = width / (width + leftMargin + rightMargin); + yscale = height / (height + topMargin + bottomMargin); + + p.translate(width / 2, height / 2); + p.scale(xscale, yscale * yflip); + p.translate(-width / 2, -height / 2); + } + } + + drawSource(&p); + p.end(); + m_renderTargets[i]->markSceneGraphDirty(); + } + } + } +} + +void ShaderEffect::sourceChanged (ChangeFlags flags) +{ + Q_UNUSED(flags); + m_changed = true; +} + +void ShaderEffect::addRenderTarget(ShaderEffectSource *target) +{ + if (!m_renderTargets.contains(target)) + m_renderTargets.append(target); +} + +void ShaderEffect::removeRenderTarget(ShaderEffectSource *target) +{ + int index = m_renderTargets.indexOf(target); + if (index >= 0) + m_renderTargets.remove(index); + else + qWarning() << "ShaderEffect::removeRenderTarget - did not find target."; +} + +bool ShaderEffect::hideOriginal() const +{ + if (m_renderTargets.count() == 0) + return false; + + // Just like scenegraph version, if there is even one source that says "hide original" we hide it. + int count = m_renderTargets.count(); + for (int i = 0; i < count; i++) { + if (m_renderTargets[i]->hideSource()) + return true; + } + return false; +} diff --git a/src/imports/shaders/shadereffect.h b/src/imports/shaders/shadereffect.h new file mode 100644 index 00000000..ac61d2ea --- /dev/null +++ b/src/imports/shaders/shadereffect.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QML Shaders plugin of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SHADEREFFECT_H +#define SHADEREFFECT_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class ShaderEffectSource; + +class ShaderEffect : public QGraphicsEffect +{ + Q_OBJECT + +public: + ShaderEffect(QObject *parent = 0); + ~ShaderEffect(); + void addRenderTarget(ShaderEffectSource *target); + void removeRenderTarget(ShaderEffectSource *target); + +protected: + virtual void draw (QPainter *painter); + virtual void sourceChanged (ChangeFlags flags); + +private: + void prepareBufferedDraw(QPainter *painter); + void updateRenderTargets(); + bool hideOriginal() const; + +public: + QVector m_renderTargets; + bool m_changed : 1; +}; + +QT_END_HEADER + +QT_END_NAMESPACE + +#endif // SHADEREFFECT_H diff --git a/src/imports/shaders/shadereffectbuffer.cpp b/src/imports/shaders/shadereffectbuffer.cpp new file mode 100644 index 00000000..415f9272 --- /dev/null +++ b/src/imports/shaders/shadereffectbuffer.cpp @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QML Shaders plugin of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "shadereffectbuffer.h" + +ShaderEffectBuffer::ShaderEffectBuffer(const QSize & size, const QGLFramebufferObjectFormat & format) + : QGLFramebufferObject(size, format) +{ +} + +ShaderEffectBuffer::~ShaderEffectBuffer() +{ +} + diff --git a/src/imports/shaders/shadereffectbuffer.h b/src/imports/shaders/shadereffectbuffer.h new file mode 100644 index 00000000..274a81ac --- /dev/null +++ b/src/imports/shaders/shadereffectbuffer.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QML Shaders plugin of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SHADEREFFECTBUFFER_H +#define SHADEREFFECTBUFFER_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class ShaderEffectBuffer : public QGLFramebufferObject +{ +public: + ShaderEffectBuffer(const QSize &size, const QGLFramebufferObjectFormat &format); + ~ShaderEffectBuffer(); +}; + +QT_END_HEADER + +QT_END_NAMESPACE + +#endif // SHADEREFFECTBUFFER_H diff --git a/src/imports/shaders/shadereffectitem.cpp b/src/imports/shaders/shadereffectitem.cpp new file mode 100644 index 00000000..3259c850 --- /dev/null +++ b/src/imports/shaders/shadereffectitem.cpp @@ -0,0 +1,955 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QML Shaders plugin of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "shadereffectitem.h" +#include "shadereffect.h" +#include "glfunctions.h" + +#include +#include + +static const char qt_default_vertex_code[] = + "uniform highp mat4 qt_ModelViewProjectionMatrix;\n" + "attribute highp vec4 qt_Vertex;\n" + "attribute highp vec2 qt_MultiTexCoord0;\n" + "varying highp vec2 qt_TexCoord0;\n" + "void main(void)\n" + "{\n" + "qt_TexCoord0 = qt_MultiTexCoord0;\n" + "gl_Position = qt_ModelViewProjectionMatrix * qt_Vertex;\n" + "}\n"; + +static const char qt_default_fragment_code[] = + "varying highp vec2 qt_TexCoord0;\n" + "uniform lowp sampler2D source;\n" + "void main(void)\n" + "{\n" + "gl_FragColor = texture2D(source, qt_TexCoord0.st);\n" + "}\n"; + +static const char qt_postion_attribute_name[] = "qt_Vertex"; +static const char qt_texcoord_attribute_name[] = "qt_MultiTexCoord0"; +static const char qt_emptyAttributeName[] = ""; + + +/*! + \qmlclass ShaderEffectItem ShaderEffectItem + \ingroup qml-shader-elements + \brief The ShaderEffectItem object alters the output of given item with OpenGL shaders. + \inherits Item + + ShaderEffectItem is available in the \bold{Qt.labs.shaders 1.0} module. + \e {Elements in the Qt.labs module are not guaranteed to remain compatible + in future versions.} + + This element provides preliminary support for embedding OpenGL shader code into QML, + and may be heavily changed or removed in later versions. + + Requirement for the use of shaders is that the application is either using + Qt OpenGL graphicssystem or is using OpenGL by setting QGLWidget as the viewport to QDeclarativeView (depending on which one is the recommened way in the targeted platform). + + ShaderEffectItem internal behaviour is such that during the paint event it first renders its + ShaderEffectSource items into a OpenGL framebuffer object which can be used as a texture. If the ShaderEffectSource is defined to be an image, + it is directly uploaded as a texture. The texture(s) containing the source pixelcontent are then bound to graphics + pipeline texture units. Finally a textured mesh is passed to the vertex- and fragmentshaders which + then produce the final output for the ShaderEffectItem. It is possible to alter the mesh structure by defining + the amount vertices it contains, but currently it is not possible to import complex 3D-models to be used as the mesh. + + It is possible to define one or more ShaderEffectItems to be a ShaderEffectSource for other ShaderEffectItems, but ShaderEffectItem + should never be declared as a child element of its source item(s) because it would cause circular loop in the painting. + + A standard set of vertex attributes are provided for the shaders: + + \list + \o qt_Vertex - The primary position of the vertex. + \o qt_MultiTexCoord0 - The texture co-ordinate at each vertex for texture unit 0. + \endlist + + Additionally following uniforms are available for shaders: + + \list + \o qt_Opacity - Effective opacity of the item. + \o qt_ModelViewProjectionMatrix - current 4x4 transformation matrix of the item. + \endlist + + Furthermore, it is possible to utilize automatic QML propertybinding into vertex- and fragment shader + uniforms. Conversions are done according to the table below: + + \table + \header + \o QML property + \o GLSL uniform + \row + \o property double foo: 1.0 + \o uniform highp float foo + \row + \o property real foo: 1.0 + \o uniform highp float foo + \row + \o property bool foo: true + \o uniform bool foo + \row + \o property int foo: 1 + \o uniform int foo + \row + \o property variant foo: Qt.point(1,1) + \o uniform highp vec2 foo + \row + \o property variant foo: Qt.size(1, 1) + \o uniform highp vec2 foo + \row + \o property variant foo: Qt.rect(1, 1, 2, 2) + \o uniform highp vec4 foo + \row + \o property color foo: "#00000000" + \o uniform lowp vec4 foo + \row + \o property variant foo: Qt.vector3d(1.0, 2.0, 0.0) + \o uniform highp vec3 foo + \row + \o property variant foo: ShaderEffectSource { SourceItem: bar } + \o uniform lowp sampler2D foo + \endtable + \note + The uniform precision definitions in the above table are not strict, it is possible to choose the uniform + precision based on what is the most suitable for the shader code for that particular uniform. + + + The below example uses fragment shader to create simple wiggly effect to a text label. + Automatic property binding takes care of binding the properties to the uniforms if their + names are identical. ShaderEffectSource referring to textLabel is bound to sampler2D uniform inside the fragment + shader code. + + \qml +import QtQuick 1.0 +import Qt.labs.shaders 1.0 + +Rectangle { + width: 300 + height: 300 + color: "black" + + Text { + id: textLabel + text: "Hello World" + anchors.centerIn: parent + font.pixelSize: 32 + color: "white" + + } + + ShaderEffectItem { + property variant source: ShaderEffectSource { sourceItem: textLabel; hideSource: true } + property real wiggleAmount: 0.005 + anchors.fill: textLabel + + fragmentShader: " + varying highp vec2 qt_TexCoord0; + uniform sampler2D source; + uniform highp float wiggleAmount; + void main(void) + { + highp vec2 wiggledTexCoord = qt_TexCoord0; + wiggledTexCoord.s += sin(4.0 * 3.141592653589 * wiggledTexCoord.t) * wiggleAmount; + gl_FragColor = texture2D(source, wiggledTexCoord.st); + } + " + } +} + \endqml + \image shaderexample.png + +*/ + +#ifdef Q_OS_SYMBIAN +#define OBSERVE_GL_CONTEXT_LOSS 1 +#endif + +ShaderEffectItem::ShaderEffectItem(QDeclarativeItem *parent) + : QDeclarativeItem(parent) + , m_program(0) + , m_meshResolution(1, 1) + , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4) + , m_blending(true) + , m_program_dirty(true) + , m_active(true) + , m_respectsMatrix(false) + , m_respectsOpacity(false) + , m_checkedViewportUpdateMode(false) + , m_checkedOpenGL(false) + , m_checkedShaderPrograms(false) + , m_hasShaderPrograms(false) + , m_mirrored(false) + , m_defaultVertexShader(true) + , m_contextObserver(0) +{ + setFlag(QGraphicsItem::ItemHasNoContents, false); + connect(this, SIGNAL(visibleChanged()), this, SLOT(handleVisibilityChange())); + m_active = isVisible(); + +#ifndef OBSERVE_GL_CONTEXT_LOSS + m_program = new QGLShaderProgram(this); +#endif +} + +ShaderEffectItem::~ShaderEffectItem() +{ + reset(); + delete m_contextObserver; +} + + +/*! + \qmlproperty string ShaderEffectItem::fragmentShader + This property holds the OpenGL fragment shader code. + + The default fragment shader is following: + + \code + varying highp vec2 qt_TexCoord0; + uniform sampler2D source; + void main(void) + { + gl_FragColor = texture2D(source, qt_TexCoord0.st); + } + \endcode + +*/ + +void ShaderEffectItem::setFragmentShader(const QString &code) +{ + if (m_fragment_code.constData() == code.constData()) + return; + + m_fragment_code = code; + if (isComponentComplete()) { + reset(); + updateProperties(); + } + emit fragmentShaderChanged(); +} + +/*! + \qmlproperty string ShaderEffectItem::vertexShader + This property holds the OpenGL vertex shader code. + + The default vertex shader is following: + + \code + uniform highp mat4 qt_ModelViewProjectionMatrix; + attribute highp vec4 qt_Vertex; + attribute highp vec2 qt_MultiTexCoord0; + varying highp vec2 qt_TexCoord0; + void main(void) + { + qt_TexCoord0 = qt_MultiTexCoord0; + gl_Position = qt_ModelViewProjectionMatrix * qt_Vertex; + } + \endcode + +*/ + +void ShaderEffectItem::setVertexShader(const QString &code) +{ + if (m_vertex_code.constData() == code.constData()) + return; + + m_vertex_code = code; + m_defaultVertexShader = false; + if (isComponentComplete()) { + reset(); + updateProperties(); + } + emit vertexShaderChanged(); +} + +/*! + \qmlproperty bool ShaderEffectItem::blending + This property defines whether item is drawn using blending. + + If true, the RGBA pixel output from the fragment shader is blended with + the pixel RGBA-values already in the framebuffer. + + If false, fragment shader output is written to framebuffer as such. + + Usually drawing without blending is slightly faster, thus disabling blending + might be a good choice when item is used as a background element. + + \note + By default the pixel data in textures is stored in 32-bit premultiplied alpha format. + This should be taken into account when blending or reading the pixel values + in the fragment shader code. + + The default value is true. +*/ + +void ShaderEffectItem::setBlending(bool enable) +{ + if (m_blending == enable) + return; + + m_blending = enable; + m_changed = true; + emit blendingChanged(); +} + + +/*! + \qmlproperty QSize ShaderEffectItem::meshResolution + This property defines to how many triangles the item is divided into before its + vertices are passed to the vertex shader. + + Triangles are defined as triangle strips and the amount of triangles can be controlled + separately for x and y-axis. + + The default value is QSize(1,1). +*/ + +void ShaderEffectItem::setMeshResolution(const QSize &size) +{ + if (size == m_meshResolution) + return; + + m_meshResolution = size; + emit meshResolutionChanged(); + updateGeometry(); +} + +void ShaderEffectItem::componentComplete() +{ + updateProperties(); + QDeclarativeItem::componentComplete(); +} + +void ShaderEffectItem::checkViewportUpdateMode() +{ + if (!m_checkedViewportUpdateMode) { + QGraphicsScene *s = scene(); + if (s){ + QList views = s->views(); + for (int i = 0; i < views.count(); i++) { + if (views[i]->viewportUpdateMode() != QGraphicsView::FullViewportUpdate) { + qWarning() << "ShaderEffectItem::checkViewportUpdateMode - consider setting QGraphicsView::FullViewportUpdate mode with OpenGL!"; + } + } + } + m_checkedViewportUpdateMode = true; + } +} + +void ShaderEffectItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) +{ + if (!m_active) return; + + const QGLContext *context = QGLContext::currentContext(); + + if (context) { + if (!m_checkedShaderPrograms) { + m_hasShaderPrograms = QGLShaderProgram::hasOpenGLShaderPrograms(context); + m_checkedShaderPrograms = true; + + if (!m_hasShaderPrograms) + qWarning() << "ShaderEffectItem::paint - Shader programs are not supported"; + } + + if ( !m_hasShaderPrograms ) + return; + + checkViewportUpdateMode(); + painter->save(); + painter->beginNativePainting(); + QMatrix4x4 combinedMatrix = QMatrix4x4(painter->transform()); + renderEffect(painter, combinedMatrix); + painter->endNativePainting(); + painter->restore(); + } else { + if (!m_checkedOpenGL) { + qWarning() << "ShaderEffectItem::paint - OpenGL not available"; + m_checkedOpenGL = true; + } + } +} + +void ShaderEffectItem::renderEffect(QPainter *painter, const QMatrix4x4 &matrix) +{ + if (!painter || !painter->device()) + return; + +#ifdef OBSERVE_GL_CONTEXT_LOSS + QGLContext *context = const_cast (QGLContext::currentContext()); + if (!m_program || !m_contextObserver || !m_contextObserver->isValid()) { + // Context has changed, re-create QGLShaderProgram + if (context) { + delete m_program; + m_program = 0; + + delete m_contextObserver; + m_contextObserver = 0; + + m_program = new QGLShaderProgram(this); + m_contextObserver = new QGLFramebufferObject(QSize(2,2)); + + if (!m_contextObserver || !m_program) { + delete m_program; + m_program = 0; + delete m_contextObserver; + m_contextObserver = 0; + qWarning() << "ShaderEffectItem::renderEffect - Creating QGLShaderProgram or QGLFrameBufferObject failed!"; + } + } + } +#endif + + if (!m_program) + return; + + if (!m_program->isLinked() || m_program_dirty) + updateShaderProgram(); + + m_program->bind(); + + QMatrix4x4 combinedMatrix; + combinedMatrix.scale(2.0 / painter->device()->width(), -2.0 / painter->device()->height(), 1.0); + combinedMatrix.translate(-painter->device()->width() / 2.0, -painter->device()->height() / 2.0 ); + combinedMatrix *= matrix; + updateEffectState(combinedMatrix); + + for (int i = 0; i < m_attributeNames.size(); ++i) { + m_program->enableAttributeArray(m_geometry.attributes()[i].position); + } + + bindGeometry(); + + // Optimization, disable depth test when we know we don't need it. + if (m_defaultVertexShader) { + glDepthMask(false); + glDisable(GL_DEPTH_TEST); + } else { + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_GREATER); + glDepthMask(true); +#if defined(QT_OPENGL_ES) + glClearDepthf(0); +#else + glClearDepth(0); +#endif + glClearColor(0, 0, 0, 0); + glClear(GL_DEPTH_BUFFER_BIT); + } + + if (m_blending){ + glEnable(GL_BLEND); + glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + } else { + glDisable(GL_BLEND); + } + + if (m_geometry.indexCount()) + glDrawElements(m_geometry.drawingMode(), m_geometry.indexCount(), m_geometry.indexType(), m_geometry.indexData()); + else + glDrawArrays(m_geometry.drawingMode(), 0, m_geometry.vertexCount()); + + glDepthMask(false); + glDisable(GL_DEPTH_TEST); + + for (int i = 0; i < m_attributeNames.size(); ++i) + m_program->disableAttributeArray(m_geometry.attributes()[i].position); +} + +void ShaderEffectItem::updateEffectState(const QMatrix4x4 &matrix) +{ + if (!m_program) + return; + + for (int i = m_sources.size() - 1; i >= 0; --i) { + const ShaderEffectItem::SourceData &source = m_sources.at(i); + if (!source.source) + continue; + + glActiveTexture(GL_TEXTURE0 + i); + source.source->bind(); + } + + if (m_respectsOpacity) + m_program->setUniformValue("qt_Opacity", static_cast (effectiveOpacity())); + + if (m_respectsMatrix){ + m_program->setUniformValue("qt_ModelViewProjectionMatrix", matrix); + } + + QSet::const_iterator it; + for (it = m_uniformNames.begin(); it != m_uniformNames.end(); ++it) { + const QByteArray &name = *it; + QVariant v = property(name.constData()); + + switch (v.type()) { + case QVariant::Color: + m_program->setUniformValue(name.constData(), qvariant_cast(v)); + break; + case QVariant::Double: + m_program->setUniformValue(name.constData(), (float) qvariant_cast(v)); + break; + case QVariant::Transform: + m_program->setUniformValue(name.constData(), qvariant_cast(v)); + break; + case QVariant::Int: + m_program->setUniformValue(name.constData(), GLint(v.toInt())); + break; + case QVariant::Bool: + m_program->setUniformValue(name.constData(), GLint(v.toBool())); + break; + case QVariant::Size: + case QVariant::SizeF: + m_program->setUniformValue(name.constData(), v.toSizeF()); + break; + case QVariant::Point: + case QVariant::PointF: + m_program->setUniformValue(name.constData(), v.toPointF()); + break; + case QVariant::Rect: + case QVariant::RectF: + { + QRectF r = v.toRectF(); + m_program->setUniformValue(name.constData(), r.x(), r.y(), r.width(), r.height()); + } + break; + case QVariant::Vector3D: + m_program->setUniformValue(name.constData(), qvariant_cast(v)); + break; + default: + break; + } + } +} + +static inline int size_of_type(GLenum type) +{ + static int sizes[] = { + sizeof(char), + sizeof(unsigned char), + sizeof(short), + sizeof(unsigned short), + sizeof(int), + sizeof(unsigned int), + sizeof(float), + 2, + 3, + 4, + sizeof(double) + }; + return sizes[type - GL_BYTE]; +} + +void ShaderEffectItem::bindGeometry() +{ + if (!m_program) + return; + + char const *const *attrNames = m_attributeNames.constData(); + int offset = 0; + for (int j = 0; j < m_attributeNames.size(); ++j) { + if (!*attrNames[j]) + continue; + Q_ASSERT_X(j < m_geometry.attributeCount(), "ShaderEffectItem::bindGeometry()", "Geometry lacks attribute required by material"); + const QSGGeometry::Attribute &a = m_geometry.attributes()[j]; + Q_ASSERT_X(j == a.position, "ShaderEffectItem::bindGeometry()", "Geometry does not have continuous attribute positions"); +#if defined(QT_OPENGL_ES_2) + GLboolean normalize = a.type != GL_FLOAT; +#else + GLboolean normalize = a.type != GL_FLOAT && a.type != GL_DOUBLE; +#endif + if (normalize) + qWarning() << "ShaderEffectItem::bindGeometry() - non supported attribute type!"; + + m_program->setAttributeArray(a.position, (GLfloat*) (((char*) m_geometry.vertexData()) + offset), a.tupleSize, m_geometry.stride()); + //glVertexAttribPointer(a.position, a.tupleSize, a.type, normalize, m_geometry.stride(), (char *) m_geometry.vertexData() + offset); + offset += a.tupleSize * size_of_type(a.type); + } +} + +void ShaderEffectItem::updateGeometry() +{ + QRectF srcRect(0, 1, 1, -1); + + if (m_mirrored) + srcRect = QRectF(0, 0, 1, 1); + + QRectF dstRect = QRectF(0,0, width(), height()); + + int vmesh = m_meshResolution.height(); + int hmesh = m_meshResolution.width(); + + QSGGeometry *g = &m_geometry; + if (vmesh == 1 && hmesh == 1) { + if (g->vertexCount() != 4) + g->allocate(4); + QSGGeometry::updateTexturedRectGeometry(g, dstRect, srcRect); + return; + } + + g->allocate((vmesh + 1) * (hmesh + 1), vmesh * 2 * (hmesh + 2)); + + QSGGeometry::TexturedPoint2D *vdata = g->vertexDataAsTexturedPoint2D(); + + for (int iy = 0; iy <= vmesh; ++iy) { + float fy = iy / float(vmesh); + float y = float(dstRect.top()) + fy * float(dstRect.height()); + float ty = float(srcRect.top()) + fy * float(srcRect.height()); + for (int ix = 0; ix <= hmesh; ++ix) { + float fx = ix / float(hmesh); + vdata->x = float(dstRect.left()) + fx * float(dstRect.width()); + vdata->y = y; + vdata->tx = float(srcRect.left()) + fx * float(srcRect.width()); + vdata->ty = ty; + ++vdata; + } + } + + quint16 *indices = (quint16 *)g->indexDataAsUShort(); + int i = 0; + for (int iy = 0; iy < vmesh; ++iy) { + *(indices++) = i + hmesh + 1; + for (int ix = 0; ix <= hmesh; ++ix, ++i) { + *(indices++) = i + hmesh + 1; + *(indices++) = i; + } + *(indices++) = i - 1; + } +} + +void ShaderEffectItem::setActive(bool enable) +{ + if (m_active == enable) + return; + + if (m_active) { + for (int i = 0; i < m_sources.size(); ++i) { + ShaderEffectSource *source = m_sources.at(i).source; + if (!source) + continue; + disconnect(source, SIGNAL(repaintRequired()), this, SLOT(markDirty())); + source->derefFromEffectItem(); + } + } + + m_active = enable; + + if (m_active) { + for (int i = 0; i < m_sources.size(); ++i) { + ShaderEffectSource *source = m_sources.at(i).source; + if (!source) + continue; + source->refFromEffectItem(); + connect(source, SIGNAL(repaintRequired()), this, SLOT(markDirty())); + } + } + + // QGLShaderProgram is deleted when not active (to minimize GPU memory usage). +#ifdef OBSERVE_GL_CONTEXT_LOSS + if (!m_active && m_program) { + delete m_program; + m_program = 0; + delete m_contextObserver; + m_contextObserver = 0; + } +#endif + + emit activeChanged(); + markDirty(); +} + +void ShaderEffectItem::preprocess() +{ + for (int i = 0; i < m_sources.size(); ++i) { + ShaderEffectSource *source = m_sources.at(i).source; + if (source) + source->updateBackbuffer(); + } +} + +void ShaderEffectItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + if (newGeometry.size() != oldGeometry.size()) + updateGeometry(); + QDeclarativeItem::geometryChanged(newGeometry, oldGeometry); +} + +void ShaderEffectItem::changeSource(int index) +{ + Q_ASSERT(index >= 0 && index < m_sources.size()); + QVariant v = property(m_sources.at(index).name.constData()); + setSource(v, index); +} + +void ShaderEffectItem::markDirty() { + update(); +} + +void ShaderEffectItem::setSource(const QVariant &var, int index) +{ + Q_ASSERT(index >= 0 && index < m_sources.size()); + + SourceData &source = m_sources[index]; + + source.source = 0; + source.item = 0; + if (var.isNull()) { + return; + } else if (!qVariantCanConvert(var)) { + qWarning("Could not assign source of type '%s' to property '%s'.", var.typeName(), source.name.constData()); + return; + } + + QObject *obj = qVariantValue(var); + + source.source = qobject_cast(obj); + source.item = qobject_cast(obj); + + if (!source.item) + qWarning("Could not assign property '%s', did not implement QDeclarativeItem.", source.name.constData()); + + if (!source.source) + qWarning("Could not assign property '%s', did not implement ShaderEffectSource.", source.name.constData()); + + // TODO: Find better solution. + // 'source.item' needs a canvas to get a scenegraph node. + // The easiest way to make sure it gets a canvas is to + // make it a part of the same item tree as 'this'. + if (source.item && source.item->parentItem() == 0) { + source.item->setParentItem(this); + // Unlike in scenegraph, we cannot set item invisible here because qgraphicsview would optimize it away. + } + + // Unlike in scenegraph, ref counting is used to optimize memory consumption. Sources themself may free fbos when not referenced. + if (m_active && source.source) { + source.source->refFromEffectItem(); + connect(source.source, SIGNAL(repaintRequired()), this, SLOT(markDirty())); + } +} + +void ShaderEffectItem::disconnectPropertySignals() +{ + disconnect(this, 0, this, SLOT(markDirty())); + for (int i = 0; i < m_sources.size(); ++i) { + SourceData &source = m_sources[i]; + disconnect(this, 0, source.mapper, 0); + disconnect(source.mapper, 0, this, 0); + } +} + +void ShaderEffectItem::connectPropertySignals() +{ + QSet::const_iterator it; + for (it = m_uniformNames.begin(); it != m_uniformNames.end(); ++it) { + int pi = metaObject()->indexOfProperty(it->constData()); + if (pi >= 0) { + QMetaProperty mp = metaObject()->property(pi); + if (!mp.hasNotifySignal()) + qWarning("ShaderEffectItem: property '%s' does not have notification method!", it->constData()); + QByteArray signalName("2"); + signalName.append(mp.notifySignal().signature()); + connect(this, signalName, this, SLOT(markDirty())); + } else { + qWarning("ShaderEffectItem: '%s' does not have a matching property!", it->constData()); + } + } + for (int i = 0; i < m_sources.size(); ++i) { + SourceData &source = m_sources[i]; + int pi = metaObject()->indexOfProperty(source.name.constData()); + if (pi >= 0) { + QMetaProperty mp = metaObject()->property(pi); + QByteArray signalName("2"); + signalName.append(mp.notifySignal().signature()); + connect(this, signalName, source.mapper, SLOT(map())); + source.mapper->setMapping(this, i); + connect(source.mapper, SIGNAL(mapped(int)), this, SLOT(changeSource(int))); + } else { + qWarning("ShaderEffectItem: '%s' does not have a matching source!", source.name.constData()); + } + } +} + +void ShaderEffectItem::reset() +{ + disconnectPropertySignals(); + + if (m_program) + m_program->removeAllShaders(); + + m_attributeNames.clear(); + m_uniformNames.clear(); + for (int i = 0; i < m_sources.size(); ++i) { + const SourceData &source = m_sources.at(i); + if (m_active && source.source) + source.source->derefFromEffectItem(); + delete source.mapper; + } + + m_sources.clear(); + m_program_dirty = true; +} + +void ShaderEffectItem::updateProperties() +{ + QString vertexCode = m_vertex_code; + QString fragmentCode = m_fragment_code; + + if (vertexCode.isEmpty()) + vertexCode = qt_default_vertex_code; + + if (fragmentCode.isEmpty()) + fragmentCode = qt_default_fragment_code; + + lookThroughShaderCode(vertexCode); + lookThroughShaderCode(fragmentCode); + + if (!m_attributeNames.contains(qt_postion_attribute_name)) + qWarning("ShaderEffectItem: Missing reference to \'%s\'.", qt_postion_attribute_name); + if (!m_attributeNames.contains(qt_texcoord_attribute_name)) + qWarning("ShaderEffectItem: Missing reference to \'%s\'.", qt_texcoord_attribute_name); + if (!m_respectsMatrix) + qWarning("ShaderEffectItem: Missing reference to \'qt_ModelViewProjectionMatrix\'."); + + for (int i = 0; i < m_sources.size(); ++i) { + QVariant v = property(m_sources.at(i).name); + setSource(v, i); // Property exists. + } + + connectPropertySignals(); +} + +void ShaderEffectItem::updateShaderProgram() +{ + if (!m_program) + return; + + QString vertexCode = m_vertex_code; + QString fragmentCode = m_fragment_code; + + if (vertexCode.isEmpty()) + vertexCode = QString::fromLatin1(qt_default_vertex_code); + + if (fragmentCode.isEmpty()) + fragmentCode = QString::fromLatin1(qt_default_fragment_code); + + m_program->addShaderFromSourceCode(QGLShader::Vertex, vertexCode); + m_program->addShaderFromSourceCode(QGLShader::Fragment, fragmentCode); + + for (int i = 0; i < m_attributeNames.size(); ++i) { + m_program->bindAttributeLocation(m_attributeNames.at(i), m_geometry.attributes()[i].position); + } + + if (!m_program->link()) { + qWarning("ShaderEffectItem: Shader compilation failed:"); + qWarning() << m_program->log(); + } + + if (!m_attributeNames.contains(qt_postion_attribute_name)) + qWarning("ShaderEffectItem: Missing reference to \'qt_Vertex\'."); + if (!m_attributeNames.contains(qt_texcoord_attribute_name)) + qWarning("ShaderEffectItem: Missing reference to \'qt_MultiTexCoord0\'."); + if (!m_respectsMatrix) + qWarning("ShaderEffectItem: Missing reference to \'qt_ModelViewProjectionMatrix\'."); + + if (m_program->isLinked()) { + m_program->bind(); + for (int i = 0; i < m_sources.size(); ++i) + m_program->setUniformValue(m_sources.at(i).name.constData(), (GLint) i); + } + + m_program_dirty = false; +} + +void ShaderEffectItem::lookThroughShaderCode(const QString &code) +{ + // Regexp for matching attributes and uniforms. + // In human readable form: attribute|uniform [lowp|mediump|highp] + static QRegExp re(QLatin1String("\\b(attribute|uniform)\\b\\s*\\b(?:lowp|mediump|highp)?\\b\\s*\\b(\\w+)\\b\\s*\\b(\\w+)")); + Q_ASSERT(re.isValid()); + + int pos = -1; + + //QString wideCode = QString::fromLatin1(code.constData(), code.size()); + QString wideCode = code; + + while ((pos = re.indexIn(wideCode, pos + 1)) != -1) { + QByteArray decl = re.cap(1).toLatin1(); // uniform or attribute + QByteArray type = re.cap(2).toLatin1(); // type + QByteArray name = re.cap(3).toLatin1(); // variable name + + if (decl == "attribute") { + if (name == qt_postion_attribute_name) { + m_attributeNames.insert(0, qt_postion_attribute_name); + } else if (name == "qt_MultiTexCoord0") { + if (m_attributeNames.at(0) == 0) { + m_attributeNames.insert(0, qt_emptyAttributeName); + } + m_attributeNames.insert(1, qt_texcoord_attribute_name); + } else { + // TODO: Support user defined attributes. + qWarning("ShaderEffectItem: Attribute \'%s\' not recognized.", name.constData()); + } + } else { + Q_ASSERT(decl == "uniform"); + + if (name == "qt_ModelViewProjectionMatrix") { + m_respectsMatrix = true; + } else if (name == "qt_Opacity") { + m_respectsOpacity = true; + } else { + m_uniformNames.insert(name); + if (type == "sampler2D") { + SourceData d; + d.mapper = new QSignalMapper; + d.source = 0; + d.name = name; + d.item = 0; + m_sources.append(d); + } + } + } + } +} + +void ShaderEffectItem::handleVisibilityChange() +{ + setActive(isVisible()); +} diff --git a/src/imports/shaders/shadereffectitem.h b/src/imports/shaders/shadereffectitem.h new file mode 100644 index 00000000..a5581b44 --- /dev/null +++ b/src/imports/shaders/shadereffectitem.h @@ -0,0 +1,154 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QML Shaders plugin of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SHADEREFFECTITEM_H +#define SHADEREFFECTITEM_H + +#include +#include +#include "shadereffectsource.h" +#include "scenegraph/qsggeometry.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class ShaderEffectItem : public QDeclarativeItem +{ + Q_OBJECT + Q_INTERFACES(QDeclarativeParserStatus) + Q_PROPERTY(QString fragmentShader READ fragmentShader WRITE setFragmentShader NOTIFY fragmentShaderChanged) + Q_PROPERTY(QString vertexShader READ vertexShader WRITE setVertexShader NOTIFY vertexShaderChanged) + Q_PROPERTY(bool blending READ blending WRITE setBlending NOTIFY blendingChanged) + Q_PROPERTY(QSize meshResolution READ meshResolution WRITE setMeshResolution NOTIFY meshResolutionChanged) + +public: + ShaderEffectItem(QDeclarativeItem* parent = 0); + ~ShaderEffectItem(); + + virtual void componentComplete(); + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0); + + QString fragmentShader() const { return m_fragment_code; } + void setFragmentShader(const QString &code); + + QString vertexShader() const { return m_vertex_code; } + void setVertexShader(const QString &code); + + bool blending() const { return m_blending; } + void setBlending(bool enable); + + QSize meshResolution() const { return m_meshResolution; } + void setMeshResolution(const QSize &size); + + void preprocess(); + +Q_SIGNALS: + void fragmentShaderChanged(); + void vertexShaderChanged(); + void blendingChanged(); + void activeChanged(); + void meshResolutionChanged(); + +protected: + virtual void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); + +private Q_SLOTS: + void changeSource(int index); + void handleVisibilityChange(); + void markDirty(); + +private: + void checkViewportUpdateMode(); + void renderEffect(QPainter *painter, const QMatrix4x4 &matrix); + void updateEffectState(const QMatrix4x4 &matrix); + void updateGeometry(); + void bindGeometry(); + void setSource(const QVariant &var, int index); + void disconnectPropertySignals(); + void connectPropertySignals(); + void reset(); + void updateProperties(); + void updateShaderProgram(); + void lookThroughShaderCode(const QString &code); + bool active() const { return m_active; } + void setActive(bool enable); + +private: + QString m_fragment_code; + QString m_vertex_code; + QGLShaderProgram* m_program; + QVector m_attributeNames; + QSet m_uniformNames; + QSize m_meshResolution; + QSGGeometry m_geometry; + + struct SourceData + { + QSignalMapper *mapper; + QPointer source; + QPointer item; + QByteArray name; + }; + + QVector m_sources; + + bool m_changed : 1; + bool m_blending : 1; + bool m_program_dirty : 1; + bool m_active : 1; + bool m_respectsMatrix : 1; + bool m_respectsOpacity : 1; + bool m_checkedViewportUpdateMode : 1; + bool m_checkedOpenGL : 1; + bool m_checkedShaderPrograms : 1; + bool m_hasShaderPrograms : 1; + bool m_mirrored : 1; + bool m_defaultVertexShader : 1; + + QGLFramebufferObject* m_contextObserver; +}; + +QT_END_HEADER + +QT_END_NAMESPACE + +#endif // SHADEREFFECTITEM_H diff --git a/src/imports/shaders/shadereffectsource.cpp b/src/imports/shaders/shadereffectsource.cpp new file mode 100644 index 00000000..aa434e1c --- /dev/null +++ b/src/imports/shaders/shadereffectsource.cpp @@ -0,0 +1,451 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QML Shaders plugin of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "shadereffectsource.h" +#include "shadereffectbuffer.h" +#include "shadereffect.h" +#include "glfunctions.h" + +#include + +/*! + \qmlclass ShaderEffectSource ShaderEffectSource + \ingroup qml-shader-elements + \brief The ShaderEffectSource object encapsulates the source content for the ShaderEffectItem. + + ShaderEffectSource is available in the \bold{Qt.labs.shaders 1.0} module. + \e {Elements in the Qt.labs module are not guaranteed to remain compatible + in future versions.} + + This element provides preliminary support for OpenGL shaders in QML, + and may be heavily changed or removed in later versions. + + Requirement for the ability to use of shaders is that the application is either using + opengl graphicssystem or has set QGLWidget as the viewport to QDeclarativeView (recommended way). + + ShaderEffectSource object encapsulates the source content so that it can be utilized in ShaderEffectItem. + Source content can be a live QML object tree, or a snapshot of QML object tree. + +*/ + +ShaderEffectSource::ShaderEffectSource(QDeclarativeItem *parent) + : QDeclarativeItem(parent) + , m_sourceItem(0) + , m_wrapMode(ClampToEdge) + , m_sourceRect(0, 0, 0, 0) + , m_textureSize(0, 0) + , m_format(RGBA) + , m_size(0, 0) + , m_fbo(0) + , m_multisampledFbo(0) + , m_refs(0) + , m_dirtyTexture(true) + , m_dirtySceneGraph(true) + , m_multisamplingSupported(false) + , m_checkedForMultisamplingSupport(false) + , m_live(true) + , m_hideSource(false) + , m_mirrored(false) +{ +} + +ShaderEffectSource::~ShaderEffectSource() +{ + if (m_refs && m_sourceItem) + detachSourceItem(); + + delete m_fbo; + delete m_multisampledFbo; +} + +/*! + \qmlproperty Item ShaderEffectSource::sourceItem + This property holds the Item which is used as the source for the shader effect. + If the item has children, those are included as well. + + \note When source item content is passed to the ShaderEffectItem(s), it is always clipped to the boundingrect of the + sourceItem regardless of its clipping property. +*/ + +void ShaderEffectSource::setSourceItem(QDeclarativeItem *item) +{ + if (item == m_sourceItem) + return; + + if (m_sourceItem) { + disconnect(m_sourceItem, SIGNAL(widthChanged()), this, SLOT(markSourceSizeDirty())); + disconnect(m_sourceItem, SIGNAL(heightChanged()), this, SLOT(markSourceSizeDirty())); + + if (m_refs) + detachSourceItem(); + } + + m_sourceItem = item; + + if (m_sourceItem) { + + // Must have some item as parent + if (m_sourceItem->parentItem() == 0) + m_sourceItem->setParentItem(this); + + if (m_refs) + attachSourceItem(); + + connect(m_sourceItem, SIGNAL(widthChanged()), this, SLOT(markSourceSizeDirty())); + connect(m_sourceItem, SIGNAL(heightChanged()), this, SLOT(markSourceSizeDirty())); + } + + updateSizeAndTexture(); + emit sourceItemChanged(); + emit repaintRequired(); +} + +/*! + \qmlproperty QRectF ShaderEffectSource::sourceRect + This property can be used to specify margins for the source content. + + If other value than Qt.rect(0,0,0,0) is assigned to this property, it is interpreted as + specifying a relative source rectangle for the source content. + + For example, setting Qt.rect(-10.0, -10.0, 120.0, 120.0) for a source that has width and height + of 100 pixels would produce 10 pixels margins to each side of the source. + + Margins are useful when the original content is wanted to be spread outside the original source area, + like when creating a dropshadow with the shader or in other similar effects. + + The default value is Qt.rect(0,0,0,0). +*/ + +void ShaderEffectSource::setSourceRect(const QRectF &rect) +{ + if (rect == m_sourceRect) + return; + m_sourceRect = rect; + updateSizeAndTexture(); + emit sourceRectChanged(); + emit repaintRequired(); + + m_dirtyTexture = true; + markSourceItemDirty(); +} + +/*! + \qmlproperty QSize ShaderEffectSource::textureSize + This property holds the size for the texture containing the source content. + + If value QSize(0,0) is assigned to this property, texture is resized + according to the source size. Otherwise source content is scaled to + the given size. + + The default value is QSize(0,0). +*/ + +void ShaderEffectSource::setTextureSize(const QSize &size) +{ + if (size == m_textureSize) + return; + + m_textureSize = size; + updateSizeAndTexture(); + emit textureSizeChanged(); + emit repaintRequired(); + + m_dirtyTexture = true; + markSourceItemDirty(); +} + +/*! + \qmlproperty bool ShaderEffectSource::live + This property holds the optimization flag to define whether the source item content is changing or + static. + + If value true is assigned to this property, source item content is re-rendered into a + texture for every frame. Setting the value to false improves the performance as it skips + rendering the source item (and its chidleren) and instead immediately passes the previously + rendered and cached texture to the shaders. + + The default value is true. +*/ + +void ShaderEffectSource::setLive(bool s) +{ + if (s == m_live) + return; + + m_live = s; + + emit liveChanged(); + emit repaintRequired(); +} + +/*! + \qmlproperty bool ShaderEffectSource::hideSource + This property holds the flag to define whether the original source item is + hidden when the effect item is drawn. + + The default value is false. +*/ + +void ShaderEffectSource::setHideSource(bool hide) +{ + if (hide == m_hideSource) + return; + + m_hideSource = hide; + + emit hideSourceChanged(); + emit repaintRequired(); +} + +/*! + \qmlproperty enumeration ShaderEffectSource::wrapMode + + This property defines the wrap parameter for the source after it has been mapped as a texture. + + \list + \o ShaderEffectSource.ClampToEdge - Causes texturecoordinates to be clamped to the range [ 1/2*N , 1 - 1/2*N ], where N is the texture width. + \o ShaderEffectSource.RepeatHorizontally - Causes the integer part of the horizontal texturecoordinate to be ignored; the GL uses only the fractional part, thereby creating a horizontal repeating pattern. + \o ShaderEffectSource.RepeatVertically - Causes the integer part of the vertical texturecoordinate to be ignored; the GL uses only the fractional part, thereby creating a vertical repeating pattern. + \o ShaderEffectSource.Repeat - Causes the integer part of both the horizontal and vertical texturecoordinates to be ignored; the GL uses only the fractional part, thereby creating a repeating pattern. + \endlist + + The default value is ClampToEdge. + +*/ + +void ShaderEffectSource::setWrapMode(WrapMode mode) +{ + if (mode == m_wrapMode) + return; + + m_wrapMode = mode; + emit wrapModeChanged(); + + m_dirtyTexture = true; + markSourceItemDirty(); +} + +/*! + \qmlmethod ShaderEffectSource::grab() + + Repaints the source item content into the texture. + + This method is useful when ShaderEffectSource::live has been set to false and + the changes in the source item content is desired to be made visible for the shaders. + +*/ + +void ShaderEffectSource::grab() +{ + m_dirtyTexture = true; + emit repaintRequired(); +} + +void ShaderEffectSource::bind() +{ + GLint filtering = smooth() ? GL_LINEAR : GL_NEAREST; + GLuint hwrap = (m_wrapMode == Repeat || m_wrapMode == RepeatHorizontally) ? GL_REPEAT : GL_CLAMP_TO_EDGE; + GLuint vwrap = (m_wrapMode == Repeat || m_wrapMode == RepeatVertically) ? GL_REPEAT : GL_CLAMP_TO_EDGE; + +#if !defined(QT_OPENGL_ES_2) + glEnable(GL_TEXTURE_2D); +#endif + + if (m_fbo && m_fbo->isValid()) { + glBindTexture(GL_TEXTURE_2D, m_fbo->texture()); + } else { + m_dirtyTexture = true; + emit repaintRequired(); + markSourceItemDirty(); + glBindTexture(GL_TEXTURE_2D, 0); + } + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, smooth() ? GL_LINEAR : GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, hwrap); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, vwrap); +} + +void ShaderEffectSource::refFromEffectItem() +{ + if (m_refs++ == 0) { + attachSourceItem(); + emit activeChanged(); + } +} + +void ShaderEffectSource::derefFromEffectItem() +{ + if (--m_refs == 0) { + detachSourceItem(); + emit activeChanged(); + } + Q_ASSERT(m_refs >= 0); +} + +void ShaderEffectSource::updateBackbuffer() +{ + if (!m_sourceItem || !QGLContext::currentContext()) + return; + + // Multisampling is not (for now) supported. + QSize size = QSize(m_sourceItem->width(), m_sourceItem->height()); + if (!m_textureSize.isEmpty()) + size = m_textureSize; + + if (size.height() > 0 && size.width() > 0) { + QGLFramebufferObjectFormat format; + format.setAttachment(QGLFramebufferObject::CombinedDepthStencil); + format.setInternalTextureFormat(m_format); + + if (!m_fbo) { + m_fbo = new ShaderEffectBuffer(size, format); + } else { + if (!m_fbo->isValid() || m_fbo->size() != size || m_fbo->format().internalTextureFormat() != GLenum(m_format)) { + delete m_fbo; + m_fbo = 0; + m_fbo = new ShaderEffectBuffer(size, format); + } + } + } + + // Note that real update for the source content happens in shadereffect.cpp + m_dirtyTexture = false; +} + +void ShaderEffectSource::markSceneGraphDirty() +{ + m_dirtySceneGraph = true; + emit repaintRequired(); +} + +void ShaderEffectSource::markSourceSizeDirty() +{ + Q_ASSERT(m_sourceItem); + if (m_textureSize.isEmpty()) + updateSizeAndTexture(); + if (m_refs) + emit repaintRequired(); +} + +void ShaderEffectSource::markSourceItemDirty() +{ + m_dirtyTexture = true; + if (m_sourceItem) { + ShaderEffect* effect = qobject_cast (m_sourceItem->graphicsEffect()); + if (effect) + effect->m_changed = true; + } +} + +void ShaderEffectSource::updateSizeAndTexture() +{ + if (m_sourceItem) { + QSize size = m_textureSize; + if (size.isEmpty()) + size = QSize(m_sourceItem->width(), m_sourceItem->height()); + if (size.width() < 1) + size.setWidth(1); + if (size.height() < 1) + size.setHeight(1); + if (m_fbo && (m_fbo->size() != size || !m_fbo->isValid())) { + delete m_fbo; + m_fbo = 0; + delete m_multisampledFbo; + m_fbo = m_multisampledFbo = 0; + } + if (m_size.width() != size.width()) { + m_size.setWidth(size.width()); + emit widthChanged(); + } + if (m_size.height() != size.height()) { + m_size.setHeight(size.height()); + emit heightChanged(); + } + m_dirtyTexture = true; + } else { + if (m_size.width() != 0) { + m_size.setWidth(0); + emit widthChanged(); + } + if (m_size.height() != 0) { + m_size.setHeight(0); + emit heightChanged(); + } + } +} + +void ShaderEffectSource::attachSourceItem() +{ + if (!m_sourceItem) + return; + + ShaderEffect *effect = qobject_cast (m_sourceItem->graphicsEffect()); + + if (!effect) { + effect = new ShaderEffect(); + m_sourceItem->setGraphicsEffect(effect); + } + + if (effect) + effect->addRenderTarget(this); + + m_sourceItem->update(); +} + +void ShaderEffectSource::detachSourceItem() +{ + if (!m_sourceItem) + return; + + ShaderEffect* effect = qobject_cast (m_sourceItem->graphicsEffect()); + + if (effect) + effect->removeRenderTarget(this); + + delete m_fbo; + m_fbo = 0; + + delete m_multisampledFbo; + m_multisampledFbo = 0; + + m_dirtyTexture = true; +} diff --git a/src/imports/shaders/shadereffectsource.h b/src/imports/shaders/shadereffectsource.h new file mode 100644 index 00000000..d7b44bcc --- /dev/null +++ b/src/imports/shaders/shadereffectsource.h @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QML Shaders plugin of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SHADEREFFECTSOURCE_H +#define SHADEREFFECTSOURCE_H + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class ShaderEffectBuffer; + +class ShaderEffectSource : public QDeclarativeItem +{ + Q_OBJECT + Q_PROPERTY(QDeclarativeItem *sourceItem READ sourceItem WRITE setSourceItem NOTIFY sourceItemChanged) + Q_PROPERTY(QRectF sourceRect READ sourceRect WRITE setSourceRect NOTIFY sourceRectChanged) + Q_PROPERTY(QSize textureSize READ textureSize WRITE setTextureSize NOTIFY textureSizeChanged) + Q_PROPERTY(bool live READ isLive WRITE setLive NOTIFY liveChanged) + Q_PROPERTY(bool hideSource READ hideSource WRITE setHideSource NOTIFY hideSourceChanged) + Q_PROPERTY(WrapMode wrapMode READ wrapMode WRITE setWrapMode NOTIFY wrapModeChanged) + Q_ENUMS(WrapMode) + Q_ENUMS(Format) + +public: + enum WrapMode { + ClampToEdge, + RepeatHorizontally, + RepeatVertically, + Repeat + }; + + enum Format { + Alpha = GL_ALPHA, + RGB = GL_RGB, + RGBA = GL_RGBA + }; + + ShaderEffectSource(QDeclarativeItem *parent = 0); + virtual ~ShaderEffectSource(); + + QDeclarativeItem *sourceItem() const { return m_sourceItem.data(); } + void setSourceItem(QDeclarativeItem *item); + + QRectF sourceRect() const { return m_sourceRect; }; + void setSourceRect(const QRectF &rect); + + QSize textureSize() const { return m_textureSize; } + void setTextureSize(const QSize &size); + + bool isLive() const { return m_live; } + void setLive(bool s); + + bool hideSource() const { return m_hideSource; } + void setHideSource(bool hide); + + WrapMode wrapMode() const { return m_wrapMode; }; + void setWrapMode(WrapMode mode); + + bool isActive() const { return m_refs; } + void bind(); + void refFromEffectItem(); + void derefFromEffectItem(); + void updateBackbuffer(); + + ShaderEffectBuffer* fbo() { return m_fbo; } + bool isDirtyTexture() { return m_dirtyTexture; } + bool isMirrored() { return m_mirrored; } + + Q_INVOKABLE void grab(); + +Q_SIGNALS: + void sourceItemChanged(); + void sourceRectChanged(); + void textureSizeChanged(); + void formatChanged(); + void liveChanged(); + void hideSourceChanged(); + void activeChanged(); + void repaintRequired(); + void wrapModeChanged(); + +public Q_SLOTS: + void markSceneGraphDirty(); + void markSourceSizeDirty(); + void markSourceItemDirty(); + +private: + void updateSizeAndTexture(); + void attachSourceItem(); + void detachSourceItem(); + +private: + QPointer m_sourceItem; + WrapMode m_wrapMode; + QRectF m_sourceRect; + QSize m_textureSize; + Format m_format; + QSize m_size; + + ShaderEffectBuffer *m_fbo; + ShaderEffectBuffer *m_multisampledFbo; + int m_refs; + bool m_dirtyTexture : 1; + bool m_dirtySceneGraph : 1; + bool m_multisamplingSupported : 1; + bool m_checkedForMultisamplingSupport : 1; + bool m_live : 1; + bool m_hideSource : 1; + bool m_mirrored : 1; +}; + +QT_END_HEADER + +QT_END_NAMESPACE + + +#endif // SHADEREFFECTSOURCE_H diff --git a/src/imports/shaders/shaders.pro b/src/imports/shaders/shaders.pro new file mode 100644 index 00000000..51a9a918 --- /dev/null +++ b/src/imports/shaders/shaders.pro @@ -0,0 +1,38 @@ +TARGET = qmlshadersplugin +TARGETPATH = Qt/labs/shaders +include(../qimportbase.pri) + +QT += declarative opengl + +SOURCES += \ + qmlshadersplugin_plugin.cpp \ + shadereffect.cpp \ + shadereffectitem.cpp \ + shadereffectsource.cpp \ + scenegraph/qsggeometry.cpp \ + shadereffectbuffer.cpp + +HEADERS += \ + qmlshadersplugin_plugin.h \ + glfunctions.h \ + shadereffect.h \ + shadereffectitem.h \ + shadereffectsource.h \ + scenegraph/qsggeometry.h \ + shadereffectbuffer.h + +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/imports/$$TARGETPATH +target.path = $$[QT_INSTALL_IMPORTS]/$$TARGETPATH + +qmldir.files += $$PWD/qmldir +qmldir.path += $$[QT_INSTALL_IMPORTS]/$$TARGETPATH + +symbian:{ + TARGET.UID3 = 0x20034907 + isEmpty(DESTDIR):importFiles.sources = qmlparticlesplugin$${QT_LIBINFIX}.dll qmldir + else:importFiles.sources = $$DESTDIR/qmlparticlesplugin$${QT_LIBINFIX}.dll qmldir + importFiles.path = $$QT_IMPORTS_BASE_DIR/$$TARGETPATH + DEPLOYMENT += importFiles +} + +INSTALLS += target qmldir diff --git a/src/plugins/platforms/uikit/examples/qmltest/main.mm b/src/plugins/platforms/uikit/examples/qmltest/main.mm new file mode 100644 index 00000000..c56d7c51 --- /dev/null +++ b/src/plugins/platforms/uikit/examples/qmltest/main.mm @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#import + +#include "../share/qmlapplicationviewer/qmlapplicationviewer.h" + +#include +#include +#include + +Q_IMPORT_PLUGIN(UIKit) + +static QString qStringFromNSString(NSString *nsstring) +{ + return QString::fromUtf8([nsstring UTF8String]); +} + +static QString documentsDirectory() +{ + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSString *documentsDirectory = [paths objectAtIndex:0]; + return qStringFromNSString(documentsDirectory); +} + +int main(int argc, char *argv[]) { + + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + + QApplication app(argc, argv); + QmlApplicationViewer viewer; + viewer.setOrientation(QmlApplicationViewer::ScreenOrientationAuto); + viewer.engine()->setOfflineStoragePath(documentsDirectory()); + NSString *resourcePath = [[NSBundle mainBundle] resourcePath]; + viewer.setMainQmlFile(qStringFromNSString([resourcePath stringByAppendingPathComponent:@"qml/main.qml"])); + viewer.showMaximized(); + int retVal = app.exec(); + [pool release]; + return retVal; +} diff --git a/src/plugins/platforms/uikit/examples/qmltest/qml/main.qml b/src/plugins/platforms/uikit/examples/qmltest/qml/main.qml new file mode 100644 index 00000000..9f787b79 --- /dev/null +++ b/src/plugins/platforms/uikit/examples/qmltest/qml/main.qml @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.0 + +Rectangle { + id: box + width: 350; height: 250 + + Rectangle { + id: redSquare + width: 80; height: 80 + anchors.top: parent.top; anchors.left: parent.left; anchors.margins: 10 + color: "red" + + Text { text: "Click"; font.pixelSize: 16; anchors.centerIn: parent } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + acceptedButtons: Qt.LeftButton | Qt.RightButton + + onEntered: info.text = 'Entered' + onExited: info.text = 'Exited (pressed=' + pressed + ')' + + onPressed: { + info.text = 'Pressed (button=' + (mouse.button == Qt.RightButton ? 'right' : 'left') + + ' shift=' + (mouse.modifiers & Qt.ShiftModifier ? 'true' : 'false') + ')' + var posInBox = redSquare.mapToItem(box, mouse.x, mouse.y) + posInfo.text = + mouse.x + ',' + mouse.y + ' in square' + + ' (' + posInBox.x + ',' + posInBox.y + ' in window)' + } + + onReleased: { + info.text = 'Released (isClick=' + mouse.isClick + ' wasHeld=' + mouse.wasHeld + ')' + posInfo.text = '' + } + + onPressAndHold: info.text = 'Press and hold' + onClicked: info.text = 'Clicked (wasHeld=' + mouse.wasHeld + ')' + onDoubleClicked: info.text = 'Double clicked' + } + } + + Rectangle { + id: blueSquare + width: 80; height: 80 + x: box.width - width - 10; y: 10 // making this item draggable, so don't use anchors + color: "blue" + + Text { text: "Drag"; font.pixelSize: 16; color: "white"; anchors.centerIn: parent } + + MouseArea { + anchors.fill: parent + drag.target: blueSquare + drag.axis: Drag.XandYAxis + drag.minimumX: 0 + drag.maximumX: box.width - parent.width + drag.minimumY: 0 + drag.maximumY: box.height - parent.width + } + } + + Text { + id: info + anchors.bottom: posInfo.top; anchors.horizontalCenter: parent.horizontalCenter; anchors.margins: 30 + + onTextChanged: console.log(text) + } + + Text { + id: posInfo + anchors.bottom: parent.bottom; anchors.horizontalCenter: parent.horizontalCenter; anchors.margins: 30 + } +} diff --git a/src/plugins/platforms/uikit/examples/qmltest/qmltest-Info.plist b/src/plugins/platforms/uikit/examples/qmltest/qmltest-Info.plist new file mode 100644 index 00000000..531d93d7 --- /dev/null +++ b/src/plugins/platforms/uikit/examples/qmltest/qmltest-Info.plist @@ -0,0 +1,36 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleDisplayName + ${PRODUCT_NAME} + CFBundleDocumentTypes + + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + + CFBundleIdentifier + com.yourcompany.${PRODUCT_NAME:rfc1034identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleURLTypes + + CFBundleVersion + 1.0 + LSRequiresIPhoneOS + + UTExportedTypeDeclarations + + UTImportedTypeDeclarations + + + diff --git a/src/plugins/platforms/uikit/examples/qmltest/qmltest.xcodeproj/project.pbxproj b/src/plugins/platforms/uikit/examples/qmltest/qmltest.xcodeproj/project.pbxproj new file mode 100644 index 00000000..e513030d --- /dev/null +++ b/src/plugins/platforms/uikit/examples/qmltest/qmltest.xcodeproj/project.pbxproj @@ -0,0 +1,494 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D30AB110D05D00D00671497 /* Foundation.framework */; }; + 1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */; }; + 288765A50DF7441C002DB57D /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 288765A40DF7441C002DB57D /* CoreGraphics.framework */; }; + D307DEC513EBD04100399BD4 /* libquikit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D307DEC413EBD04100399BD4 /* libquikit.a */; }; + D307DECF13EBD05900399BD4 /* libQtCore.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D307DEC613EBD05900399BD4 /* libQtCore.a */; }; + D307DED013EBD05900399BD4 /* libQtDeclarative.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D307DEC713EBD05900399BD4 /* libQtDeclarative.a */; }; + D307DED113EBD05900399BD4 /* libQtGui.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D307DEC813EBD05900399BD4 /* libQtGui.a */; }; + D307DED213EBD05900399BD4 /* libQtNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D307DEC913EBD05900399BD4 /* libQtNetwork.a */; }; + D307DED313EBD05900399BD4 /* libQtOpenGL.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D307DECA13EBD05900399BD4 /* libQtOpenGL.a */; }; + D307DED413EBD05900399BD4 /* libQtScript.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D307DECB13EBD05900399BD4 /* libQtScript.a */; }; + D307DED513EBD05900399BD4 /* libQtSql.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D307DECC13EBD05900399BD4 /* libQtSql.a */; }; + D307DED713EBD05900399BD4 /* libQtXmlPatterns.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D307DECE13EBD05900399BD4 /* libQtXmlPatterns.a */; }; + D333CCF913B88A690070E08E /* moc_qmlapplicationviewer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D333CCF613B88A690070E08E /* moc_qmlapplicationviewer.cpp */; }; + D333CCFA13B88A690070E08E /* moc_qmlapplicationviewer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D333CCF613B88A690070E08E /* moc_qmlapplicationviewer.cpp */; }; + D333CCFB13B88A690070E08E /* qmlapplicationviewer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D333CCF713B88A690070E08E /* qmlapplicationviewer.cpp */; }; + D333CCFC13B88A690070E08E /* qmlapplicationviewer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D333CCF713B88A690070E08E /* qmlapplicationviewer.cpp */; }; + D34F290413F29AF400E4F9AC /* CoreText.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D34F290313F29AF400E4F9AC /* CoreText.framework */; }; + D34F290713F29B0A00E4F9AC /* CoreText.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D34F290613F29B0300E4F9AC /* CoreText.framework */; }; + D35784261345D9940046D202 /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D35784251345D9940046D202 /* OpenGLES.framework */; }; + D35784281345D9E00046D202 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D35784271345D9E00046D202 /* QuartzCore.framework */; }; + D3578436134A09990046D202 /* libQtOpenGL.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D3578435134A09990046D202 /* libQtOpenGL.a */; }; + D3578439134A0AAE0046D202 /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D35784251345D9940046D202 /* OpenGLES.framework */; }; + D357843A134A0AB10046D202 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D35784271345D9E00046D202 /* QuartzCore.framework */; }; + D3CAA7C813264AAD008BB877 /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = D3CAA7C713264AAD008BB877 /* main.mm */; }; + D3CAA7EC13264F52008BB877 /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = D3CAA7C713264AAD008BB877 /* main.mm */; }; + D3CAA7F013264F52008BB877 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D30AB110D05D00D00671497 /* Foundation.framework */; }; + D3CAA7F113264F52008BB877 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */; }; + D3CAA7F213264F52008BB877 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 288765A40DF7441C002DB57D /* CoreGraphics.framework */; }; + D3CAA7FA13264F8A008BB877 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = D3CAA7F913264F8A008BB877 /* libz.dylib */; }; + D3CAA81113264FF0008BB877 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = D3CAA7F913264F8A008BB877 /* libz.dylib */; }; + D3CAA89113265310008BB877 /* qml in Resources */ = {isa = PBXBuildFile; fileRef = D3CAA88E13265310008BB877 /* qml */; }; + D3CAA89213265310008BB877 /* qml in Resources */ = {isa = PBXBuildFile; fileRef = D3CAA88E13265310008BB877 /* qml */; }; + D3D817B2132A2CFD00CDE422 /* libQtCore.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D3D817AA132A2CFD00CDE422 /* libQtCore.a */; }; + D3D817B3132A2CFD00CDE422 /* libQtDeclarative.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D3D817AB132A2CFD00CDE422 /* libQtDeclarative.a */; }; + D3D817B4132A2CFD00CDE422 /* libQtGui.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D3D817AC132A2CFD00CDE422 /* libQtGui.a */; }; + D3D817B5132A2CFD00CDE422 /* libQtNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D3D817AD132A2CFD00CDE422 /* libQtNetwork.a */; }; + D3D817B6132A2CFD00CDE422 /* libQtScript.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D3D817AE132A2CFD00CDE422 /* libQtScript.a */; }; + D3D817B7132A2CFD00CDE422 /* libQtSql.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D3D817AF132A2CFD00CDE422 /* libQtSql.a */; }; + D3D817B9132A2CFD00CDE422 /* libQtXmlPatterns.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D3D817B1132A2CFD00CDE422 /* libQtXmlPatterns.a */; }; + D3D817BB132A2D0E00CDE422 /* libquikit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D3D817BA132A2D0E00CDE422 /* libquikit.a */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 1D30AB110D05D00D00671497 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 1D6058910D05DD3D006BFB54 /* qmltest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = qmltest.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + 288765A40DF7441C002DB57D /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + 32CA4F630368D1EE00C91783 /* qmltest_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = qmltest_Prefix.pch; sourceTree = ""; }; + 8D1107310486CEB800E47090 /* qmltest-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "qmltest-Info.plist"; plistStructureDefinitionIdentifier = "com.apple.xcode.plist.structure-definition.iphone.info-plist"; sourceTree = ""; }; + D307DEC413EBD04100399BD4 /* libquikit.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libquikit.a; path = "../../../../../../../qt-lighthouse-ios-simulator/plugins/platforms/libquikit.a"; sourceTree = ""; }; + D307DEC613EBD05900399BD4 /* libQtCore.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libQtCore.a; path = "../../../../../../../qt-lighthouse-ios-simulator/lib/libQtCore.a"; sourceTree = ""; }; + D307DEC713EBD05900399BD4 /* libQtDeclarative.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libQtDeclarative.a; path = "../../../../../../../qt-lighthouse-ios-simulator/lib/libQtDeclarative.a"; sourceTree = ""; }; + D307DEC813EBD05900399BD4 /* libQtGui.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libQtGui.a; path = "../../../../../../../qt-lighthouse-ios-simulator/lib/libQtGui.a"; sourceTree = ""; }; + D307DEC913EBD05900399BD4 /* libQtNetwork.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libQtNetwork.a; path = "../../../../../../../qt-lighthouse-ios-simulator/lib/libQtNetwork.a"; sourceTree = ""; }; + D307DECA13EBD05900399BD4 /* libQtOpenGL.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libQtOpenGL.a; path = "../../../../../../../qt-lighthouse-ios-simulator/lib/libQtOpenGL.a"; sourceTree = ""; }; + D307DECB13EBD05900399BD4 /* libQtScript.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libQtScript.a; path = "../../../../../../../qt-lighthouse-ios-simulator/lib/libQtScript.a"; sourceTree = ""; }; + D307DECC13EBD05900399BD4 /* libQtSql.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libQtSql.a; path = "../../../../../../../qt-lighthouse-ios-simulator/lib/libQtSql.a"; sourceTree = ""; }; + D307DECE13EBD05900399BD4 /* libQtXmlPatterns.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libQtXmlPatterns.a; path = "../../../../../../../qt-lighthouse-ios-simulator/lib/libQtXmlPatterns.a"; sourceTree = ""; }; + D333CCF613B88A690070E08E /* moc_qmlapplicationviewer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = moc_qmlapplicationviewer.cpp; path = ../share/qmlapplicationviewer/moc_qmlapplicationviewer.cpp; sourceTree = ""; }; + D333CCF713B88A690070E08E /* qmlapplicationviewer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = qmlapplicationviewer.cpp; path = ../share/qmlapplicationviewer/qmlapplicationviewer.cpp; sourceTree = ""; }; + D333CCF813B88A690070E08E /* qmlapplicationviewer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = qmlapplicationviewer.h; path = ../share/qmlapplicationviewer/qmlapplicationviewer.h; sourceTree = ""; }; + D34F290313F29AF400E4F9AC /* CoreText.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreText.framework; path = System/Library/Frameworks/CoreText.framework; sourceTree = SDKROOT; }; + D34F290613F29B0300E4F9AC /* CoreText.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreText.framework; path = System/Library/Frameworks/CoreText.framework; sourceTree = SDKROOT; }; + D35784251345D9940046D202 /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = System/Library/Frameworks/OpenGLES.framework; sourceTree = SDKROOT; }; + D35784271345D9E00046D202 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; + D3578435134A09990046D202 /* libQtOpenGL.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libQtOpenGL.a; path = "../../../../../../../qt-lighthouse-ios-device/lib/libQtOpenGL.a"; sourceTree = ""; }; + D3CAA7C713264AAD008BB877 /* main.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = ""; }; + D3CAA7F613264F52008BB877 /* qmltest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = qmltest.app; sourceTree = BUILT_PRODUCTS_DIR; }; + D3CAA7F913264F8A008BB877 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; }; + D3CAA88E13265310008BB877 /* qml */ = {isa = PBXFileReference; lastKnownFileType = folder; path = qml; sourceTree = SOURCE_ROOT; }; + D3D817AA132A2CFD00CDE422 /* libQtCore.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libQtCore.a; path = "../../../../../../../qt-lighthouse-ios-device/lib/libQtCore.a"; sourceTree = SOURCE_ROOT; }; + D3D817AB132A2CFD00CDE422 /* libQtDeclarative.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libQtDeclarative.a; path = "../../../../../../../qt-lighthouse-ios-device/lib/libQtDeclarative.a"; sourceTree = SOURCE_ROOT; }; + D3D817AC132A2CFD00CDE422 /* libQtGui.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libQtGui.a; path = "../../../../../../../qt-lighthouse-ios-device/lib/libQtGui.a"; sourceTree = SOURCE_ROOT; }; + D3D817AD132A2CFD00CDE422 /* libQtNetwork.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libQtNetwork.a; path = "../../../../../../../qt-lighthouse-ios-device/lib/libQtNetwork.a"; sourceTree = SOURCE_ROOT; }; + D3D817AE132A2CFD00CDE422 /* libQtScript.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libQtScript.a; path = "../../../../../../../qt-lighthouse-ios-device/lib/libQtScript.a"; sourceTree = SOURCE_ROOT; }; + D3D817AF132A2CFD00CDE422 /* libQtSql.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libQtSql.a; path = "../../../../../../../qt-lighthouse-ios-device/lib/libQtSql.a"; sourceTree = SOURCE_ROOT; }; + D3D817B1132A2CFD00CDE422 /* libQtXmlPatterns.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libQtXmlPatterns.a; path = "../../../../../../../qt-lighthouse-ios-device/lib/libQtXmlPatterns.a"; sourceTree = SOURCE_ROOT; }; + D3D817BA132A2D0E00CDE422 /* libquikit.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libquikit.a; path = "../../../../../../../qt-lighthouse-ios-device/plugins/platforms/libquikit.a"; sourceTree = SOURCE_ROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 1D60588F0D05DD3D006BFB54 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D34F290413F29AF400E4F9AC /* CoreText.framework in Frameworks */, + D35784281345D9E00046D202 /* QuartzCore.framework in Frameworks */, + 1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */, + 1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */, + 288765A50DF7441C002DB57D /* CoreGraphics.framework in Frameworks */, + D35784261345D9940046D202 /* OpenGLES.framework in Frameworks */, + D3CAA7FA13264F8A008BB877 /* libz.dylib in Frameworks */, + D307DECF13EBD05900399BD4 /* libQtCore.a in Frameworks */, + D307DED013EBD05900399BD4 /* libQtDeclarative.a in Frameworks */, + D307DED113EBD05900399BD4 /* libQtGui.a in Frameworks */, + D307DED213EBD05900399BD4 /* libQtNetwork.a in Frameworks */, + D307DED313EBD05900399BD4 /* libQtOpenGL.a in Frameworks */, + D307DED413EBD05900399BD4 /* libQtScript.a in Frameworks */, + D307DED513EBD05900399BD4 /* libQtSql.a in Frameworks */, + D307DED713EBD05900399BD4 /* libQtXmlPatterns.a in Frameworks */, + D307DEC513EBD04100399BD4 /* libquikit.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D3CAA7EF13264F52008BB877 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D34F290713F29B0A00E4F9AC /* CoreText.framework in Frameworks */, + D357843A134A0AB10046D202 /* QuartzCore.framework in Frameworks */, + D3578439134A0AAE0046D202 /* OpenGLES.framework in Frameworks */, + D3CAA7F013264F52008BB877 /* Foundation.framework in Frameworks */, + D3CAA7F113264F52008BB877 /* UIKit.framework in Frameworks */, + D3CAA7F213264F52008BB877 /* CoreGraphics.framework in Frameworks */, + D3CAA81113264FF0008BB877 /* libz.dylib in Frameworks */, + D3D817B2132A2CFD00CDE422 /* libQtCore.a in Frameworks */, + D3D817B3132A2CFD00CDE422 /* libQtDeclarative.a in Frameworks */, + D3D817B4132A2CFD00CDE422 /* libQtGui.a in Frameworks */, + D3D817B5132A2CFD00CDE422 /* libQtNetwork.a in Frameworks */, + D3D817B6132A2CFD00CDE422 /* libQtScript.a in Frameworks */, + D3D817B7132A2CFD00CDE422 /* libQtSql.a in Frameworks */, + D3D817B9132A2CFD00CDE422 /* libQtXmlPatterns.a in Frameworks */, + D3D817BB132A2D0E00CDE422 /* libquikit.a in Frameworks */, + D3578436134A09990046D202 /* libQtOpenGL.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 19C28FACFE9D520D11CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 1D6058910D05DD3D006BFB54 /* qmltest.app */, + D3CAA7F613264F52008BB877 /* qmltest.app */, + ); + name = Products; + sourceTree = ""; + }; + 29B97314FDCFA39411CA2CEA /* CustomTemplate */ = { + isa = PBXGroup; + children = ( + D34F290313F29AF400E4F9AC /* CoreText.framework */, + 29B97315FDCFA39411CA2CEA /* Other Sources */, + 29B97317FDCFA39411CA2CEA /* Resources */, + D3CAA7E213264E8C008BB877 /* QMLApplicationViewer */, + 29B97323FDCFA39411CA2CEA /* Frameworks */, + 19C28FACFE9D520D11CA2CBB /* Products */, + ); + name = CustomTemplate; + sourceTree = ""; + }; + 29B97315FDCFA39411CA2CEA /* Other Sources */ = { + isa = PBXGroup; + children = ( + 32CA4F630368D1EE00C91783 /* qmltest_Prefix.pch */, + D3CAA7C713264AAD008BB877 /* main.mm */, + ); + name = "Other Sources"; + sourceTree = ""; + }; + 29B97317FDCFA39411CA2CEA /* Resources */ = { + isa = PBXGroup; + children = ( + D3CAA88E13265310008BB877 /* qml */, + 8D1107310486CEB800E47090 /* qmltest-Info.plist */, + ); + name = Resources; + sourceTree = ""; + }; + 29B97323FDCFA39411CA2CEA /* Frameworks */ = { + isa = PBXGroup; + children = ( + D3CAA81513265035008BB877 /* Simulator */, + D3CAA8141326500A008BB877 /* Device */, + 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */, + 1D30AB110D05D00D00671497 /* Foundation.framework */, + 288765A40DF7441C002DB57D /* CoreGraphics.framework */, + D35784251345D9940046D202 /* OpenGLES.framework */, + D35784271345D9E00046D202 /* QuartzCore.framework */, + D34F290613F29B0300E4F9AC /* CoreText.framework */, + D3CAA7F913264F8A008BB877 /* libz.dylib */, + ); + name = Frameworks; + sourceTree = ""; + }; + D3CAA7E213264E8C008BB877 /* QMLApplicationViewer */ = { + isa = PBXGroup; + children = ( + D333CCF613B88A690070E08E /* moc_qmlapplicationviewer.cpp */, + D333CCF713B88A690070E08E /* qmlapplicationviewer.cpp */, + D333CCF813B88A690070E08E /* qmlapplicationviewer.h */, + ); + name = QMLApplicationViewer; + sourceTree = ""; + }; + D3CAA8141326500A008BB877 /* Device */ = { + isa = PBXGroup; + children = ( + D3D817BA132A2D0E00CDE422 /* libquikit.a */, + D3D817AA132A2CFD00CDE422 /* libQtCore.a */, + D3D817AB132A2CFD00CDE422 /* libQtDeclarative.a */, + D3D817AC132A2CFD00CDE422 /* libQtGui.a */, + D3D817AD132A2CFD00CDE422 /* libQtNetwork.a */, + D3578435134A09990046D202 /* libQtOpenGL.a */, + D3D817AE132A2CFD00CDE422 /* libQtScript.a */, + D3D817AF132A2CFD00CDE422 /* libQtSql.a */, + D3D817B1132A2CFD00CDE422 /* libQtXmlPatterns.a */, + ); + name = Device; + sourceTree = ""; + }; + D3CAA81513265035008BB877 /* Simulator */ = { + isa = PBXGroup; + children = ( + D307DEC413EBD04100399BD4 /* libquikit.a */, + D307DEC613EBD05900399BD4 /* libQtCore.a */, + D307DEC713EBD05900399BD4 /* libQtDeclarative.a */, + D307DEC813EBD05900399BD4 /* libQtGui.a */, + D307DEC913EBD05900399BD4 /* libQtNetwork.a */, + D307DECA13EBD05900399BD4 /* libQtOpenGL.a */, + D307DECB13EBD05900399BD4 /* libQtScript.a */, + D307DECC13EBD05900399BD4 /* libQtSql.a */, + D307DECE13EBD05900399BD4 /* libQtXmlPatterns.a */, + ); + name = Simulator; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 1D6058900D05DD3D006BFB54 /* qmltest simulator */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "qmltest simulator" */; + buildPhases = ( + 1D60588D0D05DD3D006BFB54 /* Resources */, + 1D60588E0D05DD3D006BFB54 /* Sources */, + 1D60588F0D05DD3D006BFB54 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "qmltest simulator"; + productName = qmltest; + productReference = 1D6058910D05DD3D006BFB54 /* qmltest.app */; + productType = "com.apple.product-type.application"; + }; + D3CAA7E813264F52008BB877 /* qmltest device */ = { + isa = PBXNativeTarget; + buildConfigurationList = D3CAA7F313264F52008BB877 /* Build configuration list for PBXNativeTarget "qmltest device" */; + buildPhases = ( + D3CAA7E913264F52008BB877 /* Resources */, + D3CAA7EB13264F52008BB877 /* Sources */, + D3CAA7EF13264F52008BB877 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "qmltest device"; + productName = qmltest; + productReference = D3CAA7F613264F52008BB877 /* qmltest.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 29B97313FDCFA39411CA2CEA /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0420; + }; + buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "qmltest" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 1; + knownRegions = ( + English, + Japanese, + French, + German, + ); + mainGroup = 29B97314FDCFA39411CA2CEA /* CustomTemplate */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 1D6058900D05DD3D006BFB54 /* qmltest simulator */, + D3CAA7E813264F52008BB877 /* qmltest device */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 1D60588D0D05DD3D006BFB54 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D3CAA89113265310008BB877 /* qml in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D3CAA7E913264F52008BB877 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D3CAA89213265310008BB877 /* qml in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 1D60588E0D05DD3D006BFB54 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D3CAA7C813264AAD008BB877 /* main.mm in Sources */, + D333CCF913B88A690070E08E /* moc_qmlapplicationviewer.cpp in Sources */, + D333CCFB13B88A690070E08E /* qmlapplicationviewer.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D3CAA7EB13264F52008BB877 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D3CAA7EC13264F52008BB877 /* main.mm in Sources */, + D333CCFA13B88A690070E08E /* moc_qmlapplicationviewer.cpp in Sources */, + D333CCFC13B88A690070E08E /* qmlapplicationviewer.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 1D6058940D05DD3E006BFB54 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_UNIVERSAL_IPHONE_OS)"; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = qmltest_Prefix.pch; + HEADER_SEARCH_PATHS = "\"$(SRCROOT)/../../../../../../../qt-lighthouse-ios-simulator/include\"/**"; + INFOPLIST_FILE = "qmltest-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 4.3; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/../../../../../../../qt-lighthouse-ios-simulator/lib\"", + "\"$(SRCROOT)/../../../../../../../qt-lighthouse-ios-simulator/plugins/platforms\"", + ); + PRODUCT_NAME = qmltest; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 1D6058950D05DD3E006BFB54 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_UNIVERSAL_IPHONE_OS)"; + COPY_PHASE_STRIP = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = qmltest_Prefix.pch; + HEADER_SEARCH_PATHS = "\"$(SRCROOT)/../../../../../../../qt-lighthouse-ios-simulator/include\"/**"; + INFOPLIST_FILE = "qmltest-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 4.3; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/../../../../../../../qt-lighthouse-ios-simulator/lib\"", + "\"$(SRCROOT)/../../../../../../../qt-lighthouse-ios-simulator/plugins/platforms\"", + ); + PRODUCT_NAME = qmltest; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + C01FCF4F08A954540054247B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + SDKROOT = iphoneos; + }; + name = Debug; + }; + C01FCF5008A954540054247B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; + SDKROOT = iphoneos; + }; + name = Release; + }; + D3CAA7F413264F52008BB877 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_UNIVERSAL_IPHONE_OS)"; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = qmltest_Prefix.pch; + HEADER_SEARCH_PATHS = "\"$(SRCROOT)/../../../../../../../qt-lighthouse-ios-device/include\"/**"; + INFOPLIST_FILE = "qmltest-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 4.3; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/../../../../../../../qt-lighthouse-ios-device/lib\"", + "\"$(SRCROOT)/../../../../../../../qt-lighthouse-ios-device/plugins/platforms\"", + ); + PRODUCT_NAME = qmltest; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + D3CAA7F513264F52008BB877 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_UNIVERSAL_IPHONE_OS)"; + COPY_PHASE_STRIP = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = qmltest_Prefix.pch; + HEADER_SEARCH_PATHS = "\"$(SRCROOT)/../../../../../../../qt-lighthouse-ios-device/include\"/**"; + INFOPLIST_FILE = "qmltest-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 4.3; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/../../../../../../../qt-lighthouse-ios-device/lib\"", + "\"$(SRCROOT)/../../../../../../../qt-lighthouse-ios-device/plugins/platforms\"", + ); + PRODUCT_NAME = qmltest; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "qmltest simulator" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1D6058940D05DD3E006BFB54 /* Debug */, + 1D6058950D05DD3E006BFB54 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C01FCF4E08A954540054247B /* Build configuration list for PBXProject "qmltest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C01FCF4F08A954540054247B /* Debug */, + C01FCF5008A954540054247B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D3CAA7F313264F52008BB877 /* Build configuration list for PBXNativeTarget "qmltest device" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D3CAA7F413264F52008BB877 /* Debug */, + D3CAA7F513264F52008BB877 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; +} diff --git a/src/plugins/platforms/uikit/examples/share/qmlapplicationviewer/moc_qmlapplicationviewer.cpp b/src/plugins/platforms/uikit/examples/share/qmlapplicationviewer/moc_qmlapplicationviewer.cpp new file mode 100644 index 00000000..5b5ae731 --- /dev/null +++ b/src/plugins/platforms/uikit/examples/share/qmlapplicationviewer/moc_qmlapplicationviewer.cpp @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/**************************************************************************** +** Meta object code from reading C++ file 'qmlapplicationviewer.h' +** +** Created: Mon Jun 27 10:56:34 2011 +** by: The Qt Meta Object Compiler version 63 (Qt 4.8.0) +** +** WARNING! All changes made in this file will be lost! +*****************************************************************************/ + +#include "qmlapplicationviewer.h" +#if !defined(Q_MOC_OUTPUT_REVISION) +#error "The header file 'qmlapplicationviewer.h' doesn't include ." +#elif Q_MOC_OUTPUT_REVISION != 63 +#error "This file was generated using the moc from 4.8.0. It" +#error "cannot be used with the include files from this version of Qt." +#error "(The moc has changed too much.)" +#endif + +QT_BEGIN_MOC_NAMESPACE +static const uint qt_meta_data_QmlApplicationViewer[] = { + + // content: + 6, // revision + 0, // classname + 0, 0, // classinfo + 0, 0, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + 0 // eod +}; + +static const char qt_meta_stringdata_QmlApplicationViewer[] = { + "QmlApplicationViewer\0" +}; + +void QmlApplicationViewer::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) +{ + Q_UNUSED(_o); + Q_UNUSED(_id); + Q_UNUSED(_c); + Q_UNUSED(_a); +} + +const QMetaObjectExtraData QmlApplicationViewer::staticMetaObjectExtraData = { + 0, qt_static_metacall +}; + +const QMetaObject QmlApplicationViewer::staticMetaObject = { + { &QDeclarativeView::staticMetaObject, qt_meta_stringdata_QmlApplicationViewer, + qt_meta_data_QmlApplicationViewer, &staticMetaObjectExtraData } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &QmlApplicationViewer::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *QmlApplicationViewer::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *QmlApplicationViewer::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_QmlApplicationViewer)) + return static_cast(const_cast< QmlApplicationViewer*>(this)); + return QDeclarativeView::qt_metacast(_clname); +} + +int QmlApplicationViewer::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QDeclarativeView::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + return _id; +} +QT_END_MOC_NAMESPACE diff --git a/src/plugins/platforms/uikit/examples/share/qmlapplicationviewer/qmlapplicationviewer.cpp b/src/plugins/platforms/uikit/examples/share/qmlapplicationviewer/qmlapplicationviewer.cpp new file mode 100644 index 00000000..3a1850ff --- /dev/null +++ b/src/plugins/platforms/uikit/examples/share/qmlapplicationviewer/qmlapplicationviewer.cpp @@ -0,0 +1,198 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// checksum 0x5e4a version 0x5000d +/* + This file was generated by the Qt Quick Application wizard of Qt Creator. + QmlApplicationViewer is a convenience class containing mobile device specific + code such as screen orientation handling. Also QML paths and debugging are + handled here. + It is recommended not to modify this file, since newer versions of Qt Creator + may offer an updated version of it. +*/ + +#include "qmlapplicationviewer.h" + +#include +#include +#include +#include +#include +#include + +#if defined(QMLJSDEBUGGER) && QT_VERSION < 0x040800 + +#include + +#if !defined(NO_JSDEBUGGER) +#include +#endif +#if !defined(NO_QMLOBSERVER) +#include +#endif + +// Enable debugging before any QDeclarativeEngine is created +struct QmlJsDebuggingEnabler +{ + QmlJsDebuggingEnabler() + { + QDeclarativeDebugHelper::enableDebugging(); + } +}; + +// Execute code in constructor before first QDeclarativeEngine is instantiated +static QmlJsDebuggingEnabler enableDebuggingHelper; + +#endif // QMLJSDEBUGGER + +class QmlApplicationViewerPrivate +{ + QString mainQmlFile; + friend class QmlApplicationViewer; + static QString adjustPath(const QString &path); +}; + +QString QmlApplicationViewerPrivate::adjustPath(const QString &path) +{ +#ifdef Q_OS_UNIX +#ifdef Q_OS_MAC + if (!QDir::isAbsolutePath(path)) + return QCoreApplication::applicationDirPath() + + QLatin1String("/../Resources/") + path; +#else + const QString pathInInstallDir = QCoreApplication::applicationDirPath() + + QLatin1String("/../") + path; + if (pathInInstallDir.contains(QLatin1String("opt")) + && pathInInstallDir.contains(QLatin1String("bin")) + && QFileInfo(pathInInstallDir).exists()) { + return pathInInstallDir; + } +#endif +#endif + return path; +} + +QmlApplicationViewer::QmlApplicationViewer(QWidget *parent) : + QDeclarativeView(parent), + m_d(new QmlApplicationViewerPrivate) +{ + connect(engine(), SIGNAL(quit()), SLOT(close())); + setResizeMode(QDeclarativeView::SizeRootObjectToView); + // Qt versions prior to 4.8.0 don't have QML/JS debugging services built in +#if defined(QMLJSDEBUGGER) && QT_VERSION < 0x040800 +#if !defined(NO_JSDEBUGGER) + new QmlJSDebugger::JSDebuggerAgent(engine()); +#endif +#if !defined(NO_QMLOBSERVER) + new QmlJSDebugger::QDeclarativeViewObserver(this, this); +#endif +#endif +} + +QmlApplicationViewer::~QmlApplicationViewer() +{ + delete m_d; +} + +void QmlApplicationViewer::setMainQmlFile(const QString &file) +{ + m_d->mainQmlFile = QmlApplicationViewerPrivate::adjustPath(file); + setSource(QUrl::fromLocalFile(m_d->mainQmlFile)); +} + +void QmlApplicationViewer::addImportPath(const QString &path) +{ + engine()->addImportPath(QmlApplicationViewerPrivate::adjustPath(path)); +} + +void QmlApplicationViewer::setOrientation(ScreenOrientation orientation) +{ +//#if defined(Q_OS_SYMBIAN) +// // If the version of Qt on the device is < 4.7.2, that attribute won't work +// if (orientation != ScreenOrientationAuto) { +// const QStringList v = QString::fromAscii(qVersion()).split(QLatin1Char('.')); +// if (v.count() == 3 && (v.at(0).toInt() << 16 | v.at(1).toInt() << 8 | v.at(2).toInt()) < 0x040702) { +// qWarning("Screen orientation locking only supported with Qt 4.7.2 and above"); +// return; +// } +// } +//#endif // Q_OS_SYMBIAN + +// Qt::WidgetAttribute attribute; +// switch (orientation) { +//#if QT_VERSION < 0x040702 +// // Qt < 4.7.2 does not yet have the Qt::WA_*Orientation attributes +// case ScreenOrientationLockPortrait: +// attribute = static_cast(128); +// break; +// case ScreenOrientationLockLandscape: +// attribute = static_cast(129); +// break; +// default: +// case ScreenOrientationAuto: +// attribute = static_cast(130); +// break; +//#else // QT_VERSION < 0x040702 +// case ScreenOrientationLockPortrait: +// attribute = Qt::WA_LockPortraitOrientation; +// break; +// case ScreenOrientationLockLandscape: +// attribute = Qt::WA_LockLandscapeOrientation; +// break; +// default: +// case ScreenOrientationAuto: +// attribute = Qt::WA_AutoOrientation; +// break; +//#endif // QT_VERSION < 0x040702 +// }; +// setAttribute(attribute, true); +} + +void QmlApplicationViewer::showExpanded() +{ +#if defined(Q_OS_SYMBIAN) || defined(MEEGO_EDITION_HARMATTAN) + showFullScreen(); +#elif defined(Q_WS_MAEMO_5) + showMaximized(); +#else + show(); +#endif +} diff --git a/src/plugins/platforms/uikit/examples/share/qmlapplicationviewer/qmlapplicationviewer.h b/src/plugins/platforms/uikit/examples/share/qmlapplicationviewer/qmlapplicationviewer.h new file mode 100644 index 00000000..f6a7900c --- /dev/null +++ b/src/plugins/platforms/uikit/examples/share/qmlapplicationviewer/qmlapplicationviewer.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// checksum 0x382f version 0x5000d +/* + This file was generated by the Qt Quick Application wizard of Qt Creator. + QmlApplicationViewer is a convenience class containing mobile device specific + code such as screen orientation handling. Also QML paths and debugging are + handled here. + It is recommended not to modify this file, since newer versions of Qt Creator + may offer an updated version of it. +*/ + +#ifndef QMLAPPLICATIONVIEWER_H +#define QMLAPPLICATIONVIEWER_H + +#include + +class QmlApplicationViewer : public QDeclarativeView +{ + Q_OBJECT + +public: + enum ScreenOrientation { + ScreenOrientationLockPortrait, + ScreenOrientationLockLandscape, + ScreenOrientationAuto + }; + + explicit QmlApplicationViewer(QWidget *parent = 0); + virtual ~QmlApplicationViewer(); + + void setMainQmlFile(const QString &file); + void addImportPath(const QString &path); + + // Note that this will only have an effect on Symbian and Fremantle. + void setOrientation(ScreenOrientation orientation); + + void showExpanded(); + +private: + class QmlApplicationViewerPrivate *m_d; +}; + +#endif // QMLAPPLICATIONVIEWER_H diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro new file mode 100644 index 00000000..94ce675f --- /dev/null +++ b/src/plugins/plugins.pro @@ -0,0 +1,16 @@ +TEMPLATE = subdirs + +SUBDIRS *= sqldrivers script bearer +unix:!symbian { + contains(QT_CONFIG,iconv)|contains(QT_CONFIG,gnu-libiconv)|contains(QT_CONFIG,sun-libiconv):SUBDIRS *= codecs +} else { + SUBDIRS *= codecs +} +!contains(QT_CONFIG, no-gui): SUBDIRS *= imageformats iconengines +!embedded:!qpa:!contains(QT_CONFIG, no-gui):SUBDIRS *= graphicssystems +embedded:SUBDIRS *= gfxdrivers decorations mousedrivers kbddrivers +!win32:!embedded:!mac:!symbian:!contains(QT_CONFIG, no-gui):SUBDIRS *= inputmethods +!symbian:!contains(QT_CONFIG, no-gui):SUBDIRS += accessible +contains(QT_CONFIG, phonon): SUBDIRS *= phonon +qpa:SUBDIRS += platforms +contains(QT_CONFIG, declarative): SUBDIRS *= qmltooling diff --git a/src/plugins/qmltooling/qmldbg_inspector/abstracttool.cpp b/src/plugins/qmltooling/qmldbg_inspector/abstracttool.cpp new file mode 100644 index 00000000..a557aaeb --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_inspector/abstracttool.cpp @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "abstracttool.h" + +#include "abstractviewinspector.h" + +namespace QmlJSDebugger { + +AbstractTool::AbstractTool(AbstractViewInspector *inspector) : + QObject(inspector), + m_inspector(inspector) +{ +} + +} // namespace QmlJSDebugger diff --git a/src/plugins/qmltooling/qmldbg_inspector/abstracttool.h b/src/plugins/qmltooling/qmldbg_inspector/abstracttool.h new file mode 100644 index 00000000..57cf1fc5 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_inspector/abstracttool.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ABSTRACTTOOL_H +#define ABSTRACTTOOL_H + +#include + +QT_BEGIN_NAMESPACE +class QMouseEvent; +class QKeyEvent; +class QWheelEvent; +QT_END_NAMESPACE + +namespace QmlJSDebugger { + +class AbstractViewInspector; + +class AbstractTool : public QObject +{ + Q_OBJECT + +public: + explicit AbstractTool(AbstractViewInspector *inspector); + + AbstractViewInspector *inspector() const { return m_inspector; } + + virtual void leaveEvent(QEvent *event) = 0; + + virtual void mousePressEvent(QMouseEvent *event) = 0; + virtual void mouseMoveEvent(QMouseEvent *event) = 0; + virtual void mouseReleaseEvent(QMouseEvent *event) = 0; + virtual void mouseDoubleClickEvent(QMouseEvent *event) = 0; + + virtual void hoverMoveEvent(QMouseEvent *event) = 0; + virtual void wheelEvent(QWheelEvent *event) = 0; + + virtual void keyPressEvent(QKeyEvent *event) = 0; + virtual void keyReleaseEvent(QKeyEvent *keyEvent) = 0; + +private: + AbstractViewInspector *m_inspector; +}; + +} // namespace QmlJSDebugger + +#endif // ABSTRACTTOOL_H diff --git a/src/plugins/qmltooling/qmldbg_inspector/abstractviewinspector.cpp b/src/plugins/qmltooling/qmldbg_inspector/abstractviewinspector.cpp new file mode 100644 index 00000000..c5e127c1 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_inspector/abstractviewinspector.cpp @@ -0,0 +1,511 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "abstractviewinspector.h" + +#include "abstracttool.h" +#include "qdeclarativeinspectorprotocol.h" + +#include +#include +#include +#include "QtDeclarative/private/qdeclarativeinspectorservice_p.h" + +#include +#include +#include + +namespace QmlJSDebugger { + +AbstractViewInspector::AbstractViewInspector(QObject *parent) : + QObject(parent), + m_currentTool(0), + m_showAppOnTop(false), + m_designModeBehavior(false), + m_animationPaused(false), + m_slowDownFactor(1.0), + m_debugService(0) +{ + m_debugService = QDeclarativeInspectorService::instance(); + connect(m_debugService, SIGNAL(gotMessage(QByteArray)), + this, SLOT(handleMessage(QByteArray))); +} + +void AbstractViewInspector::createQmlObject(const QString &qml, QObject *parent, + const QStringList &importList, + const QString &filename) +{ + if (!parent) + return; + + QString imports; + foreach (const QString &s, importList) { + imports += s; + imports += QLatin1Char('\n'); + } + + QDeclarativeContext *parentContext = declarativeEngine()->contextForObject(parent); + QDeclarativeComponent component(declarativeEngine()); + QByteArray constructedQml = QString(imports + qml).toLatin1(); + + component.setData(constructedQml, QUrl::fromLocalFile(filename)); + QObject *newObject = component.create(parentContext); + if (newObject) + reparentQmlObject(newObject, parent); +} + +void AbstractViewInspector::clearComponentCache() +{ + declarativeEngine()->clearComponentCache(); +} + +void AbstractViewInspector::setDesignModeBehavior(bool value) +{ + if (m_designModeBehavior == value) + return; + + m_designModeBehavior = value; + emit designModeBehaviorChanged(value); + sendDesignModeBehavior(value); +} + +void AbstractViewInspector::setAnimationSpeed(qreal slowDownFactor) +{ + Q_ASSERT(slowDownFactor > 0); + if (m_slowDownFactor == slowDownFactor) + return; + + animationSpeedChangeRequested(slowDownFactor); + sendAnimationSpeed(slowDownFactor); +} + +void AbstractViewInspector::setAnimationPaused(bool paused) +{ + if (m_animationPaused == paused) + return; + + animationPausedChangeRequested(paused); + sendAnimationPaused(paused); +} + +void AbstractViewInspector::animationSpeedChangeRequested(qreal factor) +{ + if (m_slowDownFactor != factor) { + m_slowDownFactor = factor; + emit animationSpeedChanged(factor); + } + + const float effectiveFactor = m_animationPaused ? 0 : factor; + QDeclarativeDebugHelper::setAnimationSlowDownFactor(effectiveFactor); +} + +void AbstractViewInspector::animationPausedChangeRequested(bool paused) +{ + if (m_animationPaused != paused) { + m_animationPaused = paused; + emit animationPausedChanged(paused); + } + + const float effectiveFactor = paused ? 0 : m_slowDownFactor; + QDeclarativeDebugHelper::setAnimationSlowDownFactor(effectiveFactor); +} + +void AbstractViewInspector::setShowAppOnTop(bool appOnTop) +{ + if (viewWidget()) { + QWidget *window = viewWidget()->window(); + Qt::WindowFlags flags = window->windowFlags(); + if (appOnTop) + flags |= Qt::WindowStaysOnTopHint; + else + flags &= ~Qt::WindowStaysOnTopHint; + + window->setWindowFlags(flags); + window->show(); + } + + m_showAppOnTop = appOnTop; + sendShowAppOnTop(appOnTop); + + emit showAppOnTopChanged(appOnTop); +} + +void AbstractViewInspector::changeToColorPickerTool() +{ + changeTool(InspectorProtocol::ColorPickerTool); +} + +void AbstractViewInspector::changeToZoomTool() +{ + changeTool(InspectorProtocol::ZoomTool); +} + +void AbstractViewInspector::changeToSingleSelectTool() +{ + changeTool(InspectorProtocol::SelectTool); +} + +void AbstractViewInspector::changeToMarqueeSelectTool() +{ + changeTool(InspectorProtocol::SelectMarqueeTool); +} + +bool AbstractViewInspector::eventFilter(QObject *obj, QEvent *event) +{ + if (!designModeBehavior()) + return QObject::eventFilter(obj, event); + + switch (event->type()) { + case QEvent::Leave: + if (leaveEvent(event)) + return true; + break; + case QEvent::MouseButtonPress: + if (mousePressEvent(static_cast(event))) + return true; + break; + case QEvent::MouseMove: + if (mouseMoveEvent(static_cast(event))) + return true; + break; + case QEvent::MouseButtonRelease: + if (mouseReleaseEvent(static_cast(event))) + return true; + break; + case QEvent::KeyPress: + if (keyPressEvent(static_cast(event))) + return true; + break; + case QEvent::KeyRelease: + if (keyReleaseEvent(static_cast(event))) + return true; + break; + case QEvent::MouseButtonDblClick: + if (mouseDoubleClickEvent(static_cast(event))) + return true; + break; + case QEvent::Wheel: + if (wheelEvent(static_cast(event))) + return true; + break; + default: + break; + } + + return QObject::eventFilter(obj, event); +} + +bool AbstractViewInspector::leaveEvent(QEvent *event) +{ + m_currentTool->leaveEvent(event); + return true; +} + +bool AbstractViewInspector::mousePressEvent(QMouseEvent *event) +{ + m_currentTool->mousePressEvent(event); + return true; +} + +bool AbstractViewInspector::mouseMoveEvent(QMouseEvent *event) +{ + if (event->buttons()) { + m_currentTool->mouseMoveEvent(event); + } else { + m_currentTool->hoverMoveEvent(event); + } + return true; +} + +bool AbstractViewInspector::mouseReleaseEvent(QMouseEvent *event) +{ + m_currentTool->mouseReleaseEvent(event); + return true; +} + +bool AbstractViewInspector::keyPressEvent(QKeyEvent *event) +{ + m_currentTool->keyPressEvent(event); + return true; +} + +bool AbstractViewInspector::keyReleaseEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_V: + changeTool(InspectorProtocol::SelectTool); + break; +// disabled because multiselection does not do anything useful without design mode +// case Qt::Key_M: +// changeTool(InspectorProtocol::SelectMarqueeTool); +// break; + case Qt::Key_I: + changeTool(InspectorProtocol::ColorPickerTool); + break; + case Qt::Key_Z: + changeTool(InspectorProtocol::ZoomTool); + break; + case Qt::Key_Space: + setAnimationPaused(!animationPaused()); + break; + default: + break; + } + + m_currentTool->keyReleaseEvent(event); + return true; +} + +bool AbstractViewInspector::mouseDoubleClickEvent(QMouseEvent *event) +{ + m_currentTool->mouseDoubleClickEvent(event); + return true; +} + +bool AbstractViewInspector::wheelEvent(QWheelEvent *event) +{ + m_currentTool->wheelEvent(event); + return true; +} + +void AbstractViewInspector::handleMessage(const QByteArray &message) +{ + QDataStream ds(message); + + InspectorProtocol::Message type; + ds >> type; + + switch (type) { + case InspectorProtocol::SetCurrentObjects: { + int itemCount = 0; + ds >> itemCount; + + QList selectedObjects; + for (int i = 0; i < itemCount; ++i) { + int debugId = -1; + ds >> debugId; + if (QObject *obj = QDeclarativeDebugService::objectForId(debugId)) + selectedObjects << obj; + } + + changeCurrentObjects(selectedObjects); + break; + } + case InspectorProtocol::Reload: { + reloadView(); + break; + } + case InspectorProtocol::SetAnimationSpeed: { + qreal speed; + ds >> speed; + animationSpeedChangeRequested(speed); + break; + } + case InspectorProtocol::SetAnimationPaused: { + bool paused; + ds >> paused; + animationPausedChangeRequested(paused); + break; + } + case InspectorProtocol::ChangeTool: { + InspectorProtocol::Tool tool; + ds >> tool; + changeTool(tool); + break; + } + case InspectorProtocol::SetDesignMode: { + bool inDesignMode; + ds >> inDesignMode; + setDesignModeBehavior(inDesignMode); + break; + } + case InspectorProtocol::ShowAppOnTop: { + bool showOnTop; + ds >> showOnTop; + setShowAppOnTop(showOnTop); + break; + } + case InspectorProtocol::CreateObject: { + QString qml; + int parentId; + QString filename; + QStringList imports; + ds >> qml >> parentId >> imports >> filename; + createQmlObject(qml, QDeclarativeDebugService::objectForId(parentId), + imports, filename); + break; + } + case InspectorProtocol::DestroyObject: { + int debugId; + ds >> debugId; + if (QObject *obj = QDeclarativeDebugService::objectForId(debugId)) + obj->deleteLater(); + break; + } + case InspectorProtocol::MoveObject: { + int debugId, newParent; + ds >> debugId >> newParent; + reparentQmlObject(QDeclarativeDebugService::objectForId(debugId), + QDeclarativeDebugService::objectForId(newParent)); + break; + } + case InspectorProtocol::ObjectIdList: { + int itemCount; + ds >> itemCount; + m_stringIdForObjectId.clear(); + for (int i = 0; i < itemCount; ++i) { + int itemDebugId; + QString itemIdString; + ds >> itemDebugId + >> itemIdString; + + m_stringIdForObjectId.insert(itemDebugId, itemIdString); + } + break; + } + case InspectorProtocol::ClearComponentCache: { + clearComponentCache(); + break; + } + default: + qWarning() << "Warning: Not handling message:" << type; + } +} + +void AbstractViewInspector::sendDesignModeBehavior(bool inDesignMode) +{ + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + + ds << InspectorProtocol::SetDesignMode + << inDesignMode; + + m_debugService->sendMessage(message); +} + +void AbstractViewInspector::sendCurrentObjects(const QList &objects) +{ + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + + ds << InspectorProtocol::CurrentObjectsChanged + << objects.length(); + + foreach (QObject *object, objects) { + int id = QDeclarativeDebugService::idForObject(object); + ds << id; + } + + m_debugService->sendMessage(message); +} + +void AbstractViewInspector::sendCurrentTool(Constants::DesignTool toolId) +{ + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + + ds << InspectorProtocol::ToolChanged + << toolId; + + m_debugService->sendMessage(message); +} + +void AbstractViewInspector::sendAnimationSpeed(qreal slowDownFactor) +{ + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + + ds << InspectorProtocol::AnimationSpeedChanged + << slowDownFactor; + + m_debugService->sendMessage(message); +} + +void AbstractViewInspector::sendAnimationPaused(bool paused) +{ + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + + ds << InspectorProtocol::AnimationPausedChanged + << paused; + + m_debugService->sendMessage(message); +} + +void AbstractViewInspector::sendReloaded() +{ + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + + ds << InspectorProtocol::Reloaded; + + m_debugService->sendMessage(message); +} + +void AbstractViewInspector::sendShowAppOnTop(bool showAppOnTop) +{ + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + + ds << InspectorProtocol::ShowAppOnTop << showAppOnTop; + + m_debugService->sendMessage(message); +} + +void AbstractViewInspector::sendColorChanged(const QColor &color) +{ + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + + ds << InspectorProtocol::ColorChanged + << color; + + m_debugService->sendMessage(message); +} + +QString AbstractViewInspector::idStringForObject(QObject *obj) const +{ + const int id = QDeclarativeDebugService::idForObject(obj); + return m_stringIdForObjectId.value(id); +} + +} // namespace QmlJSDebugger + diff --git a/src/plugins/qmltooling/qmldbg_inspector/abstractviewinspector.h b/src/plugins/qmltooling/qmldbg_inspector/abstractviewinspector.h new file mode 100644 index 00000000..700476f9 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_inspector/abstractviewinspector.h @@ -0,0 +1,173 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ABSTRACTVIEWINSPECTOR_H +#define ABSTRACTVIEWINSPECTOR_H + +#include +#include +#include +#include + +#include "qdeclarativeinspectorprotocol.h" +#include "qmlinspectorconstants.h" + +QT_BEGIN_NAMESPACE +class QDeclarativeEngine; +class QDeclarativeInspectorService; +class QKeyEvent; +class QMouseEvent; +class QWheelEvent; +QT_END_NAMESPACE + +namespace QmlJSDebugger { + +class AbstractTool; + +/* + * The common code between QSGView and QDeclarativeView inspectors lives here, + */ +class AbstractViewInspector : public QObject +{ + Q_OBJECT + +public: + explicit AbstractViewInspector(QObject *parent = 0); + + virtual void changeCurrentObjects(const QList &objects) = 0; + + virtual void reloadView() = 0; + + void createQmlObject(const QString &qml, QObject *parent, + const QStringList &importList, + const QString &filename = QString()); + + virtual void reparentQmlObject(QObject *object, QObject *newParent) = 0; + + virtual void changeTool(InspectorProtocol::Tool tool) = 0; + + void clearComponentCache(); + + virtual QWidget *viewWidget() const = 0; + virtual QDeclarativeEngine *declarativeEngine() const = 0; + + + bool showAppOnTop() const { return m_showAppOnTop; } + bool designModeBehavior() const { return m_designModeBehavior; } + + bool animationPaused() const { return m_animationPaused; } + qreal slowDownFactor() const { return m_slowDownFactor; } + + void sendCurrentObjects(const QList &); + void sendAnimationSpeed(qreal slowDownFactor); + void sendAnimationPaused(bool paused); + void sendCurrentTool(Constants::DesignTool toolId); + void sendReloaded(); + void sendShowAppOnTop(bool showAppOnTop); + + QString idStringForObject(QObject *obj) const; + +public slots: + void sendDesignModeBehavior(bool inDesignMode); + void sendColorChanged(const QColor &color); + + void changeToColorPickerTool(); + void changeToZoomTool(); + void changeToSingleSelectTool(); + void changeToMarqueeSelectTool(); + + virtual void setDesignModeBehavior(bool value); + + void setShowAppOnTop(bool appOnTop); + + void setAnimationSpeed(qreal factor); + void setAnimationPaused(bool paused); + +signals: + void designModeBehaviorChanged(bool inDesignMode); + void showAppOnTopChanged(bool showAppOnTop); + void reloadRequested(); + void marqueeSelectToolActivated(); + void selectToolActivated(); + void zoomToolActivated(); + void colorPickerActivated(); + void selectedColorChanged(const QColor &color); + + void animationSpeedChanged(qreal factor); + void animationPausedChanged(bool paused); + +protected: + bool eventFilter(QObject *, QEvent *); + + virtual bool leaveEvent(QEvent *); + virtual bool mousePressEvent(QMouseEvent *event); + virtual bool mouseMoveEvent(QMouseEvent *event); + virtual bool mouseReleaseEvent(QMouseEvent *event); + virtual bool keyPressEvent(QKeyEvent *event); + virtual bool keyReleaseEvent(QKeyEvent *keyEvent); + virtual bool mouseDoubleClickEvent(QMouseEvent *event); + virtual bool wheelEvent(QWheelEvent *event); + + AbstractTool *currentTool() const { return m_currentTool; } + void setCurrentTool(AbstractTool *tool) { m_currentTool = tool; } + +private slots: + void handleMessage(const QByteArray &message); + +private: + void animationSpeedChangeRequested(qreal factor); + void animationPausedChangeRequested(bool paused); + + AbstractTool *m_currentTool; + + bool m_showAppOnTop; + bool m_designModeBehavior; + + bool m_animationPaused; + qreal m_slowDownFactor; + + QHash m_stringIdForObjectId; + QDeclarativeInspectorService *m_debugService; +}; + +} // namespace QmlJSDebugger + +#endif // ABSTRACTVIEWINSPECTOR_H diff --git a/src/plugins/qmltooling/qmldbg_inspector/editor/abstractliveedittool.cpp b/src/plugins/qmltooling/qmldbg_inspector/editor/abstractliveedittool.cpp new file mode 100644 index 00000000..9c2f70c4 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_inspector/editor/abstractliveedittool.cpp @@ -0,0 +1,196 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "abstractliveedittool.h" +#include "../qdeclarativeviewinspector_p.h" + +#include + +#include +#include +#include + +namespace QmlJSDebugger { + +AbstractLiveEditTool::AbstractLiveEditTool(QDeclarativeViewInspector *editorView) + : AbstractTool(editorView) +{ +} + + +AbstractLiveEditTool::~AbstractLiveEditTool() +{ +} + +QDeclarativeViewInspector *AbstractLiveEditTool::inspector() const +{ + return static_cast(AbstractTool::inspector()); +} + +QDeclarativeView *AbstractLiveEditTool::view() const +{ + return inspector()->declarativeView(); +} + +QGraphicsScene* AbstractLiveEditTool::scene() const +{ + return view()->scene(); +} + +void AbstractLiveEditTool::updateSelectedItems() +{ + selectedItemsChanged(items()); +} + +QList AbstractLiveEditTool::items() const +{ + return inspector()->selectedItems(); +} + +bool AbstractLiveEditTool::topItemIsMovable(const QList & itemList) +{ + QGraphicsItem *firstSelectableItem = topMovableGraphicsItem(itemList); + if (firstSelectableItem == 0) + return false; + if (toQDeclarativeItem(firstSelectableItem) != 0) + return true; + + return false; + +} + +bool AbstractLiveEditTool::topSelectedItemIsMovable(const QList &itemList) +{ + QList selectedItems = inspector()->selectedItems(); + + foreach (QGraphicsItem *item, itemList) { + QDeclarativeItem *declarativeItem = toQDeclarativeItem(item); + if (declarativeItem + && selectedItems.contains(declarativeItem) + /*&& (declarativeItem->qmlItemNode().hasShowContent() || selectNonContentItems)*/) + return true; + } + + return false; + +} + +bool AbstractLiveEditTool::topItemIsResizeHandle(const QList &/*itemList*/) +{ + return false; +} + +QDeclarativeItem *AbstractLiveEditTool::toQDeclarativeItem(QGraphicsItem *item) +{ + return qobject_cast(item->toGraphicsObject()); +} + +QGraphicsItem *AbstractLiveEditTool::topMovableGraphicsItem(const QList &itemList) +{ + foreach (QGraphicsItem *item, itemList) { + if (item->flags().testFlag(QGraphicsItem::ItemIsMovable)) + return item; + } + return 0; +} + +QDeclarativeItem *AbstractLiveEditTool::topMovableDeclarativeItem(const QList + &itemList) +{ + foreach (QGraphicsItem *item, itemList) { + QDeclarativeItem *declarativeItem = toQDeclarativeItem(item); + if (declarativeItem /*&& (declarativeItem->qmlItemNode().hasShowContent())*/) + return declarativeItem; + } + + return 0; +} + +QList AbstractLiveEditTool::toGraphicsObjectList(const QList + &itemList) +{ + QList gfxObjects; + foreach (QGraphicsItem *item, itemList) { + QGraphicsObject *obj = item->toGraphicsObject(); + if (obj) + gfxObjects << obj; + } + + return gfxObjects; +} + +QString AbstractLiveEditTool::titleForItem(QGraphicsItem *item) +{ + QString className(QLatin1String("QGraphicsItem")); + QString objectStringId; + + QString constructedName; + + QGraphicsObject *gfxObject = item->toGraphicsObject(); + if (gfxObject) { + className = QLatin1String(gfxObject->metaObject()->className()); + + className.remove(QRegExp(QLatin1String("_QMLTYPE_\\d+"))); + className.remove(QRegExp(QLatin1String("_QML_\\d+"))); + if (className.startsWith(QLatin1String("QDeclarative"))) + className = className.remove(QLatin1String("QDeclarative")); + + QDeclarativeItem *declarativeItem = qobject_cast(gfxObject); + if (declarativeItem) { + objectStringId = inspector()->idStringForObject(declarativeItem); + } + + if (!objectStringId.isEmpty()) { + constructedName = objectStringId + QLatin1String(" (") + className + QLatin1Char(')'); + } else { + if (!gfxObject->objectName().isEmpty()) { + constructedName = gfxObject->objectName() + QLatin1String(" (") + className + QLatin1Char(')'); + } else { + constructedName = className; + } + } + } + + return constructedName; +} + + +} // namespace QmlJSDebugger diff --git a/src/plugins/qmltooling/qmldbg_inspector/editor/abstractliveedittool.h b/src/plugins/qmltooling/qmldbg_inspector/editor/abstractliveedittool.h new file mode 100644 index 00000000..f2996a10 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_inspector/editor/abstractliveedittool.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ABSTRACTLIVEEDITTOOL_H +#define ABSTRACTLIVEEDITTOOL_H + +#include +#include "../abstracttool.h" + +QT_BEGIN_NAMESPACE +class QMouseEvent; +class QGraphicsItem; +class QDeclarativeItem; +class QKeyEvent; +class QGraphicsScene; +class QGraphicsObject; +class QWheelEvent; +class QDeclarativeView; +QT_END_NAMESPACE + +namespace QmlJSDebugger { + +class QDeclarativeViewInspector; + +class AbstractLiveEditTool : public AbstractTool +{ + Q_OBJECT +public: + AbstractLiveEditTool(QDeclarativeViewInspector *inspector); + + virtual ~AbstractLiveEditTool(); + + void leaveEvent(QEvent *) {} + + virtual void itemsAboutToRemoved(const QList &itemList) = 0; + + virtual void clear() = 0; + + void updateSelectedItems(); + QList items() const; + + bool topItemIsMovable(const QList &itemList); + bool topItemIsResizeHandle(const QList &itemList); + bool topSelectedItemIsMovable(const QList &itemList); + + QString titleForItem(QGraphicsItem *item); + + static QList toGraphicsObjectList(const QList &itemList); + static QGraphicsItem* topMovableGraphicsItem(const QList &itemList); + static QDeclarativeItem* topMovableDeclarativeItem(const QList &itemList); + static QDeclarativeItem *toQDeclarativeItem(QGraphicsItem *item); + +protected: + virtual void selectedItemsChanged(const QList &objectList) = 0; + + QDeclarativeViewInspector *inspector() const; + QDeclarativeView *view() const; + QGraphicsScene *scene() const; + +private: + QList m_itemList; +}; + +} + +#endif // ABSTRACTLIVEEDITTOOL_H diff --git a/src/plugins/qmltooling/qmldbg_inspector/editor/boundingrecthighlighter.cpp b/src/plugins/qmltooling/qmldbg_inspector/editor/boundingrecthighlighter.cpp new file mode 100644 index 00000000..f6305698 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_inspector/editor/boundingrecthighlighter.cpp @@ -0,0 +1,240 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "boundingrecthighlighter.h" + +#include "../qdeclarativeviewinspector.h" +#include "../qmlinspectorconstants.h" + +#include + +#include +#include +#include + +namespace QmlJSDebugger { + +BoundingBox::BoundingBox(QGraphicsObject *itemToHighlight, QGraphicsItem *parentItem, + QObject *parent) + : QObject(parent), + highlightedObject(itemToHighlight), + highlightPolygon(0), + highlightPolygonEdge(0) +{ + highlightPolygon = new BoundingBoxPolygonItem(parentItem); + highlightPolygonEdge = new BoundingBoxPolygonItem(parentItem); + + highlightPolygon->setPen(QPen(QColor(0, 22, 159))); + highlightPolygonEdge->setPen(QPen(QColor(158, 199, 255))); + + highlightPolygon->setFlag(QGraphicsItem::ItemIsSelectable, false); + highlightPolygonEdge->setFlag(QGraphicsItem::ItemIsSelectable, false); +} + +BoundingBox::~BoundingBox() +{ + highlightedObject.clear(); +} + +BoundingBoxPolygonItem::BoundingBoxPolygonItem(QGraphicsItem *item) : QGraphicsPolygonItem(item) +{ + QPen pen; + pen.setColor(QColor(108, 141, 221)); + pen.setWidth(1); + setPen(pen); +} + +int BoundingBoxPolygonItem::type() const +{ + return Constants::EditorItemType; +} + +BoundingRectHighlighter::BoundingRectHighlighter(QDeclarativeViewInspector *view) : + LiveLayerItem(view->declarativeView()->scene()), + m_view(view) +{ +} + +BoundingRectHighlighter::~BoundingRectHighlighter() +{ + +} + +void BoundingRectHighlighter::clear() +{ + foreach (BoundingBox *box, m_boxes) + freeBoundingBox(box); +} + +BoundingBox *BoundingRectHighlighter::boxFor(QGraphicsObject *item) const +{ + foreach (BoundingBox *box, m_boxes) { + if (box->highlightedObject.data() == item) + return box; + } + return 0; +} + +void BoundingRectHighlighter::highlight(QList items) +{ + if (items.isEmpty()) + return; + + QList newBoxes; + foreach (QGraphicsObject *itemToHighlight, items) { + BoundingBox *box = boxFor(itemToHighlight); + if (!box) + box = createBoundingBox(itemToHighlight); + + newBoxes << box; + } + qSort(newBoxes); + + if (newBoxes != m_boxes) { + clear(); + m_boxes << newBoxes; + } + + highlightAll(); +} + +void BoundingRectHighlighter::highlight(QGraphicsObject* itemToHighlight) +{ + if (!itemToHighlight) + return; + + BoundingBox *box = boxFor(itemToHighlight); + if (!box) { + box = createBoundingBox(itemToHighlight); + m_boxes << box; + qSort(m_boxes); + } + + highlightAll(); +} + +BoundingBox *BoundingRectHighlighter::createBoundingBox(QGraphicsObject *itemToHighlight) +{ + if (!m_freeBoxes.isEmpty()) { + BoundingBox *box = m_freeBoxes.last(); + if (box->highlightedObject.isNull()) { + box->highlightedObject = itemToHighlight; + box->highlightPolygon->show(); + box->highlightPolygonEdge->show(); + m_freeBoxes.removeLast(); + return box; + } + } + + BoundingBox *box = new BoundingBox(itemToHighlight, this, this); + + connect(itemToHighlight, SIGNAL(xChanged()), this, SLOT(refresh())); + connect(itemToHighlight, SIGNAL(yChanged()), this, SLOT(refresh())); + connect(itemToHighlight, SIGNAL(widthChanged()), this, SLOT(refresh())); + connect(itemToHighlight, SIGNAL(heightChanged()), this, SLOT(refresh())); + connect(itemToHighlight, SIGNAL(rotationChanged()), this, SLOT(refresh())); + connect(itemToHighlight, SIGNAL(destroyed(QObject*)), this, SLOT(itemDestroyed(QObject*))); + + return box; +} + +void BoundingRectHighlighter::removeBoundingBox(BoundingBox *box) +{ + delete box; + box = 0; +} + +void BoundingRectHighlighter::freeBoundingBox(BoundingBox *box) +{ + if (!box->highlightedObject.isNull()) { + disconnect(box->highlightedObject.data(), SIGNAL(xChanged()), this, SLOT(refresh())); + disconnect(box->highlightedObject.data(), SIGNAL(yChanged()), this, SLOT(refresh())); + disconnect(box->highlightedObject.data(), SIGNAL(widthChanged()), this, SLOT(refresh())); + disconnect(box->highlightedObject.data(), SIGNAL(heightChanged()), this, SLOT(refresh())); + disconnect(box->highlightedObject.data(), SIGNAL(rotationChanged()), this, SLOT(refresh())); + } + + box->highlightedObject.clear(); + box->highlightPolygon->hide(); + box->highlightPolygonEdge->hide(); + m_boxes.removeOne(box); + m_freeBoxes << box; +} + +void BoundingRectHighlighter::itemDestroyed(QObject *obj) +{ + foreach (BoundingBox *box, m_boxes) { + if (box->highlightedObject.data() == obj) { + freeBoundingBox(box); + break; + } + } +} + +void BoundingRectHighlighter::highlightAll() +{ + foreach (BoundingBox *box, m_boxes) { + if (box && box->highlightedObject.isNull()) { + // clear all highlights + clear(); + return; + } + QGraphicsObject *item = box->highlightedObject.data(); + + QRectF boundingRectInSceneSpace(item->mapToScene(item->boundingRect()).boundingRect()); + QRectF boundingRectInLayerItemSpace = mapRectFromScene(boundingRectInSceneSpace); + QRectF bboxRect = m_view->adjustToScreenBoundaries(boundingRectInLayerItemSpace); + QRectF edgeRect = bboxRect; + edgeRect.adjust(-1, -1, 1, 1); + + box->highlightPolygon->setPolygon(QPolygonF(bboxRect)); + box->highlightPolygonEdge->setPolygon(QPolygonF(edgeRect)); + } +} + +void BoundingRectHighlighter::refresh() +{ + if (!m_boxes.isEmpty()) + highlightAll(); +} + + +} // namespace QmlJSDebugger diff --git a/src/plugins/qmltooling/qmldbg_inspector/editor/boundingrecthighlighter.h b/src/plugins/qmltooling/qmldbg_inspector/editor/boundingrecthighlighter.h new file mode 100644 index 00000000..aef4e4ee --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_inspector/editor/boundingrecthighlighter.h @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BOUNDINGRECTHIGHLIGHTER_H +#define BOUNDINGRECTHIGHLIGHTER_H + +#include "livelayeritem.h" + +#include +#include + +QT_FORWARD_DECLARE_CLASS(QGraphicsItem) +QT_FORWARD_DECLARE_CLASS(QPainter) +QT_FORWARD_DECLARE_CLASS(QWidget) +QT_FORWARD_DECLARE_CLASS(QStyleOptionGraphicsItem) +QT_FORWARD_DECLARE_CLASS(QTimer) + +namespace QmlJSDebugger { + +class QDeclarativeViewInspector; +class BoundingBox; + +class BoundingRectHighlighter : public LiveLayerItem +{ + Q_OBJECT +public: + explicit BoundingRectHighlighter(QDeclarativeViewInspector *view); + ~BoundingRectHighlighter(); + void clear(); + void highlight(QList items); + void highlight(QGraphicsObject* item); + +private slots: + void refresh(); + void itemDestroyed(QObject *); + +private: + BoundingBox *boxFor(QGraphicsObject *item) const; + void highlightAll(); + BoundingBox *createBoundingBox(QGraphicsObject *itemToHighlight); + void removeBoundingBox(BoundingBox *box); + void freeBoundingBox(BoundingBox *box); + +private: + Q_DISABLE_COPY(BoundingRectHighlighter) + + QDeclarativeViewInspector *m_view; + QList m_boxes; + QList m_freeBoxes; +}; + +class BoundingBox : public QObject +{ + Q_OBJECT +public: + explicit BoundingBox(QGraphicsObject *itemToHighlight, QGraphicsItem *parentItem, + QObject *parent = 0); + ~BoundingBox(); + QWeakPointer highlightedObject; + QGraphicsPolygonItem *highlightPolygon; + QGraphicsPolygonItem *highlightPolygonEdge; + +private: + Q_DISABLE_COPY(BoundingBox) + +}; + +class BoundingBoxPolygonItem : public QGraphicsPolygonItem +{ +public: + explicit BoundingBoxPolygonItem(QGraphicsItem *item); + int type() const; +}; + +} // namespace QmlJSDebugger + +#endif // BOUNDINGRECTHIGHLIGHTER_H diff --git a/src/plugins/qmltooling/qmldbg_inspector/editor/colorpickertool.cpp b/src/plugins/qmltooling/qmldbg_inspector/editor/colorpickertool.cpp new file mode 100644 index 00000000..9fe40fbf --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_inspector/editor/colorpickertool.cpp @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "colorpickertool.h" + +#include "../qdeclarativeviewinspector.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace QmlJSDebugger { + +ColorPickerTool::ColorPickerTool(QDeclarativeViewInspector *view) : + AbstractLiveEditTool(view) +{ + m_selectedColor.setRgb(0,0,0); +} + +ColorPickerTool::~ColorPickerTool() +{ +} + +void ColorPickerTool::mousePressEvent(QMouseEvent *event) +{ + pickColor(event->pos()); +} + +void ColorPickerTool::mouseMoveEvent(QMouseEvent *event) +{ + pickColor(event->pos()); +} + +void ColorPickerTool::clear() +{ +#ifndef QT_NO_CURSOR + view()->setCursor(Qt::CrossCursor); +#endif +} + +void ColorPickerTool::pickColor(const QPoint &pos) +{ + QRgb fillColor = view()->backgroundBrush().color().rgb(); + if (view()->backgroundBrush().style() == Qt::NoBrush) + fillColor = view()->palette().color(QPalette::Base).rgb(); + + QRectF target(0,0, 1, 1); + QRect source(pos.x(), pos.y(), 1, 1); + QImage img(1, 1, QImage::Format_ARGB32); + img.fill(fillColor); + QPainter painter(&img); + view()->render(&painter, target, source); + m_selectedColor = QColor::fromRgb(img.pixel(0, 0)); + + emit selectedColorChanged(m_selectedColor); +} + +} // namespace QmlJSDebugger diff --git a/src/plugins/qmltooling/qmldbg_inspector/editor/colorpickertool.h b/src/plugins/qmltooling/qmldbg_inspector/editor/colorpickertool.h new file mode 100644 index 00000000..dda147bc --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_inspector/editor/colorpickertool.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef COLORPICKERTOOL_H +#define COLORPICKERTOOL_H + +#include "abstractliveedittool.h" + +#include + +QT_FORWARD_DECLARE_CLASS(QPoint) + +namespace QmlJSDebugger { + +class ColorPickerTool : public AbstractLiveEditTool +{ + Q_OBJECT +public: + explicit ColorPickerTool(QDeclarativeViewInspector *view); + + virtual ~ColorPickerTool(); + + void mousePressEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *) {} + void mouseDoubleClickEvent(QMouseEvent *) {} + + void hoverMoveEvent(QMouseEvent *) {} + + void keyPressEvent(QKeyEvent *) {} + void keyReleaseEvent(QKeyEvent *) {} + + void wheelEvent(QWheelEvent *) {} + + void itemsAboutToRemoved(const QList &) {} + + void clear(); + +signals: + void selectedColorChanged(const QColor &color); + +protected: + void selectedItemsChanged(const QList &) {} + +private: + void pickColor(const QPoint &pos); + +private: + QColor m_selectedColor; +}; + +} // namespace QmlJSDebugger + +#endif // COLORPICKERTOOL_H diff --git a/src/plugins/qmltooling/qmldbg_inspector/editor/livelayeritem.cpp b/src/plugins/qmltooling/qmldbg_inspector/editor/livelayeritem.cpp new file mode 100644 index 00000000..fd52b0eb --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_inspector/editor/livelayeritem.cpp @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "livelayeritem.h" + +#include "../qmlinspectorconstants.h" + +#include + +namespace QmlJSDebugger { + +LiveLayerItem::LiveLayerItem(QGraphicsScene* scene) + : QGraphicsObject() +{ + scene->addItem(this); + setZValue(1); + setFlag(QGraphicsItem::ItemIsMovable, false); +} + +LiveLayerItem::~LiveLayerItem() +{ +} + +void LiveLayerItem::paint(QPainter * /*painter*/, const QStyleOptionGraphicsItem * /*option*/, + QWidget * /*widget*/) +{ +} + +int LiveLayerItem::type() const +{ + return Constants::EditorItemType; +} + +QRectF LiveLayerItem::boundingRect() const +{ + return childrenBoundingRect(); +} + +QList LiveLayerItem::findAllChildItems() const +{ + return findAllChildItems(this); +} + +QList LiveLayerItem::findAllChildItems(const QGraphicsItem *item) const +{ + QList itemList(item->childItems()); + + foreach (QGraphicsItem *childItem, item->childItems()) + itemList += findAllChildItems(childItem); + + return itemList; +} + +} // namespace QmlJSDebugger diff --git a/src/plugins/qmltooling/qmldbg_inspector/editor/livelayeritem.h b/src/plugins/qmltooling/qmldbg_inspector/editor/livelayeritem.h new file mode 100644 index 00000000..bfff002e --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_inspector/editor/livelayeritem.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef LIVELAYERITEM_H +#define LIVELAYERITEM_H + +#include + +namespace QmlJSDebugger { + +class LiveLayerItem : public QGraphicsObject +{ +public: + LiveLayerItem(QGraphicsScene *scene); + ~LiveLayerItem(); + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, + QWidget *widget = 0); + QRectF boundingRect() const; + int type() const; + + QList findAllChildItems() const; + +protected: + QList findAllChildItems(const QGraphicsItem *item) const; +}; + +} + +#endif // LIVELAYERITEM_H diff --git a/src/plugins/qmltooling/qmldbg_inspector/editor/liverubberbandselectionmanipulator.cpp b/src/plugins/qmltooling/qmldbg_inspector/editor/liverubberbandselectionmanipulator.cpp new file mode 100644 index 00000000..7c04f62c --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_inspector/editor/liverubberbandselectionmanipulator.cpp @@ -0,0 +1,165 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "liverubberbandselectionmanipulator.h" + +#include "../qdeclarativeviewinspector_p.h" + +#include + +#include + +namespace QmlJSDebugger { + +LiveRubberBandSelectionManipulator::LiveRubberBandSelectionManipulator(QGraphicsObject *layerItem, + QDeclarativeViewInspector *editorView) + : m_selectionRectangleElement(layerItem), + m_editorView(editorView), + m_beginFormEditorItem(0), + m_isActive(false) +{ + m_selectionRectangleElement.hide(); +} + +void LiveRubberBandSelectionManipulator::clear() +{ + m_selectionRectangleElement.clear(); + m_isActive = false; + m_beginPoint = QPointF(); + m_itemList.clear(); + m_oldSelectionList.clear(); +} + +QGraphicsItem *LiveRubberBandSelectionManipulator::topFormEditorItem(const QList + &itemList) +{ + if (itemList.isEmpty()) + return 0; + + return itemList.first(); +} + +void LiveRubberBandSelectionManipulator::begin(const QPointF &beginPoint) +{ + m_beginPoint = beginPoint; + m_selectionRectangleElement.setRect(m_beginPoint, m_beginPoint); + m_selectionRectangleElement.show(); + m_isActive = true; + QDeclarativeViewInspectorPrivate *inspectorPrivate + = QDeclarativeViewInspectorPrivate::get(m_editorView); + m_beginFormEditorItem = topFormEditorItem(inspectorPrivate->selectableItems(beginPoint)); + m_oldSelectionList = m_editorView->selectedItems(); +} + +void LiveRubberBandSelectionManipulator::update(const QPointF &updatePoint) +{ + m_selectionRectangleElement.setRect(m_beginPoint, updatePoint); +} + +void LiveRubberBandSelectionManipulator::end() +{ + m_oldSelectionList.clear(); + m_selectionRectangleElement.hide(); + m_isActive = false; +} + +void LiveRubberBandSelectionManipulator::select(SelectionType selectionType) +{ + QDeclarativeViewInspectorPrivate *inspectorPrivate + = QDeclarativeViewInspectorPrivate::get(m_editorView); + QList itemList + = inspectorPrivate->selectableItems(m_selectionRectangleElement.rect(), + Qt::IntersectsItemShape); + QList newSelectionList; + + foreach (QGraphicsItem* item, itemList) { + if (item + && item->parentItem() + && !newSelectionList.contains(item) + //&& m_beginFormEditorItem->childItems().contains(item) // TODO activate this test + ) + { + newSelectionList.append(item); + } + } + + if (newSelectionList.isEmpty() && m_beginFormEditorItem) + newSelectionList.append(m_beginFormEditorItem); + + QList resultList; + + switch (selectionType) { + case AddToSelection: { + resultList.append(m_oldSelectionList); + resultList.append(newSelectionList); + } + break; + case ReplaceSelection: { + resultList.append(newSelectionList); + } + break; + case RemoveFromSelection: { + QSet oldSelectionSet(m_oldSelectionList.toSet()); + QSet newSelectionSet(newSelectionList.toSet()); + resultList.append(oldSelectionSet.subtract(newSelectionSet).toList()); + } + } + + m_editorView->setSelectedItems(resultList); +} + + +void LiveRubberBandSelectionManipulator::setItems(const QList &itemList) +{ + m_itemList = itemList; +} + +QPointF LiveRubberBandSelectionManipulator::beginPoint() const +{ + return m_beginPoint; +} + +bool LiveRubberBandSelectionManipulator::isActive() const +{ + return m_isActive; +} + +} // namespace QmlJSDebugger diff --git a/src/plugins/qmltooling/qmldbg_inspector/editor/liverubberbandselectionmanipulator.h b/src/plugins/qmltooling/qmldbg_inspector/editor/liverubberbandselectionmanipulator.h new file mode 100644 index 00000000..c1609ea3 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_inspector/editor/liverubberbandselectionmanipulator.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef RUBBERBANDSELECTIONMANIPULATOR_H +#define RUBBERBANDSELECTIONMANIPULATOR_H + +#include "liveselectionrectangle.h" + +#include + +QT_FORWARD_DECLARE_CLASS(QGraphicsItem) + +namespace QmlJSDebugger { + +class QDeclarativeViewInspector; + +class LiveRubberBandSelectionManipulator +{ +public: + enum SelectionType { + ReplaceSelection, + AddToSelection, + RemoveFromSelection + }; + + LiveRubberBandSelectionManipulator(QGraphicsObject *layerItem, + QDeclarativeViewInspector *editorView); + + void setItems(const QList &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: + QGraphicsItem *topFormEditorItem(const QList &itemList); + +private: + QList m_itemList; + QList m_oldSelectionList; + LiveSelectionRectangle m_selectionRectangleElement; + QPointF m_beginPoint; + QDeclarativeViewInspector *m_editorView; + QGraphicsItem *m_beginFormEditorItem; + bool m_isActive; +}; + +} + +#endif // RUBBERBANDSELECTIONMANIPULATOR_H diff --git a/src/plugins/qmltooling/qmldbg_inspector/editor/liveselectionindicator.cpp b/src/plugins/qmltooling/qmldbg_inspector/editor/liveselectionindicator.cpp new file mode 100644 index 00000000..9545651d --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_inspector/editor/liveselectionindicator.cpp @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "liveselectionindicator.h" + +#include "../qdeclarativeviewinspector_p.h" +#include "../qmlinspectorconstants.h" + +#include +#include +#include +#include + +namespace QmlJSDebugger { + +LiveSelectionIndicator::LiveSelectionIndicator(QDeclarativeViewInspector *viewInspector, + QGraphicsObject *layerItem) + : m_layerItem(layerItem) + , m_view(viewInspector) +{ +} + +LiveSelectionIndicator::~LiveSelectionIndicator() +{ + clear(); +} + +void LiveSelectionIndicator::show() +{ + foreach (QGraphicsRectItem *item, m_indicatorShapeHash) + item->show(); +} + +void LiveSelectionIndicator::hide() +{ + foreach (QGraphicsRectItem *item, m_indicatorShapeHash) + item->hide(); +} + +void LiveSelectionIndicator::clear() +{ + if (!m_layerItem.isNull()) { + QGraphicsScene *scene = m_layerItem.data()->scene(); + foreach (QGraphicsRectItem *item, m_indicatorShapeHash) { + scene->removeItem(item); + delete item; + } + } + + m_indicatorShapeHash.clear(); + +} + +void LiveSelectionIndicator::setItems(const QList > &itemList) +{ + clear(); + + foreach (const QWeakPointer &object, itemList) { + if (object.isNull()) + continue; + + QGraphicsItem *item = object.data(); + + if (!m_indicatorShapeHash.contains(item)) { + QGraphicsRectItem *selectionIndicator = new QGraphicsRectItem(m_layerItem.data()); + m_indicatorShapeHash.insert(item, selectionIndicator); + + const QRectF boundingRect = m_view->adjustToScreenBoundaries(item->mapRectToScene(item->boundingRect())); + const QRectF boundingRectInLayerItemSpace = m_layerItem.data()->mapRectFromScene(boundingRect); + + selectionIndicator->setData(Constants::EditorItemDataKey, true); + selectionIndicator->setFlag(QGraphicsItem::ItemIsSelectable, false); + selectionIndicator->setRect(boundingRectInLayerItemSpace); + selectionIndicator->setPen(QColor(108, 141, 221)); + } + } +} + +} //namespace QmlJSDebugger + diff --git a/src/plugins/qmltooling/qmldbg_inspector/editor/liveselectionindicator.h b/src/plugins/qmltooling/qmldbg_inspector/editor/liveselectionindicator.h new file mode 100644 index 00000000..db386288 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_inspector/editor/liveselectionindicator.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef LIVESELECTIONINDICATOR_H +#define LIVESELECTIONINDICATOR_H + +#include +#include + +QT_BEGIN_NAMESPACE +class QGraphicsObject; +class QGraphicsRectItem; +class QGraphicsItem; +class QPolygonF; +QT_END_NAMESPACE + +namespace QmlJSDebugger { + +class QDeclarativeViewInspector; + +class LiveSelectionIndicator +{ +public: + LiveSelectionIndicator(QDeclarativeViewInspector *viewInspector, QGraphicsObject *layerItem); + ~LiveSelectionIndicator(); + + void show(); + void hide(); + + void clear(); + + void setItems(const QList > &itemList); + +private: + QHash m_indicatorShapeHash; + QWeakPointer m_layerItem; + QDeclarativeViewInspector *m_view; +}; + +} + +#endif // LIVESELECTIONINDICATOR_H diff --git a/src/plugins/qmltooling/qmldbg_inspector/editor/liveselectionrectangle.cpp b/src/plugins/qmltooling/qmldbg_inspector/editor/liveselectionrectangle.cpp new file mode 100644 index 00000000..91db2866 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_inspector/editor/liveselectionrectangle.cpp @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "liveselectionrectangle.h" + +#include "../qmlinspectorconstants.h" + +#include +#include +#include +#include + +#include + +#include + +namespace QmlJSDebugger { + +class SelectionRectShape : public QGraphicsRectItem +{ +public: + SelectionRectShape(QGraphicsItem *parent = 0) : QGraphicsRectItem(parent) {} + int type() const { return Constants::EditorItemType; } +}; + +LiveSelectionRectangle::LiveSelectionRectangle(QGraphicsObject *layerItem) + : m_controlShape(new SelectionRectShape(layerItem)), + m_layerItem(layerItem) +{ + m_controlShape->setPen(QPen(Qt::black)); + m_controlShape->setBrush(QColor(128, 128, 128, 50)); +} + +LiveSelectionRectangle::~LiveSelectionRectangle() +{ + if (m_layerItem) + m_layerItem.data()->scene()->removeItem(m_controlShape); +} + +void LiveSelectionRectangle::clear() +{ + hide(); +} +void LiveSelectionRectangle::show() +{ + m_controlShape->show(); +} + +void LiveSelectionRectangle::hide() +{ + m_controlShape->hide(); +} + +QRectF LiveSelectionRectangle::rect() const +{ + return m_controlShape->mapFromScene(m_controlShape->rect()).boundingRect(); +} + +void LiveSelectionRectangle::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/qmltooling/qmldbg_inspector/editor/liveselectionrectangle.h b/src/plugins/qmltooling/qmldbg_inspector/editor/liveselectionrectangle.h new file mode 100644 index 00000000..5fe42a00 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_inspector/editor/liveselectionrectangle.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef LIVESELECTIONRECTANGLE_H +#define LIVESELECTIONRECTANGLE_H + +#include + +QT_FORWARD_DECLARE_CLASS(QGraphicsObject) +QT_FORWARD_DECLARE_CLASS(QGraphicsRectItem) +QT_FORWARD_DECLARE_CLASS(QPointF) +QT_FORWARD_DECLARE_CLASS(QRectF) + +namespace QmlJSDebugger { + +class LiveSelectionRectangle +{ +public: + LiveSelectionRectangle(QGraphicsObject *layerItem); + ~LiveSelectionRectangle(); + + void show(); + void hide(); + + void clear(); + + void setRect(const QPointF &firstPoint, + const QPointF &secondPoint); + + QRectF rect() const; + +private: + QGraphicsRectItem *m_controlShape; + QWeakPointer m_layerItem; +}; + +} // namespace QmlJSDebugger + +#endif // LIVESELECTIONRECTANGLE_H diff --git a/src/plugins/qmltooling/qmldbg_inspector/editor/liveselectiontool.cpp b/src/plugins/qmltooling/qmldbg_inspector/editor/liveselectiontool.cpp new file mode 100644 index 00000000..288f1ec5 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_inspector/editor/liveselectiontool.cpp @@ -0,0 +1,425 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "liveselectiontool.h" +#include "livelayeritem.h" + +#include "../qdeclarativeviewinspector_p.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +namespace QmlJSDebugger { + +LiveSelectionTool::LiveSelectionTool(QDeclarativeViewInspector *editorView) : + AbstractLiveEditTool(editorView), + m_rubberbandSelectionMode(false), + m_rubberbandSelectionManipulator( + QDeclarativeViewInspectorPrivate::get(editorView)->manipulatorLayer, editorView), + m_singleSelectionManipulator(editorView), + m_selectionIndicator(editorView, + QDeclarativeViewInspectorPrivate::get(editorView)->manipulatorLayer), + //m_resizeIndicator(editorView->manipulatorLayer()), + m_selectOnlyContentItems(true) +{ + +} + +LiveSelectionTool::~LiveSelectionTool() +{ +} + +void LiveSelectionTool::setRubberbandSelectionMode(bool value) +{ + m_rubberbandSelectionMode = value; +} + +LiveSingleSelectionManipulator::SelectionType LiveSelectionTool::getSelectionType(Qt::KeyboardModifiers + modifiers) +{ + LiveSingleSelectionManipulator::SelectionType selectionType + = LiveSingleSelectionManipulator::ReplaceSelection; + if (modifiers.testFlag(Qt::ControlModifier)) { + selectionType = LiveSingleSelectionManipulator::RemoveFromSelection; + } else if (modifiers.testFlag(Qt::ShiftModifier)) { + selectionType = LiveSingleSelectionManipulator::AddToSelection; + } + return selectionType; +} + +bool LiveSelectionTool::alreadySelected(const QList &itemList) const +{ + QDeclarativeViewInspectorPrivate *inspectorPrivate + = QDeclarativeViewInspectorPrivate::get(inspector()); + const QList selectedItems = inspectorPrivate->selectedItems(); + + if (selectedItems.isEmpty()) + return false; + + foreach (QGraphicsItem *item, itemList) + if (selectedItems.contains(item)) + return true; + + return false; +} + +void LiveSelectionTool::mousePressEvent(QMouseEvent *event) +{ + QDeclarativeViewInspectorPrivate *inspectorPrivate + = QDeclarativeViewInspectorPrivate::get(inspector()); + QList itemList = inspectorPrivate->selectableItems(event->pos()); + LiveSingleSelectionManipulator::SelectionType selectionType = getSelectionType(event->modifiers()); + + if (event->buttons() & Qt::LeftButton) { + m_mousePressTimer.start(); + + if (m_rubberbandSelectionMode) { + m_rubberbandSelectionManipulator.begin(event->pos()); + } else { + m_singleSelectionManipulator.begin(event->pos()); + m_singleSelectionManipulator.select(selectionType, m_selectOnlyContentItems); + } + } else if (event->buttons() & Qt::RightButton) { + createContextMenu(itemList, event->globalPos()); + } +} + +void LiveSelectionTool::createContextMenu(const QList &itemList, QPoint globalPos) +{ + QMenu contextMenu; + connect(&contextMenu, SIGNAL(hovered(QAction*)), + this, SLOT(contextMenuElementHovered(QAction*))); + + m_contextMenuItemList = itemList; + + contextMenu.addAction(tr("Items")); + contextMenu.addSeparator(); + int shortcutKey = Qt::Key_1; + int i = 0; + + foreach (QGraphicsItem * const item, itemList) { + QString itemTitle = titleForItem(item); + QAction *elementAction = contextMenu.addAction(itemTitle, this, + SLOT(contextMenuElementSelected())); + + if (inspector()->selectedItems().contains(item)) { + QFont boldFont = elementAction->font(); + boldFont.setBold(true); + elementAction->setFont(boldFont); + } + + elementAction->setData(i); + + if (shortcutKey <= Qt::Key_9) { + elementAction->setShortcut(QKeySequence(shortcutKey)); + shortcutKey++; + } + + ++i; + } + // add root item separately + // QString itemTitle = QString(tr("%1")).arg(titleForItem(view()->currentRootItem())); + // contextMenu.addAction(itemTitle, this, SLOT(contextMenuElementSelected())); + // m_contextMenuItemList.append(view()->currentRootItem()); + + contextMenu.exec(globalPos); + m_contextMenuItemList.clear(); +} + +void LiveSelectionTool::contextMenuElementSelected() +{ + QAction *senderAction = static_cast(sender()); + int itemListIndex = senderAction->data().toInt(); + if (itemListIndex >= 0 && itemListIndex < m_contextMenuItemList.length()) { + + QPointF updatePt(0, 0); + QGraphicsItem *item = m_contextMenuItemList.at(itemListIndex); + m_singleSelectionManipulator.begin(updatePt); + m_singleSelectionManipulator.select(LiveSingleSelectionManipulator::InvertSelection, + QList() << item, + false); + m_singleSelectionManipulator.end(updatePt); + } +} + +void LiveSelectionTool::contextMenuElementHovered(QAction *action) +{ + int itemListIndex = action->data().toInt(); + if (itemListIndex >= 0 && itemListIndex < m_contextMenuItemList.length()) { + QGraphicsObject *item = m_contextMenuItemList.at(itemListIndex)->toGraphicsObject(); + QDeclarativeViewInspectorPrivate::get(inspector())->highlight(item); + } +} + +void LiveSelectionTool::mouseMoveEvent(QMouseEvent *event) +{ + if (m_singleSelectionManipulator.isActive()) { + QPointF mouseMovementVector = m_singleSelectionManipulator.beginPoint() - event->pos(); + + if ((mouseMovementVector.toPoint().manhattanLength() > Constants::DragStartDistance) + && (m_mousePressTimer.elapsed() > Constants::DragStartTime)) + { + m_singleSelectionManipulator.end(event->pos()); + //view()->changeToMoveTool(m_singleSelectionManipulator.beginPoint()); + return; + } + } else if (m_rubberbandSelectionManipulator.isActive()) { + QPointF mouseMovementVector = m_rubberbandSelectionManipulator.beginPoint() - event->pos(); + + if ((mouseMovementVector.toPoint().manhattanLength() > Constants::DragStartDistance) + && (m_mousePressTimer.elapsed() > Constants::DragStartTime)) { + m_rubberbandSelectionManipulator.update(event->pos()); + + if (event->modifiers().testFlag(Qt::ControlModifier)) + m_rubberbandSelectionManipulator.select( + LiveRubberBandSelectionManipulator::RemoveFromSelection); + else if (event->modifiers().testFlag(Qt::ShiftModifier)) + m_rubberbandSelectionManipulator.select( + LiveRubberBandSelectionManipulator::AddToSelection); + else + m_rubberbandSelectionManipulator.select( + LiveRubberBandSelectionManipulator::ReplaceSelection); + } + } +} + +void LiveSelectionTool::hoverMoveEvent(QMouseEvent * event) +{ +// ### commented out until move tool is re-enabled +// QList itemList = view()->items(event->pos()); +// if (!itemList.isEmpty() && !m_rubberbandSelectionMode) { +// +// foreach (QGraphicsItem *item, itemList) { +// if (item->type() == Constants::ResizeHandleItemType) { +// ResizeHandleItem* resizeHandle = ResizeHandleItem::fromGraphicsItem(item); +// if (resizeHandle) +// view()->changeTool(Constants::ResizeToolMode); +// return; +// } +// } +// if (topSelectedItemIsMovable(itemList)) +// view()->changeTool(Constants::MoveToolMode); +// } + QDeclarativeViewInspectorPrivate *inspectorPrivate + = QDeclarativeViewInspectorPrivate::get(inspector()); + + QList selectableItemList = inspectorPrivate->selectableItems(event->pos()); + if (!selectableItemList.isEmpty()) { + QGraphicsObject *item = selectableItemList.first()->toGraphicsObject(); + if (item) + QDeclarativeViewInspectorPrivate::get(inspector())->highlight(item); + + return; + } + + QDeclarativeViewInspectorPrivate::get(inspector())->clearHighlight(); +} + +void LiveSelectionTool::mouseReleaseEvent(QMouseEvent *event) +{ + if (m_singleSelectionManipulator.isActive()) { + m_singleSelectionManipulator.end(event->pos()); + } + else if (m_rubberbandSelectionManipulator.isActive()) { + + QPointF mouseMovementVector = m_rubberbandSelectionManipulator.beginPoint() - event->pos(); + if (mouseMovementVector.toPoint().manhattanLength() < Constants::DragStartDistance) { + m_singleSelectionManipulator.begin(event->pos()); + + if (event->modifiers().testFlag(Qt::ControlModifier)) + m_singleSelectionManipulator.select(LiveSingleSelectionManipulator::RemoveFromSelection, + m_selectOnlyContentItems); + else if (event->modifiers().testFlag(Qt::ShiftModifier)) + m_singleSelectionManipulator.select(LiveSingleSelectionManipulator::AddToSelection, + m_selectOnlyContentItems); + else + m_singleSelectionManipulator.select(LiveSingleSelectionManipulator::InvertSelection, + m_selectOnlyContentItems); + + m_singleSelectionManipulator.end(event->pos()); + } else { + m_rubberbandSelectionManipulator.update(event->pos()); + + if (event->modifiers().testFlag(Qt::ControlModifier)) + m_rubberbandSelectionManipulator.select( + LiveRubberBandSelectionManipulator::RemoveFromSelection); + else if (event->modifiers().testFlag(Qt::ShiftModifier)) + m_rubberbandSelectionManipulator.select( + LiveRubberBandSelectionManipulator::AddToSelection); + else + m_rubberbandSelectionManipulator.select( + LiveRubberBandSelectionManipulator::ReplaceSelection); + + m_rubberbandSelectionManipulator.end(); + } + } +} + +void LiveSelectionTool::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Left: + case Qt::Key_Right: + case Qt::Key_Up: + case Qt::Key_Down: + // disabled for now, cannot move stuff yet. + //view()->changeTool(Constants::MoveToolMode); + //view()->currentTool()->keyPressEvent(event); + break; + } +} + +void LiveSelectionTool::wheelEvent(QWheelEvent *event) +{ + if (event->orientation() == Qt::Horizontal || m_rubberbandSelectionMode) + return; + + QDeclarativeViewInspectorPrivate *inspectorPrivate + = QDeclarativeViewInspectorPrivate::get(inspector()); + QList itemList = inspectorPrivate->selectableItems(event->pos()); + + if (itemList.isEmpty()) + return; + + int selectedIdx = 0; + if (!inspector()->selectedItems().isEmpty()) { + selectedIdx = itemList.indexOf(inspector()->selectedItems().first()); + if (selectedIdx >= 0) { + if (event->delta() > 0) { + selectedIdx++; + if (selectedIdx == itemList.length()) + selectedIdx = 0; + } else if (event->delta() < 0) { + selectedIdx--; + if (selectedIdx == -1) + selectedIdx = itemList.length() - 1; + } + } else { + selectedIdx = 0; + } + } + + QPointF updatePt(0, 0); + m_singleSelectionManipulator.begin(updatePt); + m_singleSelectionManipulator.select(LiveSingleSelectionManipulator::ReplaceSelection, + QList() << itemList.at(selectedIdx), + false); + m_singleSelectionManipulator.end(updatePt); + +} + +void LiveSelectionTool::setSelectOnlyContentItems(bool selectOnlyContentItems) +{ + m_selectOnlyContentItems = selectOnlyContentItems; +} + +void LiveSelectionTool::clear() +{ +#ifndef QT_NO_CURSOR + view()->setCursor(Qt::ArrowCursor); +#endif + m_rubberbandSelectionManipulator.clear(), + m_singleSelectionManipulator.clear(); + m_selectionIndicator.clear(); + //m_resizeIndicator.clear(); +} + +void LiveSelectionTool::selectedItemsChanged(const QList &itemList) +{ + foreach (const QWeakPointer &obj, m_selectedItemList) { + if (!obj.isNull()) { + disconnect(obj.data(), SIGNAL(xChanged()), this, SLOT(repaintBoundingRects())); + disconnect(obj.data(), SIGNAL(yChanged()), this, SLOT(repaintBoundingRects())); + disconnect(obj.data(), SIGNAL(widthChanged()), this, SLOT(repaintBoundingRects())); + disconnect(obj.data(), SIGNAL(heightChanged()), this, SLOT(repaintBoundingRects())); + disconnect(obj.data(), SIGNAL(rotationChanged()), this, SLOT(repaintBoundingRects())); + } + } + + QList objects = toGraphicsObjectList(itemList); + m_selectedItemList.clear(); + + foreach (QGraphicsObject *obj, objects) { + m_selectedItemList.append(obj); + connect(obj, SIGNAL(xChanged()), this, SLOT(repaintBoundingRects())); + connect(obj, SIGNAL(yChanged()), this, SLOT(repaintBoundingRects())); + connect(obj, SIGNAL(widthChanged()), this, SLOT(repaintBoundingRects())); + connect(obj, SIGNAL(heightChanged()), this, SLOT(repaintBoundingRects())); + connect(obj, SIGNAL(rotationChanged()), this, SLOT(repaintBoundingRects())); + } + + m_selectionIndicator.setItems(m_selectedItemList); + //m_resizeIndicator.setItems(toGraphicsObjectList(itemList)); +} + +void LiveSelectionTool::repaintBoundingRects() +{ + m_selectionIndicator.setItems(m_selectedItemList); +} + +void LiveSelectionTool::selectUnderPoint(QMouseEvent *event) +{ + m_singleSelectionManipulator.begin(event->pos()); + + if (event->modifiers().testFlag(Qt::ControlModifier)) + m_singleSelectionManipulator.select(LiveSingleSelectionManipulator::RemoveFromSelection, + m_selectOnlyContentItems); + else if (event->modifiers().testFlag(Qt::ShiftModifier)) + m_singleSelectionManipulator.select(LiveSingleSelectionManipulator::AddToSelection, + m_selectOnlyContentItems); + else + m_singleSelectionManipulator.select(LiveSingleSelectionManipulator::InvertSelection, + m_selectOnlyContentItems); + + m_singleSelectionManipulator.end(event->pos()); +} + +} // namespace QmlJSDebugger diff --git a/src/plugins/qmltooling/qmldbg_inspector/editor/liveselectiontool.h b/src/plugins/qmltooling/qmldbg_inspector/editor/liveselectiontool.h new file mode 100644 index 00000000..eb3c63df --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_inspector/editor/liveselectiontool.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef LIVESELECTIONTOOL_H +#define LIVESELECTIONTOOL_H + +#include "abstractliveedittool.h" +#include "liverubberbandselectionmanipulator.h" +#include "livesingleselectionmanipulator.h" +#include "liveselectionindicator.h" + +#include +#include + +QT_FORWARD_DECLARE_CLASS(QGraphicsItem) +QT_FORWARD_DECLARE_CLASS(QMouseEvent) +QT_FORWARD_DECLARE_CLASS(QKeyEvent) +QT_FORWARD_DECLARE_CLASS(QAction) + +namespace QmlJSDebugger { + +class LiveSelectionTool : public AbstractLiveEditTool +{ + Q_OBJECT + +public: + LiveSelectionTool(QDeclarativeViewInspector* editorView); + ~LiveSelectionTool(); + + void mousePressEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void mouseDoubleClickEvent(QMouseEvent *) {} + void hoverMoveEvent(QMouseEvent *event); + void keyPressEvent(QKeyEvent *event); + void keyReleaseEvent(QKeyEvent *) {} + void wheelEvent(QWheelEvent *event); + + void itemsAboutToRemoved(const QList &) {} +// QVariant itemChange(const QList &itemList, +// QGraphicsItem::GraphicsItemChange change, +// const QVariant &value ); + +// void update(); + + void clear(); + + void selectedItemsChanged(const QList &itemList); + + void selectUnderPoint(QMouseEvent *event); + + void setSelectOnlyContentItems(bool selectOnlyContentItems); + + void setRubberbandSelectionMode(bool value); + +private slots: + void contextMenuElementSelected(); + void contextMenuElementHovered(QAction *action); + void repaintBoundingRects(); + +private: + void createContextMenu(const QList &itemList, QPoint globalPos); + LiveSingleSelectionManipulator::SelectionType getSelectionType(Qt::KeyboardModifiers modifiers); + bool alreadySelected(const QList &itemList) const; + +private: + bool m_rubberbandSelectionMode; + LiveRubberBandSelectionManipulator m_rubberbandSelectionManipulator; + LiveSingleSelectionManipulator m_singleSelectionManipulator; + LiveSelectionIndicator m_selectionIndicator; + //ResizeIndicator m_resizeIndicator; + QTime m_mousePressTimer; + bool m_selectOnlyContentItems; + + QList > m_selectedItemList; + + QList m_contextMenuItemList; +}; + +} // namespace QmlJSDebugger + +#endif // LIVESELECTIONTOOL_H diff --git a/src/plugins/qmltooling/qmldbg_inspector/editor/livesingleselectionmanipulator.cpp b/src/plugins/qmltooling/qmldbg_inspector/editor/livesingleselectionmanipulator.cpp new file mode 100644 index 00000000..67583943 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_inspector/editor/livesingleselectionmanipulator.cpp @@ -0,0 +1,151 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "livesingleselectionmanipulator.h" + +#include "../qdeclarativeviewinspector_p.h" + +#include + +namespace QmlJSDebugger { + +LiveSingleSelectionManipulator::LiveSingleSelectionManipulator(QDeclarativeViewInspector *editorView) + : m_editorView(editorView), + m_isActive(false) +{ +} + + +void LiveSingleSelectionManipulator::begin(const QPointF &beginPoint) +{ + m_beginPoint = beginPoint; + m_isActive = true; + m_oldSelectionList = QDeclarativeViewInspectorPrivate::get(m_editorView)->selectedItems(); +} + +void LiveSingleSelectionManipulator::update(const QPointF &/*updatePoint*/) +{ + m_oldSelectionList.clear(); +} + +void LiveSingleSelectionManipulator::clear() +{ + m_beginPoint = QPointF(); + m_oldSelectionList.clear(); +} + + +void LiveSingleSelectionManipulator::end(const QPointF &/*updatePoint*/) +{ + m_oldSelectionList.clear(); + m_isActive = false; +} + +void LiveSingleSelectionManipulator::select(SelectionType selectionType, + const QList &items, + bool /*selectOnlyContentItems*/) +{ + QGraphicsItem *selectedItem = 0; + + foreach (QGraphicsItem* item, items) + { + //FormEditorItem *formEditorItem = FormEditorItem::fromQGraphicsItem(item); + if (item + /*&& !formEditorItem->qmlItemNode().isRootNode() + && (formEditorItem->qmlItemNode().hasShowContent() || !selectOnlyContentItems)*/) + { + selectedItem = item; + break; + } + } + + QList resultList; + + switch (selectionType) { + case AddToSelection: { + resultList.append(m_oldSelectionList); + if (selectedItem && !m_oldSelectionList.contains(selectedItem)) + resultList.append(selectedItem); + } + break; + case ReplaceSelection: { + if (selectedItem) + resultList.append(selectedItem); + } + break; + case RemoveFromSelection: { + resultList.append(m_oldSelectionList); + if (selectedItem) + resultList.removeAll(selectedItem); + } + break; + case InvertSelection: { + if (selectedItem + && !m_oldSelectionList.contains(selectedItem)) + { + resultList.append(selectedItem); + } + } + } + + m_editorView->setSelectedItems(resultList); +} + +void LiveSingleSelectionManipulator::select(SelectionType selectionType, bool selectOnlyContentItems) +{ + QDeclarativeViewInspectorPrivate *inspectorPrivate = + QDeclarativeViewInspectorPrivate::get(m_editorView); + QList itemList = inspectorPrivate->selectableItems(m_beginPoint); + select(selectionType, itemList, selectOnlyContentItems); +} + + +bool LiveSingleSelectionManipulator::isActive() const +{ + return m_isActive; +} + +QPointF LiveSingleSelectionManipulator::beginPoint() const +{ + return m_beginPoint; +} + +} // namespace QmlJSDebugger diff --git a/src/plugins/qmltooling/qmldbg_inspector/editor/livesingleselectionmanipulator.h b/src/plugins/qmltooling/qmldbg_inspector/editor/livesingleselectionmanipulator.h new file mode 100644 index 00000000..256a6ead --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_inspector/editor/livesingleselectionmanipulator.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef LIVESINGLESELECTIONMANIPULATOR_H +#define LIVESINGLESELECTIONMANIPULATOR_H + +#include +#include + +QT_FORWARD_DECLARE_CLASS(QGraphicsItem) + +namespace QmlJSDebugger { + +class QDeclarativeViewInspector; + +class LiveSingleSelectionManipulator +{ +public: + LiveSingleSelectionManipulator(QDeclarativeViewInspector *editorView); + + 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, const QList &items, + bool selectOnlyContentItems); + void select(SelectionType selectionType, bool selectOnlyContentItems); + + void clear(); + + QPointF beginPoint() const; + + bool isActive() const; + +private: + QList m_oldSelectionList; + QPointF m_beginPoint; + QDeclarativeViewInspector *m_editorView; + bool m_isActive; +}; + +} // namespace QmlJSDebugger + +#endif // LIVESINGLESELECTIONMANIPULATOR_H diff --git a/src/plugins/qmltooling/qmldbg_inspector/editor/qmltoolbar.cpp b/src/plugins/qmltooling/qmldbg_inspector/editor/qmltoolbar.cpp new file mode 100644 index 00000000..ead0e166 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_inspector/editor/qmltoolbar.cpp @@ -0,0 +1,328 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmltoolbar.h" +#include "toolbarcolorbox.h" + +#include +#include +#include +#include + +#include + +namespace QmlJSDebugger { + +QmlToolBar::QmlToolBar(QWidget *parent) + : QToolBar(parent) + , m_emitSignals(true) + , m_paused(false) + , m_animationSpeed(1.0f) + , ui(new Ui) +{ + ui->playIcon = QIcon(QLatin1String(":/qml/images/play-24.png")); + ui->pauseIcon = QIcon(QLatin1String(":/qml/images/pause-24.png")); + + ui->designmode = new QAction(QIcon(QLatin1String(":/qml/images/inspectormode-24.png")), + tr("Inspector Mode"), this); + ui->play = new QAction(ui->pauseIcon, tr("Play/Pause Animations"), this); + ui->select = new QAction(QIcon(QLatin1String(":/qml/images/select-24.png")), tr("Select"), this); + ui->selectMarquee = new QAction(QIcon(QLatin1String(":/qml/images/select-marquee-24.png")), + tr("Select (Marquee)"), this); + ui->zoom = new QAction(QIcon(QLatin1String(":/qml/images/zoom-24.png")), tr("Zoom"), this); + ui->colorPicker = new QAction(QIcon(QLatin1String(":/qml/images/color-picker-24.png")), + tr("Color Picker"), this); + ui->toQml = new QAction(QIcon(QLatin1String(":/qml/images/to-qml-24.png")), + tr("Apply Changes to QML Viewer"), this); + ui->fromQml = new QAction(QIcon(QLatin1String(":/qml/images/from-qml-24.png")), + tr("Apply Changes to Document"), this); + ui->designmode->setCheckable(true); + ui->designmode->setChecked(false); + + ui->play->setCheckable(false); + ui->select->setCheckable(true); + ui->selectMarquee->setCheckable(true); + ui->zoom->setCheckable(true); + ui->colorPicker->setCheckable(true); + + setWindowTitle(tr("Tools")); + + addAction(ui->designmode); + addAction(ui->play); + addSeparator(); + + addAction(ui->select); + // disabled because multi selection does not do anything useful without design mode + //addAction(ui->selectMarquee); + addSeparator(); + addAction(ui->zoom); + addAction(ui->colorPicker); + //addAction(ui->fromQml); + + ui->colorBox = new ToolBarColorBox(this); + ui->colorBox->setMinimumSize(24, 24); + ui->colorBox->setMaximumSize(28, 28); + ui->colorBox->setColor(Qt::black); + addWidget(ui->colorBox); + + setWindowFlags(Qt::Tool); + + QMenu *playSpeedMenu = new QMenu(this); + ui->playSpeedMenuActions = new QActionGroup(this); + ui->playSpeedMenuActions->setExclusive(true); + + QAction *speedAction = playSpeedMenu->addAction(tr("1x"), this, SLOT(changeAnimationSpeed())); + speedAction->setCheckable(true); + speedAction->setChecked(true); + speedAction->setData(1.0f); + ui->playSpeedMenuActions->addAction(speedAction); + + speedAction = playSpeedMenu->addAction(tr("0.5x"), this, SLOT(changeAnimationSpeed())); + speedAction->setCheckable(true); + speedAction->setData(2.0f); + ui->playSpeedMenuActions->addAction(speedAction); + + speedAction = playSpeedMenu->addAction(tr("0.25x"), this, SLOT(changeAnimationSpeed())); + speedAction->setCheckable(true); + speedAction->setData(4.0f); + ui->playSpeedMenuActions->addAction(speedAction); + + speedAction = playSpeedMenu->addAction(tr("0.125x"), this, SLOT(changeAnimationSpeed())); + speedAction->setCheckable(true); + speedAction->setData(8.0f); + ui->playSpeedMenuActions->addAction(speedAction); + + speedAction = playSpeedMenu->addAction(tr("0.1x"), this, SLOT(changeAnimationSpeed())); + speedAction->setCheckable(true); + speedAction->setData(10.0f); + ui->playSpeedMenuActions->addAction(speedAction); + + ui->play->setMenu(playSpeedMenu); + + connect(ui->designmode, SIGNAL(toggled(bool)), SLOT(setDesignModeBehaviorOnClick(bool))); + + connect(ui->colorPicker, SIGNAL(triggered()), SLOT(activateColorPickerOnClick())); + + connect(ui->play, SIGNAL(triggered()), SLOT(activatePlayOnClick())); + + connect(ui->zoom, SIGNAL(triggered()), SLOT(activateZoomOnClick())); + connect(ui->colorPicker, SIGNAL(triggered()), SLOT(activateColorPickerOnClick())); + connect(ui->select, SIGNAL(triggered()), SLOT(activateSelectToolOnClick())); + connect(ui->selectMarquee, SIGNAL(triggered()), SLOT(activateMarqueeSelectToolOnClick())); + + connect(ui->toQml, SIGNAL(triggered()), SLOT(activateToQml())); + connect(ui->fromQml, SIGNAL(triggered()), SLOT(activateFromQml())); +} + +QmlToolBar::~QmlToolBar() +{ + delete ui; +} + +void QmlToolBar::activateColorPicker() +{ + m_emitSignals = false; + activateColorPickerOnClick(); + m_emitSignals = true; +} + +void QmlToolBar::activateSelectTool() +{ + m_emitSignals = false; + activateSelectToolOnClick(); + m_emitSignals = true; +} + +void QmlToolBar::activateMarqueeSelectTool() +{ + m_emitSignals = false; + activateMarqueeSelectToolOnClick(); + m_emitSignals = true; +} + +void QmlToolBar::activateZoom() +{ + m_emitSignals = false; + activateZoomOnClick(); + m_emitSignals = true; +} + +void QmlToolBar::setAnimationSpeed(qreal slowDownFactor) +{ + if (m_animationSpeed == slowDownFactor) + return; + + m_emitSignals = false; + m_animationSpeed = slowDownFactor; + + foreach (QAction *action, ui->playSpeedMenuActions->actions()) { + if (action->data().toReal() == slowDownFactor) { + action->setChecked(true); + break; + } + } + + m_emitSignals = true; +} + +void QmlToolBar::setAnimationPaused(bool paused) +{ + if (m_paused == paused) + return; + + m_paused = paused; + updatePlayAction(); +} + +void QmlToolBar::changeAnimationSpeed() +{ + QAction *action = qobject_cast(sender()); + m_animationSpeed = action->data().toReal(); + emit animationSpeedChanged(m_animationSpeed); +} + +void QmlToolBar::setDesignModeBehavior(bool inDesignMode) +{ + m_emitSignals = false; + ui->designmode->setChecked(inDesignMode); + setDesignModeBehaviorOnClick(inDesignMode); + m_emitSignals = true; +} + +void QmlToolBar::setDesignModeBehaviorOnClick(bool checked) +{ + ui->select->setEnabled(checked); + ui->selectMarquee->setEnabled(checked); + ui->zoom->setEnabled(checked); + ui->colorPicker->setEnabled(checked); + ui->toQml->setEnabled(checked); + ui->fromQml->setEnabled(checked); + + if (m_emitSignals) + emit designModeBehaviorChanged(checked); +} + +void QmlToolBar::setColorBoxColor(const QColor &color) +{ + ui->colorBox->setColor(color); +} + +void QmlToolBar::activatePlayOnClick() +{ + m_paused = !m_paused; + emit animationPausedChanged(m_paused); + updatePlayAction(); +} + +void QmlToolBar::updatePlayAction() +{ + ui->play->setIcon(m_paused ? ui->playIcon : ui->pauseIcon); +} + +void QmlToolBar::activateColorPickerOnClick() +{ + ui->zoom->setChecked(false); + ui->select->setChecked(false); + ui->selectMarquee->setChecked(false); + + ui->colorPicker->setChecked(true); + if (m_activeTool != Constants::ColorPickerMode) { + m_activeTool = Constants::ColorPickerMode; + if (m_emitSignals) + emit colorPickerSelected(); + } +} + +void QmlToolBar::activateSelectToolOnClick() +{ + ui->zoom->setChecked(false); + ui->selectMarquee->setChecked(false); + ui->colorPicker->setChecked(false); + + ui->select->setChecked(true); + if (m_activeTool != Constants::SelectionToolMode) { + m_activeTool = Constants::SelectionToolMode; + if (m_emitSignals) + emit selectToolSelected(); + } +} + +void QmlToolBar::activateMarqueeSelectToolOnClick() +{ + ui->zoom->setChecked(false); + ui->select->setChecked(false); + ui->colorPicker->setChecked(false); + + ui->selectMarquee->setChecked(true); + if (m_activeTool != Constants::MarqueeSelectionToolMode) { + m_activeTool = Constants::MarqueeSelectionToolMode; + if (m_emitSignals) + emit marqueeSelectToolSelected(); + } +} + +void QmlToolBar::activateZoomOnClick() +{ + ui->select->setChecked(false); + ui->selectMarquee->setChecked(false); + ui->colorPicker->setChecked(false); + + ui->zoom->setChecked(true); + if (m_activeTool != Constants::ZoomMode) { + m_activeTool = Constants::ZoomMode; + if (m_emitSignals) + emit zoomToolSelected(); + } +} + +void QmlToolBar::activateFromQml() +{ + if (m_emitSignals) + emit applyChangesFromQmlFileSelected(); +} + +void QmlToolBar::activateToQml() +{ + if (m_emitSignals) + emit applyChangesToQmlFileSelected(); +} + +} // namespace QmlJSDebugger diff --git a/src/plugins/qmltooling/qmldbg_inspector/editor/qmltoolbar.h b/src/plugins/qmltooling/qmldbg_inspector/editor/qmltoolbar.h new file mode 100644 index 00000000..3833ff28 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_inspector/editor/qmltoolbar.h @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMLTOOLBAR_H +#define QMLTOOLBAR_H + +#include +#include + +#include "../qmlinspectorconstants.h" + +QT_FORWARD_DECLARE_CLASS(QActionGroup) + +namespace QmlJSDebugger { + +class ToolBarColorBox; + +class QmlToolBar : public QToolBar +{ + Q_OBJECT + +public: + explicit QmlToolBar(QWidget *parent = 0); + ~QmlToolBar(); + +public slots: + void setDesignModeBehavior(bool inDesignMode); + void setColorBoxColor(const QColor &color); + void activateColorPicker(); + void activateSelectTool(); + void activateMarqueeSelectTool(); + void activateZoom(); + + void setAnimationSpeed(qreal slowDownFactor); + void setAnimationPaused(bool paused); + +signals: + void animationSpeedChanged(qreal factor); + void animationPausedChanged(bool paused); + + void designModeBehaviorChanged(bool inDesignMode); + void colorPickerSelected(); + void selectToolSelected(); + void marqueeSelectToolSelected(); + void zoomToolSelected(); + + void applyChangesToQmlFileSelected(); + void applyChangesFromQmlFileSelected(); + +private slots: + void setDesignModeBehaviorOnClick(bool inDesignMode); + void activatePlayOnClick(); + void activateColorPickerOnClick(); + void activateSelectToolOnClick(); + void activateMarqueeSelectToolOnClick(); + void activateZoomOnClick(); + + void activateFromQml(); + void activateToQml(); + + void changeAnimationSpeed(); + + void updatePlayAction(); + +private: + class Ui { + public: + QAction *designmode; + QAction *play; + QAction *select; + QAction *selectMarquee; + QAction *zoom; + QAction *colorPicker; + QAction *toQml; + QAction *fromQml; + QIcon playIcon; + QIcon pauseIcon; + ToolBarColorBox *colorBox; + + QActionGroup *playSpeedMenuActions; + }; + + bool m_emitSignals; + bool m_paused; + qreal m_animationSpeed; + + Constants::DesignTool m_activeTool; + + Ui *ui; +}; + +} + +#endif // QMLTOOLBAR_H diff --git a/src/plugins/qmltooling/qmldbg_inspector/editor/subcomponentmasklayeritem.cpp b/src/plugins/qmltooling/qmldbg_inspector/editor/subcomponentmasklayeritem.cpp new file mode 100644 index 00000000..62b2799b --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_inspector/editor/subcomponentmasklayeritem.cpp @@ -0,0 +1,130 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "subcomponentmasklayeritem.h" + +#include "../qmlinspectorconstants.h" +#include "../qdeclarativeviewinspector.h" + +#include + +namespace QmlJSDebugger { + +SubcomponentMaskLayerItem::SubcomponentMaskLayerItem(QDeclarativeViewInspector *inspector, + QGraphicsItem *parentItem) : + QGraphicsPolygonItem(parentItem), + m_inspector(inspector), + m_currentItem(0), + m_borderRect(new QGraphicsRectItem(this)) +{ + m_borderRect->setRect(0,0,0,0); + m_borderRect->setPen(QPen(QColor(60, 60, 60), 1)); + m_borderRect->setData(Constants::EditorItemDataKey, QVariant(true)); + + setBrush(QBrush(QColor(160,160,160))); + setPen(Qt::NoPen); +} + +int SubcomponentMaskLayerItem::type() const +{ + return Constants::EditorItemType; +} + +static QRectF resizeRect(const QRectF &newRect, const QRectF &oldRect) +{ + QRectF result = newRect; + if (oldRect.left() < newRect.left()) + result.setLeft(oldRect.left()); + + if (oldRect.top() < newRect.top()) + result.setTop(oldRect.top()); + + if (oldRect.right() > newRect.right()) + result.setRight(oldRect.right()); + + if (oldRect.bottom() > newRect.bottom()) + result.setBottom(oldRect.bottom()); + + return result; +} + +static QPolygonF regionToPolygon(const QRegion ®ion) +{ + QPainterPath path; + foreach (const QRect &rect, region.rects()) + path.addRect(rect); + return path.toFillPolygon(); +} + +void SubcomponentMaskLayerItem::setCurrentItem(QGraphicsItem *item) +{ + QGraphicsItem *prevItem = m_currentItem; + m_currentItem = item; + + if (!m_currentItem) + return; + + QRect viewRect = m_inspector->declarativeView()->rect(); + viewRect = m_inspector->declarativeView()->mapToScene(viewRect).boundingRect().toRect(); + + QRectF itemRect = item->boundingRect() | item->childrenBoundingRect(); + itemRect = item->mapRectToScene(itemRect); + + // if updating the same item as before, resize the rectangle only bigger, not smaller. + if (prevItem == item && prevItem != 0) { + m_itemPolyRect = resizeRect(itemRect, m_itemPolyRect); + } else { + m_itemPolyRect = itemRect; + } + QRectF borderRect = m_itemPolyRect; + borderRect.adjust(-1, -1, 1, 1); + m_borderRect->setRect(borderRect); + + const QRegion externalRegion = QRegion(viewRect).subtracted(m_itemPolyRect.toRect()); + setPolygon(regionToPolygon(externalRegion)); +} + +QGraphicsItem *SubcomponentMaskLayerItem::currentItem() const +{ + return m_currentItem; +} + +} // namespace QmlJSDebugger diff --git a/src/plugins/qmltooling/qmldbg_inspector/editor/subcomponentmasklayeritem.h b/src/plugins/qmltooling/qmldbg_inspector/editor/subcomponentmasklayeritem.h new file mode 100644 index 00000000..53864cc0 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_inspector/editor/subcomponentmasklayeritem.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SUBCOMPONENTMASKLAYERITEM_H +#define SUBCOMPONENTMASKLAYERITEM_H + +#include + +namespace QmlJSDebugger { + +class QDeclarativeViewInspector; + +class SubcomponentMaskLayerItem : public QGraphicsPolygonItem +{ +public: + explicit SubcomponentMaskLayerItem(QDeclarativeViewInspector *inspector, + QGraphicsItem *parentItem = 0); + int type() const; + void setCurrentItem(QGraphicsItem *item); + void setBoundingBox(const QRectF &boundingBox); + QGraphicsItem *currentItem() const; + QRectF itemRect() const; + +private: + QDeclarativeViewInspector *m_inspector; + QGraphicsItem *m_currentItem; + QGraphicsRectItem *m_borderRect; + QRectF m_itemPolyRect; +}; + +} // namespace QmlJSDebugger + +#endif // SUBCOMPONENTMASKLAYERITEM_H diff --git a/src/plugins/qmltooling/qmldbg_inspector/editor/toolbarcolorbox.cpp b/src/plugins/qmltooling/qmldbg_inspector/editor/toolbarcolorbox.cpp new file mode 100644 index 00000000..6c56acd0 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_inspector/editor/toolbarcolorbox.cpp @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "toolbarcolorbox.h" + +#include "../qmlinspectorconstants.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace QmlJSDebugger { + +ToolBarColorBox::ToolBarColorBox(QWidget *parent) : + QLabel(parent) +{ + m_copyHexColor = new QAction(QIcon(QLatin1String(":/qml/images/color-picker-hicontrast.png")), + tr("Copy Color"), this); + connect(m_copyHexColor, SIGNAL(triggered()), SLOT(copyColorToClipboard())); + setScaledContents(false); +} + +void ToolBarColorBox::setColor(const QColor &color) +{ + m_color = color; + + QPixmap pix = createDragPixmap(width()); + setPixmap(pix); + update(); +} + +void ToolBarColorBox::mousePressEvent(QMouseEvent *event) +{ + m_dragBeginPoint = event->pos(); + m_dragStarted = false; +} + +void ToolBarColorBox::mouseMoveEvent(QMouseEvent *event) +{ + + if (event->buttons() & Qt::LeftButton + && (QPoint(event->pos() - m_dragBeginPoint).manhattanLength() + > Constants::DragStartDistance) + && !m_dragStarted) + { + m_dragStarted = true; + QDrag *drag = new QDrag(this); + QMimeData *mimeData = new QMimeData; + + mimeData->setText(m_color.name()); + drag->setMimeData(mimeData); + drag->setPixmap(createDragPixmap()); + + drag->exec(); + } +} + +QPixmap ToolBarColorBox::createDragPixmap(int size) const +{ + QPixmap pix(size, size); + QPainter p(&pix); + + QColor borderColor1 = QColor(143, 143 ,143); + QColor borderColor2 = QColor(43, 43, 43); + + p.setBrush(QBrush(m_color)); + p.setPen(QPen(QBrush(borderColor2),1)); + + p.fillRect(0, 0, size, size, borderColor1); + p.drawRect(1,1, size - 3, size - 3); + return pix; +} + +void ToolBarColorBox::contextMenuEvent(QContextMenuEvent *ev) +{ + QMenu contextMenu; + contextMenu.addAction(m_copyHexColor); + contextMenu.exec(ev->globalPos()); +} + +void ToolBarColorBox::copyColorToClipboard() +{ + QClipboard *clipboard = QApplication::clipboard(); + clipboard->setText(m_color.name()); +} + +} // namespace QmlJSDebugger diff --git a/src/plugins/qmltooling/qmldbg_inspector/editor/toolbarcolorbox.h b/src/plugins/qmltooling/qmldbg_inspector/editor/toolbarcolorbox.h new file mode 100644 index 00000000..bc39fb4a --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_inspector/editor/toolbarcolorbox.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TOOLBARCOLORBOX_H +#define TOOLBARCOLORBOX_H + +#include +#include +#include + +QT_FORWARD_DECLARE_CLASS(QContextMenuEvent) +QT_FORWARD_DECLARE_CLASS(QAction) + +namespace QmlJSDebugger { + +class ToolBarColorBox : public QLabel +{ + Q_OBJECT + +public: + explicit ToolBarColorBox(QWidget *parent = 0); + void setColor(const QColor &color); + +protected: + void contextMenuEvent(QContextMenuEvent *ev); + void mousePressEvent(QMouseEvent *ev); + void mouseMoveEvent(QMouseEvent *ev); +private slots: + void copyColorToClipboard(); + +private: + QPixmap createDragPixmap(int size = 24) const; + +private: + bool m_dragStarted; + QPoint m_dragBeginPoint; + QAction *m_copyHexColor; + QColor m_color; +}; + +} // namespace QmlJSDebugger + +#endif // TOOLBARCOLORBOX_H diff --git a/src/plugins/qmltooling/qmldbg_inspector/editor/zoomtool.cpp b/src/plugins/qmltooling/qmldbg_inspector/editor/zoomtool.cpp new file mode 100644 index 00000000..3e41b3be --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_inspector/editor/zoomtool.cpp @@ -0,0 +1,330 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "zoomtool.h" + +#include "../qdeclarativeviewinspector_p.h" + +#include +#include +#include +#include +#include + +#include +#include + +namespace QmlJSDebugger { + +ZoomTool::ZoomTool(QDeclarativeViewInspector *view) : + AbstractLiveEditTool(view), + m_rubberbandManipulator(), + m_smoothZoomMultiplier(0.05f), + m_currentScale(1.0f) +{ + m_zoomTo100Action = new QAction(tr("Zoom to &100%"), this); + m_zoomInAction = new QAction(tr("Zoom In"), this); + m_zoomOutAction = new QAction(tr("Zoom Out"), this); + m_zoomInAction->setShortcut(QKeySequence(Qt::Key_Plus)); + m_zoomOutAction->setShortcut(QKeySequence(Qt::Key_Minus)); + + + LiveLayerItem *layerItem = QDeclarativeViewInspectorPrivate::get(view)->manipulatorLayer; + QGraphicsObject *layerObject = reinterpret_cast(layerItem); + m_rubberbandManipulator = new LiveRubberBandSelectionManipulator(layerObject, view); + + + connect(m_zoomTo100Action, SIGNAL(triggered()), SLOT(zoomTo100())); + connect(m_zoomInAction, SIGNAL(triggered()), SLOT(zoomIn())); + connect(m_zoomOutAction, SIGNAL(triggered()), SLOT(zoomOut())); +} + +ZoomTool::~ZoomTool() +{ + delete m_rubberbandManipulator; +} + +void ZoomTool::mousePressEvent(QMouseEvent *event) +{ + m_mousePos = event->pos(); + + QPointF scenePos = view()->mapToScene(event->pos()); + + if (event->buttons() & Qt::RightButton) { + QMenu contextMenu; + contextMenu.addAction(m_zoomTo100Action); + contextMenu.addSeparator(); + contextMenu.addAction(m_zoomInAction); + contextMenu.addAction(m_zoomOutAction); + contextMenu.exec(event->globalPos()); + } else if (event->buttons() & Qt::LeftButton) { + m_dragBeginPos = scenePos; + m_dragStarted = false; + } +} + +void ZoomTool::mouseMoveEvent(QMouseEvent *event) +{ + m_mousePos = event->pos(); + + QPointF scenePos = view()->mapToScene(event->pos()); + + if (event->buttons() & Qt::LeftButton + && (QPointF(scenePos - m_dragBeginPos).manhattanLength() + > Constants::DragStartDistance / 3) + && !m_dragStarted) + { + m_dragStarted = true; + m_rubberbandManipulator->begin(m_dragBeginPos); + return; + } + + if (m_dragStarted) + m_rubberbandManipulator->update(scenePos); + +} + +void ZoomTool::mouseReleaseEvent(QMouseEvent *event) +{ + m_mousePos = event->pos(); + QPointF scenePos = view()->mapToScene(event->pos()); + + if (m_dragStarted) { + m_rubberbandManipulator->end(); + + int x1 = qMin(scenePos.x(), m_rubberbandManipulator->beginPoint().x()); + int x2 = qMax(scenePos.x(), m_rubberbandManipulator->beginPoint().x()); + int y1 = qMin(scenePos.y(), m_rubberbandManipulator->beginPoint().y()); + int y2 = qMax(scenePos.y(), m_rubberbandManipulator->beginPoint().y()); + + QPointF scenePosTopLeft = QPoint(x1, y1); + QPointF scenePosBottomRight = QPoint(x2, y2); + + QRectF sceneArea(scenePosTopLeft, scenePosBottomRight); + + m_currentScale = qMin(view()->rect().width() / sceneArea.width(), + view()->rect().height() / sceneArea.height()); + + + QTransform transform; + transform.scale(m_currentScale, m_currentScale); + + view()->setTransform(transform); + view()->setSceneRect(sceneArea); + } else { + Qt::KeyboardModifier modifierKey = Qt::ControlModifier; +#ifdef Q_WS_MAC + modifierKey = Qt::AltModifier; +#endif + if (event->modifiers() & modifierKey) { + zoomOut(); + } else { + zoomIn(); + } + } +} + +void ZoomTool::zoomIn() +{ + m_currentScale = nextZoomScale(ZoomIn); + scaleView(view()->mapToScene(m_mousePos)); +} + +void ZoomTool::zoomOut() +{ + m_currentScale = nextZoomScale(ZoomOut); + scaleView(view()->mapToScene(m_mousePos)); +} + +void ZoomTool::mouseDoubleClickEvent(QMouseEvent *event) +{ + m_mousePos = event->pos(); +} + + +void ZoomTool::hoverMoveEvent(QMouseEvent *event) +{ + m_mousePos = event->pos(); +} + + +void ZoomTool::keyPressEvent(QKeyEvent * /*event*/) +{ +} + +void ZoomTool::wheelEvent(QWheelEvent *event) +{ + if (event->orientation() != Qt::Vertical) + return; + + Qt::KeyboardModifier smoothZoomModifier = Qt::ControlModifier; + if (event->modifiers() & smoothZoomModifier) { + int numDegrees = event->delta() / 8; + m_currentScale += m_smoothZoomMultiplier * (numDegrees / 15.0f); + + scaleView(view()->mapToScene(m_mousePos)); + + } else if (!event->modifiers()) { + if (event->delta() > 0) { + m_currentScale = nextZoomScale(ZoomIn); + } else if (event->delta() < 0) { + m_currentScale = nextZoomScale(ZoomOut); + } + scaleView(view()->mapToScene(m_mousePos)); + } +} + +void ZoomTool::keyReleaseEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Plus: + zoomIn(); + break; + case Qt::Key_Minus: + zoomOut(); + break; + case Qt::Key_1: + case Qt::Key_2: + case Qt::Key_3: + case Qt::Key_4: + case Qt::Key_5: + case Qt::Key_6: + case Qt::Key_7: + case Qt::Key_8: + case Qt::Key_9: + { + m_currentScale = ((event->key() - Qt::Key_0) * 1.0f); + scaleView(view()->mapToScene(m_mousePos)); // view()->mapToScene(view()->rect().center()) + break; + } + + default: + break; + } + +} + +void ZoomTool::clear() +{ +#ifndef QT_NO_CURSOR + view()->setCursor(Qt::ArrowCursor); +#endif +} + +void ZoomTool::scaleView(const QPointF ¢erPos) +{ + + QTransform transform; + transform.scale(m_currentScale, m_currentScale); + view()->setTransform(transform); + + QPointF adjustedCenterPos = centerPos; + QSize rectSize(view()->rect().width() / m_currentScale, + view()->rect().height() / m_currentScale); + + QRectF sceneRect; + if (qAbs(m_currentScale - 1.0f) < Constants::ZoomSnapDelta) { + adjustedCenterPos.rx() = rectSize.width() / 2; + adjustedCenterPos.ry() = rectSize.height() / 2; + } + + if (m_currentScale < 1.0f) { + adjustedCenterPos.rx() = rectSize.width() / 2; + adjustedCenterPos.ry() = rectSize.height() / 2; + sceneRect.setRect(view()->rect().width() / 2 -rectSize.width() / 2, + view()->rect().height() / 2 -rectSize.height() / 2, + rectSize.width(), + rectSize.height()); + } else { + sceneRect.setRect(adjustedCenterPos.x() - rectSize.width() / 2, + adjustedCenterPos.y() - rectSize.height() / 2, + rectSize.width(), + rectSize.height()); + } + + view()->setSceneRect(sceneRect); +} + +void ZoomTool::zoomTo100() +{ + m_currentScale = 1.0f; + scaleView(view()->mapToScene(view()->rect().center())); +} + +qreal ZoomTool::nextZoomScale(ZoomDirection direction) const +{ + static QList zoomScales = + QList() + << 0.125f + << 1.0f / 6.0f + << 0.25f + << 1.0f / 3.0f + << 0.5f + << 2.0f / 3.0f + << 1.0f + << 2.0f + << 3.0f + << 4.0f + << 5.0f + << 6.0f + << 7.0f + << 8.0f + << 12.0f + << 16.0f + << 32.0f + << 48.0f; + + if (direction == ZoomIn) { + for (int i = 0; i < zoomScales.length(); ++i) { + if (zoomScales[i] > m_currentScale || i == zoomScales.length() - 1) + return zoomScales[i]; + } + } else { + for (int i = zoomScales.length() - 1; i >= 0; --i) { + if (zoomScales[i] < m_currentScale || i == 0) + return zoomScales[i]; + } + } + + return 1.0f; +} + +} // namespace QmlJSDebugger diff --git a/src/plugins/qmltooling/qmldbg_inspector/editor/zoomtool.h b/src/plugins/qmltooling/qmldbg_inspector/editor/zoomtool.h new file mode 100644 index 00000000..d5c40553 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_inspector/editor/zoomtool.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ZOOMTOOL_H +#define ZOOMTOOL_H + +#include "abstractliveedittool.h" +#include "liverubberbandselectionmanipulator.h" + +QT_FORWARD_DECLARE_CLASS(QAction) + +namespace QmlJSDebugger { + +class ZoomTool : public AbstractLiveEditTool +{ + Q_OBJECT + +public: + enum ZoomDirection { + ZoomIn, + ZoomOut + }; + + explicit ZoomTool(QDeclarativeViewInspector *view); + + virtual ~ZoomTool(); + + void mousePressEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void mouseDoubleClickEvent(QMouseEvent *event); + + void hoverMoveEvent(QMouseEvent *event); + void wheelEvent(QWheelEvent *event); + + void keyPressEvent(QKeyEvent *event); + void keyReleaseEvent(QKeyEvent *keyEvent); + void itemsAboutToRemoved(const QList &) {} + + void clear(); + +protected: + void selectedItemsChanged(const QList &) {} + +private slots: + void zoomTo100(); + void zoomIn(); + void zoomOut(); + +private: + qreal nextZoomScale(ZoomDirection direction) const; + void scaleView(const QPointF ¢erPos); + +private: + bool m_dragStarted; + QPoint m_mousePos; // in view coords + QPointF m_dragBeginPos; + QAction *m_zoomTo100Action; + QAction *m_zoomInAction; + QAction *m_zoomOutAction; + LiveRubberBandSelectionManipulator *m_rubberbandManipulator; + + qreal m_smoothZoomMultiplier; + qreal m_currentScale; +}; + +} // namespace QmlJSDebugger + +#endif // ZOOMTOOL_H diff --git a/src/plugins/qmltooling/qmldbg_inspector/qdeclarativeinspectorplugin.cpp b/src/plugins/qmltooling/qmldbg_inspector/qdeclarativeinspectorplugin.cpp new file mode 100644 index 00000000..1b74caf7 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_inspector/qdeclarativeinspectorplugin.cpp @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativeinspectorplugin.h" + +#include "qdeclarativeviewinspector_p.h" + +#include +#include + +namespace QmlJSDebugger { + +QDeclarativeInspectorPlugin::QDeclarativeInspectorPlugin() : + m_inspector(0) +{ +} + +QDeclarativeInspectorPlugin::~QDeclarativeInspectorPlugin() +{ + delete m_inspector; +} + +void QDeclarativeInspectorPlugin::activate() +{ + QDeclarativeInspectorService *service = QDeclarativeInspectorService::instance(); + QList views = service->views(); + if (views.isEmpty()) + return; + + // TODO: Support multiple views + QDeclarativeView *view = service->views().at(0); + m_inspector = new QDeclarativeViewInspector(view, view); +} + +void QDeclarativeInspectorPlugin::deactivate() +{ + delete m_inspector; +} + +} // namespace QmlJSDebugger + +Q_EXPORT_PLUGIN2(declarativeinspector, QmlJSDebugger::QDeclarativeInspectorPlugin) diff --git a/src/plugins/qmltooling/qmldbg_inspector/qdeclarativeinspectorplugin.h b/src/plugins/qmltooling/qmldbg_inspector/qdeclarativeinspectorplugin.h new file mode 100644 index 00000000..a910edcb --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_inspector/qdeclarativeinspectorplugin.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEINSPECTORPLUGIN_H +#define QDECLARATIVEINSPECTORPLUGIN_H + +#include +#include + +namespace QmlJSDebugger { + +class AbstractViewInspector; + +class QDeclarativeInspectorPlugin : public QObject, public QDeclarativeInspectorInterface +{ + Q_OBJECT + Q_DISABLE_COPY(QDeclarativeInspectorPlugin) + Q_INTERFACES(QDeclarativeInspectorInterface) + +public: + QDeclarativeInspectorPlugin(); + ~QDeclarativeInspectorPlugin(); + + void activate(); + void deactivate(); + +private: + QPointer m_inspector; +}; + +} // namespace QmlJSDebugger + +#endif // QDECLARATIVEINSPECTORPLUGIN_H diff --git a/src/plugins/qmltooling/qmldbg_inspector/qdeclarativeinspectorprotocol.h b/src/plugins/qmltooling/qmldbg_inspector/qdeclarativeinspectorprotocol.h new file mode 100644 index 00000000..8de5a193 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_inspector/qdeclarativeinspectorprotocol.h @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEINSPECTORPROTOCOL_H +#define QDECLARATIVEINSPECTORPROTOCOL_H + +#include +#include +#include +#include + +namespace QmlJSDebugger { + +class InspectorProtocol : public QObject +{ + Q_OBJECT + Q_ENUMS(Message Tool) + +public: + enum Message { + AnimationSpeedChanged = 0, + AnimationPausedChanged = 19, // highest value + ChangeTool = 1, + ClearComponentCache = 2, + ColorChanged = 3, + CreateObject = 5, + CurrentObjectsChanged = 6, + DestroyObject = 7, + MoveObject = 8, + ObjectIdList = 9, + Reload = 10, + Reloaded = 11, + SetAnimationSpeed = 12, + SetAnimationPaused = 18, + SetCurrentObjects = 14, + SetDesignMode = 15, + ShowAppOnTop = 16, + ToolChanged = 17 + }; + + enum Tool { + ColorPickerTool, + SelectMarqueeTool, + SelectTool, + ZoomTool + }; + + static inline QString toString(Message message) + { + return QLatin1String(staticMetaObject.enumerator(0).valueToKey(message)); + } + + static inline QString toString(Tool tool) + { + return QLatin1String(staticMetaObject.enumerator(1).valueToKey(tool)); + } +}; + +inline QDataStream & operator<< (QDataStream &stream, InspectorProtocol::Message message) +{ + return stream << static_cast(message); +} + +inline QDataStream & operator>> (QDataStream &stream, InspectorProtocol::Message &message) +{ + quint32 i; + stream >> i; + message = static_cast(i); + return stream; +} + +inline QDebug operator<< (QDebug dbg, InspectorProtocol::Message message) +{ + dbg << InspectorProtocol::toString(message); + return dbg; +} + +inline QDataStream & operator<< (QDataStream &stream, InspectorProtocol::Tool tool) +{ + return stream << static_cast(tool); +} + +inline QDataStream & operator>> (QDataStream &stream, InspectorProtocol::Tool &tool) +{ + quint32 i; + stream >> i; + tool = static_cast(i); + return stream; +} + +inline QDebug operator<< (QDebug dbg, InspectorProtocol::Tool tool) +{ + dbg << InspectorProtocol::toString(tool); + return dbg; +} + +} // namespace QmlJSDebugger + +#endif // QDECLARATIVEINSPECTORPROTOCOL_H diff --git a/src/plugins/qmltooling/qmldbg_inspector/qdeclarativeviewinspector.cpp b/src/plugins/qmltooling/qmldbg_inspector/qdeclarativeviewinspector.cpp new file mode 100644 index 00000000..d8696c5f --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_inspector/qdeclarativeviewinspector.cpp @@ -0,0 +1,436 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativeviewinspector.h" +#include "qdeclarativeviewinspector_p.h" + +#include "editor/liveselectiontool.h" +#include "editor/zoomtool.h" +#include "editor/colorpickertool.h" +#include "editor/livelayeritem.h" +#include "editor/boundingrecthighlighter.h" + +#include +#include +#include +#include + +namespace QmlJSDebugger { + +QDeclarativeViewInspectorPrivate::QDeclarativeViewInspectorPrivate(QDeclarativeViewInspector *q) : + q(q) +{ +} + +QDeclarativeViewInspectorPrivate::~QDeclarativeViewInspectorPrivate() +{ +} + +QDeclarativeViewInspector::QDeclarativeViewInspector(QDeclarativeView *view, + QObject *parent) : + AbstractViewInspector(parent), + data(new QDeclarativeViewInspectorPrivate(this)) +{ + data->view = view; + data->manipulatorLayer = new LiveLayerItem(view->scene()); + data->selectionTool = new LiveSelectionTool(this); + data->zoomTool = new ZoomTool(this); + data->colorPickerTool = new ColorPickerTool(this); + data->boundingRectHighlighter = new BoundingRectHighlighter(this); + setCurrentTool(data->selectionTool); + + // to capture ChildRemoved event when viewport changes + data->view->installEventFilter(this); + + data->setViewport(data->view->viewport()); + + connect(data->view, SIGNAL(statusChanged(QDeclarativeView::Status)), + data.data(), SLOT(_q_onStatusChanged(QDeclarativeView::Status))); + + connect(data->colorPickerTool, SIGNAL(selectedColorChanged(QColor)), + SIGNAL(selectedColorChanged(QColor))); + connect(data->colorPickerTool, SIGNAL(selectedColorChanged(QColor)), + this, SLOT(sendColorChanged(QColor))); + + changeTool(InspectorProtocol::SelectTool); +} + +QDeclarativeViewInspector::~QDeclarativeViewInspector() +{ +} + +void QDeclarativeViewInspector::changeCurrentObjects(const QList &objects) +{ + QList items; + QList gfxObjects; + foreach (QObject *obj, objects) { + if (QDeclarativeItem *declarativeItem = qobject_cast(obj)) { + items << declarativeItem; + gfxObjects << declarativeItem; + } + } + if (designModeBehavior()) { + data->setSelectedItemsForTools(items); + data->clearHighlight(); + data->highlight(gfxObjects); + } +} + +void QDeclarativeViewInspector::reloadView() +{ + data->clearHighlight(); + emit reloadRequested(); +} + +void QDeclarativeViewInspector::changeTool(InspectorProtocol::Tool tool) +{ + switch (tool) { + case InspectorProtocol::ColorPickerTool: + data->changeToColorPickerTool(); + break; + case InspectorProtocol::SelectMarqueeTool: + data->changeToMarqueeSelectTool(); + break; + case InspectorProtocol::SelectTool: + data->changeToSingleSelectTool(); + break; + case InspectorProtocol::ZoomTool: + data->changeToZoomTool(); + break; + } +} + +AbstractLiveEditTool *QDeclarativeViewInspector::currentTool() const +{ + return static_cast(AbstractViewInspector::currentTool()); +} + +QDeclarativeEngine *QDeclarativeViewInspector::declarativeEngine() const +{ + return data->view->engine(); +} + +void QDeclarativeViewInspectorPrivate::setViewport(QWidget *widget) +{ + if (viewport.data() == widget) + return; + + if (viewport) + viewport.data()->removeEventFilter(q); + + viewport = widget; + if (viewport) { + // make sure we get mouse move events + viewport.data()->setMouseTracking(true); + viewport.data()->installEventFilter(q); + } +} + +void QDeclarativeViewInspectorPrivate::clearEditorItems() +{ + clearHighlight(); + setSelectedItems(QList()); +} + +bool QDeclarativeViewInspector::eventFilter(QObject *obj, QEvent *event) +{ + if (obj == data->view) { + // Event from view + if (event->type() == QEvent::ChildRemoved) { + // Might mean that viewport has changed + if (data->view->viewport() != data->viewport.data()) + data->setViewport(data->view->viewport()); + } + return QObject::eventFilter(obj, event); + } + + return AbstractViewInspector::eventFilter(obj, event); +} + +bool QDeclarativeViewInspector::leaveEvent(QEvent *event) +{ + data->clearHighlight(); + return AbstractViewInspector::leaveEvent(event); +} + +bool QDeclarativeViewInspector::mouseMoveEvent(QMouseEvent *event) +{ + QList selItems = data->selectableItems(event->pos()); + if (!selItems.isEmpty()) { + declarativeView()->setToolTip(currentTool()->titleForItem(selItems.first())); + } else { + declarativeView()->setToolTip(QString()); + } + + return AbstractViewInspector::mouseMoveEvent(event); +} + +void QDeclarativeViewInspector::reparentQmlObject(QObject *object, QObject *newParent) +{ + if (!newParent) + return; + + object->setParent(newParent); + QDeclarativeItem *newParentItem = qobject_cast(newParent); + QDeclarativeItem *item = qobject_cast(object); + if (newParentItem && item) + item->setParentItem(newParentItem); +} + +void QDeclarativeViewInspectorPrivate::_q_removeFromSelection(QObject *obj) +{ + QList items = selectedItems(); + if (QGraphicsItem *item = qobject_cast(obj)) + items.removeOne(item); + setSelectedItems(items); +} + +void QDeclarativeViewInspectorPrivate::setSelectedItemsForTools(const QList &items) +{ + foreach (const QWeakPointer &obj, currentSelection) { + if (QGraphicsItem *item = obj.data()) { + if (!items.contains(item)) { + QObject::disconnect(obj.data(), SIGNAL(destroyed(QObject*)), + this, SLOT(_q_removeFromSelection(QObject*))); + currentSelection.removeOne(obj); + } + } + } + + foreach (QGraphicsItem *item, items) { + if (QGraphicsObject *obj = item->toGraphicsObject()) { + if (!currentSelection.contains(obj)) { + QObject::connect(obj, SIGNAL(destroyed(QObject*)), + this, SLOT(_q_removeFromSelection(QObject*))); + currentSelection.append(obj); + } + } + } + + q->currentTool()->updateSelectedItems(); +} + +void QDeclarativeViewInspectorPrivate::setSelectedItems(const QList &items) +{ + QList > oldList = currentSelection; + setSelectedItemsForTools(items); + if (oldList != currentSelection) { + QList objectList; + foreach (const QWeakPointer &graphicsObject, currentSelection) { + if (graphicsObject) + objectList << graphicsObject.data(); + } + + q->sendCurrentObjects(objectList); + } +} + +QList QDeclarativeViewInspectorPrivate::selectedItems() const +{ + QList selection; + foreach (const QWeakPointer &selectedObject, currentSelection) { + if (selectedObject.data()) + selection << selectedObject.data(); + } + + return selection; +} + +void QDeclarativeViewInspector::setSelectedItems(QList items) +{ + data->setSelectedItems(items); +} + +QList QDeclarativeViewInspector::selectedItems() const +{ + return data->selectedItems(); +} + +QDeclarativeView *QDeclarativeViewInspector::declarativeView() const +{ + return data->view; +} + +void QDeclarativeViewInspectorPrivate::clearHighlight() +{ + boundingRectHighlighter->clear(); +} + +void QDeclarativeViewInspectorPrivate::highlight(const QList &items) +{ + if (items.isEmpty()) + return; + + QList objectList; + foreach (QGraphicsItem *item, items) { + QGraphicsItem *child = item; + + if (child) { + QGraphicsObject *childObject = child->toGraphicsObject(); + if (childObject) + objectList << childObject; + } + } + + boundingRectHighlighter->highlight(objectList); +} + +QList QDeclarativeViewInspectorPrivate::selectableItems( + const QPointF &scenePos) const +{ + QList itemlist = view->scene()->items(scenePos); + return filterForSelection(itemlist); +} + +QList QDeclarativeViewInspectorPrivate::selectableItems(const QPoint &pos) const +{ + QList itemlist = view->items(pos); + return filterForSelection(itemlist); +} + +QList QDeclarativeViewInspectorPrivate::selectableItems( + const QRectF &sceneRect, Qt::ItemSelectionMode selectionMode) const +{ + QList itemlist = view->scene()->items(sceneRect, selectionMode); + return filterForSelection(itemlist); +} + +void QDeclarativeViewInspectorPrivate::changeToSingleSelectTool() +{ + selectionTool->setRubberbandSelectionMode(false); + + changeToSelectTool(); + + emit q->selectToolActivated(); + q->sendCurrentTool(Constants::SelectionToolMode); +} + +void QDeclarativeViewInspectorPrivate::changeToSelectTool() +{ + if (q->currentTool() == selectionTool) + return; + + q->currentTool()->clear(); + q->setCurrentTool(selectionTool); + q->currentTool()->clear(); + q->currentTool()->updateSelectedItems(); +} + +void QDeclarativeViewInspectorPrivate::changeToMarqueeSelectTool() +{ + changeToSelectTool(); + selectionTool->setRubberbandSelectionMode(true); + + emit q->marqueeSelectToolActivated(); + q->sendCurrentTool(Constants::MarqueeSelectionToolMode); +} + +void QDeclarativeViewInspectorPrivate::changeToZoomTool() +{ + q->currentTool()->clear(); + q->setCurrentTool(zoomTool); + q->currentTool()->clear(); + + emit q->zoomToolActivated(); + q->sendCurrentTool(Constants::ZoomMode); +} + +void QDeclarativeViewInspectorPrivate::changeToColorPickerTool() +{ + if (q->currentTool() == colorPickerTool) + return; + + q->currentTool()->clear(); + q->setCurrentTool(colorPickerTool); + q->currentTool()->clear(); + + emit q->colorPickerActivated(); + q->sendCurrentTool(Constants::ColorPickerMode); +} + + +static bool isEditorItem(QGraphicsItem *item) +{ + return (item->type() == Constants::EditorItemType + || item->type() == Constants::ResizeHandleItemType + || item->data(Constants::EditorItemDataKey).toBool()); +} + +QList QDeclarativeViewInspectorPrivate::filterForSelection( + QList &itemlist) const +{ + foreach (QGraphicsItem *item, itemlist) { + if (isEditorItem(item)) + itemlist.removeOne(item); + } + + return itemlist; +} + +void QDeclarativeViewInspectorPrivate::_q_onStatusChanged(QDeclarativeView::Status status) +{ + if (status == QDeclarativeView::Ready) + q->sendReloaded(); +} + +// adjusts bounding boxes on edges of screen to be visible +QRectF QDeclarativeViewInspector::adjustToScreenBoundaries(const QRectF &boundingRectInSceneSpace) +{ + int marginFromEdge = 1; + QRectF boundingRect(boundingRectInSceneSpace); + if (qAbs(boundingRect.left()) - 1 < 2) + boundingRect.setLeft(marginFromEdge); + + QRect rect = data->view->rect(); + + if (boundingRect.right() >= rect.right()) + boundingRect.setRight(rect.right() - marginFromEdge); + + if (qAbs(boundingRect.top()) - 1 < 2) + boundingRect.setTop(marginFromEdge); + + if (boundingRect.bottom() >= rect.bottom()) + boundingRect.setBottom(rect.bottom() - marginFromEdge); + + return boundingRect; +} + +} // namespace QmlJSDebugger diff --git a/src/plugins/qmltooling/qmldbg_inspector/qdeclarativeviewinspector.h b/src/plugins/qmltooling/qmldbg_inspector/qdeclarativeviewinspector.h new file mode 100644 index 00000000..5fbf6c6a --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_inspector/qdeclarativeviewinspector.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEVIEWINSPECTOR_H +#define QDECLARATIVEVIEWINSPECTOR_H + +#include + +#include "qmlinspectorconstants.h" +#include "abstractviewinspector.h" + +#include +#include + +namespace QmlJSDebugger { + +class AbstractLiveEditTool; +class QDeclarativeViewInspectorPrivate; + +class QDeclarativeViewInspector : public AbstractViewInspector +{ + Q_OBJECT + +public: + explicit QDeclarativeViewInspector(QDeclarativeView *view, QObject *parent = 0); + ~QDeclarativeViewInspector(); + + // AbstractViewInspector + void changeCurrentObjects(const QList &objects); + void reloadView(); + void reparentQmlObject(QObject *object, QObject *newParent); + void changeTool(InspectorProtocol::Tool tool); + QWidget *viewWidget() const { return declarativeView(); } + QDeclarativeEngine *declarativeEngine() const; + + void setSelectedItems(QList items); + QList selectedItems() const; + + QDeclarativeView *declarativeView() const; + + QRectF adjustToScreenBoundaries(const QRectF &boundingRectInSceneSpace); + +protected: + bool eventFilter(QObject *obj, QEvent *event); + + bool leaveEvent(QEvent *); + bool mouseMoveEvent(QMouseEvent *event); + + AbstractLiveEditTool *currentTool() const; + +private: + Q_DISABLE_COPY(QDeclarativeViewInspector) + + inline QDeclarativeViewInspectorPrivate *d_func() { return data.data(); } + QScopedPointer data; + friend class QDeclarativeViewInspectorPrivate; + friend class AbstractLiveEditTool; +}; + +} // namespace QmlJSDebugger + +#endif // QDECLARATIVEVIEWINSPECTOR_H diff --git a/src/plugins/qmltooling/qmldbg_inspector/qdeclarativeviewinspector_p.h b/src/plugins/qmltooling/qmldbg_inspector/qdeclarativeviewinspector_p.h new file mode 100644 index 00000000..7093e4fc --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_inspector/qdeclarativeviewinspector_p.h @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEVIEWINSPECTOR_P_H +#define QDECLARATIVEVIEWINSPECTOR_P_H + +#include "qdeclarativeviewinspector.h" + +#include +#include + +#include "QtDeclarative/private/qdeclarativeinspectorservice_p.h" + +namespace QmlJSDebugger { + +class QDeclarativeViewInspector; +class LiveSelectionTool; +class ZoomTool; +class ColorPickerTool; +class LiveLayerItem; +class BoundingRectHighlighter; +class AbstractLiveEditTool; + +class QDeclarativeViewInspectorPrivate : public QObject +{ + Q_OBJECT +public: + QDeclarativeViewInspectorPrivate(QDeclarativeViewInspector *); + ~QDeclarativeViewInspectorPrivate(); + + QDeclarativeView *view; + QDeclarativeViewInspector *q; + QWeakPointer viewport; + + QList > currentSelection; + + LiveSelectionTool *selectionTool; + ZoomTool *zoomTool; + ColorPickerTool *colorPickerTool; + LiveLayerItem *manipulatorLayer; + + BoundingRectHighlighter *boundingRectHighlighter; + + void setViewport(QWidget *widget); + + void clearEditorItems(); + void changeToSelectTool(); + QList filterForSelection(QList &itemlist) const; + + QList selectableItems(const QPoint &pos) const; + QList selectableItems(const QPointF &scenePos) const; + QList selectableItems(const QRectF &sceneRect, Qt::ItemSelectionMode selectionMode) const; + + void setSelectedItemsForTools(const QList &items); + void setSelectedItems(const QList &items); + QList selectedItems() const; + + void clearHighlight(); + void highlight(const QList &item); + inline void highlight(QGraphicsObject *item) + { highlight(QList() << item); } + + void changeToSingleSelectTool(); + void changeToMarqueeSelectTool(); + void changeToZoomTool(); + void changeToColorPickerTool(); + +public slots: + void _q_onStatusChanged(QDeclarativeView::Status status); + + void _q_removeFromSelection(QObject *); + +public: + static QDeclarativeViewInspectorPrivate *get(QDeclarativeViewInspector *v) { return v->d_func(); } +}; + +} // namespace QmlJSDebugger + +#endif // QDECLARATIVEVIEWINSPECTOR_P_H diff --git a/src/plugins/qmltooling/qmldbg_inspector/qmldbg_inspector.pro b/src/plugins/qmltooling/qmldbg_inspector/qmldbg_inspector.pro new file mode 100644 index 00000000..e441a781 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_inspector/qmldbg_inspector.pro @@ -0,0 +1,49 @@ +TARGET = qmldbg_inspector +QT += declarative + +include(../../qpluginbase.pri) + +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/qmltooling +QTDIR_build:REQUIRES += "contains(QT_CONFIG, declarative)" + +SOURCES += \ + abstractviewinspector.cpp \ + qdeclarativeinspectorplugin.cpp \ + qdeclarativeviewinspector.cpp \ + editor/abstractliveedittool.cpp \ + editor/liveselectiontool.cpp \ + editor/livelayeritem.cpp \ + editor/livesingleselectionmanipulator.cpp \ + editor/liverubberbandselectionmanipulator.cpp \ + editor/liveselectionrectangle.cpp \ + editor/liveselectionindicator.cpp \ + editor/boundingrecthighlighter.cpp \ + editor/subcomponentmasklayeritem.cpp \ + editor/zoomtool.cpp \ + editor/colorpickertool.cpp \ + abstracttool.cpp + +HEADERS += \ + abstractviewinspector.h \ + qdeclarativeinspectorplugin.h \ + qdeclarativeinspectorprotocol.h \ + qdeclarativeviewinspector.h \ + qdeclarativeviewinspector_p.h \ + qmlinspectorconstants.h \ + editor/abstractliveedittool.h \ + editor/liveselectiontool.h \ + editor/livelayeritem.h \ + editor/livesingleselectionmanipulator.h \ + editor/liverubberbandselectionmanipulator.h \ + editor/liveselectionrectangle.h \ + editor/liveselectionindicator.h \ + editor/boundingrecthighlighter.h \ + editor/subcomponentmasklayeritem.h \ + editor/zoomtool.h \ + editor/colorpickertool.h \ + abstracttool.h + +target.path += $$[QT_INSTALL_PLUGINS]/qmltooling +INSTALLS += target + +symbian:TARGET.UID3=0x20031E90 diff --git a/src/plugins/qmltooling/qmldbg_inspector/qmlinspectorconstants.h b/src/plugins/qmltooling/qmldbg_inspector/qmlinspectorconstants.h new file mode 100644 index 00000000..7377bc65 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_inspector/qmlinspectorconstants.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMLINSPECTORCONSTANTS_H +#define QMLINSPECTORCONSTANTS_H + +#include + +namespace QmlJSDebugger { +namespace Constants { + +enum DesignTool { + NoTool = 0, + SelectionToolMode = 1, + MarqueeSelectionToolMode = 2, + MoveToolMode = 3, + ResizeToolMode = 4, + ColorPickerMode = 5, + ZoomMode = 6 +}; + +static const int DragStartTime = 50; + +static const int DragStartDistance = 20; + +static const double ZoomSnapDelta = 0.04; + +static const int EditorItemDataKey = 1000; + +enum GraphicsItemTypes { + EditorItemType = 0xEAAA, + ResizeHandleItemType = 0xEAEA +}; + + +} // namespace Constants +} // namespace QmlJSDebugger + +#endif // QMLINSPECTORCONSTANTS_H diff --git a/src/plugins/qmltooling/qmldbg_ost/qmldbg_ost.pro b/src/plugins/qmltooling/qmldbg_ost/qmldbg_ost.pro new file mode 100644 index 00000000..27488894 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_ost/qmldbg_ost.pro @@ -0,0 +1,21 @@ +TARGET = qmldbg_ost +QT += declarative network + +include(../../qpluginbase.pri) + +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/qmltooling +QTDIR_build:REQUIRES += "contains(QT_CONFIG, declarative)" + +SOURCES += \ + qmlostplugin.cpp \ + qostdevice.cpp + +HEADERS += \ + qmlostplugin.h \ + qostdevice.h \ + usbostcomm.h + +target.path += $$[QT_INSTALL_PLUGINS]/qmltooling +INSTALLS += target + +symbian:TARGET.UID3=0x20031E92 diff --git a/src/plugins/qmltooling/qmldbg_ost/qmlostplugin.cpp b/src/plugins/qmltooling/qmldbg_ost/qmlostplugin.cpp new file mode 100644 index 00000000..d999de43 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_ost/qmlostplugin.cpp @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmlostplugin.h" +#include "qostdevice.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +static const TInt KQmlOstProtocolId = 0x94; + +class QmlOstPluginPrivate { +public: + QmlOstPluginPrivate(); + + QOstDevice *ost; + QPacketProtocol *protocol; + QDeclarativeDebugServer *debugServer; +}; + +QmlOstPluginPrivate::QmlOstPluginPrivate() : + ost(0), + protocol(0), + debugServer(0) +{ +} + +QmlOstPlugin::QmlOstPlugin() : + d_ptr(new QmlOstPluginPrivate) +{ +} + +QmlOstPlugin::~QmlOstPlugin() +{ + delete d_ptr; +} + +void QmlOstPlugin::setServer(QDeclarativeDebugServer *server) +{ + Q_D(QmlOstPlugin); + d->debugServer = server; +} + +bool QmlOstPlugin::isConnected() const +{ + Q_D(const QmlOstPlugin); + return d->ost && d->ost->isOpen(); +} + +void QmlOstPlugin::send(const QByteArray &message) +{ + Q_D(QmlOstPlugin); + + if (!isConnected()) + return; + + QPacket pack; + pack.writeRawData(message.data(), message.length()); + + d->protocol->send(pack); + //d->socket->flush(); +} + +void QmlOstPlugin::disconnect() +{ + Q_D(QmlOstPlugin); + + delete d->protocol; + d->protocol = 0; +} + +bool QmlOstPlugin::waitForMessage() +{ + Q_D(QmlOstPlugin); + return d->protocol->waitForReadyRead(-1); +} + +void QmlOstPlugin::setPort(int port, bool block) +{ + Q_UNUSED(port); + Q_UNUSED(block); + + Q_D(QmlOstPlugin); + + d->ost = new QOstDevice(this); + bool ok = d->ost->open(KQmlOstProtocolId); + if (!ok) { + if (d->ost->errorString().length()) + qDebug("Error from QOstDevice: %s", qPrintable(d->ost->errorString())); + qWarning("QDeclarativeDebugServer: Unable to listen on OST"); // This message is part of the signalling - do not change the format! + return; + } + d->protocol = new QPacketProtocol(d->ost, this); + QObject::connect(d->protocol, SIGNAL(readyRead()), this, SLOT(readyRead())); + qWarning("QDeclarativeDebugServer: Waiting for connection via OST"); // This message is part of the signalling - do not change the format! +} + +void QmlOstPlugin::readyRead() +{ + Q_D(QmlOstPlugin); + QPacket packet = d->protocol->read(); + + QByteArray content = packet.data(); + d->debugServer->receiveMessage(content); +} + +Q_EXPORT_PLUGIN2(qmlostplugin, QmlOstPlugin) + +QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/qmldbg_ost/qmlostplugin.h b/src/plugins/qmltooling/qmldbg_ost/qmlostplugin.h new file mode 100644 index 00000000..dc32e2e7 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_ost/qmlostplugin.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMLOSTPLUGIN_H +#define QMLOSTPLUGIN_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeDebugServer; +class QmlOstPluginPrivate; + +class QmlOstPlugin : public QObject, public QDeclarativeDebugServerConnection +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlOstPlugin) + Q_DISABLE_COPY(QmlOstPlugin) + Q_INTERFACES(QDeclarativeDebugServerConnection) + + +public: + QmlOstPlugin(); + ~QmlOstPlugin(); + + void setServer(QDeclarativeDebugServer *server); + void setPort(int port, bool bock); + + bool isConnected() const; + void send(const QByteArray &message); + void disconnect(); + bool waitForMessage(); + +private Q_SLOTS: + void readyRead(); + +private: + QmlOstPluginPrivate *d_ptr; +}; + +QT_END_NAMESPACE + +#endif // QMLOSTPLUGIN_H diff --git a/src/plugins/qmltooling/qmldbg_ost/qostdevice.cpp b/src/plugins/qmltooling/qmldbg_ost/qostdevice.cpp new file mode 100644 index 00000000..31030585 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_ost/qostdevice.cpp @@ -0,0 +1,220 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qostdevice.h" +#include + +#include "usbostcomm.h" + +class QOstDevicePrivate : public CActive +{ + QOstDevice* q_ptr; + Q_DECLARE_PUBLIC(QOstDevice) + +public: + QOstDevicePrivate() : CActive(CActive::EPriorityStandard) { + CActiveScheduler::Add(this); + } + ~QOstDevicePrivate() { + Cancel(); + } + + TInt& AoFlags() { return ((TInt*)&iStatus)[1]; } + +private: + void RunL(); + void DoCancel(); + +private: + RUsbOstComm ost; + TBuf8<4096> readBuf; + QByteArray dataBuf; + TBool inReadyRead; +}; + +QOstDevice::QOstDevice(QObject *parent) : + QIODevice(parent), d_ptr(new QOstDevicePrivate) +{ + d_ptr->q_ptr = this; +} + +QOstDevice::~QOstDevice() +{ + close(); + delete d_ptr; +} + +bool QOstDevice::open(int ostProtocolId) +{ + if (isOpen()) + return false; + + Q_D(QOstDevice); + TInt err = d->ost.Connect(); + if (!err) err = d->ost.Open(); + const TVersion KRequiredVersion(1,1,0); + TVersion version = d->ost.Version(); + if (version.iMajor < KRequiredVersion.iMajor || + (version.iMajor == KRequiredVersion.iMajor && version.iMinor < KRequiredVersion.iMinor)) { + setErrorString("CODA version too old. At least version 4.0.18 (without TRK) is required."); + return false; + } + + if (!err) err = d->ost.RegisterProtocolID((TOstProtIds)ostProtocolId, EFalse); + if (!err) { + d->ost.ReadMessage(d->iStatus, d->readBuf); + d->SetActive(); + return QIODevice::open(ReadWrite | Unbuffered); + } + return false; +} + +void QOstDevicePrivate::RunL() +{ + Q_Q(QOstDevice); + //qDebug("QOstDevice received %d bytes q=%x", readBuf.Size(), q); + if (iStatus == KErrNone) { + QByteArray data = QByteArray::fromRawData((const char*)readBuf.Ptr(), readBuf.Size()); + dataBuf.append(data); + + readBuf.Zero(); + ost.ReadMessage(iStatus, readBuf); + SetActive(); + + if (!inReadyRead) { + inReadyRead = true; + emit q->readyRead(); + inReadyRead = false; + } + } else { + q->setErrorString(QString("Error %1 from RUsbOstComm::ReadMessage()").arg(iStatus.Int())); + } + //qDebug("-QOstDevicePrivate RunL"); +} + +void QOstDevicePrivate::DoCancel() +{ + ost.ReadCancel(); +} + +void QOstDevice::close() +{ + Q_D(QOstDevice); + QIODevice::close(); + d->Cancel(); + // RDbgTrcComm::Close isn't safe to call when not open, sigh + if (d->ost.Handle()) { + d->ost.Close(); + } +} + +qint64 QOstDevice::readData(char *data, qint64 maxSize) +{ + Q_D(QOstDevice); + if (d->dataBuf.length() == 0 && !d->IsActive()) + return -1; + qint64 available = qMin(maxSize, (qint64)d->dataBuf.length()); + memcpy(data, d->dataBuf.constData(), available); + d->dataBuf.remove(0, available); + return available; +} + +static const TInt KMaxOstPacketLen = 4096; + +qint64 QOstDevice::writeData(const char *data, qint64 maxSize) +{ + Q_D(QOstDevice); + TPtrC8 ptr((const TUint8*)data, (TInt)maxSize); + while (ptr.Length()) { + TPtrC8 fragment = ptr.Left(qMin(ptr.Length(), KMaxOstPacketLen)); + //qDebug("QOstDevice writing %d bytes", fragment.Length()); + TRequestStatus stat; + d->ost.WriteMessage(stat, fragment); + User::WaitForRequest(stat); + if (stat.Int() != KErrNone) { + setErrorString(QString("Error %1 from RUsbOstComm::WriteMessage()").arg(stat.Int())); + return -1; + } + ptr.Set(ptr.Mid(fragment.Length())); + } + emit bytesWritten(maxSize); //TODO does it matter this is emitted synchronously? + //qDebug("QOstDevice wrote %d bytes", ptr.Size()); + return maxSize; +} + +qint64 QOstDevice::bytesAvailable() const +{ + Q_D(const QOstDevice); + return d->dataBuf.length(); +} + +bool QOstDevice::waitForReadyRead(int msecs) +{ + Q_D(QOstDevice); + if (msecs >= 0) { + RTimer timer; + TInt err = timer.CreateLocal(); + if (err) return false; + TRequestStatus timeoutStat; + timer.After(timeoutStat, msecs*1000); + User::WaitForRequest(timeoutStat, d->iStatus); + if (timeoutStat != KRequestPending) { + // Timed out + timer.Close(); + return false; + } else { + // We got data, so cancel timer + timer.Cancel(); + User::WaitForRequest(timeoutStat); + timer.Close(); + // And drop through + } + } else { + // Just wait forever for data + User::WaitForRequest(d->iStatus); + } + + // If we get here we have data + TInt err = d->iStatus.Int(); + d->AoFlags() &= ~3; // This is necessary to clean up the scheduler as you're not supposed to bypass it like this + TRAP_IGNORE(d->RunL()); + return err == KErrNone; +} diff --git a/src/plugins/qmltooling/qmldbg_ost/qostdevice.h b/src/plugins/qmltooling/qmldbg_ost/qostdevice.h new file mode 100644 index 00000000..0601bc3b --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_ost/qostdevice.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QOSTDEVICE_H +#define QOSTDEVICE_H + +#include + +QT_BEGIN_NAMESPACE + +class QOstDevicePrivate; + +class QOstDevice : public QIODevice +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QOstDevice) + Q_DISABLE_COPY(QOstDevice) + +public: + explicit QOstDevice(QObject *parent=0); + ~QOstDevice(); + + bool open(int ostProtocolId); + void close(); + + bool waitForReadyRead(int msecs); + qint64 bytesAvailable() const; + +protected: + qint64 readData(char *data, qint64 maxSize); + qint64 writeData(const char *data, qint64 maxSize); + +private: + QOstDevicePrivate* d_ptr; +}; + +QT_END_NAMESPACE + +#endif // QOSTDEVICE_H diff --git a/src/plugins/qmltooling/qmldbg_ost/usbostcomm.h b/src/plugins/qmltooling/qmldbg_ost/usbostcomm.h new file mode 100644 index 00000000..552543bf --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_ost/usbostcomm.h @@ -0,0 +1,191 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef USBHOSTCOMM_H +#define USBHOSTCOMM_H + +// Based on the official usbostrouter header, modified to remove dependancy on +// the client DLL + +#include + +typedef int TOstProtIds; + +class RUsbOstComm : public RSessionBase +{ +public: + RUsbOstComm(); + TInt Connect(); + TInt Disconnect(); + TInt Open(); + TInt Close(); + TInt RegisterProtocolID(TOstProtIds aId, TBool aNeedHeader); + void ReadMessage(TRequestStatus& aStatus, TDes8& aDes); + TInt ReadCancel(); + void WriteMessage(TRequestStatus& aStatus, const TDesC8& aDes, TBool aHasHeader=EFalse); + TVersion Version() const; + +private: + enum TUsbOstCmdCode + { + EUsbOstCmdCodeFirst, + EUsbOstCmdConnect, + EUsbOstCmdDisconnect, + EUsbOstCmdCodeGetAcmConfig, + EUsbOstCmdCodeSetAcmConfig, + EUsbOstCmdCodeOpen, + EUsbOstCmdCodeClose, + EUsbOstCmdCodeRegisterId, + EUsbOstCmdCodeRegisterIds, + EUsbOstCmdCodeUnRegisterId, + EUsbOstCmdCodeUnRegisterIds, + EUsbOstCmdCodeReadMsg, + EUsbOstCmdCodeReadCancel, + EUsbOstCmdCodeWriteMsg, + EUsbOstCmdCodeWriteCancel, + EUsbOstCmdCodeLast + }; +}; + +RUsbOstComm::RUsbOstComm() +{ +} + +TInt RUsbOstComm::Connect() +{ + _LIT(KUsbOstServerName, "!UsbOstRouter"); + _LIT(KUsbOstServerImageName, "usbostrouter"); + const TUid KUsbOstServerUid = { 0x200170BE }; + TInt startupAttempts = 2; + for(;;) { + TInt ret = CreateSession(KUsbOstServerName, TVersion(1,0,0)); + if (ret != KErrNotFound && ret != KErrServerTerminated) { + return ret; + } + + if (startupAttempts-- == 0) { + return ret; + } + + RProcess server; + ret = server.Create(KUsbOstServerImageName, KNullDesC, KUsbOstServerUid); + if (ret != KErrNone) + return ret; + + TRequestStatus serverDiedRequestStatus; + server.Rendezvous(serverDiedRequestStatus); + + if (serverDiedRequestStatus != KRequestPending) { + // Abort startup + server.Kill(KErrNone); + } else { + // Logon OK - start the server + server.Resume(); + } + User::WaitForRequest(serverDiedRequestStatus); + ret = (server.ExitType() == EExitPanic) ? KErrGeneral : serverDiedRequestStatus.Int(); + server.Close(); + + if (ret != KErrNone && ret != KErrAlreadyExists) { + return ret; + } + } +} + +TInt RUsbOstComm::Disconnect() +{ + return SendReceive(EUsbOstCmdDisconnect); +} + +TInt RUsbOstComm::Open() +{ + return SendReceive(EUsbOstCmdCodeOpen); +} + +TInt RUsbOstComm::Close() +{ + TInt err = SendReceive(EUsbOstCmdCodeClose); + RHandleBase::Close(); + return err; +} + +TInt RUsbOstComm::RegisterProtocolID(const TOstProtIds aId, TBool aNeedHeader) +{ + TIpcArgs args(aId, aNeedHeader); + return SendReceive(EUsbOstCmdCodeRegisterId, args); +} + +void RUsbOstComm::ReadMessage(TRequestStatus& aStatus, TDes8& aDes) +{ + TIpcArgs args(aDes.MaxLength(), &aDes); + SendReceive(EUsbOstCmdCodeReadMsg, args, aStatus); +} + +TInt RUsbOstComm::ReadCancel() +{ + return SendReceive(EUsbOstCmdCodeReadCancel); +} + +void RUsbOstComm::WriteMessage(TRequestStatus& aStatus, const TDesC8& aDes, TBool aHasHeader) +{ + TIpcArgs args(aHasHeader, aDes.Length(), &aDes); + SendReceive(EUsbOstCmdCodeWriteMsg, args, aStatus); +} + +typedef TVersion (*TVersionFunction)(const RUsbOstComm*); +const TInt KVersionOrdinal = 17; + +TVersion RUsbOstComm::Version() const +{ + // This function has to go to the DLL, unfortunately + TVersion result; // Return 0.0.0 on any error + RLibrary lib; + TInt err = lib.Load(_L("usbostcomm")); + if (err) return result; + + TLibraryFunction fn = lib.Lookup(KVersionOrdinal); + if (fn) + result = ((TVersionFunction)fn)(this); + lib.Close(); + return result; +} + +#endif //USBHOSTCOMM_H diff --git a/src/plugins/qmltooling/qmldbg_tcp/qmldbg_tcp.pro b/src/plugins/qmltooling/qmldbg_tcp/qmldbg_tcp.pro new file mode 100644 index 00000000..e8ab962d --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_tcp/qmldbg_tcp.pro @@ -0,0 +1,18 @@ +TARGET = qmldbg_tcp +QT += declarative network + +include(../../qpluginbase.pri) + +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/qmltooling +QTDIR_build:REQUIRES += "contains(QT_CONFIG, declarative)" + +SOURCES += \ + qtcpserverconnection.cpp + +HEADERS += \ + qtcpserverconnection.h + +target.path += $$[QT_INSTALL_PLUGINS]/qmltooling +INSTALLS += target + +symbian:TARGET.UID3=0x20031E90 diff --git a/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.cpp b/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.cpp new file mode 100644 index 00000000..01aa97a3 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.cpp @@ -0,0 +1,198 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtcpserverconnection.h" + +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class QTcpServerConnectionPrivate { +public: + QTcpServerConnectionPrivate(); + + int port; + bool block; + QTcpSocket *socket; + QPacketProtocol *protocol; + QTcpServer *tcpServer; + + QDeclarativeDebugServer *debugServer; +}; + +QTcpServerConnectionPrivate::QTcpServerConnectionPrivate() : + port(0), + block(false), + socket(0), + protocol(0), + tcpServer(0), + debugServer(0) +{ +} + +QTcpServerConnection::QTcpServerConnection() : + d_ptr(new QTcpServerConnectionPrivate) +{ + +} + +QTcpServerConnection::~QTcpServerConnection() +{ + delete d_ptr; +} + +void QTcpServerConnection::setServer(QDeclarativeDebugServer *server) +{ + Q_D(QTcpServerConnection); + d->debugServer = server; +} + +bool QTcpServerConnection::isConnected() const +{ + Q_D(const QTcpServerConnection); + return d->socket && d->socket->state() == QTcpSocket::ConnectedState; +} + +void QTcpServerConnection::send(const QByteArray &message) +{ + Q_D(QTcpServerConnection); + + if (!isConnected() + || !d->protocol || !d->socket) + return; + + QPacket pack; + pack.writeRawData(message.data(), message.length()); + + d->protocol->send(pack); + d->socket->flush(); +} + +void QTcpServerConnection::disconnect() +{ + Q_D(QTcpServerConnection); + + // protocol might still be processing packages at this point + d->protocol->deleteLater(); + d->protocol = 0; + d->socket->deleteLater(); + d->socket = 0; +} + +bool QTcpServerConnection::waitForMessage() +{ + Q_D(QTcpServerConnection); + if (d->protocol->packetsAvailable() > 0) { + QPacket packet = d->protocol->read(); + d->debugServer->receiveMessage(packet.data()); + return true; + } else { + return d->protocol->waitForReadyRead(-1); + } +} + +void QTcpServerConnection::setPort(int port, bool block) +{ + Q_D(QTcpServerConnection); + d->port = port; + d->block = block; + + listen(); + if (block) + d->tcpServer->waitForNewConnection(-1); +} + +void QTcpServerConnection::listen() +{ + Q_D(QTcpServerConnection); + + d->tcpServer = new QTcpServer(this); + QObject::connect(d->tcpServer, SIGNAL(newConnection()), this, SLOT(newConnection())); + if (d->tcpServer->listen(QHostAddress::Any, d->port)) { + qDebug("QDeclarativeDebugServer: Waiting for connection on port %d...", d->port); + } else { + qWarning("QDeclarativeDebugServer: Unable to listen on port %d", d->port); + } +} + + +void QTcpServerConnection::readyRead() +{ + Q_D(QTcpServerConnection); + if (!d->protocol) + return; + + while (d->protocol->packetsAvailable() > 0) { + QPacket packet = d->protocol->read(); + d->debugServer->receiveMessage(packet.data()); + } +} + +void QTcpServerConnection::newConnection() +{ + Q_D(QTcpServerConnection); + + if (d->socket) { + qWarning("QDeclarativeDebugServer: Another client is already connected"); + QTcpSocket *faultyConnection = d->tcpServer->nextPendingConnection(); + delete faultyConnection; + return; + } + + d->socket = d->tcpServer->nextPendingConnection(); + d->socket->setParent(this); + d->protocol = new QPacketProtocol(d->socket, this); + QObject::connect(d->protocol, SIGNAL(readyRead()), this, SLOT(readyRead())); + + if (d->block) { + d->protocol->waitForReadyRead(-1); + } +} + +Q_EXPORT_PLUGIN2(tcpserver, QTcpServerConnection) + +QT_END_NAMESPACE + diff --git a/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.h b/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.h new file mode 100644 index 00000000..d3b23336 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTCPSERVERCONNECTION_H +#define QTCPSERVERCONNECTION_H + +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeDebugServer; +class QTcpServerConnectionPrivate; +class QTcpServerConnection : public QObject, public QDeclarativeDebugServerConnection +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QTcpServerConnection) + Q_DISABLE_COPY(QTcpServerConnection) + Q_INTERFACES(QDeclarativeDebugServerConnection) + + +public: + QTcpServerConnection(); + ~QTcpServerConnection(); + + void setServer(QDeclarativeDebugServer *server); + void setPort(int port, bool bock); + + bool isConnected() const; + void send(const QByteArray &message); + void disconnect(); + bool waitForMessage(); + + void listen(); + void waitForConnection(); + +private Q_SLOTS: + void readyRead(); + void newConnection(); + +private: + QTcpServerConnectionPrivate *d_ptr; +}; + +QT_END_NAMESPACE + +#endif // QTCPSERVERCONNECTION_H diff --git a/src/plugins/qmltooling/qmltooling.pro b/src/plugins/qmltooling/qmltooling.pro new file mode 100644 index 00000000..8220109e --- /dev/null +++ b/src/plugins/qmltooling/qmltooling.pro @@ -0,0 +1,4 @@ +TEMPLATE = subdirs + +SUBDIRS = qmldbg_tcp qmldbg_inspector +symbian:SUBDIRS += qmldbg_ost diff --git a/src/plugins/qpluginbase.pri b/src/plugins/qpluginbase.pri new file mode 100644 index 00000000..b2a7cf29 --- /dev/null +++ b/src/plugins/qpluginbase.pri @@ -0,0 +1,41 @@ +TEMPLATE = lib +isEmpty(QT_MAJOR_VERSION) { + VERSION=4.8.0 +} else { + VERSION=$${QT_MAJOR_VERSION}.$${QT_MINOR_VERSION}.$${QT_PATCH_VERSION} +} +CONFIG += qt plugin + +win32|mac:!wince*:!win32-msvc:!macx-xcode:CONFIG += debug_and_release +TARGET = $$qtLibraryTarget($$TARGET) +contains(QT_CONFIG, reduce_exports):CONFIG += hide_symbols + +include(../qt_targets.pri) + +wince*:LIBS += $$QMAKE_LIBS_GUI + +symbian: { + TARGET.EPOCALLOWDLLDATA=1 + TARGET.CAPABILITY = All -Tcb + TARGET = $${TARGET}$${QT_LIBINFIX} + load(armcc_warnings) + + # Make partial upgrade SIS file for Qt plugin dll's + # Partial upgrade SIS file + vendorinfo = \ + "; Localised Vendor name" \ + "%{\"Nokia\"}" \ + " " \ + "; Unique Vendor name" \ + ":\"Nokia, Qt\"" \ + " " + isEmpty(QT_LIBINFIX): PARTIAL_UPGRADE_UID = 0x2001E61C + else: PARTIAL_UPGRADE_UID = 0xE001E61C + + pu_header = "; Partial upgrade package for testing $${TARGET} changes without reinstalling everything" \ + "$${LITERAL_HASH}{\"$${TARGET}\"}, ($$PARTIAL_UPGRADE_UID), $${QT_MAJOR_VERSION},$${QT_MINOR_VERSION},$${QT_PATCH_VERSION}, TYPE=PU,RU" + partial_upgrade.pkg_prerules = pu_header vendorinfo + partial_upgrade.files = $$QMAKE_LIBDIR_QT/$${TARGET}.dll + partial_upgrade.path = c:/sys/bin + DEPLOYMENT += partial_upgrade +} diff --git a/src/qt_targets.pri b/src/qt_targets.pri new file mode 100644 index 00000000..ec15869b --- /dev/null +++ b/src/qt_targets.pri @@ -0,0 +1,4 @@ +QMAKE_TARGET_COMPANY = Nokia Corporation and/or its subsidiary(-ies) +QMAKE_TARGET_PRODUCT = Qt4 +QMAKE_TARGET_DESCRIPTION = C++ application development framework. +QMAKE_TARGET_COPYRIGHT = Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). -- cgit v1.2.3